diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..1bb8032 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,10 @@ +root = true + +[*] +end_of_line = lf +insert_final_newline = true +indent_style = space +indent_size = 2 + +[*.txt] +end_of_line = crlf diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0707290 --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +.vscode +bin +etc +lib +node_modules + +docs +!docs/compile.bat +!docs/LICENSE +!docs/update.bat diff --git a/.vscodeignore b/.vscodeignore new file mode 100644 index 0000000..5b93824 --- /dev/null +++ b/.vscodeignore @@ -0,0 +1,30 @@ +.vscode/** +.gitignore +.travis.yml +tsconfig.json +contributing.md +package-lock.json + +bin +etc +src +test + +docs/** +!docs/cache +!docs/LICENSE + +node_modules/** +!node_modules/fast-levenshtein +!node_modules/handlebars +!node_modules/long +!node_modules/promise-queue +!node_modules/sax +!node_modules/semver +!node_modules/vscode-languageclient +!node_modules/vscode-languageserver +!node_modules/vscode-languageserver-protocol/** +!node_modules/vscode-languageserver-types/** +!node_modules/vscode-jsonrpc/** +!node_modules/xml2js +!node_modules/xmlbuilder diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..9287bcc --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,3 @@ +# Version 1.0.0 + +Initial release diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..83be731 --- /dev/null +++ b/LICENSE @@ -0,0 +1,15 @@ +MIT License + +Copyright (c) 2018 Sebastian Lenz + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files +(the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..98b0351 --- /dev/null +++ b/README.md @@ -0,0 +1,157 @@ +# Divinity Engine Script Support for VS Code + +This extensions enables language features for the scripting language Osiris found in The Divinity Engine 2 based games. + +## Getting started + +### Installation + +- Open the extensions tab (usually the last button in the action bar. You can lso use the shortcut Ctrl+Shift+X) +- Search for "divinity-vscode" +- On the plugin page, click the "Install", then the "Enable" button + +### Open project + +- Before editing a project for the first time in VS Code, make sure you have a fresh built of the project. + - Start the Glasses editor and switch to your project. + - Open the story editor and choose "File" > "Build". +- In VS Code, select "File" > "Open Folder..." and select the data folder of your project. The path usually looks something like this: `../Divinity Original Sin 2/Data/Mods/MyModeName_########-####-####-####-############` +- After a moment the story outline panel should show up beneath your files and you are ready to go. + +## Features + +### Story outline + +The story outline brings you the familiar tree view from the native editor to VS Code. You can add, delete, move and rename goals using it. + +![Story outline](resources/features/story-outline.gif) + +### Code analysis + +The extension analyzes your code while you type and immediately shows you errors and problems. + +![Code analysis](resources/features/code-analysis.gif) + +### Completion + +Code completion shows you matching symbols for your current input, it will show up automatically when typing or by pressing Shift+Space. The suggestions are context sensitive and only show you the symbols that are currently available. + +![Code completion](resources/features/completion.gif) + +### Find all references and go to definition + +Bring up a list of all usages of a procedure, query or database by pressing Shift+F12 or selecting the command `Find All References` from the context menu. You can also jump to the definition by pressing F12 or using the command `Go to Definition`. + +![Goto definition and find all references](resources/features/find-references.gif) + +### Document structure + +Use the outline panel to get a broad overview over your current goal or jump to individial rules using the the shortcut Ctrl+Shift+O. + +![Document structure](resources/features/document-structure.gif) + +### Hover and signature help + +Quickly gain information about the symbols on screen, move your mouse over them and the extension will show you a short info. The signature help shows up everytime you start writing a function call (when pressing `(` or with the shortcut Ctrl+Shift+Space) and shows you a preview of the parameters. + +![Hover and signature help](resources/features/hover.gif) + +### Rename + +You can rename custom procedures, databases and queries as well as variables and GUID references. Press F2 while the cursor is above the symbol you want to rename or use the rename command from the context menu. + +![Rename](resources/features/rename.gif) + +### API Explorer + +Built in browser for the complete API with contents fetched from the Divinity Wiki when available. Open the command palette (e.g. press Ctrl+Shift+P) and search for the command `Show API explorer` to open the API explorer. + +![API explorer](resources/features/api-explorer.gif) + +### Custom documentation support + +The code completion, hovers and the signature help will show you short snippet from the Wiki to assist you. You can add documentation to your own procedures and queries by placing a JSDoc comment above them. + +## Differences to the built in editor + +Unlike the built in editor VS Code will not display three different regions when editing story goals. Instead you'll see the complete source file of each goal. The basic file structure looks like this: + +```javascript +Version 1 +SubGoalCombiner SGC_AND +INITSECTION + +// Init section contents go here + +KBSECTION + +// KB section contents go here + +EXITSECTION + +// Exit section contents go here + +ENDEXITSECTION +ParentTargetEdge "NameOfParentGoal" +``` + +You can safely ignore the header section, it will be the same for all your goals. The three main sections are self explonary and are the equvalent of the three panels you see in the built in editor. Beneath the exit section you'll notice the command `ParentTargetEdge`, this command tells the game the name of the parent goal. You may use the story outline to move goals around or you can directly edit the structure here. + +## Feedback + +All feedback is welcome. Please head over to the GitHub page of this project and start a new issue if you find any problems: +https://github.com/sebastian-lenz/divinity-vscode + +## License + +This extension is released under the MIT License. + +### Packaged software + +This extension uses several external dependencies, the following modules +are part of the installed extension: + +- **djsdoc** + Copyright (c) 2016-present Zeit, Inc. + GPL v3.0 License + https://github.com/EYHN/djsdoc +- **fast-levenshtein** + Copyright (c) 2013 Ramesh Nair + MIT License + https://github.com/hiddentao/fast-levenshtein +- **Handlebars.js** + Copyright (C) 2011-2017 by Yehuda Katz + MIT License + https://github.com/wycats/handlebars.js +- **long.js** + Copyright (C) Daniel Wirtz + Apache License Version 2.0 + https://github.com/dcodeIO/long.js +- **lslib** + Copyright (c) 2015 Norbyte + MIT License + https://github.com/Norbyte/lslib +- **promise-queue** + Copyright (c) 2013 Mikhail Davydov and other contributors + MIT License + https://github.com/promise-queue/promise-queue +- **sax** + Copyright (c) Isaac Z. Schlueter and Contributors + ISC License + https://github.com/isaacs/sax-js/blob/master/LICENSE +- **semver** + Copyright (c) Isaac Z. Schlueter and Contributors + ISC License + https://github.com/npm/node-semver +- **vscode** + Copyright (c) Microsoft Corporation + MIT License + https://github.com/Microsoft/vscode +- **node-xml2js** + Copyright 2010, 2011, 2012, 2013. All rights reserved. + MIT License + https://github.com/Leonidas-from-XIV/node-xml2js +- **xmlbuilder-js** + Copyright (c) 2013 Ozgur Ozcitak + MIT License + https://github.com/oozcitak/xmlbuilder-js diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..f9a9abf --- /dev/null +++ b/package-lock.json @@ -0,0 +1,2211 @@ +{ + "name": "divinity-vscode", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@types/events": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@types/events/-/events-1.2.0.tgz", + "integrity": "sha512-KEIlhXnIutzKwRbQkGWb/I4HFqBuUykAdHgDED6xqwXJfONCjF5VoE0cXEiurh3XauygxzeDzgtXUqvLkxFzzA==", + "dev": true + }, + "@types/fast-levenshtein": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/@types/fast-levenshtein/-/fast-levenshtein-0.0.1.tgz", + "integrity": "sha1-OjYVzxc2Rcj8pY0FHk4ygk5L0oY=", + "dev": true + }, + "@types/glob": { + "version": "5.0.35", + "resolved": "https://registry.npmjs.org/@types/glob/-/glob-5.0.35.tgz", + "integrity": "sha512-wc+VveszMLyMWFvXLkloixT4n0harUIVZjnpzztaZ0nKLuul7Z32iMt2fUFGAaZ4y1XWjFRMtCI5ewvyh4aIeg==", + "dev": true, + "requires": { + "@types/events": "*", + "@types/minimatch": "*", + "@types/node": "*" + } + }, + "@types/handlebars": { + "version": "4.0.39", + "resolved": "https://registry.npmjs.org/@types/handlebars/-/handlebars-4.0.39.tgz", + "integrity": "sha512-vjaS7Q0dVqFp85QhyPSZqDKnTTCemcSHNHFvDdalO1s0Ifz5KuE64jQD5xoUkfdWwF4WpqdJEl7LsWH8rzhKJA==", + "dev": true + }, + "@types/js-yaml": { + "version": "3.11.2", + "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-3.11.2.tgz", + "integrity": "sha512-JRDtMPEqXrzfuYAdqbxLot1GvAr/QvicIZAnOAigZaj8xVMhuSJTg/xsv9E1TvyL+wujYhRLx9ZsQ0oFOSmwyA==", + "dev": true + }, + "@types/long": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.0.tgz", + "integrity": "sha512-1w52Nyx4Gq47uuu0EVcsHBxZFJgurQ+rTKS3qMHxR1GY2T8c2AJYd6vZoZ9q1rupaDjU0yT+Jc2XTyXkjeMA+Q==", + "dev": true + }, + "@types/marked": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@types/marked/-/marked-0.4.0.tgz", + "integrity": "sha512-xkURX55US18wHme+O2UlqJf3Fo7FqT5VAL+OJ/zK+jP2NX57naryDHoiqt/pMIwZjDc62sRvXUWuQQxQiBdheQ==", + "dev": true + }, + "@types/minimatch": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz", + "integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==", + "dev": true + }, + "@types/node": { + "version": "8.10.26", + "resolved": "https://registry.npmjs.org/@types/node/-/node-8.10.26.tgz", + "integrity": "sha512-opk6bLLErLSwyVVJeSH5Ek7ZWOBSsN0JrvXTNVGLXLAXKB9xlTYajrplR44xVyMrmbut94H6uJ9jqzM/12jxkA==", + "dev": true + }, + "@types/promise-queue": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@types/promise-queue/-/promise-queue-2.2.0.tgz", + "integrity": "sha512-9QLtid6GxEWqpF+QImxBRG6bSVOHtpAm2kXuIyEvZBbSOupLvqhhJv8uaHbS8kUL8FDjzH3RWcSyC/52WOVtGw==", + "dev": true + }, + "@types/rimraf": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@types/rimraf/-/rimraf-2.0.2.tgz", + "integrity": "sha512-Hm/bnWq0TCy7jmjeN5bKYij9vw5GrDFWME4IuxV08278NtU/VdGbzsBohcCUJ7+QMqmUq5hpRKB39HeQWJjztQ==", + "dev": true, + "requires": { + "@types/glob": "*", + "@types/node": "*" + } + }, + "@types/xml2js": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@types/xml2js/-/xml2js-0.4.3.tgz", + "integrity": "sha512-Pv2HGRE4gWLs31In7nsyXEH4uVVsd0HNV9i2dyASvtDIlOtSTr1eczPLDpdEuyv5LWH5LT20GIXwPjkshKWI1g==", + "dev": true, + "requires": { + "@types/events": "*", + "@types/node": "*" + } + }, + "ajv": { + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", + "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", + "requires": { + "co": "^4.6.0", + "fast-deep-equal": "^1.0.0", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.3.0" + } + }, + "align-text": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", + "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", + "requires": { + "kind-of": "^3.0.2", + "longest": "^1.0.1", + "repeat-string": "^1.5.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "amdefine": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", + "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=" + }, + "ansi-cyan": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ansi-cyan/-/ansi-cyan-0.1.1.tgz", + "integrity": "sha1-U4rlKK+JgvKK4w2G8vF0VtJgmHM=", + "requires": { + "ansi-wrap": "0.1.0" + } + }, + "ansi-red": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ansi-red/-/ansi-red-0.1.1.tgz", + "integrity": "sha1-jGOPnRCAgAo1PJwoyKgcpHBdlGw=", + "requires": { + "ansi-wrap": "0.1.0" + } + }, + "ansi-wrap": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/ansi-wrap/-/ansi-wrap-0.1.0.tgz", + "integrity": "sha1-qCJQ3bABXponyoLoLqYDu/pF768=" + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "arr-diff": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-1.1.0.tgz", + "integrity": "sha1-aHwydYFjWI/vfeezb6vklesaOZo=", + "requires": { + "arr-flatten": "^1.0.1", + "array-slice": "^0.2.3" + } + }, + "arr-flatten": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==" + }, + "arr-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-2.1.0.tgz", + "integrity": "sha1-IPnqtexw9cfSFbEHexw5Fh0pLH0=" + }, + "array-differ": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-differ/-/array-differ-1.0.0.tgz", + "integrity": "sha1-7/UuN1gknTO+QCuLuOVkuytdQDE=" + }, + "array-slice": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-0.2.3.tgz", + "integrity": "sha1-3Tz7gO15c6dRF82sabC5nshhhvU=" + }, + "array-union": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", + "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", + "requires": { + "array-uniq": "^1.0.1" + } + }, + "array-uniq": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", + "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=" + }, + "array-unique": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", + "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=" + }, + "arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=" + }, + "asn1": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", + "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "requires": { + "safer-buffer": "~2.1.0" + } + }, + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + }, + "async": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=" + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + }, + "aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" + }, + "aws4": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", + "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==" + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + }, + "bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", + "optional": true, + "requires": { + "tweetnacl": "^0.14.3" + } + }, + "block-stream": { + "version": "0.0.9", + "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz", + "integrity": "sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo=", + "requires": { + "inherits": "~2.0.0" + } + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz", + "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=", + "requires": { + "expand-range": "^1.8.1", + "preserve": "^0.2.0", + "repeat-element": "^1.1.2" + } + }, + "browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true + }, + "buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=" + }, + "buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==" + }, + "camelcase": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", + "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=", + "optional": true + }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" + }, + "center-align": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz", + "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=", + "optional": true, + "requires": { + "align-text": "^0.1.3", + "lazy-cache": "^1.0.3" + } + }, + "cliui": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", + "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=", + "optional": true, + "requires": { + "center-align": "^0.1.1", + "right-align": "^0.1.1", + "wordwrap": "0.0.2" + }, + "dependencies": { + "wordwrap": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", + "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=", + "optional": true + } + } + }, + "clone": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/clone/-/clone-0.2.0.tgz", + "integrity": "sha1-xhJqkK1Pctv1rNskPMN3JP6T/B8=" + }, + "clone-buffer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/clone-buffer/-/clone-buffer-1.0.0.tgz", + "integrity": "sha1-4+JbIHrE5wGvch4staFnksrD3Fg=" + }, + "clone-stats": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-0.0.1.tgz", + "integrity": "sha1-uI+UqCzzi4eR1YBG6kAprYjKmdE=" + }, + "cloneable-readable": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/cloneable-readable/-/cloneable-readable-1.1.2.tgz", + "integrity": "sha512-Bq6+4t+lbM8vhTs/Bef5c5AdEMtapp/iFb6+s4/Hh9MVTt8OLKH7ZOOZSCT+Ys7hsHvqv0GuMPJ1lnQJVHvxpg==", + "requires": { + "inherits": "^2.0.1", + "process-nextick-args": "^2.0.0", + "readable-stream": "^2.3.5" + } + }, + "co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=" + }, + "combined-stream": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.6.tgz", + "integrity": "sha1-cj599ugBrFYTETp+RFqbactjKBg=", + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "commander": { + "version": "2.15.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz", + "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "convert-source-map": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.5.1.tgz", + "integrity": "sha1-uCeAl7m8IpNl3lxiz1/K7YtVmeU=" + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "requires": { + "assert-plus": "^1.0.0" + } + }, + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "requires": { + "ms": "2.0.0" + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "optional": true + }, + "deep-assign": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/deep-assign/-/deep-assign-1.0.0.tgz", + "integrity": "sha1-sJJ0O+hCfcYh6gBnzex+cN0Z83s=", + "requires": { + "is-obj": "^1.0.0" + } + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" + }, + "diff": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", + "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", + "dev": true + }, + "duplexer": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz", + "integrity": "sha1-rOb/gIwc5mtX0ev5eXessCM0z8E=" + }, + "duplexify": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.6.0.tgz", + "integrity": "sha512-fO3Di4tBKJpYTFHAxTU00BcfWMY9w24r/x21a6rZRbsD/ToUgGxsMbiGRmB7uVAXeGKXD9MwiLZa5E97EVgIRQ==", + "requires": { + "end-of-stream": "^1.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.0.0", + "stream-shift": "^1.0.0" + } + }, + "ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", + "optional": true, + "requires": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "end-of-stream": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", + "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", + "requires": { + "once": "^1.4.0" + } + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true + }, + "event-stream": { + "version": "3.3.4", + "resolved": "http://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz", + "integrity": "sha1-SrTJoPWlTbkzi0w02Gv86PSzVXE=", + "requires": { + "duplexer": "~0.1.1", + "from": "~0", + "map-stream": "~0.1.0", + "pause-stream": "0.0.11", + "split": "0.3", + "stream-combiner": "~0.0.4", + "through": "~2.3.1" + } + }, + "expand-brackets": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", + "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=", + "requires": { + "is-posix-bracket": "^0.1.0" + } + }, + "expand-range": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz", + "integrity": "sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc=", + "requires": { + "fill-range": "^2.1.0" + } + }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, + "extend-shallow": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-1.1.4.tgz", + "integrity": "sha1-Gda/lN/AnXa6cR85uHLSH/TdkHE=", + "requires": { + "kind-of": "^1.1.0" + } + }, + "extglob": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz", + "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=", + "requires": { + "is-extglob": "^1.0.0" + }, + "dependencies": { + "is-extglob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", + "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=" + } + } + }, + "extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" + }, + "fast-deep-equal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", + "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=" + }, + "fast-json-stable-stringify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", + "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=" + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=" + }, + "fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=", + "requires": { + "pend": "~1.2.0" + } + }, + "filename-regex": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz", + "integrity": "sha1-wcS5vuPglyXdsQa3XB4wH+LxiyY=" + }, + "fill-range": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.4.tgz", + "integrity": "sha512-cnrcCbj01+j2gTG921VZPnHbjmdAf8oQV/iGeV2kZxGSyfYjjTyY79ErsK1WJWMpw6DaApEX72binqJE+/d+5Q==", + "requires": { + "is-number": "^2.1.0", + "isobject": "^2.0.0", + "randomatic": "^3.0.0", + "repeat-element": "^1.1.2", + "repeat-string": "^1.5.2" + } + }, + "first-chunk-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/first-chunk-stream/-/first-chunk-stream-1.0.0.tgz", + "integrity": "sha1-Wb+1DNkF9g18OUzT2ayqtOatk04=" + }, + "for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=" + }, + "for-own": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/for-own/-/for-own-0.1.5.tgz", + "integrity": "sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4=", + "requires": { + "for-in": "^1.0.1" + } + }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" + }, + "form-data": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.2.tgz", + "integrity": "sha1-SXBJi+YEwgwAXU9cI67NIda0kJk=", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "1.0.6", + "mime-types": "^2.1.12" + } + }, + "from": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz", + "integrity": "sha1-g8YK/Fi5xWmXAH7Rp2izqzA6RP4=" + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + }, + "fstream": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz", + "integrity": "sha1-XB+x8RdHcRTwYyoOtLcbPLD9MXE=", + "requires": { + "graceful-fs": "^4.1.2", + "inherits": "~2.0.0", + "mkdirp": ">=0.5 0", + "rimraf": "2" + } + }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "requires": { + "assert-plus": "^1.0.0" + } + }, + "glob": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "glob-base": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/glob-base/-/glob-base-0.3.0.tgz", + "integrity": "sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q=", + "requires": { + "glob-parent": "^2.0.0", + "is-glob": "^2.0.0" + }, + "dependencies": { + "glob-parent": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", + "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=", + "requires": { + "is-glob": "^2.0.0" + } + }, + "is-extglob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", + "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=" + }, + "is-glob": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", + "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", + "requires": { + "is-extglob": "^1.0.0" + } + } + } + }, + "glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "requires": { + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + } + }, + "glob-stream": { + "version": "5.3.5", + "resolved": "https://registry.npmjs.org/glob-stream/-/glob-stream-5.3.5.tgz", + "integrity": "sha1-pVZlqajM3EGRWofHAeMtTgFvrSI=", + "requires": { + "extend": "^3.0.0", + "glob": "^5.0.3", + "glob-parent": "^3.0.0", + "micromatch": "^2.3.7", + "ordered-read-streams": "^0.3.0", + "through2": "^0.6.0", + "to-absolute-glob": "^0.1.1", + "unique-stream": "^2.0.2" + }, + "dependencies": { + "glob": { + "version": "5.0.15", + "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz", + "integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=", + "requires": { + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "2 || 3", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" + }, + "readable-stream": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" + }, + "through2": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-0.6.5.tgz", + "integrity": "sha1-QaucZ7KdVyCQcUEOHXp6lozTrUg=", + "requires": { + "readable-stream": ">=1.0.33-1 <1.1.0-0", + "xtend": ">=4.0.0 <4.1.0-0" + } + } + } + }, + "graceful-fs": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=" + }, + "growl": { + "version": "1.10.5", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", + "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", + "dev": true + }, + "gulp-chmod": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/gulp-chmod/-/gulp-chmod-2.0.0.tgz", + "integrity": "sha1-AMOQuSigeZslGsz2MaoJ4BzGKZw=", + "requires": { + "deep-assign": "^1.0.0", + "stat-mode": "^0.2.0", + "through2": "^2.0.0" + } + }, + "gulp-filter": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/gulp-filter/-/gulp-filter-5.1.0.tgz", + "integrity": "sha1-oF4Rr/sHz33PQafeHLe2OsN4PnM=", + "requires": { + "multimatch": "^2.0.0", + "plugin-error": "^0.1.2", + "streamfilter": "^1.0.5" + } + }, + "gulp-gunzip": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/gulp-gunzip/-/gulp-gunzip-1.0.0.tgz", + "integrity": "sha1-FbdBFF6Dqcb1CIYkG1fMWHHxUak=", + "requires": { + "through2": "~0.6.5", + "vinyl": "~0.4.6" + }, + "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" + }, + "readable-stream": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" + }, + "through2": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-0.6.5.tgz", + "integrity": "sha1-QaucZ7KdVyCQcUEOHXp6lozTrUg=", + "requires": { + "readable-stream": ">=1.0.33-1 <1.1.0-0", + "xtend": ">=4.0.0 <4.1.0-0" + } + } + } + }, + "gulp-remote-src-vscode": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/gulp-remote-src-vscode/-/gulp-remote-src-vscode-0.5.0.tgz", + "integrity": "sha512-/9vtSk9eI9DEWCqzGieglPqmx0WUQ9pwPHyHFpKmfxqdgqGJC2l0vFMdYs54hLdDsMDEZFLDL2J4ikjc4hQ5HQ==", + "requires": { + "event-stream": "^3.3.4", + "node.extend": "^1.1.2", + "request": "^2.79.0", + "through2": "^2.0.3", + "vinyl": "^2.0.1" + }, + "dependencies": { + "clone": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", + "integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=" + }, + "clone-stats": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-1.0.0.tgz", + "integrity": "sha1-s3gt/4u1R04Yuba/D9/ngvh3doA=" + }, + "vinyl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-2.2.0.tgz", + "integrity": "sha512-MBH+yP0kC/GQ5GwBqrTPTzEfiiLjta7hTtvQtbxBgTeSXsmKQRQecjibMbxIXzVT3Y9KJK+drOz1/k+vsu8Nkg==", + "requires": { + "clone": "^2.1.1", + "clone-buffer": "^1.0.0", + "clone-stats": "^1.0.0", + "cloneable-readable": "^1.0.0", + "remove-trailing-separator": "^1.0.1", + "replace-ext": "^1.0.0" + } + } + } + }, + "gulp-sourcemaps": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/gulp-sourcemaps/-/gulp-sourcemaps-1.6.0.tgz", + "integrity": "sha1-uG/zSdgBzrVuHZ59x7vLS33uYAw=", + "requires": { + "convert-source-map": "^1.1.1", + "graceful-fs": "^4.1.2", + "strip-bom": "^2.0.0", + "through2": "^2.0.0", + "vinyl": "^1.0.0" + }, + "dependencies": { + "clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=" + }, + "replace-ext": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-0.0.1.tgz", + "integrity": "sha1-KbvZIHinOfC8zitO5B6DeVNSKSQ=" + }, + "vinyl": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-1.2.0.tgz", + "integrity": "sha1-XIgDbPVl5d8FVYv8kR+GVt8hiIQ=", + "requires": { + "clone": "^1.0.0", + "clone-stats": "^0.0.1", + "replace-ext": "0.0.1" + } + } + } + }, + "gulp-symdest": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/gulp-symdest/-/gulp-symdest-1.1.0.tgz", + "integrity": "sha1-wWUyBzLRks5W/ZQnH/oSMjS/KuA=", + "requires": { + "event-stream": "^3.3.1", + "mkdirp": "^0.5.1", + "queue": "^3.1.0", + "vinyl-fs": "^2.4.3" + } + }, + "gulp-untar": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/gulp-untar/-/gulp-untar-0.0.7.tgz", + "integrity": "sha512-0QfbCH2a1k2qkTLWPqTX+QO4qNsHn3kC546YhAP3/n0h+nvtyGITDuDrYBMDZeW4WnFijmkOvBWa5HshTic1tw==", + "requires": { + "event-stream": "~3.3.4", + "streamifier": "~0.1.1", + "tar": "^2.2.1", + "through2": "~2.0.3", + "vinyl": "^1.2.0" + }, + "dependencies": { + "clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=" + }, + "replace-ext": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-0.0.1.tgz", + "integrity": "sha1-KbvZIHinOfC8zitO5B6DeVNSKSQ=" + }, + "vinyl": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-1.2.0.tgz", + "integrity": "sha1-XIgDbPVl5d8FVYv8kR+GVt8hiIQ=", + "requires": { + "clone": "^1.0.0", + "clone-stats": "^0.0.1", + "replace-ext": "0.0.1" + } + } + } + }, + "gulp-vinyl-zip": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/gulp-vinyl-zip/-/gulp-vinyl-zip-2.1.0.tgz", + "integrity": "sha1-JOQGhdwFtxSZlSRQmeBZAmO+ja0=", + "requires": { + "event-stream": "^3.3.1", + "queue": "^4.2.1", + "through2": "^2.0.3", + "vinyl": "^2.0.2", + "vinyl-fs": "^2.0.0", + "yauzl": "^2.2.1", + "yazl": "^2.2.1" + }, + "dependencies": { + "clone": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", + "integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=" + }, + "clone-stats": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-1.0.0.tgz", + "integrity": "sha1-s3gt/4u1R04Yuba/D9/ngvh3doA=" + }, + "queue": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/queue/-/queue-4.4.2.tgz", + "integrity": "sha512-fSMRXbwhMwipcDZ08enW2vl+YDmAmhcNcr43sCJL8DIg+CFOsoRLG23ctxA+fwNk1w55SePSiS7oqQQSgQoVJQ==", + "requires": { + "inherits": "~2.0.0" + } + }, + "vinyl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-2.2.0.tgz", + "integrity": "sha512-MBH+yP0kC/GQ5GwBqrTPTzEfiiLjta7hTtvQtbxBgTeSXsmKQRQecjibMbxIXzVT3Y9KJK+drOz1/k+vsu8Nkg==", + "requires": { + "clone": "^2.1.1", + "clone-buffer": "^1.0.0", + "clone-stats": "^1.0.0", + "cloneable-readable": "^1.0.0", + "remove-trailing-separator": "^1.0.1", + "replace-ext": "^1.0.0" + } + } + } + }, + "handlebars": { + "version": "4.0.11", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.0.11.tgz", + "integrity": "sha1-Ywo13+ApS8KB7a5v/F0yn8eYLcw=", + "requires": { + "async": "^1.4.0", + "optimist": "^0.6.1", + "source-map": "^0.4.4", + "uglify-js": "^2.6" + }, + "dependencies": { + "source-map": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", + "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", + "requires": { + "amdefine": ">=0.0.4" + } + } + } + }, + "har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" + }, + "har-validator": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.0.tgz", + "integrity": "sha512-+qnmNjI4OfH2ipQ9VQOw23bBd/ibtfbVdK2fYbY4acTDqKTW/YDp9McimZdDbG8iV9fZizUqQMD5xvriB146TA==", + "requires": { + "ajv": "^5.3.0", + "har-schema": "^2.0.0" + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "he": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", + "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=" + }, + "http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "requires": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + } + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "is": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/is/-/is-3.2.1.tgz", + "integrity": "sha1-0Kwq1V63sL7JJqUmb2xmKqqD3KU=" + }, + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" + }, + "is-dotfile": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.3.tgz", + "integrity": "sha1-pqLzL/0t+wT1yiXs0Pa4PPeYoeE=" + }, + "is-equal-shallow": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz", + "integrity": "sha1-IjgJj8Ih3gvPpdnqxMRdY4qhxTQ=", + "requires": { + "is-primitive": "^2.0.0" + } + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=" + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=" + }, + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "requires": { + "is-extglob": "^2.1.0" + } + }, + "is-number": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz", + "integrity": "sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=", + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", + "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=" + }, + "is-posix-bracket": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz", + "integrity": "sha1-MzTceXdDaOkvAW5vvAqI9c1ua8Q=" + }, + "is-primitive": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-primitive/-/is-primitive-2.0.0.tgz", + "integrity": "sha1-IHurkWOEmcB7Kt8kCkGochADRXU=" + }, + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" + }, + "is-utf8": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", + "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=" + }, + "is-valid-glob": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/is-valid-glob/-/is-valid-glob-0.3.0.tgz", + "integrity": "sha1-1LVcafUYhvm2XHDWwmItN+KfSP4=" + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "requires": { + "isarray": "1.0.0" + } + }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" + }, + "js-yaml": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.0.tgz", + "integrity": "sha512-PIt2cnwmPfL4hKNwqeiuz4bKfnzHTBv6HyVgjahA6mPLwPDzjDWrplJBMjHUFxku/N3FlmrbyPclad+I+4mJ3A==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", + "optional": true + }, + "json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" + }, + "json-schema-traverse": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", + "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=" + }, + "json-stable-stringify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", + "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=", + "requires": { + "jsonify": "~0.0.0" + } + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" + }, + "jsonify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", + "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=" + }, + "jsprim": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + } + }, + "kind-of": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-1.1.0.tgz", + "integrity": "sha1-FAo9LUGjbS78+pN3tiwk+ElaXEQ=" + }, + "lazy-cache": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", + "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=", + "optional": true + }, + "lazystream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.0.tgz", + "integrity": "sha1-9plf4PggOS9hOWvolGJAe7dxaOQ=", + "requires": { + "readable-stream": "^2.0.5" + } + }, + "lodash.isequal": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA=" + }, + "long": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", + "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==" + }, + "longest": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", + "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=" + }, + "map-stream": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.1.0.tgz", + "integrity": "sha1-5WqpTEyAVaFkBKBnS3jyFffI4ZQ=" + }, + "math-random": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/math-random/-/math-random-1.0.1.tgz", + "integrity": "sha1-izqsWIuKZuSXXjzepn97sylgH6w=" + }, + "merge-stream": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-1.0.1.tgz", + "integrity": "sha1-QEEgLVCKNCugAXQAjfDCUbjBNeE=", + "requires": { + "readable-stream": "^2.0.1" + } + }, + "micromatch": { + "version": "2.3.11", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", + "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=", + "requires": { + "arr-diff": "^2.0.0", + "array-unique": "^0.2.1", + "braces": "^1.8.2", + "expand-brackets": "^0.1.4", + "extglob": "^0.3.1", + "filename-regex": "^2.0.0", + "is-extglob": "^1.0.0", + "is-glob": "^2.0.1", + "kind-of": "^3.0.2", + "normalize-path": "^2.0.1", + "object.omit": "^2.0.0", + "parse-glob": "^3.0.4", + "regex-cache": "^0.4.2" + }, + "dependencies": { + "arr-diff": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", + "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=", + "requires": { + "arr-flatten": "^1.0.1" + } + }, + "is-extglob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", + "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=" + }, + "is-glob": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", + "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", + "requires": { + "is-extglob": "^1.0.0" + } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "mime-db": { + "version": "1.35.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.35.0.tgz", + "integrity": "sha512-JWT/IcCTsB0Io3AhWUMjRqucrHSPsSf2xKLaRldJVULioggvkJvggZ3VXNNSRkCddE6D+BUI4HEIZIA2OjwIvg==" + }, + "mime-types": { + "version": "2.1.19", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.19.tgz", + "integrity": "sha512-P1tKYHVSZ6uFo26mtnve4HQFE3koh1UWVkp8YUC+ESBHe945xWSoXuHHiGarDqcEZ+whpCDnlNw5LON0kLo+sw==", + "requires": { + "mime-db": "~1.35.0" + } + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "requires": { + "minimist": "0.0.8" + } + }, + "mocha": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-5.2.0.tgz", + "integrity": "sha512-2IUgKDhc3J7Uug+FxMXuqIyYzH7gJjXECKe/w43IGgQHTSj3InJi+yAA7T24L9bQMRKiUEHxEX37G5JpVUGLcQ==", + "dev": true, + "requires": { + "browser-stdout": "1.3.1", + "commander": "2.15.1", + "debug": "3.1.0", + "diff": "3.5.0", + "escape-string-regexp": "1.0.5", + "glob": "7.1.2", + "growl": "1.10.5", + "he": "1.1.1", + "minimatch": "3.0.4", + "mkdirp": "0.5.1", + "supports-color": "5.4.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "multimatch": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/multimatch/-/multimatch-2.1.0.tgz", + "integrity": "sha1-nHkGoi+0wCkZ4vX3UWG0zb1LKis=", + "requires": { + "array-differ": "^1.0.0", + "array-union": "^1.0.1", + "arrify": "^1.0.0", + "minimatch": "^3.0.0" + } + }, + "node.extend": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/node.extend/-/node.extend-1.1.6.tgz", + "integrity": "sha1-p7iCyC1sk6SGOlUEvV3o7IYli5Y=", + "requires": { + "is": "^3.1.0" + } + }, + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "requires": { + "remove-trailing-separator": "^1.0.1" + } + }, + "oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + }, + "object.omit": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/object.omit/-/object.omit-2.0.1.tgz", + "integrity": "sha1-Gpx0SCnznbuFjHbKNXmuKlTr0fo=", + "requires": { + "for-own": "^0.1.4", + "is-extendable": "^0.1.1" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "requires": { + "wrappy": "1" + } + }, + "optimist": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", + "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", + "requires": { + "minimist": "~0.0.1", + "wordwrap": "~0.0.2" + } + }, + "ordered-read-streams": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/ordered-read-streams/-/ordered-read-streams-0.3.0.tgz", + "integrity": "sha1-cTfmmzKYuzQiR6G77jiByA4v14s=", + "requires": { + "is-stream": "^1.0.1", + "readable-stream": "^2.0.1" + } + }, + "parse-glob": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz", + "integrity": "sha1-ssN2z7EfNVE7rdFz7wu246OIORw=", + "requires": { + "glob-base": "^0.3.0", + "is-dotfile": "^1.0.0", + "is-extglob": "^1.0.0", + "is-glob": "^2.0.0" + }, + "dependencies": { + "is-extglob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", + "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=" + }, + "is-glob": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", + "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", + "requires": { + "is-extglob": "^1.0.0" + } + } + } + }, + "path-dirname": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", + "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=" + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" + }, + "pause-stream": { + "version": "0.0.11", + "resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz", + "integrity": "sha1-/lo0sMvOErWqaitAPuLnO2AvFEU=", + "requires": { + "through": "~2.3" + } + }, + "pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=" + }, + "performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" + }, + "plugin-error": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-0.1.2.tgz", + "integrity": "sha1-O5uzM1zPAPQl4HQ34ZJ2ln2kes4=", + "requires": { + "ansi-cyan": "^0.1.1", + "ansi-red": "^0.1.1", + "arr-diff": "^1.0.1", + "arr-union": "^2.0.1", + "extend-shallow": "^1.1.2" + } + }, + "preserve": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/preserve/-/preserve-0.2.0.tgz", + "integrity": "sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks=" + }, + "process-nextick-args": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", + "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==" + }, + "promise-queue": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/promise-queue/-/promise-queue-2.2.5.tgz", + "integrity": "sha1-L29ffA9tCBCelnZZx5uIqe1ek7Q=" + }, + "psl": { + "version": "1.1.29", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.29.tgz", + "integrity": "sha512-AeUmQ0oLN02flVHXWh9sSJF7mcdFq0ppid/JkErufc3hGIV/AMa8Fo9VgDo/cT2jFdOWoFvHp90qqBH54W+gjQ==" + }, + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" + }, + "qs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" + }, + "querystringify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.0.0.tgz", + "integrity": "sha512-eTPo5t/4bgaMNZxyjWx6N2a6AuE0mq51KWvpc7nU/MAqixcI6v6KrGUKES0HaomdnolQBBXU/++X6/QQ9KL4tw==" + }, + "queue": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/queue/-/queue-3.1.0.tgz", + "integrity": "sha1-bEnQHwCeIlZ4h4nyv/rGuLmZBYU=", + "requires": { + "inherits": "~2.0.0" + } + }, + "randomatic": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-3.1.0.tgz", + "integrity": "sha512-KnGPVE0lo2WoXxIZ7cPR8YBpiol4gsSuOwDSg410oHh80ZMp5EiypNqL2K4Z77vJn6lB5rap7IkAmcUlalcnBQ==", + "requires": { + "is-number": "^4.0.0", + "kind-of": "^6.0.0", + "math-random": "^1.0.1" + }, + "dependencies": { + "is-number": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", + "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==" + }, + "kind-of": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==" + } + } + }, + "readable-stream": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "regex-cache": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.4.tgz", + "integrity": "sha512-nVIZwtCjkC9YgvWkpM55B5rBhBYRZhAaJbgcFYXXsHnbZ9UZI9nnVWYZpBlCqv9ho2eZryPnWrZGsOdPwVWXWQ==", + "requires": { + "is-equal-shallow": "^0.1.3" + } + }, + "remove-trailing-separator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", + "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=" + }, + "repeat-element": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.2.tgz", + "integrity": "sha1-7wiaF40Ug7quTZPrmLT55OEdmQo=" + }, + "repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=" + }, + "replace-ext": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.0.tgz", + "integrity": "sha1-3mMSg3P8v3w8z6TeWkgMRaZ5WOs=" + }, + "request": { + "version": "2.88.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", + "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", + "requires": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.0", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.4.3", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + } + }, + "requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=" + }, + "right-align": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", + "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=", + "optional": true, + "requires": { + "align-text": "^0.1.1" + } + }, + "rimraf": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", + "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", + "requires": { + "glob": "^7.0.5" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" + }, + "semver": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", + "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==" + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "source-map-support": { + "version": "0.5.8", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.8.tgz", + "integrity": "sha512-WqAEWPdb78u25RfKzOF0swBpY0dKrNdjc4GvLwm7ScX/o9bj8Eh/YL8mcMhBHYDGl87UkkSXDOFnW4G7GhWhGg==", + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "split": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/split/-/split-0.3.3.tgz", + "integrity": "sha1-zQ7qXmOiEd//frDwkcQTPi0N0o8=", + "requires": { + "through": "2" + } + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, + "sshpk": { + "version": "1.14.2", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.14.2.tgz", + "integrity": "sha1-xvxhZIo9nE52T9P8306hBeSSupg=", + "requires": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + } + }, + "stat-mode": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/stat-mode/-/stat-mode-0.2.2.tgz", + "integrity": "sha1-5sgLYjEj19gM8TLOU480YokHJQI=" + }, + "stream-combiner": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.0.4.tgz", + "integrity": "sha1-TV5DPBhSYd3mI8o/RMWGvPXErRQ=", + "requires": { + "duplexer": "~0.1.1" + } + }, + "stream-shift": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.0.tgz", + "integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI=" + }, + "streamfilter": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/streamfilter/-/streamfilter-1.0.7.tgz", + "integrity": "sha512-Gk6KZM+yNA1JpW0KzlZIhjo3EaBJDkYfXtYSbOwNIQ7Zd6006E6+sCFlW1NDvFG/vnXhKmw6TJJgiEQg/8lXfQ==", + "requires": { + "readable-stream": "^2.0.2" + } + }, + "streamifier": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/streamifier/-/streamifier-0.1.1.tgz", + "integrity": "sha1-l+mNj6TRBdYqJpHR3AfoINuN/E8=" + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", + "requires": { + "is-utf8": "^0.2.0" + } + }, + "strip-bom-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-bom-stream/-/strip-bom-stream-1.0.0.tgz", + "integrity": "sha1-5xRDmFd9Uaa+0PoZlPoF9D/ZiO4=", + "requires": { + "first-chunk-stream": "^1.0.0", + "strip-bom": "^2.0.0" + } + }, + "supports-color": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", + "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "tar": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.1.tgz", + "integrity": "sha1-jk0qJWwOIYXGsYrWlK7JaLg8sdE=", + "requires": { + "block-stream": "*", + "fstream": "^1.0.2", + "inherits": "2" + } + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" + }, + "through2": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.3.tgz", + "integrity": "sha1-AARWmzfHx0ujnEPzzteNGtlBQL4=", + "requires": { + "readable-stream": "^2.1.5", + "xtend": "~4.0.1" + } + }, + "through2-filter": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/through2-filter/-/through2-filter-2.0.0.tgz", + "integrity": "sha1-YLxVoNrLdghdsfna6Zq0P4PWIuw=", + "requires": { + "through2": "~2.0.0", + "xtend": "~4.0.0" + } + }, + "to-absolute-glob": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/to-absolute-glob/-/to-absolute-glob-0.1.1.tgz", + "integrity": "sha1-HN+kcqnvUMI57maZm2YsoOs5k38=", + "requires": { + "extend-shallow": "^2.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "tough-cookie": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", + "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", + "requires": { + "psl": "^1.1.24", + "punycode": "^1.4.1" + } + }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", + "optional": true + }, + "typescript": { + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.8.3.tgz", + "integrity": "sha512-K7g15Bb6Ra4lKf7Iq2l/I5/En+hLIHmxWZGq3D4DIRNFxMNV6j2SHSvDOqs2tGd4UvD/fJvrwopzQXjLrT7Itw==", + "dev": true + }, + "uglify-js": { + "version": "2.8.29", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", + "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=", + "optional": true, + "requires": { + "source-map": "~0.5.1", + "uglify-to-browserify": "~1.0.0", + "yargs": "~3.10.0" + }, + "dependencies": { + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "optional": true + } + } + }, + "uglify-to-browserify": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz", + "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=", + "optional": true + }, + "unique-stream": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/unique-stream/-/unique-stream-2.2.1.tgz", + "integrity": "sha1-WqADz76Uxf+GbE59ZouxxNuts2k=", + "requires": { + "json-stable-stringify": "^1.0.0", + "through2-filter": "^2.0.0" + } + }, + "url-parse": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.4.3.tgz", + "integrity": "sha512-rh+KuAW36YKo0vClhQzLLveoj8FwPJNu65xLb7Mrt+eZht0IPT0IXgSv8gcMegZ6NvjJUALf6Mf25POlMwD1Fw==", + "requires": { + "querystringify": "^2.0.0", + "requires-port": "^1.0.0" + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "uuid": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", + "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" + }, + "vali-date": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/vali-date/-/vali-date-1.0.0.tgz", + "integrity": "sha1-G5BKWWCfsyjvB4E4Qgk09rhnCaY=" + }, + "verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "requires": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "vinyl": { + "version": "0.4.6", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-0.4.6.tgz", + "integrity": "sha1-LzVsh6VQolVGHza76ypbqL94SEc=", + "requires": { + "clone": "^0.2.0", + "clone-stats": "^0.0.1" + } + }, + "vinyl-fs": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/vinyl-fs/-/vinyl-fs-2.4.4.tgz", + "integrity": "sha1-vm/zJwy1Xf19MGNkDegfJddTIjk=", + "requires": { + "duplexify": "^3.2.0", + "glob-stream": "^5.3.2", + "graceful-fs": "^4.0.0", + "gulp-sourcemaps": "1.6.0", + "is-valid-glob": "^0.3.0", + "lazystream": "^1.0.0", + "lodash.isequal": "^4.0.0", + "merge-stream": "^1.0.0", + "mkdirp": "^0.5.0", + "object-assign": "^4.0.0", + "readable-stream": "^2.0.4", + "strip-bom": "^2.0.0", + "strip-bom-stream": "^1.0.0", + "through2": "^2.0.0", + "through2-filter": "^2.0.0", + "vali-date": "^1.0.0", + "vinyl": "^1.0.0" + }, + "dependencies": { + "clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=" + }, + "replace-ext": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-0.0.1.tgz", + "integrity": "sha1-KbvZIHinOfC8zitO5B6DeVNSKSQ=" + }, + "vinyl": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-1.2.0.tgz", + "integrity": "sha1-XIgDbPVl5d8FVYv8kR+GVt8hiIQ=", + "requires": { + "clone": "^1.0.0", + "clone-stats": "^0.0.1", + "replace-ext": "0.0.1" + } + } + } + }, + "vinyl-source-stream": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vinyl-source-stream/-/vinyl-source-stream-1.1.2.tgz", + "integrity": "sha1-YrU6E1YQqJbpjKlr7jqH8Aio54A=", + "requires": { + "through2": "^2.0.3", + "vinyl": "^0.4.3" + } + }, + "vscode": { + "version": "1.1.21", + "resolved": "https://registry.npmjs.org/vscode/-/vscode-1.1.21.tgz", + "integrity": "sha512-tJl9eL15ZMm6vzCYYeQ26sSYRuXGMGPsaeIAmG2rOOYRn01jdaDg6I4b9G5Ed6FISdmn6egpKThk4o4om8Ax/A==", + "requires": { + "glob": "^7.1.2", + "gulp-chmod": "^2.0.0", + "gulp-filter": "^5.0.1", + "gulp-gunzip": "1.0.0", + "gulp-remote-src-vscode": "^0.5.0", + "gulp-symdest": "^1.1.0", + "gulp-untar": "^0.0.7", + "gulp-vinyl-zip": "^2.1.0", + "mocha": "^4.0.1", + "request": "^2.83.0", + "semver": "^5.4.1", + "source-map-support": "^0.5.0", + "url-parse": "^1.4.3", + "vinyl-source-stream": "^1.1.0" + }, + "dependencies": { + "browser-stdout": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.0.tgz", + "integrity": "sha1-81HTKWnTL6XXpVZxVCY9korjvR8=" + }, + "commander": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.11.0.tgz", + "integrity": "sha512-b0553uYA5YAEGgyYIGYROzKQ7X5RAqedkfjiZxwi0kL1g3bOaBNNZfYkzt/CL0umgD5wc9Jec2FbB98CjkMRvQ==" + }, + "diff": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.3.1.tgz", + "integrity": "sha512-MKPHZDMB0o6yHyDryUOScqZibp914ksXwAMYMTHj6KO8UeKsRYNJD3oNCKjTqZon+V488P7N/HzXF8t7ZR95ww==" + }, + "growl": { + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.3.tgz", + "integrity": "sha512-hKlsbA5Vu3xsh1Cg3J7jSmX/WaW6A5oBeqzM88oNbCRQFz+zUaXm6yxS4RVytp1scBoJzSYl4YAEOQIt6O8V1Q==" + }, + "has-flag": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", + "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=" + }, + "mocha": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-4.1.0.tgz", + "integrity": "sha512-0RVnjg1HJsXY2YFDoTNzcc1NKhYuXKRrBAG2gDygmJJA136Cs2QlRliZG1mA0ap7cuaT30mw16luAeln+4RiNA==", + "requires": { + "browser-stdout": "1.3.0", + "commander": "2.11.0", + "debug": "3.1.0", + "diff": "3.3.1", + "escape-string-regexp": "1.0.5", + "glob": "7.1.2", + "growl": "1.10.3", + "he": "1.1.1", + "mkdirp": "0.5.1", + "supports-color": "4.4.0" + } + }, + "supports-color": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.4.0.tgz", + "integrity": "sha512-rKC3+DyXWgK0ZLKwmRsrkyHVZAjNkfzeehuFWdGGcqGDTZFH73+RH6S/RDAAxl9GusSjZSUWYLmT9N5pzXFOXQ==", + "requires": { + "has-flag": "^2.0.0" + } + } + } + }, + "vscode-jsonrpc": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-3.6.2.tgz", + "integrity": "sha512-T24Jb5V48e4VgYliUXMnZ379ItbrXgOimweKaJshD84z+8q7ZOZjJan0MeDe+Ugb+uqERDVV8SBmemaGMSMugA==" + }, + "vscode-languageclient": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-5.0.0.tgz", + "integrity": "sha512-zotks0PzR/ByGIYyM8SBobXHXsrdb6JqZ7m+qBpZLZP6HIUpF2oyYjQomVc4nFxjNdn0b2PfwkJZJKqRnV/UoQ==", + "requires": { + "semver": "^5.5.0", + "vscode-languageserver-protocol": "^3.10.3" + } + }, + "vscode-languageserver": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-5.0.1.tgz", + "integrity": "sha512-162KNZtWXBABfBWvDFZ0OwwzQkJ/sCed40MJiNZ763RZlAGCP6WW7vFtTkDgPB4MhsNEBnYIWQdwDkyx5XNDEg==", + "requires": { + "vscode-languageserver-protocol": "^3.10.3", + "vscode-uri": "^1.0.5" + } + }, + "vscode-languageserver-protocol": { + "version": "3.10.3", + "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.10.3.tgz", + "integrity": "sha512-R9hKsmXmpIXBLpy6I0eztfAcWU0KHr1lADJiJq+VCmdiHGVUJugMIvU6qVCzLP9wRtZ02AF98j09NAKq10hWeQ==", + "requires": { + "vscode-jsonrpc": "^3.6.2", + "vscode-languageserver-types": "^3.10.1" + } + }, + "vscode-languageserver-types": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.10.1.tgz", + "integrity": "sha512-HeQ1BPYJDly4HfKs0h2TUAZyHfzTAhgQsCwsa1tW9PhuvGGsd2r3Q53FFVugwP7/2bUv3GWPoTgAuIAkIdBc4w==" + }, + "vscode-uri": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-1.0.6.tgz", + "integrity": "sha512-sLI2L0uGov3wKVb9EB+vIQBl9tVP90nqRvxSoJ35vI3NjxE8jfsE5DSOhWgSunHSZmKS4OCi2jrtfxK7uyp2ww==" + }, + "window-size": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", + "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=", + "optional": true + }, + "wordwrap": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", + "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=" + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "xml2js": { + "version": "0.4.19", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz", + "integrity": "sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==", + "requires": { + "sax": ">=0.6.0", + "xmlbuilder": "~9.0.1" + } + }, + "xmlbuilder": { + "version": "9.0.7", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz", + "integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=" + }, + "xtend": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", + "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=" + }, + "yargs": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", + "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", + "optional": true, + "requires": { + "camelcase": "^1.0.2", + "cliui": "^2.1.0", + "decamelize": "^1.0.0", + "window-size": "0.1.0" + } + }, + "yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=", + "requires": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + }, + "yazl": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/yazl/-/yazl-2.4.3.tgz", + "integrity": "sha1-7CblzIfVYBud+EMtvdPNLlFzoHE=", + "requires": { + "buffer-crc32": "~0.2.3" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..9cb88a8 --- /dev/null +++ b/package.json @@ -0,0 +1,274 @@ +{ + "name": "divinity-vscode", + "displayName": "Divinity Engine Script Support", + "description": "VS Code language support for Divinity Engine story scripts", + "author": "Sebastian Lenz", + "license": "MIT", + "version": "1.0.0", + "publisher": "sebastian-lenz", + "categories": [ + "Programming Languages" + ], + "icon": "resources/extension.png", + "keywords": [ + "Larian", + "Divinity Engine", + "Osiris", + "Story Script" + ], + "engines": { + "vscode": "^1.25.0" + }, + "activationEvents": [ + "workspaceContains:**/Story/story.div", + "workspaceContains:**/RawFiles/story_header.div", + "onLanguage:divinity-story-goal", + "onLanguage:divinity-story-div", + "onWebviewPanel:divinity.apiExplorer" + ], + "homepage": "https://github.com/sebastian-lenz/divinity-vscode", + "repository": { + "type": "git", + "url": "https://github.com/sebastian-lenz/divinity-vscode.git" + }, + "bugs": { + "url": "https://github.com/sebastian-lenz/divinity-vscode/issues" + }, + "main": "./lib/client", + "contributes": { + "configuration": { + "type": "object", + "title": "Divinity Engine configuration", + "properties": { + "divinityScript.autoStart": { + "scope": "window", + "type": "boolean", + "default": false, + "description": "When enabled, the language support will start automatically. Otherwise it will start when opening an Osiris script file." + } + } + }, + "commands": [ + { + "command": "divinity.showApiExplorer", + "title": "Show API explorer", + "category": "Divinity" + }, + { + "command": "divinity.storyOutline.openGoal", + "title": "Open Goal", + "category": "Divinity" + }, + { + "command": "divinity.storyOutline.addGoal", + "title": "Add new Goal", + "category": "Divinity" + }, + { + "command": "divinity.storyOutline.renameGoal", + "title": "Rename Goal", + "category": "Divinity" + }, + { + "command": "divinity.storyOutline.deleteGoal", + "title": "Delete Goal", + "category": "Divinity" + }, + { + "command": "divinity.storyOutline.moveGoal", + "title": "Move Goal", + "category": "Divinity" + } + ], + "languages": [ + { + "id": "divinity-story-goal", + "aliases": [ + "Divinity story goal", + "divinity-story-goal" + ], + "extensions": [ + ".divGoal" + ], + "filenamePatterns": [ + "**/Story/RawFiles/Goals/*.txt" + ], + "configuration": "./syntaxes/divinity-story-goal.language-configuration.json" + }, + { + "id": "divinity-story-div", + "aliases": [ + "Divinity story div", + "divinity-story-div" + ], + "extensions": [ + ".div" + ], + "configuration": "./syntaxes/divinity-story-div.language-configuration.json" + } + ], + "menus": { + "view/item/context": [ + { + "command": "divinity.storyOutline.openGoal", + "when": "view == divinity.storyOutline", + "group": "navigation" + }, + { + "command": "divinity.storyOutline.addGoal", + "when": "view == divinity.storyOutline", + "group": "navigation" + }, + { + "command": "divinity.storyOutline.renameGoal", + "when": "view == divinity.storyOutline && viewItem != sharedGoal", + "group": "1_modification" + }, + { + "command": "divinity.storyOutline.deleteGoal", + "when": "view == divinity.storyOutline && viewItem != sharedGoal", + "group": "1_modification" + }, + { + "command": "divinity.storyOutline.moveGoal", + "when": "view == divinity.storyOutline && viewItem != sharedGoal", + "group": "1_modification" + } + ] + }, + "grammars": [ + { + "language": "divinity-story-div", + "scopeName": "text.divinity.storydiv", + "path": "./syntaxes/divinity-story-div.tmLanguage.json" + }, + { + "language": "divinity-story-goal", + "scopeName": "text.divinity.storygoal", + "path": "./syntaxes/divinity-story-goal.tmLanguage.json" + } + ], + "problemMatchers": [ + { + "name": "divinity.problemMatcher", + "owner": "divinity-vscode", + "fileLocation": "absolute", + "pattern": { + "regexp": "^(WARN|ERR!) ((?:[^:]+:\\\\)?[^:]*):(\\d+):(\\d+): \\[(\\d+)\\] (.*)$", + "severity": 1, + "file": 2, + "line": 3, + "column": 4, + "code": 5, + "message": 6 + } + } + ], + "snippets": [ + { + "language": "divinity-story-goal", + "path": "./syntaxes/divinity-story-goal.snippets.json" + } + ], + "taskDefinitions": [ + { + "type": "divinity.task.compiler", + "required": [ + "gameDataPath", + "output", + "mod" + ], + "properties": { + "gameDataPath": { + "type": "string", + "description": "Location of the game Data folder" + }, + "output": { + "type": "string", + "description": "Compiled story output path" + }, + "mod": { + "type": "array", + "description": "Check and compile all goals from the specified mod" + }, + "noWarn": { + "type": "array", + "description": "Suppress warnings with diagnostic code ", + "enum": [ + "alias-mismatch", + "db-naming", + "guid-prefix", + "rule-naming", + "string-lt", + "unused-db" + ] + }, + "checkOnly": { + "type": "boolean", + "description": "Only check scripts for errors, don't generate compiled story file" + }, + "checkNames": { + "type": "boolean", + "description": "Verify game object names (slow!)" + }, + "reload": { + "type": "string", + "description": "Turns on editor level reloading.", + "enum": [ + "reloadStory", + "reloadLevelAndStory" + ] + } + } + }, + { + "type": "divinity.task.reload" + } + ], + "views": { + "explorer": [ + { + "id": "divinity.storyOutline", + "name": "Story Outline", + "when": "divinity.storyOutline.enabled" + } + ] + } + }, + "scripts": { + "compile": "rimraf ./lib/** && tsc -p ./tsconfig.json", + "compile:publish": "rimraf ./lib/** && tsc -p ./tsconfig.publish.json", + "package": "vsce package", + "postinstall": "node ./node_modules/vscode/bin/install", + "test": "mocha", + "tidyup": "rimraf ./node_modules/handlebars/.idea && rimraf ./node_modules/handlebars/dist/amd && rimraf ./node_modules/handlebars/lib/handlebars && rimraf ./node_modules/handlebars/package-lock.json", + "update-vscode": "node ./node_modules/vscode/bin/install", + "vscode:prepublish": "npm run update-vscode && npm run tidyup && npm run compile:publish", + "watch": "tsc -w -p ./tsconfig.json" + }, + "devDependencies": { + "@types/fast-levenshtein": "0.0.1", + "@types/handlebars": "^4.0.39", + "@types/js-yaml": "^3.11.2", + "@types/long": "^4.0.0", + "@types/marked": "^0.4.0", + "@types/node": "^8.10.26", + "@types/promise-queue": "^2.2.0", + "@types/rimraf": "^2.0.2", + "@types/xml2js": "^0.4.3", + "js-yaml": "^3.12.0", + "mocha": "^5.2.0", + "rimraf": "^2.6.2", + "typescript": "2.8.3" + }, + "dependencies": { + "fast-levenshtein": "^2.0.6", + "handlebars": "^4.0.11", + "long": "^4.0.0", + "promise-queue": "^2.2.5", + "vscode": "^1.1.21", + "vscode-languageclient": "^5.0.0", + "vscode-languageserver": "^5.0.0", + "xml2js": "^0.4.19" + } +} diff --git a/resources/assets/api.css b/resources/assets/api.css new file mode 100644 index 0000000..9570042 --- /dev/null +++ b/resources/assets/api.css @@ -0,0 +1,204 @@ +body { + padding-top: 20px; + padding-bottom: 20px; +} + +h1 { + font-weight: normal; +} + +h2, +h3, +h4, +h5, +h6 { + margin: 1em 0 0.5em 0; + + padding-bottom: 4px; + border-bottom: 1px solid var(--vscode-panel-border); + + font-size: 1.5em; + font-weight: normal; +} + +a { + color: var(--color); +} + +p, +li { + line-height: 1.5em; +} + +ul { + padding-left: 20px; +} + +.divinitySection { + padding-bottom: 4px; + border-bottom: 1px solid var(--vscode-panel-border); +} + +.divinityIcon { + display: inline-block; + height: 14px; + width: 16px; + margin-top: -1px; + + background-image: url("../icons/category-light.svg"); + background-repeat: no-repeat; + + vertical-align: middle; +} + +.divinityIcon.large { + height: 21px; + width: 24px; + margin-top: -6px; +} + +.vscode-dark .divinityIcon { + background-image: url("../icons/category-dark.svg"); +} + +.divinityIcon.call { + background-image: url("../icons/call-light.svg"); +} + +.vscode-dark .divinityIcon.call { + background-image: url("../icons/call-dark.svg"); +} + +.divinityIcon.event { + background-image: url("../icons/event-light.svg"); +} + +.vscode-dark .divinityIcon.event { + background-image: url("../icons/event-dark.svg"); +} + +.divinityIcon.query { + background-image: url("../icons/query-light.svg"); +} + +.vscode-dark .divinityIcon.query { + background-image: url("../icons/query-dark.svg"); +} + +.divinityLink { + cursor: pointer; +} + +.divinityLink:hover { + text-decoration: underline; +} + +.divinityBreadcrumb { + display: flex; + flex-flow: row wrap; +} + +.divinityBreadcrumb--item:after { + content: "⮞"; + margin: -1px 8px 0; + + font-size: 75%; +} + +.divinityBreadcrumb + h1 { + margin-top: 2px; +} + +@media (min-width: 500px) { + .divinityCompactList { + columns: 2; + } +} + +@media (min-width: 750px) { + .divinityCompactList { + columns: 3; + } +} + +.divinityCompactList--item { + overflow: hidden; + box-sizing: border-box; + + padding: 3px 20px 3px 0; + white-space: nowrap; + text-overflow: ellipsis; + break-inside: avoid-column; +} + +pre, +.divinitySignatutes { + padding: 10px; + overflow: auto; + + border-radius: 3px; + background: var(--vscode-editorSuggestWidget-background); + + font-family: Consolas, "Courier New", monospace; +} + +.divinitySignatutes--item:not(:first-child) { + margin-top: 0.5em; +} + +.mtk12 { + color: var(--vscode-gitDecoration-conflictingResourceForeground); +} + +.mtk11 { + color: var(--vscode-list-warningForeground); +} + +.mtk13 { + color: var(--vscode-list-highlightForeground); +} + +.divinityProgress { + position: fixed; + top: 0; + right: 0; + left: 0; + height: 2px; + overflow: hidden; +} + +.divinityProgress--line { + position: absolute; + height: 100%; + + background: var(--vscode-activityBarBadge-background); +} + +.divinityProgress--line.inc { + animation: increase 2s infinite; +} + +.divinityProgress--line.dec { + animation: decrease 2s 0.5s infinite; +} + +@keyframes increase { + from { + left: -5%; + width: 5%; + } + to { + left: 130%; + width: 100%; + } +} +@keyframes decrease { + from { + left: -80%; + width: 80%; + } + to { + left: 110%; + width: 10%; + } +} diff --git a/resources/assets/api.js b/resources/assets/api.js new file mode 100644 index 0000000..8c9c568 --- /dev/null +++ b/resources/assets/api.js @@ -0,0 +1,42 @@ +(function() { + const vscode = acquireVsCodeApi(); + const location = document.body.getAttribute("data-location"); + let hasProgress = false; + + // Send state to vscode + vscode.setState({ location: location }); + + // Execute a command + function executeCommand(command, args) { + vscode.postMessage({ + command: command, + args: args + }); + + if (!hasProgress) { + const el = document.createElement("div"); + el.className = "divinityProgress"; + el.innerHTML = [ + '
', + '
' + ].join(""); + + document.body.appendChild(el); + hasProgress = true; + } + } + + document.addEventListener("click", function(event) { + var target = event.target; + while (target && target.getAttribute) { + var goto = target.getAttribute("data-goto"); + if (goto) { + executeCommand("goto", [goto]); + event.preventDefault(); + break; + } + + target = target.parentNode; + } + }); +})(); diff --git a/resources/extension.png b/resources/extension.png new file mode 100644 index 0000000..c7d5dcb Binary files /dev/null and b/resources/extension.png differ diff --git a/resources/extension.svg b/resources/extension.svg new file mode 100644 index 0000000..6a96769 --- /dev/null +++ b/resources/extension.svg @@ -0,0 +1,25 @@ + + + +]> + + + + + + + diff --git a/resources/features/api-explorer.gif b/resources/features/api-explorer.gif new file mode 100644 index 0000000..181ccfd Binary files /dev/null and b/resources/features/api-explorer.gif differ diff --git a/resources/features/code-analysis.gif b/resources/features/code-analysis.gif new file mode 100644 index 0000000..7fee7bc Binary files /dev/null and b/resources/features/code-analysis.gif differ diff --git a/resources/features/completion.gif b/resources/features/completion.gif new file mode 100644 index 0000000..1c15be3 Binary files /dev/null and b/resources/features/completion.gif differ diff --git a/resources/features/document-structure.gif b/resources/features/document-structure.gif new file mode 100644 index 0000000..7223a51 Binary files /dev/null and b/resources/features/document-structure.gif differ diff --git a/resources/features/find-references.gif b/resources/features/find-references.gif new file mode 100644 index 0000000..b0b88c0 Binary files /dev/null and b/resources/features/find-references.gif differ diff --git a/resources/features/hover.gif b/resources/features/hover.gif new file mode 100644 index 0000000..1ffe419 Binary files /dev/null and b/resources/features/hover.gif differ diff --git a/resources/features/rename.gif b/resources/features/rename.gif new file mode 100644 index 0000000..3c988eb Binary files /dev/null and b/resources/features/rename.gif differ diff --git a/resources/features/story-outline.gif b/resources/features/story-outline.gif new file mode 100644 index 0000000..20c2ab9 Binary files /dev/null and b/resources/features/story-outline.gif differ diff --git a/resources/icons/call-dark.svg b/resources/icons/call-dark.svg new file mode 100644 index 0000000..64d7c37 --- /dev/null +++ b/resources/icons/call-dark.svg @@ -0,0 +1 @@ +Method_16x diff --git a/resources/icons/call-light.svg b/resources/icons/call-light.svg new file mode 100644 index 0000000..5ebc1f4 --- /dev/null +++ b/resources/icons/call-light.svg @@ -0,0 +1 @@ +Method_16x diff --git a/resources/icons/category-dark.svg b/resources/icons/category-dark.svg new file mode 100644 index 0000000..98e91b5 --- /dev/null +++ b/resources/icons/category-dark.svg @@ -0,0 +1 @@ + diff --git a/resources/icons/category-light.svg b/resources/icons/category-light.svg new file mode 100644 index 0000000..dbf0292 --- /dev/null +++ b/resources/icons/category-light.svg @@ -0,0 +1 @@ + diff --git a/resources/icons/event-dark.svg b/resources/icons/event-dark.svg new file mode 100644 index 0000000..466429b --- /dev/null +++ b/resources/icons/event-dark.svg @@ -0,0 +1 @@ + diff --git a/resources/icons/event-light.svg b/resources/icons/event-light.svg new file mode 100644 index 0000000..268938e --- /dev/null +++ b/resources/icons/event-light.svg @@ -0,0 +1 @@ + diff --git a/resources/icons/goal-custom-dark.svg b/resources/icons/goal-custom-dark.svg new file mode 100644 index 0000000..aab3ffd --- /dev/null +++ b/resources/icons/goal-custom-dark.svg @@ -0,0 +1,3 @@ + + + diff --git a/resources/icons/goal-custom-light.svg b/resources/icons/goal-custom-light.svg new file mode 100644 index 0000000..37db136 --- /dev/null +++ b/resources/icons/goal-custom-light.svg @@ -0,0 +1,3 @@ + + + diff --git a/resources/icons/goal-shared-dark.svg b/resources/icons/goal-shared-dark.svg new file mode 100644 index 0000000..366b868 --- /dev/null +++ b/resources/icons/goal-shared-dark.svg @@ -0,0 +1,3 @@ + + + diff --git a/resources/icons/goal-shared-light.svg b/resources/icons/goal-shared-light.svg new file mode 100644 index 0000000..e0f2583 --- /dev/null +++ b/resources/icons/goal-shared-light.svg @@ -0,0 +1,3 @@ + + + diff --git a/resources/icons/query-dark.svg b/resources/icons/query-dark.svg new file mode 100644 index 0000000..21ba9d9 --- /dev/null +++ b/resources/icons/query-dark.svg @@ -0,0 +1 @@ + diff --git a/resources/icons/query-light.svg b/resources/icons/query-light.svg new file mode 100644 index 0000000..db77e35 --- /dev/null +++ b/resources/icons/query-light.svg @@ -0,0 +1 @@ + diff --git a/resources/templates/pages/category.hbs b/resources/templates/pages/category.hbs new file mode 100644 index 0000000..01eee60 --- /dev/null +++ b/resources/templates/pages/category.hbs @@ -0,0 +1,19 @@ +{{> breadcrumbs}} +

{{ category.displayName }}

+ +{{#if category.children}} +

Child categories

+
+ {{#each category.children}} +
+ + + {{ displayName }} + +
+ {{/each}} +
+{{/if}} + +

Definitions

+{{> definitions}} diff --git a/resources/templates/pages/definition.hbs b/resources/templates/pages/definition.hbs new file mode 100644 index 0000000..51f8724 --- /dev/null +++ b/resources/templates/pages/definition.hbs @@ -0,0 +1,39 @@ +{{> breadcrumbs includeSelf=1 }} +

{{ title }}

+ +

Signatures

+
+ {{#each signatures}} +
+ {{~ name ~}}( + {{#if parameters}} + {{~#each parameters~}}
   + {{~#if flow}}[{{ flow }}]{{/if~}} + {{~#if type}}({{ type }}){{/if~}} + {{~ name ~}} + {{~#if @last}}{{else}}, {{/if~}} + {{~/each~}}
+ {{/if}}) +
+ {{/each}} +
+ +{{#if wiki}} + {{#if wiki.error}} +

Documentation

+

The Wiki could not be reached or returned an error.

+ {{/if}} + {{#if wiki.notFound}} +

Documentation

+

There is no documentation available for this definition.

+ {{/if}} + {{#if wiki.content}} + {{{ wiki.content }}} + {{/if}} + {{#if wiki.editLink}} +

Edit this page

+ {{/if}} +{{/if}} + +

See also

+{{> definitions}} diff --git a/resources/templates/pages/layout.hbs b/resources/templates/pages/layout.hbs new file mode 100644 index 0000000..847840e --- /dev/null +++ b/resources/templates/pages/layout.hbs @@ -0,0 +1,17 @@ + + + + + + + + API Explorer + + + + + {{{content}}} + + + + diff --git a/resources/templates/partials/breadcrumbs.hbs b/resources/templates/partials/breadcrumbs.hbs new file mode 100644 index 0000000..047639c --- /dev/null +++ b/resources/templates/partials/breadcrumbs.hbs @@ -0,0 +1,11 @@ +
+ {{#each category.parents}} +
{{ displayName + }} +
+ {{/each}}{{#if includeSelf}} +
{{ + category.displayName }} +
+ {{/if}} +
diff --git a/resources/templates/partials/definitions.hbs b/resources/templates/partials/definitions.hbs new file mode 100644 index 0000000..4c8db99 --- /dev/null +++ b/resources/templates/partials/definitions.hbs @@ -0,0 +1,13 @@ +{{#each symbols}} +

{{ title }}

+
+ {{#each symbols}} +
+ + + {{ name }} + +
+ {{/each}} +
+{{/each}} diff --git a/src/client/Client.ts b/src/client/Client.ts new file mode 100644 index 0000000..aa0376c --- /dev/null +++ b/src/client/Client.ts @@ -0,0 +1,116 @@ +import { EventEmitter } from "events"; +import * as path from "path"; + +import { ExtensionContext, OutputChannel, window } from "vscode"; +import { + LanguageClient, + LanguageClientOptions, + TransportKind, + ServerOptions +} from "vscode-languageclient"; + +import features, { Feature } from "./features"; +import { readyEvent } from "../shared/notifications"; + +export default class Client extends EventEmitter { + clientId = "osiris-language-server"; + clientName = "Osiris language server"; + connection: LanguageClient | null; + connectCallbacks: Array = []; + context: ExtensionContext; + features: Array = []; + isReady: boolean = false; + languages: Array = ["divinity-story-goal"]; + outputChannel: OutputChannel; + + constructor(context: ExtensionContext) { + super(); + + this.context = context; + this.outputChannel = window.createOutputChannel(this.clientName); + this.connection = this.createConnection(); + this.features = features.map(feature => new feature(this)); + } + + private createConnection() { + const { context, languages, outputChannel } = this; + const module = context.asAbsolutePath( + path.join("lib", "server", "index.js") + ); + + let serverOptions: ServerOptions = { + run: { + module, + transport: TransportKind.ipc + }, + debug: { + module, + transport: TransportKind.ipc, + options: { execArgv: ["--nolazy", "--inspect=6009"] } + } + }; + + let clientOptions: LanguageClientOptions = { + documentSelector: [ + ...languages.map(language => ({ + scheme: "file", + language + })), + { + scheme: "divinity" + } + ], + outputChannel + }; + + const client = new LanguageClient( + this.clientId, + this.clientName, + serverOptions, + clientOptions + ); + + client.start(); + client.onReady().then(() => { + client.onNotification(readyEvent, () => { + if (this.isReady) return; + this.isReady = true; + + for (const feature of this.features) { + feature.initialize(client); + } + + for (const callback of this.connectCallbacks) { + callback(client); + } + }); + }); + + return client; + } + + dispose(): Thenable { + const { connection, features } = this; + const promises: Thenable[] = features.map(feature => + feature.dispose() + ); + + if (connection) { + promises.push(connection.stop()); + } + + this.connection = null; + this.features.length = 0; + return Promise.all(promises).then(() => undefined); + } + + async getConnection(): Promise { + if (this.connection && this.isReady) { + return this.connection; + } else { + return new Promise(resolve => { + this.connectCallbacks.push(resolve); + }); + } + } +} diff --git a/src/client/features/Feature.ts b/src/client/features/Feature.ts new file mode 100644 index 0000000..ffcda9f --- /dev/null +++ b/src/client/features/Feature.ts @@ -0,0 +1,21 @@ +import { LanguageClient } from "vscode-languageclient/lib/main"; + +import Client from "../Client"; + +export interface FeatureFactory { + new (client: Client): Feature; +} + +export default class Feature { + readonly client: Client; + + constructor(client: Client) { + this.client = client; + } + + initialize(connection: LanguageClient) {} + + async dispose(): Promise { + return Promise.resolve(); + } +} diff --git a/src/client/features/activityIndicator/index.ts b/src/client/features/activityIndicator/index.ts new file mode 100644 index 0000000..0d06e90 --- /dev/null +++ b/src/client/features/activityIndicator/index.ts @@ -0,0 +1,89 @@ +import { EventEmitter } from "events"; +import { LanguageClient } from "vscode-languageclient/lib/main"; +import { Progress, ProgressLocation, window } from "vscode"; + +import Feature from "../Feature"; +import { + levelIndexReadyEvent, + levelIndexStartEvent, + projectAddedEvent, + ProjectEventArgs, + projectReadyEvent +} from "../../../shared/notifications"; + +type WindowProgress = Progress<{ message?: string; increment?: number }>; + +export default class ActivityIndicatorFeature extends Feature { + private events: EventEmitter = new EventEmitter(); + + initialize(connection: LanguageClient) { + const { events } = this; + + connection.onNotification(levelIndexStartEvent, this.handleLevelIndexStart); + + connection.onNotification(levelIndexReadyEvent, args => + events.emit(levelIndexReadyEvent, args) + ); + + connection.onNotification(projectAddedEvent, this.handleProjectAdded); + + connection.onNotification(projectReadyEvent, args => + events.emit(projectReadyEvent, args) + ); + } + + private handleLevelIndexStart = (args: ProjectEventArgs) => { + const { UUID } = args.project.meta; + + const createProgress = () => + new Promise(resolve => { + const { events } = this; + + function onReady({ project }: ProjectEventArgs) { + if (UUID !== project.meta.UUID) return; + events.removeListener(levelIndexReadyEvent, onReady); + resolve(); + } + + events.addListener(levelIndexReadyEvent, onReady); + }); + + window.withProgress( + { + cancellable: false, + location: ProgressLocation.Window, + title: `Indexing levels` + }, + createProgress + ); + }; + + private handleProjectAdded = (args: ProjectEventArgs) => { + const { Name, UUID } = args.project.meta; + + // Reemitt for task provider + this.client.emit(projectAddedEvent, args); + + const createProgress = () => + new Promise(resolve => { + const { events } = this; + + function onReady({ project }: ProjectEventArgs) { + if (UUID !== project.meta.UUID) return; + events.removeListener(projectReadyEvent, onReady); + resolve(); + } + + events.addListener(projectReadyEvent, onReady); + }); + + window.withProgress( + { + cancellable: false, + location: ProgressLocation.Notification, + title: `Loading project ${Name}` + }, + createProgress + ); + }; +} diff --git a/src/client/features/apiExplorer/index.ts b/src/client/features/apiExplorer/index.ts new file mode 100644 index 0000000..67847fa --- /dev/null +++ b/src/client/features/apiExplorer/index.ts @@ -0,0 +1,163 @@ +import { join } from "path"; +import { LanguageClient, Disposable } from "vscode-languageclient"; +import { + commands, + Uri, + ViewColumn, + WebviewPanel, + WebviewPanelSerializer, + window +} from "vscode"; + +import Client from "../../Client"; +import Feature from "../Feature"; +import { apiShowEvent } from "../../../shared/notifications"; +import { apiRequest, ApiResult } from "../../../shared/requests"; + +const viewType: string = "divinity.apiExplorer"; + +export default class ApiExplorerFeature extends Feature + implements WebviewPanelSerializer { + location: string = ""; + panel: WebviewPanel | null = null; + panelResources: Array = []; + + constructor(client: Client) { + super(client); + + commands.registerCommand("divinity.showApiExplorer", this.handleShowApi); + window.registerWebviewPanelSerializer(viewType, this); + } + + initialize(connection: LanguageClient) { + connection.onNotification(apiShowEvent, this.handleApiShow); + } + + async deserializeWebviewPanel(panel: WebviewPanel, state: any) { + let location: string; + try { + location = state.location; + } catch (e) { + location = "/"; + } + + this.setPanel(panel); + this.navigate(typeof location === "string" ? location : "/"); + } + + getPanel(): WebviewPanel { + let { panel } = this; + if (panel) { + if (!panel.visible) { + panel.reveal(); + } + + return panel; + } + + panel = window.createWebviewPanel( + viewType, + "API Explorer", + ViewColumn.Beside, + { + enableFindWidget: true, + enableScripts: true, + localResourceRoots: this.getResourceConfig().localResourceRoots + } + ); + + this.setPanel(panel); + return panel; + } + + getResourceConfig() { + const { extensionPath } = this.client.context; + const path = join(extensionPath, "resources"); + + const scriptUri = Uri.file(join(path, "assets", "api.js")) + .with({ scheme: "vscode-resource" }) + .toString(); + + const styleUri = Uri.file(join(path, "assets", "api.css")) + .with({ scheme: "vscode-resource" }) + .toString(); + + return { + localResourceRoots: [Uri.file(path)], + uris: [ + { + pattern: /%STYLE_URI%/g, + value: styleUri + }, + { + pattern: /%SCRIPT_URI%/g, + value: scriptUri + } + ] + }; + } + + handleApiShow = (location: string) => { + commands.executeCommand("divinity.showApiExplorer", location); + }; + + handleMessage = (message: any) => { + if (typeof message !== "object") return; + const { args, command } = message; + + switch (command) { + case "goto": + this.navigate(args[0]); + break; + case "reload": + this.navigate(this.location, true); + break; + } + }; + + handlePanelDispose = () => { + for (const resource of this.panelResources) { + resource.dispose(); + } + + this.location = ""; + this.panelResources.length = 0; + this.panel = null; + }; + + handleShowApi = (location: string = "/") => { + this.getPanel(); + this.navigate(location); + }; + + async navigate(location: string, force?: boolean) { + const { panel } = this; + if (!panel) return; + if (this.location === location && !force) return; + this.location = location; + + const connection = await this.client.getConnection(); + const result = await connection.sendRequest( + apiRequest, + location + ); + + if (!result) return; + const { uris } = this.getResourceConfig(); + let { content } = result; + + for (const { pattern, value } of uris) { + content = content.replace(pattern, value); + } + + panel.webview.html = content; + } + + setPanel(panel: WebviewPanel) { + this.panelResources.push(panel.onDidDispose(this.handlePanelDispose)); + this.panelResources.push( + panel.webview.onDidReceiveMessage(this.handleMessage) + ); + this.panel = panel; + } +} diff --git a/src/client/features/divProvider/index.ts b/src/client/features/divProvider/index.ts new file mode 100644 index 0000000..9f7d5fd --- /dev/null +++ b/src/client/features/divProvider/index.ts @@ -0,0 +1,58 @@ +import { EventEmitter } from "events"; +import { LanguageClient } from "vscode-languageclient/lib/main"; +import { + ProviderResult, + TextDocumentContentProvider, + Uri, + workspace, + TextDocument +} from "vscode"; + +import Client from "../../Client"; +import Feature from "../Feature"; +import { + divRequestEvent, + divRequestResultEvent, + DivRequestResult +} from "../../../shared/notifications"; + +const scheme = "divinity"; + +export default class DivProviderFeature extends Feature + implements TextDocumentContentProvider { + emitter: EventEmitter = new EventEmitter(); + + constructor(client: Client) { + super(client); + + client.context.subscriptions.push( + workspace.registerTextDocumentContentProvider(scheme, this) + ); + } + + initialize(connection: LanguageClient) { + connection.onNotification(divRequestResultEvent, this.handleDivResult); + } + + handleDivResult = (result: DivRequestResult) => { + this.emitter.emit("result", result); + }; + + async provideTextDocumentContent(uri: Uri): Promise { + const { emitter } = this; + const connection = await this.client.getConnection(); + const uriString = uri.toString(); + + return new Promise(resolve => { + function callback(result: DivRequestResult) { + if (result.uri === uriString) { + emitter.removeListener("result", callback); + resolve(result.content); + } + } + + emitter.addListener("result", callback); + connection.sendNotification(divRequestEvent, uriString); + }); + } +} diff --git a/src/client/features/index.ts b/src/client/features/index.ts new file mode 100644 index 0000000..2e08a47 --- /dev/null +++ b/src/client/features/index.ts @@ -0,0 +1,18 @@ +import Feature, { FeatureFactory } from "./Feature"; + +import ActivityIndicatorFeature from "./activityIndicator"; +import ApiExplorerFeature from "./apiExplorer"; +import DivProviderFeature from "./divProvider"; +import StoryOutlineFeature from "./storyOutline"; +import TaskProviderFeature from "./taskProvider"; + +const factories: Array = [ + ActivityIndicatorFeature, + ApiExplorerFeature, + DivProviderFeature, + StoryOutlineFeature, + TaskProviderFeature +]; + +export { Feature }; +export default factories; diff --git a/src/client/features/storyOutline/index.ts b/src/client/features/storyOutline/index.ts new file mode 100644 index 0000000..95abcb8 --- /dev/null +++ b/src/client/features/storyOutline/index.ts @@ -0,0 +1,359 @@ +import { join, dirname } from "path"; +import { LanguageClient, Event } from "vscode-languageclient"; +import { + commands, + EventEmitter, + ProviderResult, + TreeDataProvider, + TreeItem, + window, + TreeItemCollapsibleState, + Uri, + workspace +} from "vscode"; + +import Client from "../../Client"; +import goalTemplate from "../../../shared/goalTemplate"; +import { Feature } from ".."; +import { unlink, writeFile, rename } from "../../../shared/fs"; + +import { + goalsChangedEvent, + GoalsChanged, + GoalInfo, + ProjectInfo +} from "../../../shared/notifications"; + +import { + renameGoalRequest, + RenameGoalParams, + RenameGoalResult, + MoveGoalParams, + MoveGoalResult, + moveGoalRequest +} from "../../../shared/requests"; + +export default class StoryOutlineFeature extends Feature + implements TreeDataProvider { + emitter: EventEmitter; + iconCustom: { dark: string; light: string }; + iconShared: { dark: string; light: string }; + project: ProjectInfo | null = null; + onDidChangeTreeData: Event; + rootGoals: Array = []; + treeVersion: number = -1; + + constructor(client: Client) { + super(client); + this.emitter = new EventEmitter(); + this.iconCustom = this.createIcon("goal-custom"); + this.iconShared = this.createIcon("goal-shared"); + this.onDidChangeTreeData = this.emitter.event; + + window.registerTreeDataProvider("divinity.storyOutline", this); + + commands.registerCommand( + "divinity.storyOutline.addGoal", + this.handleAddGoal + ); + + commands.registerCommand( + "divinity.storyOutline.deleteGoal", + this.handleDeleteGoal + ); + + commands.registerCommand( + "divinity.storyOutline.moveGoal", + this.handleMoveGoal + ); + + commands.registerCommand( + "divinity.storyOutline.openGoal", + this.handleOpenGoal + ); + + commands.registerCommand( + "divinity.storyOutline.renameGoal", + this.handleRenameGoal + ); + } + + initialize(connection: LanguageClient) { + connection.onNotification(goalsChangedEvent, this.handleGoalsChanged); + } + + collectChildren( + goal: GoalInfo, + result: Array = [] + ): Array { + result.push(...goal.children); + for (const child of goal.children) { + this.collectChildren(child, result); + } + + return result; + } + + createIcon(name: string) { + const { context } = this.client; + return { + dark: context.asAbsolutePath( + join("resources", "icons", `${name}-dark.svg`) + ), + light: context.asAbsolutePath( + join("resources", "icons", `${name}-light.svg`) + ) + }; + } + + findGoal(name: string, scope?: GoalInfo): GoalInfo | null { + const children = scope ? scope.children : this.rootGoals; + for (const child of children) { + if (child.name.toLowerCase() === name.toLowerCase()) { + return child; + } + + const childGoal = this.findGoal(name, child); + if (childGoal) { + return childGoal; + } + } + + return null; + } + + getGoalFileName(name: string): string | undefined { + const { project } = this; + if (!project) return undefined; + return join(project.path, "Story", "RawFiles", "Goals", `${name}.txt`); + } + + getTreeItem(element: GoalInfo): TreeItem { + let state = TreeItemCollapsibleState.None; + if (element.children.length) { + state = TreeItemCollapsibleState.Collapsed; + } + + const { iconCustom, iconShared } = this; + const item = new TreeItem(element.name, state); + item.resourceUri = Uri.parse(element.uri); + item.contextValue = element.isShared ? "sharedGoal" : "customGoal"; + item.iconPath = element.isShared ? iconShared : iconCustom; + item.command = { + command: "divinity.storyOutline.openGoal", + title: "Open Goal", + arguments: [element] + }; + + return item; + } + + getChildren(element?: GoalInfo | undefined): ProviderResult> { + return element ? element.children : this.rootGoals; + } + + getMoveTargets( + goal: GoalInfo, + scope?: GoalInfo, + depth: string = "", + result: Array = [] + ): Array { + const children = scope ? scope.children : this.rootGoals; + for (const child of children) { + if (child !== goal) { + result.push(`${depth}${child.name}`); + this.getMoveTargets(goal, child, `${depth}\t`, result); + } + } + + return result; + } + + handleAddGoal = async (goal?: GoalInfo) => { + let name = await window.showInputBox({ + prompt: "Enter the name of the new goal:" + }); + + name = this.validateNewGoalName(name); + if (!name) return; + + const fileName = this.getGoalFileName(name); + if (!fileName) { + window.showErrorMessage("Cannot create goal: No project opened."); + return; + } + + try { + await writeFile( + fileName, + goalTemplate({ + parents: goal ? [goal.name] : undefined + }) + ); + } catch (error) { + window.showErrorMessage(`Cannot create goal: ${error.message}`); + return; + } + + window.showTextDocument(Uri.file(fileName)); + }; + + handleDeleteGoal = async (goal?: GoalInfo | null) => { + if (!goal) return; + + goal = this.findGoal(goal.name); + if (!goal || goal.isShared) { + window.showErrorMessage("This goal cannot be deleted."); + return; + } + + const goalsToDelete = this.collectChildren(goal).filter( + goal => !goal.isShared + ); + goalsToDelete.push(goal); + + if (goalsToDelete.length > 1) { + const result = await window.showQuickPick(["Yes", "No"], { + placeHolder: `This will delete ${ + goalsToDelete.length + } goals. Are you sure?` + }); + if (result === "No") return; + } + + for (const goalToDelete of goalsToDelete) { + try { + await unlink(Uri.parse(goalToDelete.uri).fsPath); + } catch (error) { + window.showErrorMessage( + `Could not delete "${goalToDelete.name}": ${error.message}` + ); + } + } + }; + + handleMoveGoal = async (goal?: GoalInfo) => { + const { project } = this; + if (!goal || !project) return; + + const targets = this.getMoveTargets(goal); + let newParent = await window.showQuickPick(targets, { + placeHolder: "Select the new parent" + }); + + if (!newParent) return; + const connection = await this.client.getConnection(); + const params: MoveGoalParams = { + goalName: goal.name, + newParent: newParent.trim(), + projectUid: project.meta.UUID + }; + + const result = await connection.sendRequest( + moveGoalRequest, + params + ); + + if (result.error) { + window.showErrorMessage(result.error); + return; + } + + const converter = connection.protocol2CodeConverter; + const workspaceEdit = converter.asWorkspaceEdit(result); + const didEdit = await workspace.applyEdit(workspaceEdit); + if (!didEdit) { + window.showErrorMessage("The move could not be performed."); + return; + } + }; + + handleOpenGoal = (goal?: GoalInfo) => { + if (goal) { + window.showTextDocument(Uri.parse(goal.uri)); + } + }; + + handleRenameGoal = async (goal?: GoalInfo) => { + const { project } = this; + if (!goal || !project) return; + + let newName = await window.showInputBox({ + prompt: "Enter the new name of the goal:", + value: goal.name + }); + + newName = this.validateNewGoalName(newName); + if (!newName) return; + + const connection = await this.client.getConnection(); + const params: RenameGoalParams = { + goalName: goal.name, + newName, + projectUid: project.meta.UUID + }; + + const result = await connection.sendRequest( + renameGoalRequest, + params + ); + + if (result.error) { + window.showErrorMessage(result.error); + return; + } + + const converter = connection.protocol2CodeConverter; + const workspaceEdit = converter.asWorkspaceEdit(result); + const didEdit = await workspace.applyEdit(workspaceEdit); + if (!didEdit) { + window.showErrorMessage("The rename could not be performed."); + return; + } + + const from = Uri.parse(goal.uri).fsPath; + const to = join(dirname(from), `${newName}.txt`); + await rename(from, to); + + window.showTextDocument(Uri.file(to)); + }; + + handleGoalsChanged = (event: GoalsChanged) => { + if (!this.project) { + this.project = event.project; + commands.executeCommand( + "setContext", + "divinity.storyOutline.enabled", + true + ); + } else { + if (event.project.path !== this.project.path) return; + } + + if (event.treeVersion > this.treeVersion) { + this.rootGoals = event.goals; + this.treeVersion = event.treeVersion; + this.emitter.fire(); + } + }; + + validateNewGoalName(name: string | undefined): string | undefined { + if (!name) return undefined; + if (name.endsWith(".txt")) { + name = name.substr(0, name.length - 4); + } + + if (!/^[A-Za-z0-9_-]+$/.test(name)) { + window.showErrorMessage(`The goal name "${name}" is invalid.`); + return undefined; + } + + const existingGoal = this.findGoal(name); + if (existingGoal) { + window.showErrorMessage(`The goal "${name}" already exists.`); + return undefined; + } + + return name; + } +} diff --git a/src/client/features/taskProvider/index.ts b/src/client/features/taskProvider/index.ts new file mode 100644 index 0000000..b994c7f --- /dev/null +++ b/src/client/features/taskProvider/index.ts @@ -0,0 +1,226 @@ +import { join, normalize } from "path"; +import { + ProgressLocation, + ShellExecution, + ShellQuotedString, + ShellQuoting, + Task, + tasks, + TaskDefinition, + TaskGroup, + TaskProcessEndEvent, + TaskProvider, + TaskRevealKind, + TaskStartEvent, + workspace, + window +} from "vscode"; + +import Client from "../../Client"; +import Feature from "../Feature"; +import getPackagePath from "../../../shared/getPackagePath"; +import { + projectAddedEvent, + ProjectInfo, + ProjectEventArgs +} from "../../../shared/notifications"; + +function quotedString(value: string): ShellQuotedString { + return { quoting: ShellQuoting.Strong, value }; +} + +const modes: Array<[string, ReloadMode]> = [ + ["Compile", ReloadMode.None], + ["Compile, reload story", ReloadMode.ReloadStory], + ["Compile, reload story and level", ReloadMode.ReloadLevelAndStory] +]; + +const taskTitle = "Divinity"; +const reloadTaskTitle = "Reload editor"; +const compilerTaskType = "divinity.task.compiler"; +const reloadTaskType = "divinity.task.reload"; +const problemMatcher = "divinity.problemMatcher"; + +export const enum ReloadMode { + None = "", + ReloadStory = "reloadStory", + ReloadLevelAndStory = "reloadLevelAndStory" +} + +export interface DivinityTaskDefinition extends TaskDefinition { + checkNames?: boolean; + checkOnly?: boolean; + gameDataPath: string; + mod: Array; + noWarn?: Array; + output: string; + reload?: ReloadMode; + type: "divinity.task.compiler"; +} + +export default class TaskProviderFeature extends Feature + implements TaskProvider { + projects: Array = []; + tasks: Array = []; + + constructor(client: Client) { + super(client); + + client.addListener(projectAddedEvent, this.handleProjectAdded); + tasks.onDidStartTask(this.handleDidStartTask); + tasks.onDidEndTaskProcess(this.handleDidEndTaskProcess); + workspace.registerTaskProvider(compilerTaskType, this); + } + + getCompilerPath(): string { + return join(getPackagePath(), "bin", "StoryCompiler.exe"); + } + + getRconPath(): string { + return join(getPackagePath(), "bin", "RconClient.exe"); + } + + handleProjectAdded = ({ project }: ProjectEventArgs) => { + const { projects } = this; + const tasks: Array = []; + projects.push(project); + + for (const project of projects) { + for (const [caption, mode] of modes) { + const definition: DivinityTaskDefinition = { + gameDataPath: normalize(join(project.path, "..", "..")), + mod: ["Shared", project.meta.Folder], + output: join(project.path, "Story", "story.div.osi"), + reload: mode, + type: compilerTaskType + }; + + const task = new Task(definition, caption, project.meta.Name); + task.group = TaskGroup.Build; + task.problemMatchers = [problemMatcher]; + task.presentationOptions = { + reveal: TaskRevealKind.Never + }; + + const resolved = this.resolveTask(task); + if (resolved) tasks.push(resolved); + } + } + + this.tasks = tasks; + }; + + handleDidEndTaskProcess = (event: TaskProcessEndEvent) => { + const { definition, scope } = event.execution.task; + if (definition.type !== compilerTaskType) return; + + if (event.exitCode === 0) { + window.showInformationMessage("Story successful compiled"); + } else { + window.showErrorMessage("Story compile failed"); + } + + const { reload } = definition as DivinityTaskDefinition; + if (reload && scope && event.exitCode === 0) { + const task = new Task( + { + type: reloadTaskType + }, + scope, + reloadTaskTitle, + taskTitle, + new ShellExecution(quotedString(this.getRconPath()), [ + "127.0.0.1:5384", + reload + ]) + ); + + task.presentationOptions = { + reveal: TaskRevealKind.Never + }; + + tasks.executeTask(task); + } + }; + + handleDidStartTask = (event: TaskStartEvent) => { + const { definition } = event.execution.task; + if (definition.type !== compilerTaskType) { + return; + } + + const createProgress = () => + new Promise(resolve => { + const listener = tasks.onDidEndTaskProcess(event => { + const { definition } = event.execution.task; + if (definition.type !== compilerTaskType) { + return; + } + + listener.dispose(); + resolve(); + }); + }); + + window.withProgress( + { + cancellable: false, + location: ProgressLocation.Notification, + title: `Compiling...` + }, + createProgress + ); + }; + + provideTasks(): Array { + return this.tasks; + } + + resolveTask(task: Task): Task | undefined { + const { definition } = task; + if (definition.type !== compilerTaskType) { + return undefined; + } + + const { + checkNames, + checkOnly, + gameDataPath, + mod, + noWarn, + output + } = definition as DivinityTaskDefinition; + + const args = [ + "--game-data-path", + quotedString(gameDataPath), + "--output", + quotedString(output) + ]; + + for (const modName of mod) { + args.push("--mod", quotedString(modName)); + } + + if (noWarn) { + for (const noWarnName of noWarn) { + args.push("--no-warn", quotedString(noWarnName)); + } + } + + if (checkOnly) { + args.push("--check-only"); + } + + if (checkNames) { + args.push("--check-names"); + } + + task.execution = new ShellExecution( + quotedString(this.getCompilerPath()), + args + ); + + return task; + } +} diff --git a/src/client/index.ts b/src/client/index.ts new file mode 100644 index 0000000..cafc182 --- /dev/null +++ b/src/client/index.ts @@ -0,0 +1,21 @@ +import { ExtensionContext } from "vscode"; + +import Client from "./Client"; + +let instance: Client | null = null; + +export function activate(context: ExtensionContext) { + if (!instance) { + instance = new Client(context); + } +} + +export function deactivate(): Thenable { + if (!instance) { + return Promise.resolve(); + } + + let result = instance.dispose(); + instance = null; + return result; +} diff --git a/src/server/Server.ts b/src/server/Server.ts new file mode 100644 index 0000000..9d60859 --- /dev/null +++ b/src/server/Server.ts @@ -0,0 +1,253 @@ +import { + createConnection, + Connection, + DidChangeConfigurationNotification, + DidChangeConfigurationParams, + InitializeParams, + InitializeResult, + ProposedFeatures, + ServerCapabilities, + TextDocuments +} from "vscode-languageserver"; + +import Projects from "./projects"; +import features, { Feature } from "./features"; +import { readyEvent } from "../shared/notifications"; + +interface ExampleSettings { + maxNumberOfProblems: number; +} + +const defaultSettings: ExampleSettings = { maxNumberOfProblems: 1000 }; + +let globalSettings: ExampleSettings = defaultSettings; + +export default class Server { + connection: Connection; + documents: TextDocuments; + documentSettings: Map> = new Map(); + features: Array = []; + hasConfigurationCapability: boolean = false; + hasWorkspaceFolderCapability: boolean = false; + hasDiagnosticRelatedInformationCapability: boolean = false; + projects: Projects = new Projects(); + + constructor() { + // Create a connection for the server. The connection uses Node's IPC as a transport. + // Also include all preview / proposed LSP features. + const connection = createConnection(ProposedFeatures.all); + connection.onInitialize(this.handleInitialize); + connection.onInitialized(this.handleInitialized); + connection.onDidChangeConfiguration(this.handleDidChangeConfiguration); + + connection.onExit(() => { + this.projects.dispose(); + this.features.forEach(feature => feature.dispose()); + }); + + // Create a simple text document manager. The text document manager + // supports full document sync only + const documents = new TextDocuments(); + + documents.listen(connection); + connection.listen(); + + this.connection = connection; + this.documents = documents; + } + + getDocumentSettings(resource: string): Thenable { + const { connection, documentSettings, hasConfigurationCapability } = this; + + if (!hasConfigurationCapability) { + return Promise.resolve(globalSettings); + } + + let result = documentSettings.get(resource); + if (!result) { + result = connection.workspace.getConfiguration({ + scopeUri: resource, + section: "osiris" + }); + documentSettings.set(resource, result); + } + + return result; + } + + handleDidChangeConfiguration = (change: DidChangeConfigurationParams) => { + const { documents, documentSettings, hasConfigurationCapability } = this; + + if (hasConfigurationCapability) { + // Reset all cached document settings + documentSettings.clear(); + } else { + globalSettings = ( + (change.settings.languageServerExample || defaultSettings) + ); + } + + // Revalidate all open text documents + // documents.all().forEach(document => this.project.analyze(document)); + }; + + handleInitialize = (params: InitializeParams): InitializeResult => { + const { capabilities } = params; + const { textDocument, workspace } = capabilities; + + this.features = features.map(factory => new factory(this)); + + // Does the client support the `workspace/configuration` request? + // If not, we will fall back using global settings + this.hasConfigurationCapability = !!( + workspace && !!workspace.configuration + ); + + this.hasWorkspaceFolderCapability = !!( + workspace && !!workspace.workspaceFolders + ); + + this.hasDiagnosticRelatedInformationCapability = !!( + textDocument && + textDocument.publishDiagnostics && + textDocument.publishDiagnostics.relatedInformation + ); + + return { + capabilities: this.features.reduce( + (capabilities, feature) => ({ + ...capabilities, + ...feature.getCapabilities() + }), + { + textDocumentSync: this.documents.syncKind + } as ServerCapabilities + ) + }; + }; + + handleInitialized = () => { + const { connection, features, hasConfigurationCapability } = this; + features.forEach(feature => feature.initialize(connection)); + + connection.sendNotification(readyEvent); + + if (hasConfigurationCapability) { + // Register for all configuration changes. + connection.client.register( + DidChangeConfigurationNotification.type, + undefined + ); + } + }; +} + +/* + +async function validateTextDocument(textDocument: TextDocument): Promise { + // In this simple example we get the settings for every validate run. + let settings = await getDocumentSettings(textDocument.uri); + + // The validator creates diagnostics for all uppercase words length 2 and more + let text = textDocument.getText(); + let pattern = /\b[A-Z]{2,}\b/g; + let m: RegExpExecArray; + + let problems = 0; + let diagnostics: Diagnostic[] = []; + while ((m = pattern.exec(text)) && problems < settings.maxNumberOfProblems) { + problems++; + let diagnosic: Diagnostic = { + severity: DiagnosticSeverity.Warning, + range: { + start: textDocument.positionAt(m.index), + end: textDocument.positionAt(m.index + m[0].length) + }, + message: `${m[0]} is all uppercase.`, + source: 'ex' + }; + if (hasDiagnosticRelatedInformationCapability) { + diagnosic.relatedInformation = [ + { + location: { + uri: textDocument.uri, + range: Object.assign({}, diagnosic.range) + }, + message: 'Spelling matters' + }, + { + location: { + uri: textDocument.uri, + range: Object.assign({}, diagnosic.range) + }, + message: 'Particularly for names' + } + ]; + } + diagnostics.push(diagnosic); + } + + // Send the computed diagnostics to VSCode. + connection.sendDiagnostics({ uri: textDocument.uri, diagnostics }); +} + +connection.onDidChangeWatchedFiles(_change => { + // Monitored files have change in VSCode + connection.console.log('We received an file change event'); +}); + +// This handler provides the initial list of the completion items. +connection.onCompletion( + (_textDocumentPosition: TextDocumentPositionParams): CompletionItem[] => { + // The pass parameter contains the position of the text document in + // which code complete got requested. For the example we ignore this + // info and always provide the same completion items. + return [ + { + label: 'TypeScript', + kind: CompletionItemKind.Text, + data: 1 + }, + { + label: 'JavaScript', + kind: CompletionItemKind.Text, + data: 2 + } + ]; + } +); + +// This handler resolves additional information for the item selected in +// the completion list. +connection.onCompletionResolve( + (item: CompletionItem): CompletionItem => { + if (item.data === 1) { + item.detail = 'TypeScript details'; + item.documentation = 'TypeScript documentation'; + } else if (item.data === 2) { + item.detail = 'JavaScript details'; + item.documentation = 'JavaScript documentation'; + } + return item; + } +); + + +connection.onDidOpenTextDocument((params) => { + // A text document got opened in VSCode. + // params.uri uniquely identifies the document. For documents store on disk this is a file URI. + // params.text the initial full content of the document. + connection.console.log(`${params.textDocument.uri} opened.`); +}); +connection.onDidChangeTextDocument((params) => { + // The content of a text document did change in VSCode. + // params.uri uniquely identifies the document. + // params.contentChanges describe the content changes to the document. + connection.console.log(`${params.textDocument.uri} changed: ${JSON.stringify(params.contentChanges)}`); +}); +connection.onDidCloseTextDocument((params) => { + // A text document got closed in VSCode. + // params.uri uniquely identifies the document. + connection.console.log(`${params.textDocument.uri} closed.`); +}); +*/ diff --git a/src/server/documentation/Documentation.ts b/src/server/documentation/Documentation.ts new file mode 100644 index 0000000..456d808 --- /dev/null +++ b/src/server/documentation/Documentation.ts @@ -0,0 +1,83 @@ +import { join } from "path"; + +import getPackagePath from "../../shared/getPackagePath"; +import Repository from "./raw/Repository"; +import { CategoryData } from "./raw/Category"; +import { DefinitionDoc } from "./raw/Definition"; +import { readFile } from "../../shared/fs"; + +export interface SymbolCategoryMap { + [symbol: string]: string; +} + +export default class Documentation { + cachePath: string; + repository: Repository | null = null; + useRepository: boolean = false; + + constructor() { + this.cachePath = join(getPackagePath(), "docs", "cache"); + } + + async getCategory(name: string): Promise { + if (this.useRepository) { + const repository = this.getRepository(); + const category = repository.getCategory(name); + + return category ? category.getCategoryData() : null; + } + + const cacheFile = join(this.cachePath, `category.${name}.json`); + return this.readCache(cacheFile); + } + + async getDocumentation(name: string): Promise { + if (this.useRepository) { + const repository = this.getRepository(); + const definition = repository.getDefinition(name); + + return definition ? definition.getDocumentation() : null; + } + + const cacheFile = join( + this.cachePath, + `definition.${name.toLowerCase()}.json` + ); + + return this.readCache(cacheFile); + } + + getRepository() { + if (!this.repository) { + this.repository = new Repository(); + } + + return this.repository; + } + + async getSymbolCategories(): Promise { + if (this.useRepository) { + const result: SymbolCategoryMap = {}; + const repository = this.getRepository(); + + for (const definition of repository.definitions) { + const searchName = definition.name.toLowerCase(); + result[searchName] = definition.category.qualifiedName; + } + + return result; + } + + const cacheFile = join(this.cachePath, `index.json`); + return this.readCache(cacheFile); + } + + async readCache(path: string): Promise { + try { + const data = await readFile(path, { encoding: "utf-8" }); + return JSON.parse(data) as T; + } catch (error) { + return null; + } + } +} diff --git a/src/server/documentation/commands/compile.ts b/src/server/documentation/commands/compile.ts new file mode 100644 index 0000000..15ef02a --- /dev/null +++ b/src/server/documentation/commands/compile.ts @@ -0,0 +1,13 @@ +import { normalize, join } from "path"; + +import Repository from "../raw/Repository"; + +const repositoryPath = normalize(join(process.cwd(), process.argv[2])); +const repository = new Repository(repositoryPath); + +console.log(""); +repository.compile().then(() => { + console.log("Done!"); + console.log(""); + process.exit(); +}); diff --git a/src/server/documentation/commands/update.ts b/src/server/documentation/commands/update.ts new file mode 100644 index 0000000..f75455e --- /dev/null +++ b/src/server/documentation/commands/update.ts @@ -0,0 +1,15 @@ +import { normalize, join } from "path"; + +import Repository from "../raw/Repository"; + +const repositoryPath = normalize(join(process.cwd(), process.argv[2])); +const projectPath = normalize(join(process.cwd(), process.argv[3])); + +const repository = new Repository(repositoryPath); + +console.log(""); +repository.update(projectPath).then(() => { + console.log("Done!"); + console.log(""); + process.exit(); +}); diff --git a/src/server/documentation/raw/Category.ts b/src/server/documentation/raw/Category.ts new file mode 100644 index 0000000..7874ef2 --- /dev/null +++ b/src/server/documentation/raw/Category.ts @@ -0,0 +1,97 @@ +import { readdirSync, statSync } from "fs"; +import { join, extname, basename } from "path"; + +import Definition from "./Definition"; +import ucfirst from "../../utils/ucfirst"; + +export interface CategoryInfoData { + displayName: string; + name: string; + qualifiedName: string; +} + +export interface CategoryData extends CategoryInfoData { + children: Array; + parents: Array; +} + +export default class Category { + definitions: Array; + displayName: string; + name: string; + children: Array; + parent: Category | undefined; + path: string; + qualifiedName: string; + + constructor(path: string, parent?: Category) { + const definitions: Array = []; + const names = readdirSync(path); + const namespaces: Array = []; + + this.name = parent ? basename(path) : "root"; + this.qualifiedName = parent + ? `${parent.qualifiedName}.${this.name}` + : this.name; + + for (const name of names) { + const fileName = join(path, name); + const stats = statSync(fileName); + + if (stats.isDirectory()) { + namespaces.push(new Category(fileName, this)); + } else if (extname(fileName) === ".yml") { + definitions.push(new Definition(fileName, this)); + } + } + + let displayName = ucfirst(this.name); + if (!parent) { + displayName = "Divinity API"; + } + + this.definitions = definitions; + this.displayName = displayName; + this.children = namespaces; + this.parent = parent; + this.path = path; + } + + getAllCategories(): Array { + return this.children.reduce( + (result, category) => { + return [...result, ...category.getAllCategories()]; + }, + [this] as Array + ); + } + + getAllDefinitions(): Array { + return this.children.reduce((result, category) => { + return [...result, ...category.getAllDefinitions()]; + }, this.definitions); + } + + getCategoryInfoData(): CategoryInfoData { + return { + displayName: this.displayName, + name: this.name, + qualifiedName: this.qualifiedName + }; + } + + getCategoryData(): CategoryData { + return { + ...this.getCategoryInfoData(), + children: this.children.map(child => child.getCategoryInfoData()), + parents: this.getParents().map(parent => parent.getCategoryInfoData()) + }; + } + + getParents(): Array { + const { parent } = this; + const result: Array = parent ? parent.getParents() : []; + if (parent) result.push(parent); + return result; + } +} diff --git a/src/server/documentation/raw/Definition.ts b/src/server/documentation/raw/Definition.ts new file mode 100644 index 0000000..ac2cd7b --- /dev/null +++ b/src/server/documentation/raw/Definition.ts @@ -0,0 +1,115 @@ +import { basename } from "path"; +import { existsSync, readFileSync, writeFileSync } from "fs"; + +import Category from "./Category"; +import printSymbol from "../../projects/story/utils/printSymbol"; +import printSymbolType from "../../projects/story/utils/printSymbolType"; +import sleep from "../../utils/sleep"; +import Symbol from "../../projects/story/Symbol"; +import WikiParser from "./WikiReader"; +import { + SymbolDoc, + SymbolParameterDoc +} from "../../projects/story/models/symbol"; + +export interface DefinitionParameterDoc extends SymbolParameterDoc { + customDescription?: string; +} + +export interface DefinitionDoc extends SymbolDoc { + customDescription?: string; + overloads?: Array; + signature?: string; + type?: string; +} + +export default class Definition { + category: Category; + data: DefinitionDoc; + name: string; + path: string; + + constructor(path: string, category: Category) { + const { safeLoad } = require("js-yaml"); + + if (existsSync(path)) { + try { + this.data = safeLoad(readFileSync(path, "utf-8")); + } catch (error) { + throw new Error(`Could not load "${path}": ${error.message}`); + } + } else { + this.data = {}; + } + + this.category = category; + this.name = basename(path, ".yml"); + this.path = path; + } + + addOverload(symbol: Symbol) { + const { data } = this; + const overloads = data.overloads || (data.overloads = []); + overloads.push(printSymbol(symbol)); + } + + async apply(symbol: Symbol) { + console.log(`Fetching ${symbol.name}...`); + const wiki = new WikiParser(); + await sleep(); + await wiki.load(symbol.name); + + const { data } = this; + data.signature = printSymbol(symbol); + data.type = printSymbolType(symbol.type); + + if (data.overloads) { + data.overloads.length = 0; + } + + data.description = wiki.getDescription(); + + for (let index = 0; index < symbol.parameters.length; index++) { + const target = data.parameters || (data.parameters = []); + while (target.length <= index) { + target.push({}); + } + + const name = symbol.parameters[index].name; + target[index].name = name; + target[index].description = wiki.getParameter(name); + } + } + + getDocumentation(): DefinitionDoc { + const result = { ...this.data }; + if (result.customDescription) { + result.description = result.customDescription; + delete result.customDescription; + } + + if (result.parameters) { + result.parameters = result.parameters.map(parameter => { + const result = { ...parameter }; + if (result.customDescription) { + result.description = result.customDescription; + delete result.customDescription; + } + + return result; + }); + } + + return result; + } + + save() { + const { safeDump } = require("js-yaml"); + const { data } = this; + if (data.overloads && data.overloads.length === 0) { + delete data.overloads; + } + + writeFileSync(this.path, safeDump(this.data, { lineWidth: 200 }), "utf-8"); + } +} diff --git a/src/server/documentation/raw/Repository.ts b/src/server/documentation/raw/Repository.ts new file mode 100644 index 0000000..2df4210 --- /dev/null +++ b/src/server/documentation/raw/Repository.ts @@ -0,0 +1,129 @@ +import { join } from "path"; + +import Category from "./Category"; +import Definition from "./Definition"; +import getPackagePath from "../../../shared/getPackagePath"; +import Project from "../../projects/Project"; +import Projects from "../../projects"; +import Symbol from "../../projects/story/Symbol"; +import Documentation from "../Documentation"; +import { mkdirSync } from "fs"; +import { writeFile } from "../../../shared/fs"; + +export default class Repository { + categories: Array; + definitions: Array; + path: string; + rootCategory: Category; + + constructor(path: string = join(getPackagePath(), "docs")) { + const root = new Category(join(path, "definitions")); + + this.categories = root.getAllCategories(); + this.definitions = root.getAllDefinitions(); + this.path = path; + this.rootCategory = root; + } + + async compile() { + const rimraf = require("rimraf"); + const cachePath = join(this.path, "cache"); + rimraf.sync(cachePath); + mkdirSync(cachePath); + + const docs = new Documentation(); + docs.repository = this; + docs.useRepository = true; + + let data: any = await docs.getSymbolCategories(); + await writeFile(join(cachePath, "index.json"), JSON.stringify(data)); + + for (const category of this.categories) { + data = category.getCategoryData(); + await writeFile( + join(cachePath, `category.${category.qualifiedName}.json`), + JSON.stringify(data) + ); + } + + for (const definition of this.definitions) { + data = definition.getDocumentation(); + await writeFile( + join(cachePath, `definition.${definition.name.toLowerCase()}.json`), + JSON.stringify(data) + ); + } + } + + createDefinition(name: string): Definition { + let definition = this.getDefinition(name); + if (definition) return definition; + + const fileName = join(this.rootCategory.path, `${name}.yml`); + definition = new Definition(fileName, this.rootCategory); + + this.rootCategory.definitions.push(definition); + this.definitions.push(definition); + return definition; + } + + getCategory(name: string): Category | undefined { + return this.categories.find(category => category.qualifiedName === name); + } + + getDefinition(name: string): Definition | undefined { + name = name.toLowerCase(); + return this.definitions.find( + definition => definition.name.toLowerCase() === name + ); + } + + async update(path: string) { + const projects = new Projects(); + const project = new Project(projects, { + path, + meta: { + Folder: "", + Name: "Divinity Engine", + UUID: "00000000-0000-0000-0000-000000000000" + } + }); + + console.log("Loading project..."); + project.initialize(); + await project.story.whenReady(); + + console.log("Collecting symbols..."); + await this.updateDefinitions(project.story.symbols.symbols); + + projects.dispose(); + } + + async updateDefinitions(symbols: Array) { + const definitions: Array = []; + let definition: Definition | undefined; + + for (const symbol of symbols) { + if (symbol.isSystem) { + const definition = this.createDefinition(symbol.name); + await definition.apply(symbol); + definitions.push(definition); + } else { + definition = this.getDefinition(symbol.name); + if (definition) { + definition.addOverload(symbol); + } + } + } + + for (const definition of definitions) { + definition.save(); + } + + for (const oldDefinition of this.definitions) { + if (!definitions.some(def => def.name === oldDefinition.name)) { + console.log(`Deprecated definition: ${oldDefinition.name}`); + } + } + } +} diff --git a/src/server/documentation/raw/WikiReader.ts b/src/server/documentation/raw/WikiReader.ts new file mode 100644 index 0000000..315abc0 --- /dev/null +++ b/src/server/documentation/raw/WikiReader.ts @@ -0,0 +1,254 @@ +import fetch from "../../utils/fetch"; +import { SymbolData } from "../../features/apiExplorer/Handler"; +import Project from "../../projects/Project"; +import printSymbolType from "../../projects/story/utils/printSymbolType"; + +const wikiUrl = "https://docs.larian.game"; +const wikiApiPath = "Osiris/API/"; + +function extractContent(value: string): string { + const titleTag = /^<(h[1-6])/.exec(value); + if (titleTag) { + const end = value.indexOf(``); + if (end !== -1) { + value = value.substr(end); + } + } + + return sanitize(value).trim(); +} + +function extractParameters(value: string): Array { + const matcher = /]*>(.*?)<\/li>/g; + const result: Array = []; + let match: RegExpExecArray | null; + + while ((match = matcher.exec(value))) { + const chunk = sanitize(match[1]).trim(); + const splitAt = /[: ]/.exec(chunk); + if (splitAt) { + result.push({ + content: chunk.substr(splitAt.index + 1).trim(), + name: chunk.substr(0, splitAt.index).trim() + }); + } + } + + return result; +} + +function sanitize(value: string): string { + return value.replace(/<[^>]*>/g, ""); +} + +function symbolFromPath(path?: string): string | null { + if (!path) return null; + if (path.startsWith("/")) path = path.substr(1); + + const params = /index\.php\?title=([^&]+)/.exec(path); + if (params) path = params[1]; + + if (!path.startsWith(wikiApiPath)) return null; + + path = path.substr(wikiApiPath.length); + if (path.indexOf("#") !== -1) path = path.substr(0, path.indexOf("#")); + if (path.indexOf("?") !== -1) path = path.substr(0, path.indexOf("?")); + return path; +} + +function transformLink(string: string, url: string): string { + const symbolName = symbolFromPath(url); + if (symbolName) { + return `href="#" data-goto="/definition/${symbolName}"`; + } else { + return `href="${wikiUrl}${url}"`; + } +} + +function transformLinks(string: string): string { + return string.replace(/href="([^"]*)"/g, transformLink); +} + +export interface WikiData { + links: Array<{ + "*": string; + }>; + sections: Array<{ + anchor: string; + line: string; + }>; + text: { + "*": string; + }; +} + +export interface WikiSection { + content: string; + title: string; +} + +export interface WikiParameter { + content: string; + name: string; +} + +export default class WikiParser { + code: number = -1; + data: WikiData | undefined; + title: string = ""; + private parameters: Array | undefined; + private sections: Array | undefined; + private text: string = ""; + + async load(title: string): Promise { + const { code, data } = await this.fetch(title); + this.code = code; + this.data = data; + this.sections = undefined; + this.text = data ? data.text["*"] : ""; + this.title = title; + + return code; + } + + appendLinkedSymbols( + project: Project, + symbols: Array + ): Array { + const { data } = this; + if (!data) return symbols; + + for (const link of data.links) { + const name = symbolFromPath(link["*"]); + if (!name) continue; + + const compare = name.toLowerCase(); + if (symbols.some(({ name }) => name.toLowerCase() === compare)) { + continue; + } + + const storySymbols = project.story.symbols.findSymbols(name); + if (storySymbols.length) { + symbols.push({ + name, + type: printSymbolType(storySymbols[0].type) + }); + } + } + + return symbols; + } + + getApiContent(): string { + return this.getSections() + .filter( + section => + section.title.indexOf("Definition") === -1 && + section.title.indexOf("See") === -1 + ) + .map(section => section.content) + .join(""); + } + + getDescription(): string { + const description = this.getSections().find( + section => section.title.indexOf("Description") !== -1 + ); + + return description ? extractContent(description.content) : ""; + } + + getEditLink(): string { + const params = [ + "action=edit", + `title=${encodeURIComponent(`${wikiApiPath}${this.title}`)}` + ]; + + return `${wikiUrl}/index.php?${params.join("&")}`; + } + + getParameter(name: string): string { + name = name.toLowerCase(); + const parameter = this.getParameters().find( + paramater => paramater.name.toLowerCase() === name + ); + + return parameter ? parameter.content : ""; + } + + getParameters() { + if (!this.parameters) { + const parameters = this.getSections().find( + section => section.title.indexOf("Parameters") !== -1 + ); + + this.parameters = parameters ? extractParameters(parameters.content) : []; + } + + return this.parameters; + } + + getSections(): Array { + if (!this.sections) { + const { text } = this; + const splitter = /; + variables: Array; +} + +export default class Feature { + readonly server: Server; + + constructor(server: Server) { + this.server = server; + } + + dispose(): void {} + + getCapabilities(): Partial { + return {}; + } + + async getNodesAt({ + position, + textDocument + }: LocationOptions): Promise<{ + nodes?: Array; + resource?: Resource; + }> { + const { projects } = this.server; + const resource = await projects.findResource(textDocument.uri); + if (!resource) return {}; + + const nodes = await resource.getNodesAt(position); + if (!nodes) return {}; + + nodes.reverse(); + return { resource, nodes }; + } + + async getSymbolAt( + options: LocationOptions + ): Promise<{ + nodes?: Array; + resource?: Resource; + symbol?: Symbol; + }> { + const { nodes, resource } = await this.getNodesAt(options); + if (!nodes || !resource) return { nodes, resource }; + + for (const node of nodes) { + if (isCallerNode(node) && node.symbol) { + return { nodes, resource, symbol: node.symbol }; + } + } + + return { nodes, resource }; + } + + getVariablesAt(nodes?: Array): VariablesAt | undefined { + if (!nodes) { + return undefined; + } + + const ruleIndex = nodes.findIndex(node => node.type === NodeType.Rule); + if (ruleIndex === -1) { + return undefined; + } + + const rule = nodes[ruleIndex] as RuleNode; + let result: VariablesAt = { rule, variablesBefore: [], variables: [] }; + + for (const { node, variablesBefore, variables } of eachRuleNode(rule)) { + result = { rule, variablesBefore, variables }; + + const index = nodes.indexOf(node); + if (index !== -1 && index < ruleIndex) { + break; + } + } + + return result; + } + + initialize(connection: Connection): void {} +} diff --git a/src/server/features/activityIndicator/index.ts b/src/server/features/activityIndicator/index.ts new file mode 100644 index 0000000..4ab7098 --- /dev/null +++ b/src/server/features/activityIndicator/index.ts @@ -0,0 +1,59 @@ +import Feature from "../Feature"; +import Project from "../../projects/Project"; +import Server from "../../Server"; + +import { + levelIndexReadyEvent, + levelIndexStartEvent, + projectAddedEvent, + ProjectEventArgs, + projectReadyEvent, + ShowErrorArgs, + showErrorEvent +} from "../../../shared/notifications"; + +export default class ActivityIndicatorFeature extends Feature { + treeVersion: number = 0; + + constructor(server: Server) { + super(server); + + server.projects.on("levelInitStart", this.handleLevelInitStart); + server.projects.on("levelInitReady", this.handleLevelInitReady); + + server.projects.on("projectAdded", this.handleProjectAdded); + server.projects.on("projectReady", this.handleProjectReady); + + server.projects.on("showError", this.handleShowError); + } + + handleLevelInitStart = (project: Project) => { + this.server.connection.sendNotification(levelIndexStartEvent, { + project: project.getInfo() + } as ProjectEventArgs); + }; + + handleLevelInitReady = (project: Project) => { + this.server.connection.sendNotification(levelIndexReadyEvent, { + project: project.getInfo() + } as ProjectEventArgs); + }; + + handleProjectAdded = (project: Project) => { + this.server.connection.sendNotification(projectAddedEvent, { + project: project.getInfo() + } as ProjectEventArgs); + }; + + handleProjectReady = (project: Project) => { + this.server.connection.sendNotification(projectReadyEvent, { + project: project.getInfo() + } as ProjectEventArgs); + }; + + handleShowError = (message: string) => { + this.server.connection.sendNotification(showErrorEvent, { + message + } as ShowErrorArgs); + }; +} diff --git a/src/server/features/apiExplorer/CategoryHandler.ts b/src/server/features/apiExplorer/CategoryHandler.ts new file mode 100644 index 0000000..0d293eb --- /dev/null +++ b/src/server/features/apiExplorer/CategoryHandler.ts @@ -0,0 +1,40 @@ +import Handler, { groupSymbols } from "./Handler"; +import { CategoryData } from "../../documentation/raw/Category"; + +export interface Parameters { + category: CategoryData; + location: string; +} + +export default class CategoryHandler extends Handler { + async canHandle(location: string): Promise { + const documentation = this.getDocumentation(); + let category: CategoryData | null = null; + + if (location === "/") { + category = await documentation.getCategory("root"); + } + + const match = /^\/category\/(.*)/.exec(location); + if (match) { + category = await documentation.getCategory(match[1]); + } + + return category ? { category, location } : undefined; + } + + async getResponse({ category, location }: Parameters): Promise { + await this.feature.loadPartial("breadcrumbs"); + await this.feature.loadPartial("definitions"); + const symbols = await this.getSymbols(category.qualifiedName); + + return this.render({ + location, + template: "category", + context: { + category, + symbols: groupSymbols(symbols) + } + }); + } +} diff --git a/src/server/features/apiExplorer/DefinitionHandler.ts b/src/server/features/apiExplorer/DefinitionHandler.ts new file mode 100644 index 0000000..443e293 --- /dev/null +++ b/src/server/features/apiExplorer/DefinitionHandler.ts @@ -0,0 +1,96 @@ +import Handler, { SymbolData, groupSymbols } from "./Handler"; +import printParameterType from "../../projects/story/utils/printParameterType"; +import printParameterFlow from "../../projects/story/utils/printParameterFlow"; +import printSymbolType from "../../projects/story/utils/printSymbolType"; +import Symbol from "../../projects/story/Symbol"; +import ucfirst from "../../utils/ucfirst"; +import WikiParser from "../../documentation/raw/WikiReader"; +import { CategoryData } from "../../documentation/raw/Category"; +import { SymbolType } from "../../projects/story/models/symbol"; + +export interface Parameters { + category: CategoryData; + location: string; + signatures: Array; +} + +export default class DefinitionHandler extends Handler { + async canHandle(location: string): Promise { + const match = /^\/definition\/(.*)/.exec(location); + if (!match) return undefined; + + const project = await this.feature.getProject(); + const signatures = project.story.symbols + .findSymbols(match[1]) + .filter(symbol => symbol.type !== SymbolType.Unknown); + if (!signatures.length) return undefined; + + const categoryName = signatures[0].category; + if (!categoryName) return undefined; + + const category = await this.getDocumentation().getCategory(categoryName); + if (!category) return undefined; + + return { category, location, signatures }; + } + + async loadWiki(name: string, symbols: Array) { + const parser = new WikiParser(); + const statusCode = await parser.load(name); + const editLink = parser.getEditLink(); + + if (statusCode === -1) { + return { editLink, error: true }; + } else if (statusCode !== 200) { + return { editLink, notFound: true }; + } + + const project = await this.feature.getProject(); + parser.appendLinkedSymbols(project, symbols); + + return { + content: parser.getApiContent(), + editLink + }; + } + + async getResponse({ + category, + location, + signatures + }: Parameters): Promise { + await this.feature.loadPartial("breadcrumbs"); + await this.feature.loadPartial("definitions"); + + const signature = signatures[0]; + const type = printSymbolType(signature.type); + + const allSymbols = await this.getSymbols(category.qualifiedName); + const symbols = allSymbols.filter(symbol => symbol.name !== signature.name); + const wiki = await this.loadWiki(signature.name, symbols); + + return this.render({ + location, + template: "definition", + context: { + category, + signatures: signatures.map(signature => ({ + name: signature.name, + parameters: signature.parameters.map(parameter => ({ + flow: parameter.flow + ? printParameterFlow(parameter.flow) + : undefined, + name: parameter.name, + type: parameter.type + ? printParameterType(parameter.type) + : undefined + })) + })), + symbols: groupSymbols(symbols), + title: `${ucfirst(type)} ${signature.name}`, + type, + wiki + } + }); + } +} diff --git a/src/server/features/apiExplorer/Handler.ts b/src/server/features/apiExplorer/Handler.ts new file mode 100644 index 0000000..3ddeb11 --- /dev/null +++ b/src/server/features/apiExplorer/Handler.ts @@ -0,0 +1,112 @@ +import ApiExplorerFeature from "./index"; +import Documentation from "../../documentation/Documentation"; +import printSymbol from "../../projects/story/utils/printSymbol"; +import { SymbolType } from "../../projects/story/models/symbol"; +import printSymbolType from "../../projects/story/utils/printSymbolType"; +import ucfirst from "../../utils/ucfirst"; + +export interface SymbolData { + name: string; + type: string; +} + +export interface SymbolGroup { + symbols: Array; + title: string; +} + +export function sortSymbols(a: SymbolData, b: SymbolData) { + return a.name.localeCompare(b.name); +} + +export function groupSymbols(symbols: Array): Array { + const groups: { [type: string]: SymbolGroup } = {}; + for (const symbol of symbols) { + if (!(symbol.type in groups)) { + let title = `${ucfirst(symbol.type)}s`; + if (title === "Querys") title = "Queries"; + groups[symbol.type] = { symbols: [], title }; + } + + groups[symbol.type].symbols.push(symbol); + } + + return Object.keys(groups) + .sort() + .map(key => { + groups[key].symbols.sort(sortSymbols); + return groups[key]; + }); +} + +export default abstract class Handler { + feature: ApiExplorerFeature; + + constructor(feature: ApiExplorerFeature) { + this.feature = feature; + } + + abstract async canHandle(location: string): Promise; + + abstract async getResponse(params: T): Promise; + + getDocumentation(): Documentation { + return this.feature.server.projects.docProvider; + } + + getNonce() { + let text = ""; + const possible = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + for (let i = 0; i < 32; i++) { + text += possible.charAt(Math.floor(Math.random() * possible.length)); + } + + return text; + } + + async getSymbols(categoryName: string): Promise> { + const result: Array = []; + const nameMap: { [name: string]: SymbolData } = {}; + const project = await this.feature.getProject(); + const { symbols } = project.story.symbols; + + for (const symbol of symbols) { + const { category, name } = symbol; + if (name in nameMap || !category || !category.startsWith(categoryName)) { + continue; + } + + const data: SymbolData = { + name: symbol.name, + type: printSymbolType(symbol.type) + }; + + nameMap[name] = data; + result.push(data); + } + + return result; + } + + async render({ + context, + location, + template + }: { + context: any; + location: string; + template: string; + }) { + const { feature } = this; + const page = await feature.getTemplate(template); + const content = page(context); + + const layout = await feature.getTemplate("layout"); + return layout({ + content, + location, + nonce: this.getNonce() + }); + } +} diff --git a/src/server/features/apiExplorer/index.ts b/src/server/features/apiExplorer/index.ts new file mode 100644 index 0000000..b2dc700 --- /dev/null +++ b/src/server/features/apiExplorer/index.ts @@ -0,0 +1,106 @@ +import { compile, registerPartial, registerHelper } from "handlebars"; +import { join } from "path"; + +import CategoryHandler from "./CategoryHandler"; +import DefinitionHandler from "./DefinitionHandler"; +import getPackagePath from "../../../shared/getPackagePath"; +import Handler from "./Handler"; +import Project from "../../projects/Project"; +import runSafeAsync from "../../utils/runSafeAsync"; +import Server from "../../Server"; +import { apiRequest, ApiResult } from "../../../shared/requests"; +import { Feature } from ".."; +import { readFile } from "../../../shared/fs"; + +export default class ApiExplorerFeature extends Feature { + handlers: Array | undefined; + partials: { [name: string]: boolean } = {}; + templates: { [name: string]: HandlebarsTemplateDelegate } = {}; + + constructor(server: Server) { + super(server); + + const { connection } = server; + connection.onRequest(apiRequest, (location, token) => + runSafeAsync( + () => this.handleApiRequest(location), + null, + `Error while fetching api ${location}`, + token + ) + ); + } + + getHandlers(): Array { + let { handlers } = this; + if (!handlers) { + this.handlers = handlers = [ + new CategoryHandler(this), + new DefinitionHandler(this) + ]; + } + + return handlers; + } + + async getProject(): Promise { + const { projects } = this.server; + if (projects.projects.length) { + const project = projects.projects[0]; + if (!project.story.isInitializing) { + return project; + } + } + + return new Promise(resolve => { + function onProjectReady(project: Project) { + projects.removeListener("projectReady", onProjectReady); + resolve(project); + } + + projects.addListener("projectReady", onProjectReady); + }); + } + + async getTemplate(name: string) { + const { templates } = this; + if (!(name in this.templates)) { + templates[name] = await this.loadTemplate("pages", name); + } + + return templates[name]; + } + + async handleApiRequest(location: string): Promise { + const handlers = this.getHandlers(); + for (const handler of handlers) { + const params = await handler.canHandle(location); + if (params) { + const content = await handler.getResponse(params); + return { content, location }; + } + } + + return null; + } + + async loadPartial(name: string) { + if (!this.partials[name]) { + registerPartial(name, await this.loadTemplate("partials", name)); + this.partials[name] = true; + } + } + + private async loadTemplate(group: string, name: string) { + const fileName = join( + getPackagePath(), + "resources", + "templates", + group, + `${name}.hbs` + ); + + const input = await readFile(fileName, { encoding: "utf-8" }); + return compile(input); + } +} diff --git a/src/server/features/completion/index.ts b/src/server/features/completion/index.ts new file mode 100644 index 0000000..49fab2b --- /dev/null +++ b/src/server/features/completion/index.ts @@ -0,0 +1,335 @@ +import { + ServerCapabilities, + CompletionParams, + CompletionItem, + CompletionItemKind +} from "vscode-languageserver"; + +import printSymbol from "../../projects/story/utils/printSymbol"; +import printSymbolType from "../../projects/story/utils/printSymbolType"; +import Project from "../../projects/Project"; +import Resource from "../../projects/story/resources/Resource"; +import runSafeAsync from "../../utils/runSafeAsync"; +import Server from "../../Server"; +import Symbol from "../../projects/story/Symbol"; +import ucfirst from "../../utils/ucfirst"; +import unpackPosition from "../../parsers/story/utils/unpackPosition"; +import { Feature } from ".."; +import { NodeType, AnyNode } from "../../parsers/story/models/nodes"; +import { SymbolType } from "../../projects/story/models/symbol"; +import printParameterType from "../../projects/story/utils/printParameterType"; + +const SYMBOL_DATA = "divinity.symbol:"; + +const KEYWORDS: Array = [ + // Better handeled by snippets + // "IF", + // "PROC", + // "QRY", + // "AND", + + "THEN", + "NOT", + "INITSECTION", + "KBSECTION", + "EXITSECTION", + "ENDEXITSECTION", + "GoalCompleted" +].map(keyword => ({ + label: keyword, + kind: 14 as any // CompletionItemKind.Keyword +})); + +export enum CompletionType { + Condition, + Events, + Global, + Parameter, + Procedures, + Queries, + Rule, + Type +} + +function fromRuleType(ruleType: string, type: CompletionType): CompletionType { + if (ruleType === "IF") { + return CompletionType.Events; + } else if (ruleType === "PROC") { + return CompletionType.Procedures; + } else if (ruleType === "QRY") { + return CompletionType.Queries; + } + + return type; +} + +export default class CompletionFeature extends Feature { + constructor(server: Server) { + super(server); + + const { connection } = server; + connection.onCompletion((params, token) => + runSafeAsync( + () => this.handleCompletion(params), + null, + `Error while computing completions for ${params.textDocument.uri}`, + token + ) + ); + + connection.onCompletionResolve((params, token) => + this.handleResolveCompletion(params) + ); + } + + getCapabilities(): Partial { + return { + completionProvider: { + resolveProvider: true + } + }; + } + + getConditionCompletions(resource: Resource): Array { + const { symbols } = resource.story.symbols; + return this.getSymbolCompletions( + resource.story.project, + symbols.filter( + symbol => + symbol.type === SymbolType.Database || + symbol.type === SymbolType.Query + ) + ); + } + + getEventCompletions(resource: Resource): Array { + const { symbols } = resource.story.symbols; + return this.getSymbolCompletions( + resource.story.project, + symbols.filter( + symbol => + symbol.type === SymbolType.Database || + symbol.type === SymbolType.Event + ) + ); + } + + getGlobalCompletions(resource: Resource): Array { + const { symbols } = resource.story.symbols; + return this.getSymbolCompletions( + resource.story.project, + symbols.filter( + symbol => + symbol.type === SymbolType.Call || symbol.type === SymbolType.Database + ) + ); + } + + getParameterCompletions( + resource: Resource, + nodes?: Array + ): Array { + const { instances } = resource.story.project.levels; + const result: Array = instances.map(instance => ({ + kind: CompletionItemKind.Reference, + label: `${instance.name}_${instance.guid}` + })); + + const variablesAt = this.getVariablesAt(nodes); + if (variablesAt) { + for (const variable of variablesAt.variablesBefore) { + result.push({ + detail: printParameterType(variable.type), + kind: CompletionItemKind.Variable, + label: variable.displayName + }); + } + } + + return result; + } + + getProceduresCompletions(resource: Resource): Array { + const { symbols } = resource.story.symbols; + return this.getSymbolCompletions( + resource.story.project, + symbols.filter(symbol => symbol.type === SymbolType.Call) + ); + } + + getQueriesCompletions(resource: Resource): Array { + const { symbols } = resource.story.symbols; + return this.getSymbolCompletions( + resource.story.project, + symbols.filter(symbol => symbol.type === SymbolType.Query) + ); + } + + getSymbolCompletions( + project: Project, + symbols: Array + ): Array { + const names: { [name: string]: boolean } = {}; + const result: Array = []; + + for (const symbol of symbols) { + if (symbol.name in names) continue; + names[symbol.name] = true; + + let kind: CompletionItemKind = CompletionItemKind.Struct; + switch (symbol.type) { + case SymbolType.Call: + kind = CompletionItemKind.Function; + break; + case SymbolType.Database: + kind = CompletionItemKind.Enum; + break; + case SymbolType.Event: + kind = CompletionItemKind.Event; + break; + case SymbolType.Query: + kind = CompletionItemKind.Interface; + break; + default: + continue; + } + + result.push({ + data: `${SYMBOL_DATA}${project.meta.UUID}`, + kind, + label: symbol.name + }); + } + + return result.concat(KEYWORDS); + } + + getTypeCompletions(resource: Resource): Array { + const { types } = resource.story; + return types.map(type => ({ + kind: CompletionItemKind.TypeParameter, + label: type + })); + } + + async handleCompletion( + params: CompletionParams + ): Promise | null> { + const { nodes, resource } = await this.getNodesAt(params); + if (!resource) { + return null; + } + + const document = resource.getDocument(); + let type: CompletionType = CompletionType.Global; + + if (document && nodes) { + const offset = document.offsetAt(params.position); + for (const node of nodes) { + if (node.type === NodeType.TypeAnnotation) { + type = CompletionType.Type; + break; + } + + if ( + node.type === NodeType.Signature && + node.identifier.endOffset < offset + ) { + const chunk = document.getText({ + start: unpackPosition(node.identifier.endPosition), + end: params.position + }); + + type = /\([^\)]*$/.test(chunk.substr(1)) + ? CompletionType.Type + : CompletionType.Parameter; + break; + } + + if (node.type === NodeType.ActionBlock) { + type = CompletionType.Global; + break; + } + + if (node.type === NodeType.ConditionBlock) { + type = CompletionType.Condition; + break; + } + + if (node.type === NodeType.Rule) { + if (offset > node.signature.endOffset) { + type = CompletionType.Condition; + } else { + type = fromRuleType(node.ruleType, type); + } + break; + } + } + } + + if (document && type === CompletionType.Global) { + const chunk = document.getText({ + start: document.positionAt( + Math.max(0, document.offsetAt(params.position) - 64) + ), + end: params.position + }); + + const match = /(AND|IF|PROC|QRY)[\n\r\s]+[A-Za-z0-9_-]*$/.exec(chunk); + if (match) { + if (match[1] === "AND") { + type = CompletionType.Condition; + } else { + type = fromRuleType(match[1], type); + } + } + } + + switch (type) { + case CompletionType.Condition: + return this.getConditionCompletions(resource); + case CompletionType.Events: + return this.getEventCompletions(resource); + case CompletionType.Parameter: + return this.getParameterCompletions(resource, nodes); + case CompletionType.Procedures: + return this.getProceduresCompletions(resource); + case CompletionType.Queries: + return this.getQueriesCompletions(resource); + case CompletionType.Type: + return this.getTypeCompletions(resource); + default: + return this.getGlobalCompletions(resource); + } + } + + async handleResolveCompletion(item: CompletionItem): Promise { + const { data } = item; + if (typeof data === "string" && data.startsWith(SYMBOL_DATA)) { + const uid = data.substr(SYMBOL_DATA.length); + const project = this.server.projects.findProjectByUid(uid); + if (!project) return item; + + const { symbols } = project.story; + const chunks: Array = []; + const matchingSymbols = symbols.findSymbols(item.label); + if (matchingSymbols.length === 0) return item; + + const symbol = matchingSymbols[0]; + item.detail = ucfirst(printSymbolType(symbol.type)); + chunks.push("```divinity-story-goal", printSymbol(symbol, true), "```"); + + const documentation = await symbols.getDocumentation(symbol); + if (documentation && documentation.description) { + chunks.push(documentation.description); + } + + item.documentation = { + kind: "markdown", + value: chunks.join("\n") + }; + } + + return item; + } +} diff --git a/src/server/features/definition/index.ts b/src/server/features/definition/index.ts new file mode 100644 index 0000000..ce3570d --- /dev/null +++ b/src/server/features/definition/index.ts @@ -0,0 +1,75 @@ +import { + Location, + ServerCapabilities, + TextDocumentPositionParams +} from "vscode-languageserver"; + +import Feature from "../Feature"; +import runSafeAsync from "../../utils/runSafeAsync"; +import Server from "../../Server"; +import Symbol from "../../projects/story/Symbol"; +import unpackRange from "../../parsers/story/utils/unpackRange"; +import { apiShowEvent } from "../../../shared/notifications"; + +export default class DefinitionFeature extends Feature { + constructor(server: Server) { + super(server); + + server.connection.onDefinition((params, token) => + runSafeAsync( + () => this.handleDefinition(params), + null, + `Error while computing definition for ${params.textDocument.uri}`, + token + ) + ); + } + + getCapabilities(): ServerCapabilities { + return { + definitionProvider: true + }; + } + + async handleDefinition( + params: TextDocumentPositionParams + ): Promise { + const { resource, symbol } = await this.getSymbolAt(params); + if (!symbol) { + return null; + } + + let systemSymbol: Symbol | undefined; + if (symbol.isSystem) { + systemSymbol = symbol; + } else if (resource) { + const symbols = resource.story.symbols.findSymbols(symbol.name); + systemSymbol = symbols.find(symbol => symbol.isSystem); + } + + if (systemSymbol) { + this.server.connection.sendNotification( + apiShowEvent, + `/definition/${symbol.name}` + ); + + return { + uri: params.textDocument.uri, + range: { + end: params.position, + start: params.position + } + }; + } + + if (symbol.resolvedDefinition) { + const definition = symbol.resolvedDefinition; + return { + uri: definition.goal.resource.getUri(), + range: unpackRange(definition) + }; + } + + return null; + } +} diff --git a/src/server/features/diagnostics/index.ts b/src/server/features/diagnostics/index.ts new file mode 100644 index 0000000..98bf1a1 --- /dev/null +++ b/src/server/features/diagnostics/index.ts @@ -0,0 +1,89 @@ +import { Connection } from "vscode-languageserver/lib/main"; +import { TextDocumentChangeEvent } from "vscode-languageserver/lib/main"; + +import Feature from "../Feature"; +import Resource from "../../projects/story/resources/Resource"; +import Server from "../../Server"; +import parseUri from "../../utils/parseUri"; + +export default class DiagnosticsFeature extends Feature { + constructor(server: Server) { + super(server); + + const { documents, projects } = server; + documents.onDidClose(this.handleDocumentDidClose); + documents.onDidChangeContent(this.handleDocumentChangeContent); + documents.onDidOpen(this.handleDocumentOpen); + projects.on("diagnostics", this.handleDiagnostics); + } + + private async findFile( + event: TextDocumentChangeEvent + ): Promise { + const { projects } = this.server; + return projects.findResource(event.document.uri); + } + + private handleDocumentDidClose = async (event: TextDocumentChangeEvent) => { + // this.documentSettings.delete(event.document.uri); + const goal = await this.findFile(event); + if (goal) { + goal.setDocument(null); + } + }; + + private handleDocumentChangeContent = async ( + event: TextDocumentChangeEvent + ) => { + const file = await this.findFile(event); + if (file) { + file.invalidate(); + } + }; + + private handleDocumentOpen = async (event: TextDocumentChangeEvent) => { + const file = await this.findFile(event); + if (file) { + file.setDocument(event.document); + this.handleDiagnostics(file); + } else { + this.server.connection.window.showErrorMessage( + "This document seems not be part of a Divinity modification." + ); + } + }; + + private handleDiagnostics = (file: Resource) => { + const { connection } = this.server; + + connection.sendDiagnostics({ + uri: file.getUri(), + diagnostics: file.getDiagnostics().map(d => ({ + severity: d.severity, + range: { + start: d.range.start, + end: d.range.end + }, + message: d.message + })) + }); + }; + + initialize(connection: Connection): void { + connection.workspace.getWorkspaceFolders().then(folders => { + if (!folders) { + return; + } + + const { projects } = this.server; + for (const folder of folders) { + const parsedUri = parseUri(folder.uri); + if (!parsedUri || parsedUri.type !== "path") { + continue; + } + + projects.tryCreateForFolder(parsedUri.path); + } + }); + } +} diff --git a/src/server/features/divProvider/index.ts b/src/server/features/divProvider/index.ts new file mode 100644 index 0000000..c028f9c --- /dev/null +++ b/src/server/features/divProvider/index.ts @@ -0,0 +1,34 @@ +import { Connection } from "vscode-languageserver/lib/main"; + +import Feature from "../Feature"; +import { + divRequestEvent, + divRequestResultEvent, + DivRequestResult +} from "../../../shared/notifications"; + +export default class DivProviderFeature extends Feature { + initialize(connection: Connection): void { + connection.onNotification(divRequestEvent, this.handleDivRequest); + } + + handleDivRequest = async (uri: string) => { + const { connection, projects } = this.server; + if (!connection) { + return; + } + + let content: string | null = null; + const resource = await projects.findResource(uri); + if (resource) { + content = await resource.getSource(); + } + + const result: DivRequestResult = { + content, + uri + }; + + connection.sendNotification(divRequestResultEvent, result); + }; +} diff --git a/src/server/features/documentSymbols/index.ts b/src/server/features/documentSymbols/index.ts new file mode 100644 index 0000000..84c12e7 --- /dev/null +++ b/src/server/features/documentSymbols/index.ts @@ -0,0 +1,101 @@ +import { + ServerCapabilities, + SymbolKind, + SymbolInformation, + TextDocument +} from "vscode-languageserver/lib/main"; + +import Feature from "../Feature"; +import GoalResource from "../../projects/story/resources/GoalResource"; +import HeaderGoalResource from "../../projects/story/resources/HeaderGoalResource"; +import printNode from "../../parsers/story/utils/printNode"; +import runSafeAsync from "../../utils/runSafeAsync"; +import Server from "../../Server"; +import toLocation from "../../utils/toLocation"; +import { AbstractGoalNode } from "../../parsers/story/models/nodes"; + +export default class DocumentSymbolsFeature extends Feature { + constructor(server: Server) { + super(server); + + server.connection.onDocumentSymbol((params, token) => + runSafeAsync( + () => this.getSymbols(params.textDocument.uri), + [], + `Error while computing document symbols for ${params.textDocument.uri}`, + token + ) + ); + } + + createGoalSymbolInfos(document: TextDocument, story: AbstractGoalNode) { + const result: Array = []; + if (story.init) { + result.push({ + containerName: "", + name: "Init section", + kind: 3, // SymbolKind.Namespace, + location: toLocation(document, story.init) + }); + } + + if (story.kb) { + result.push({ + containerName: "", + name: "KB section", + kind: 3, // SymbolKind.Namespace, + location: toLocation(document, story.kb) + }); + + for (const rule of story.kb.rules) { + let kind: SymbolKind = 12; // SymbolKind.Function; + if (rule.ruleType == "IF") { + kind = 24; // SymbolKind.Event; + } else if (rule.ruleType === "QRY") { + kind = 11; // SymbolKind.Interface; + } + + result.push({ + containerName: "KB section", + name: printNode(rule), + kind, + location: toLocation(document, rule) + }); + } + + if (story.exit) { + result.push({ + containerName: "", + name: "Exit section", + kind: 3, // SymbolKind.Namespace, + location: toLocation(document, story.exit) + }); + } + } + + return result; + } + + getCapabilities(): Partial { + return { + documentSymbolProvider: true + }; + } + + async getSymbols(uri: string) { + const { projects } = this.server; + const goal = await projects.findResource(uri); + if ( + !(goal instanceof GoalResource) && + !(goal instanceof HeaderGoalResource) + ) { + return null; + } + + await goal.story.whenReady(); + + const document = goal.getDocument(); + const node = await goal.getRootNode(); + return document && node ? this.createGoalSymbolInfos(document, node) : null; + } +} diff --git a/src/server/features/hover/ConstantProvider.ts b/src/server/features/hover/ConstantProvider.ts new file mode 100644 index 0000000..73313a2 --- /dev/null +++ b/src/server/features/hover/ConstantProvider.ts @@ -0,0 +1,43 @@ +import { Hover } from "vscode-languageserver"; + +import unpackRange from "../../parsers/story/utils/unpackRange"; +import { NodeType } from "../../parsers/story/models/nodes"; +import { SyncProvider, ProviderContext } from "./Provider"; + +export default class ConstantProvider extends SyncProvider { + invoke({ node }: ProviderContext): Hover | null { + let contents: Array | undefined; + + if (node.type === NodeType.RealLiteral) { + contents = [ + "**Constant real**", + "```divinity-story-goal", + `(REAL)${node.value}`, + "```" + ]; + } else if (node.type === NodeType.IntegerLiteral) { + contents = [ + "**Constant integer**", + "```divinity-story-goal", + `(INTEGER)${node.value}`, + "```" + ]; + } else if (node.type === NodeType.StringLiteral) { + contents = [ + "**Constant string**", + "```divinity-story-goal", + `(STRING)"${node.value}"`, + "```" + ]; + } + + if (contents) { + return { + contents: contents.join("\n"), + range: unpackRange(node) + }; + } + + return null; + } +} diff --git a/src/server/features/hover/GuidProvider.ts b/src/server/features/hover/GuidProvider.ts new file mode 100644 index 0000000..585f9b0 --- /dev/null +++ b/src/server/features/hover/GuidProvider.ts @@ -0,0 +1,35 @@ +import { Hover } from "vscode-languageserver"; + +import ucfirst from "../../utils/ucfirst"; +import unpackRange from "../../parsers/story/utils/unpackRange"; +import { NodeType } from "../../parsers/story/models/nodes"; +import { SyncProvider, ProviderContext } from "./Provider"; + +export default class GuidProvider extends SyncProvider { + invoke({ node, resource }: ProviderContext): Hover | null { + if (node.type !== NodeType.GuidLiteral) return null; + + const { levels } = resource.story.project; + const instance = levels.instances.find( + instance => instance.guid === node.guid + ); + + let instanceInfo = ""; + if (instance) { + instanceInfo = `${ucfirst(instance.type)} "${instance.name}" from level ${ + instance.level + }`; + } + + return { + contents: [ + "**GUID reference** ", + instanceInfo, + "```divinity-story-goal", + `(GUIDSTRING)${node.guid}`, + "```" + ].join("\n"), + range: unpackRange(node) + }; + } +} diff --git a/src/server/features/hover/Provider.ts b/src/server/features/hover/Provider.ts new file mode 100644 index 0000000..8278096 --- /dev/null +++ b/src/server/features/hover/Provider.ts @@ -0,0 +1,32 @@ +import { Hover } from "vscode-languageserver"; + +import HoverFeature from "./index"; +import Resource from "../../projects/story/resources/Resource"; +import { AnyNode } from "../../parsers/story/models/nodes"; + +export type AnyProvider = AsyncProvider | SyncProvider; + +export interface ProviderContext { + index: number; + node: AnyNode; + nodes: Array; + resource: Resource; +} + +export abstract class Provider { + feature: HoverFeature; + + constructor(feature: HoverFeature) { + this.feature = feature; + } +} + +export abstract class AsyncProvider extends Provider { + abstract canHandle(context: ProviderContext): T | null; + + abstract async invoke(data: T): Promise; +} + +export abstract class SyncProvider extends Provider { + abstract invoke(context: ProviderContext): Hover | null; +} diff --git a/src/server/features/hover/SignatureProvider.ts b/src/server/features/hover/SignatureProvider.ts new file mode 100644 index 0000000..eb4c3c5 --- /dev/null +++ b/src/server/features/hover/SignatureProvider.ts @@ -0,0 +1,83 @@ +import { Hover } from "vscode-languageserver"; + +import printSymbol from "../../projects/story/utils/printSymbol"; +import Resource from "../../projects/story/resources/Resource"; +import Symbol from "../../projects/story/Symbol"; +import unpackRange from "../../parsers/story/utils/unpackRange"; +import { AsyncProvider, ProviderContext } from "./Provider"; +import { NodeType, AnyNode } from "../../parsers/story/models/nodes"; +import { SymbolType } from "../../projects/story/models/symbol"; +import isCallerNode, { + CallerNode +} from "../../parsers/story/utils/isCallerNode"; + +export interface Params { + callerNode: CallerNode; + node: AnyNode; + resource: Resource; + symbol: Symbol; +} + +export default class SignatureProvider extends AsyncProvider { + canHandle({ index, node, nodes, resource }: ProviderContext): Params | null { + const callerNode = nodes[index + 2]; + if (!isCallerNode(callerNode) || node.type !== NodeType.Identifier) { + return null; + } + + const { symbol } = callerNode; + if (!symbol) return null; + + return { callerNode, node, resource, symbol }; + } + + getUsageDescription(node: CallerNode, symbolType: SymbolType): string { + switch (node.type) { + case NodeType.SignatureAction: + return symbolType === SymbolType.Database + ? "Database write" + : "Procedure call"; + case NodeType.SignatureCondition: + return symbolType === SymbolType.Database + ? "Database query" + : "Query condition"; + case NodeType.Rule: + switch (node.ruleType) { + case "IF": + return symbolType === SymbolType.Database + ? "Database event handler" + : "Event handler"; + case "PROC": + return "Procedure definition"; + case "QRY": + return "Query definition"; + } + } + + return "Unknown invocation"; + } + + async invoke({ + callerNode, + node, + resource, + symbol + }: Params): Promise { + const documentation = await resource.story.symbols.getDocumentation(symbol); + let description = ""; + if (documentation && documentation.description) { + description = documentation.description; + } + + return { + contents: [ + `**${this.getUsageDescription(callerNode, symbol.type)}** `, + description, + "```divinity-story-goal", + `${printSymbol(symbol, true)}`, + "```" + ].join("\n"), + range: unpackRange(node) + }; + } +} diff --git a/src/server/features/hover/VariableProvider.ts b/src/server/features/hover/VariableProvider.ts new file mode 100644 index 0000000..e33522b --- /dev/null +++ b/src/server/features/hover/VariableProvider.ts @@ -0,0 +1,49 @@ +import { Hover } from "vscode-languageserver"; + +import printParameterType from "../../projects/story/utils/printParameterType"; +import unpackRange from "../../parsers/story/utils/unpackRange"; +import { IdentifierType, NodeType } from "../../parsers/story/models/nodes"; +import { SyncProvider, ProviderContext } from "./Provider"; + +export default class VariableProvider extends SyncProvider { + invoke({ node, nodes }: ProviderContext): Hover | null { + if ( + node.type !== NodeType.Identifier || + node.identifierType !== IdentifierType.Variable + ) { + return null; + } + + const variablesAt = this.feature.getVariablesAt(nodes); + if (!variablesAt) return null; + + const { rule, variables } = variablesAt; + const variable = variables.find( + variable => variable.name === node.name.toLowerCase() + ); + + if (!variable) { + return null; + } + + let origin: string; + if (variable.fromSymbol === rule.symbol && rule.ruleType !== "IF") { + origin = `From parameter #${variable.fromIndex + 1} of definition`; + } else { + origin = `From parameter #${variable.fromIndex + 1} of \`${ + variable.fromSymbol.name + }\``; + } + + return { + contents: [ + "**Local variable** ", + origin, + "```divinity-story-goal", + `(${printParameterType(variable.type)})${node.name}`, + "```" + ].join("\n"), + range: unpackRange(node) + }; + } +} diff --git a/src/server/features/hover/index.ts b/src/server/features/hover/index.ts new file mode 100644 index 0000000..43dc9e3 --- /dev/null +++ b/src/server/features/hover/index.ts @@ -0,0 +1,75 @@ +import { + Hover, + ServerCapabilities, + TextDocumentPositionParams +} from "vscode-languageserver/lib/main"; + +import ConstantProvider from "./ConstantProvider"; +import GuidProvider from "./GuidProvider"; +import runSafeAsync from "../../utils/runSafeAsync"; +import Server from "../../Server"; +import SignatureProvider from "./SignatureProvider"; +import VariableProvider from "./VariableProvider"; +import { AnyProvider, ProviderContext, SyncProvider } from "./Provider"; +import { Feature } from ".."; + +export default class HoverFeature extends Feature { + providers: Array; + + constructor(server: Server) { + super(server); + + const { connection } = server; + connection.onHover((params, token) => + runSafeAsync( + () => this.handleHover(params), + null, + `Error while computing document hover for ${params.textDocument.uri}`, + token + ) + ); + + this.providers = [ + new ConstantProvider(this), + new GuidProvider(this), + new SignatureProvider(this), + new VariableProvider(this) + ]; + } + + getCapabilities(): Partial { + return { + hoverProvider: true + }; + } + + async handleHover(params: TextDocumentPositionParams): Promise { + const { nodes, resource } = await this.getNodesAt(params); + if (!nodes || !resource) return null; + + const { providers } = this; + let result: Hover | null = null; + let localContext: {} | null; + + for (let index = 0; index < nodes.length; index++) { + const context: ProviderContext = { + index, + node: nodes[index], + nodes, + resource + }; + + for (const provider of providers) { + if (provider instanceof SyncProvider) { + result = provider.invoke(context); + } else if ((localContext = provider.canHandle(context))) { + result = await provider.invoke(localContext); + } + + if (result) return result; + } + } + + return null; + } +} diff --git a/src/server/features/index.ts b/src/server/features/index.ts new file mode 100644 index 0000000..e90d6bd --- /dev/null +++ b/src/server/features/index.ts @@ -0,0 +1,32 @@ +import Feature, { FeatureFactory } from "./Feature"; + +import ActivityIndicatorFeature from "./activityIndicator"; +import ApiExplorerFeature from "./apiExplorer"; +import CompletionFeature from "./completion"; +import DefinitionFeature from "./definition"; +import DiagnosticsFeature from "./diagnostics"; +import DivProviderFeature from "./divProvider"; +import DocumentSymbolsFeature from "./documentSymbols"; +import HoverFeature from "./hover"; +import ReferencesFeature from "./references"; +import RenameFeature from "./rename"; +import SignatureHelpFeature from "./signatureHelp"; +import StoryOutlineFeature from "./storyOutline"; + +const factories: Array = [ + ActivityIndicatorFeature, + ApiExplorerFeature, + CompletionFeature, + DefinitionFeature, + DiagnosticsFeature, + DivProviderFeature, + DocumentSymbolsFeature, + HoverFeature, + ReferencesFeature, + RenameFeature, + SignatureHelpFeature, + StoryOutlineFeature +]; + +export { Feature }; +export default factories; diff --git a/src/server/features/references/index.ts b/src/server/features/references/index.ts new file mode 100644 index 0000000..ac4dca3 --- /dev/null +++ b/src/server/features/references/index.ts @@ -0,0 +1,60 @@ +import { + Location, + ReferenceParams, + ServerCapabilities +} from "vscode-languageserver"; + +import eachCaller from "../../parsers/story/utils/eachCaller"; +import Feature from "../Feature"; +import runSafeAsync from "../../utils/runSafeAsync"; +import Server from "../../Server"; +import Symbol from "../../projects/story/Symbol"; +import unpackRange from "../../parsers/story/utils/unpackRange"; + +export default class ReferencesFeature extends Feature { + constructor(server: Server) { + super(server); + + server.connection.onReferences((params, token) => + runSafeAsync( + () => this.handleReferences(params), + null, + `Error while computing document hover for ${params.textDocument.uri}`, + token + ) + ); + } + + getCapabilities(): Partial { + return { + referencesProvider: true + }; + } + + async getSymbolReferences(symbol: Symbol): Promise> { + const result: Array = []; + + for (const goal of symbol.usages) { + const rootNode = await goal.resource.getRootNode(true); + const uri = goal.resource.getUri(); + + for (const { node } of eachCaller(rootNode)) { + if (node.symbol === symbol) { + result.push({ + uri, + range: unpackRange(node) + }); + } + } + } + + return result; + } + + async handleReferences( + params: ReferenceParams + ): Promise | null> { + const { symbol } = await this.getSymbolAt(params); + return symbol ? this.getSymbolReferences(symbol) : null; + } +} diff --git a/src/server/features/rename/index.ts b/src/server/features/rename/index.ts new file mode 100644 index 0000000..c6ca228 --- /dev/null +++ b/src/server/features/rename/index.ts @@ -0,0 +1,222 @@ +import { + ServerCapabilities, + RenameParams, + WorkspaceEdit, + ResponseError, + TextDocumentEdit +} from "vscode-languageserver"; + +import GoalResource from "../../projects/story/resources/GoalResource"; +import eachNodeRecursive from "../../parsers/story/utils/eachNodeRecursive"; +import Feature from "../Feature"; +import Resource from "../../projects/story/resources/Resource"; +import runSafeAsync from "../../utils/runSafeAsync"; +import Server from "../../Server"; +import unpackRange from "../../parsers/story/utils/unpackRange"; + +import { + NodeType, + GuidLiteralNode, + IdentifierType, + IdentifierNode, + AnyNode +} from "../../parsers/story/models/nodes"; + +import isCallerNode, { + CallerNode +} from "../../parsers/story/utils/isCallerNode"; + +const GUID_VALIDATION = /^[A-Za-z0-9_-]*?[0-9A-Fa-f]{8}-([0-9A-Fa-f]{4}-){3}[0-9A-Fa-f]{12}$/; +const SIGNATURE_VALIDATION = /^[A-Za-z_][A-Za-z0-9_-]*$/; +const VARIABLE_VALIDATION = /^_[A-Za-z0-9_-]*$/; + +export interface Context { + newName: string; + nodes: Array; + resource: Resource; +} + +export default class RenameFeature extends Feature { + constructor(server: Server) { + super(server); + + server.connection.onRenameRequest((params, token) => + runSafeAsync( + () => this.handleRename(params), + null, + `Error while computing rename for ${params.textDocument.uri}`, + token + ) + ); + } + + getCapabilities(): Partial { + return { + renameProvider: true + }; + } + + async renameGuid( + identifier: GuidLiteralNode, + { newName, resource }: Context + ): Promise> { + const documentChanges: Array = []; + const { guid } = identifier; + + if (!GUID_VALIDATION.test(newName)) { + return new ResponseError(-1, "The entered guid is invalid."); + } + + for (const goal of resource.story.getGoals()) { + if (!(goal.resource instanceof GoalResource)) { + continue; + } + + const rootNode = await goal.resource.getRootNode(true); + let change: TextDocumentEdit | undefined; + + for (const { node } of eachNodeRecursive(rootNode)) { + if (node.type !== NodeType.GuidLiteral || node.guid !== guid) { + continue; + } + + if (!change) { + change = goal.resource.getTextEdit(); + documentChanges.push(change); + } + + change.edits.push({ + newText: newName, + range: unpackRange(node) + }); + } + } + + return { documentChanges }; + } + + async renameSignature( + origin: CallerNode, + { newName }: Context + ): Promise> { + const { symbol } = origin; + + if (!symbol) { + return new ResponseError(-1, "Cannot rename unresolved signatures."); + } + + if (symbol.isSystem) { + return new ResponseError(-1, "Cannot rename system signatures."); + } + + if (!SIGNATURE_VALIDATION.test(newName)) { + return new ResponseError(-1, "The entered name is invalid."); + } + + const documentChanges: Array = []; + + for (let index = 0; index < symbol.usages.length; index++) { + const resource: GoalResource = symbol.usages[index] + .resource as GoalResource; + if (!(resource instanceof GoalResource)) { + return new ResponseError( + -1, + "Cannot rename signatures from the shared mod." + ); + } + + const rootNode = await resource.getRootNode(true); + let change: TextDocumentEdit | undefined; + + for (const { node } of eachNodeRecursive(rootNode)) { + if (!isCallerNode(node) || node.symbol !== symbol) { + continue; + } + + if (!change) { + change = resource.getTextEdit(); + documentChanges.push(change); + } + + change.edits.push({ + newText: newName, + range: unpackRange(node.signature.identifier) + }); + } + } + + return { documentChanges }; + } + + async renameVariable( + origin: IdentifierNode, + { nodes, newName, resource }: Context + ): Promise> { + const rule = nodes.find(node => node.type === NodeType.Rule); + if (!rule) { + return new ResponseError( + -1, + "Only variables inside rules can be renamed." + ); + } + + if (!VARIABLE_VALIDATION.test(newName)) { + return new ResponseError(-1, "The entered variable name is invalid."); + } + + const changes = resource.getTextEdit(); + const name = origin.name.toLowerCase(); + + for (const { node } of eachNodeRecursive(rule)) { + if ( + node.type === NodeType.Identifier && + node.identifierType === IdentifierType.Variable && + node.name.toLowerCase() === name + ) { + changes.edits.push({ newText: newName, range: unpackRange(node) }); + } + } + + return { documentChanges: [changes] }; + } + + async handleRename( + params: RenameParams + ): Promise> { + const { nodes, resource } = await this.getNodesAt(params); + if (!nodes || !resource) { + return new ResponseError( + -1, + "Could not resolve an symbols at the given location." + ); + } + + const context = { + newName: params.newName, + nodes, + resource + }; + + for (let index = 0; index < nodes.length; index++) { + const origin = nodes[index]; + const caller = nodes[index + 2]; + + if (origin.type === NodeType.GuidLiteral) { + return await this.renameGuid(origin, context); + } else if ( + origin.type === NodeType.Identifier && + origin.identifierType === IdentifierType.Variable + ) { + return await this.renameVariable(origin, context); + } else if ( + caller && + isCallerNode(caller) && + origin === caller.signature.identifier + ) { + return await this.renameSignature(caller, context); + } + } + + return new ResponseError(-1, "This symbol cannot be renamed."); + } +} diff --git a/src/server/features/signatureHelp/index.ts b/src/server/features/signatureHelp/index.ts new file mode 100644 index 0000000..e28bba4 --- /dev/null +++ b/src/server/features/signatureHelp/index.ts @@ -0,0 +1,176 @@ +import { + ParameterInformation, + ServerCapabilities, + SignatureHelp, + SignatureInformation, + TextDocumentPositionParams +} from "vscode-languageserver"; + +import Feature from "../Feature"; +import printSymbol from "../../projects/story/utils/printSymbol"; +import Resource from "../../projects/story/resources/Resource"; +import runSafeAsync from "../../utils/runSafeAsync"; +import Server from "../../Server"; +import Symbol from "../../projects/story/Symbol"; +import ucfirst from "../../utils/ucfirst"; +import { DefinitionDoc } from "../../documentation/raw/Definition"; +import { SymbolType } from "../../projects/story/models/symbol"; + +import isCallerNode, { + CallerNode +} from "../../parsers/story/utils/isCallerNode"; + +function getParameters( + symbol: Symbol, + definition: DefinitionDoc | null +): Array { + return symbol.parameters.map(symbolParam => { + let documentation: string | undefined; + if (definition && definition.parameters) { + const paramDefinition = definition.parameters.find( + paramDefinition => paramDefinition.name === symbolParam.name + ); + + if (paramDefinition && paramDefinition.description) { + documentation = ucfirst(paramDefinition.description); + } + } + + return { + documentation, + label: symbolParam.name + }; + }); +} + +async function getSignatures( + node: CallerNode, + resource: Resource +): Promise> { + const { docProvider } = resource.story.project.projects; + const { name } = node.signature.identifier; + const doc = await docProvider.getDocumentation(name); + const signatures: Array = []; + const symbols = resource.story.symbols.findSymbols(name); + + for (const symbol of symbols) { + if (symbol.type === SymbolType.Unknown) { + continue; + } + + const localDoc = symbol.documentation ? symbol.documentation : doc; + let symbolDescription: string | undefined; + if (localDoc && localDoc.description) { + symbolDescription = ucfirst(localDoc.description); + } + + signatures.push({ + documentation: symbolDescription, + label: printSymbol(symbol), + parameters: getParameters(symbol, localDoc) + }); + } + + return signatures; +} + +function sortSignatures( + a: SignatureInformation, + b: SignatureInformation +): number { + return ( + (a.parameters ? a.parameters.length : 0) - + (b.parameters ? b.parameters.length : 0) + ); +} + +export default class SignatureHelpFeature extends Feature { + constructor(server: Server) { + super(server); + + const { connection } = server; + connection.onSignatureHelp((params, token) => + runSafeAsync( + () => this.handleSignatureHelp(params), + null, + `Error while creating signature help for "${params.textDocument.uri}".`, + token + ) + ); + } + + getCapabilities(): Partial { + return { + signatureHelpProvider: { + triggerCharacters: ["("] + } + }; + } + + async handleSignatureHelp( + params: TextDocumentPositionParams + ): Promise { + const { nodes, resource } = await this.getNodesAt(params); + if (!nodes || !resource) return null; + + const document = resource.getDocument(); + if (!document) return null; + + const offset = document.offsetAt(params.position); + + for (const node of nodes) { + if (!isCallerNode(node)) { + continue; + } + + const { signature } = node; + const { identifier, parameters } = signature; + if (offset < signature.startOffset || offset > signature.endOffset) { + return null; + } + + const signatures = await getSignatures(node, resource); + let activeSignature: number | null = null; + let activeParameter: number | null = null; + let numParams = parameters.length; + + for (let index = 0; index < parameters.length; index++) { + if (offset <= parameters[index].endOffset) { + activeParameter = index; + break; + } + } + + if (activeParameter == null) { + activeParameter = parameters.length; + numParams += 1; + } + + if (offset <= identifier.endOffset || offset >= signature.endOffset) { + activeParameter = -1; + } + + signatures.sort(sortSignatures); + for (let index = 0; index < signatures.length; index++) { + const signatureParams = signatures[index].parameters; + const numSignatureParams = signatureParams ? signatureParams.length : 0; + if (numParams <= numSignatureParams) { + activeSignature = index; + break; + } + } + + if (activeSignature === null) { + activeSignature = signatures.length - 1; + } + + return { + activeSignature, + activeParameter, + signatures + }; + } + + return null; + } +} diff --git a/src/server/features/storyOutline/index.ts b/src/server/features/storyOutline/index.ts new file mode 100644 index 0000000..e93b697 --- /dev/null +++ b/src/server/features/storyOutline/index.ts @@ -0,0 +1,189 @@ +import { TextDocumentEdit } from "vscode-languageserver"; + +import debounce from "../../utils/debounce"; +import Feature from "../Feature"; +import Goal from "../../projects/story/Goal"; +import HeaderGoalResource from "../../projects/story/resources/HeaderGoalResource"; +import Project from "../../projects/Project"; +import runSafeAsync from "../../utils/runSafeAsync"; +import Server from "../../Server"; +import unpackRange from "../../parsers/story/utils/unpackRange"; +import { NodeType } from "../../parsers/story/models/nodes"; + +import { + renameGoalRequest, + RenameGoalParams, + RenameGoalResult, + moveGoalRequest, + MoveGoalParams, + MoveGoalResult +} from "../../../shared/requests"; + +import { + goalsChangedEvent, + GoalsChanged, + GoalInfo +} from "../../../shared/notifications"; + +export default class StoryOutlineFeature extends Feature { + treeVersion: number = 0; + + constructor(server: Server) { + super(server); + + const { connection } = server; + connection.onRequest(moveGoalRequest, (params: MoveGoalParams, token) => + runSafeAsync( + () => this.handleMoveGoal(params), + null, + `Error while moving goal "${params.goalName}".`, + token + ) + ); + + connection.onRequest(renameGoalRequest, (params: RenameGoalParams, token) => + runSafeAsync( + () => this.handleRenameGoal(params), + null, + `Error while renaming goal "${params.goalName}".`, + token + ) + ); + + server.projects.on("goalsChanged", this.handleGoalsChanged); + } + + handleGoalsChanged = debounce((project: Project) => { + const goals = this.createTree(project.story.getSortedRootGoals()); + const payload: GoalsChanged = { + project: project.getInfo(), + goals, + treeVersion: this.treeVersion++ + }; + + this.server.connection.sendNotification(goalsChangedEvent, payload); + }, 50); + + async handleMoveGoal({ + goalName, + newParent, + projectUid + }: MoveGoalParams): Promise { + const project = this.server.projects.findProjectByUid(projectUid); + if (!project) { + return { error: `Could not find project with uid "${projectUid}".` }; + } + + const goal = project.story.findGoal(goalName); + if (!goal) { + return { error: `Could not find goal "${goalName}".` }; + } + + const rootNode = await goal.resource.getRootNode(); + if ( + !rootNode || + rootNode.type !== NodeType.StoryGoal || + !rootNode.parentTargetEdges + ) { + return { error: `Could not open goal "${goal.name}" for editing.` }; + } + + const documentChanges: Array = []; + const document = goal.resource.getDocument(); + documentChanges.push({ + textDocument: { + uri: goal.resource.getUri(), + version: document ? document.version : null + }, + edits: [ + { + newText: `"${newParent}"`, + range: unpackRange(rootNode.parentTargetEdges[0]) + } + ] + }); + + return { documentChanges }; + } + + async handleRenameGoal({ + goalName, + newName, + projectUid + }: RenameGoalParams): Promise { + const project = this.server.projects.findProjectByUid(projectUid); + if (!project) { + return { error: `Could not find project with uid "${projectUid}".` }; + } + + const goal = project.story.findGoal(goalName); + if (!goal) { + return { error: `Could not find goal "${goalName}".` }; + } + + const collision = project.story.findGoal(newName); + if (collision) { + return { error: `A goal with the name "${newName}" already exists.` }; + } + + const documentChanges: Array = []; + const children = goal.getChildren(); + + for (const child of children) { + if (child.resource instanceof HeaderGoalResource) { + return { + error: `A goal which contains subgoals from the shared mod cannot be renamed.` + }; + } + + const rootNode = await child.resource.getRootNode(); + if ( + !rootNode || + rootNode.type !== NodeType.StoryGoal || + !rootNode.parentTargetEdges + ) { + return { error: `Could not open subgoal "${child.name}" for editing.` }; + } + + const edge = rootNode.parentTargetEdges.find( + edge => edge.value === goalName + ); + + if (!edge) { + return { + error: `Could not locate parentTargetEdges pointing to "${goalName}" in "${ + child.name + }".` + }; + } + + const document = child.resource.getDocument(); + documentChanges.push({ + textDocument: { + uri: child.resource.getUri(), + version: document ? document.version : null + }, + edits: [ + { + newText: `"${newName}"`, + range: unpackRange(edge) + } + ] + }); + } + + return { documentChanges }; + } + + private createTree( + goals: Array, + stack: Array = [] + ): Array { + return goals.filter(goal => stack.indexOf(goal) === -1).map(goal => ({ + children: this.createTree(goal.getSortedChildren(), [...stack, goal]), + isShared: !goal.resource || goal.resource instanceof HeaderGoalResource, + name: goal.name, + uri: goal.resource.getUri() + })); + } +} diff --git a/src/server/index.ts b/src/server/index.ts new file mode 100644 index 0000000..8614599 --- /dev/null +++ b/src/server/index.ts @@ -0,0 +1,3 @@ +import Server from "./Server"; + +new Server(); diff --git a/src/server/parsers/lsf/BufferReader.ts b/src/server/parsers/lsf/BufferReader.ts new file mode 100644 index 0000000..b537822 --- /dev/null +++ b/src/server/parsers/lsf/BufferReader.ts @@ -0,0 +1,101 @@ +import { inflateSync } from "zlib"; + +import { CompressionMethod } from "./utils/compressionMethod"; + +export default class BufferReader { + buffer: Buffer; + length: number; + offset: number = 0; + + constructor(buffer: Buffer) { + this.buffer = buffer; + this.length = buffer.length; + } + + readBytes( + length: number, + compression: CompressionMethod = CompressionMethod.None, + compressedLength: number = 0 + ): Buffer { + const { offset } = this; + let result: Buffer; + + switch (compression) { + case CompressionMethod.Zlib: + result = inflateSync( + this.buffer.slice(offset, offset + compressedLength) + ); + this.offset += compressedLength; + break; + case CompressionMethod.None: + result = this.buffer.slice(offset, offset + length); + this.offset += length; + break; + default: + throw new Error("Unsupported compression"); + } + + return result; + } + + readByte() { + const result = this.buffer[this.offset]; + this.offset += 1; + return result; + } + + readString(length: number) { + const { offset } = this; + const result = this.buffer.toString("utf-8", offset, offset + length); + this.offset += length; + return result; + } + + readFloat() { + const result = this.buffer.readFloatLE(this.offset); + this.offset += 4; + return result; + } + + readDouble() { + const result = this.buffer.readDoubleLE(this.offset); + this.offset += 8; + return result; + } + + readInt8() { + const result = this.buffer.readInt8(this.offset); + this.offset += 1; + return result; + } + + readUInt8() { + const result = this.buffer.readUInt8(this.offset); + this.offset += 1; + return result; + } + + readInt16() { + const result = this.buffer.readInt16LE(this.offset); + this.offset += 2; + return result; + } + + readUInt16() { + const result = this.buffer.readUInt16LE(this.offset); + this.offset += 2; + return result; + } + + readInt32() { + const result = this.buffer.readInt32LE(this.offset); + this.offset += 4; + return result; + } + + readUInt32() { + const result = this.buffer.readUInt32LE(this.offset); + this.offset += 4; + return result; + } +} diff --git a/src/server/parsers/lsf/LICENSE b/src/server/parsers/lsf/LICENSE new file mode 100644 index 0000000..59715e6 --- /dev/null +++ b/src/server/parsers/lsf/LICENSE @@ -0,0 +1,25 @@ +The LSF parser is a port of the LSF parser found in lslib +https://github.com/Norbyte/lslib + + +The MIT License (MIT) + +Copyright (c) 2015 Norbyte + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/src/server/parsers/lsf/Parser.ts b/src/server/parsers/lsf/Parser.ts new file mode 100644 index 0000000..fbe529a --- /dev/null +++ b/src/server/parsers/lsf/Parser.ts @@ -0,0 +1,443 @@ +import BufferReader from "./BufferReader"; + +import Node from "./models/Node"; +import Region from "./models/Region"; +import Resource from "./models/Resource"; +import NodeAttribute, { + DataType, + TranslatedFSString, + TranslatedFSStringArgument, + TranslatedString +} from "./models/NodeAttribute"; + +import LSFAttributeEntryV2 from "./structs/LSFAttributeEntryV2"; +import LSFAttributeEntryV3 from "./structs/LSFAttributeEntryV3"; +import LSFHeader from "./structs/LSFHeader"; +import LSFNodeEntryV2 from "./structs/LSFNodeEntryV2"; +import LSFNodeEntryV3 from "./structs/LSFNodeEntryV3"; + +import { CompressionFlagsToMethod } from "./utils/compressionMethod"; +import { readAttribute } from "./utils/readAttribute"; + +export type NodeEntry = LSFNodeEntryV2 | LSFNodeEntryV3; +export type AttributeEntry = LSFAttributeEntryV2 | LSFAttributeEntryV3; + +export default class Parser { + /** + * Turns on some debug console output + */ + private debug: boolean = false; + + /** + * Static string hash map + */ + private names: Array> = []; + + /** + * Preprocessed list of nodes (structures) + */ + private nodes: Array = []; + + /** + * Preprocessed list of node attributes + */ + private attributes: Array = []; + + read(buffer: Buffer): Resource { + const reader = new BufferReader(buffer); + const header = new LSFHeader(reader); + if (!header.validate()) { + throw new Error("Invalid LSF signature"); + } + + if ( + header.version < LSFHeader.versionInitial || + header.version > LSFHeader.currentVersion + ) { + throw new Error(`LSF version ${header.version} is not supported`); + } + + const compression = CompressionFlagsToMethod(header.compressionFlags); + + if (header.stringsSizeOnDisk > 0 || header.stringsUncompressedSize > 0) { + const namesStream = reader.readBytes( + header.stringsUncompressedSize, + compression, + header.stringsSizeOnDisk + ); + this.readNames(namesStream); + } + + if (header.nodesSizeOnDisk > 0 || header.nodesUncompressedSize > 0) { + const nodesStream = reader.readBytes( + header.nodesUncompressedSize, + compression, + header.nodesSizeOnDisk + ); + const longNodes = + header.version >= LSFHeader.versionExtendedNodes && + header.extended == 1; + this.readNodes(nodesStream, longNodes); + } + + if ( + header.attributesSizeOnDisk > 0 || + header.attributesUncompressedSize > 0 + ) { + const attributesStream = reader.readBytes( + header.attributesUncompressedSize, + compression, + header.attributesSizeOnDisk + ); + var longAttributes = + header.version >= LSFHeader.versionExtendedNodes && + header.extended == 1; + if (longAttributes) { + this.readAttributesV3(attributesStream); + } else { + this.readAttributesV2(attributesStream); + } + } + + let values: Buffer; + if (header.valuesSizeOnDisk > 0 || header.valuesUncompressedSize > 0) { + var valueStream = reader.readBytes( + header.valuesUncompressedSize, + compression, + header.valuesSizeOnDisk + ); + values = valueStream; + } else { + values = new Buffer(0); + } + + const resource = new Resource(); + resource.metadata.majorVersion = (header.engineVersion & 0xff000000) >> 24; + resource.metadata.minorVersion = (header.engineVersion & 0xff0000) >> 16; + resource.metadata.revision = (header.engineVersion & 0xff00) >> 8; + resource.metadata.buildNumber = header.engineVersion & 0xff; + + this.readRegions(resource, values); + + return resource; + } + + /** + * Return the name of the given entry from the name table + * @param entry The entry whose name should be returned + */ + private getName(entry: AttributeEntry | NodeEntry) { + return this.names[entry.getNameIndex()][entry.getNameOffset()]; + } + + /** + * Reads the static string hash table from the specified stream. + * @param buffer Stream to read the hash table from + */ + private readNames(buffer: Buffer) { + // Format: + // 32-bit hash entry count (N) + // N x 16-bit chain length (L) + // L x 16-bit string length (S) + // [S bytes of UTF-8 string data] + + const names: Array> = []; + const reader = new BufferReader(buffer); + let numHashEntries = reader.readUInt32(); + + if (this.debug) { + console.log(" ----- DUMP OF NAME TABLE -----"); + } + + while (numHashEntries-- > 0) { + const hash: Array = []; + names.push(hash); + + let numStrings = reader.readUInt16(); + while (numStrings-- > 0) { + const nameLen = reader.readUInt16(); + const name = reader.readString(nameLen); + hash.push(name); + + if (this.debug) { + console.log(`${names.length - 1}/${hash.length - 1}: ${name}`); + } + } + } + + this.names = names; + } + + /** + * Reads the structure headers for the LSOF resource + * @param buffer Stream to read the node headers from + * @param longNodes Use the long (V3) on-disk node format + */ + private readNodes(buffer: Buffer, longNodes: boolean) { + const nodes: Array = []; + const reader = new BufferReader(buffer); + let index = 0; + + if (this.debug) { + console.log(" ----- DUMP OF NODE TABLE -----"); + } + + while (reader.offset < reader.length) { + const pos = reader.offset; + const resolved = longNodes + ? new LSFNodeEntryV3(reader) + : new LSFNodeEntryV2(reader); + + if (this.debug) { + console.log( + `${index}: ${this.getName(resolved)} @ ${pos} (parent ${ + resolved.parentIndex + }, firstAttribute ${resolved.firstAttributeIndex})` + ); + } + + nodes.push(resolved); + index++; + } + + this.nodes = nodes; + } + + /** + * Reads the V2 attribute headers for the LSOF resource + * @param buffer Stream to read the attribute headers from + */ + private readAttributesV2(buffer: Buffer) { + const attributes: Array = []; + const prevAttributeRefs: Array = []; + const reader = new BufferReader(buffer); + let dataOffset = 0; + let index = 0; + + while (reader.offset < reader.length) { + const attribute = new LSFAttributeEntryV2(reader, dataOffset); + const nodeIndex = attribute.nodeIndex + 1; + + if (prevAttributeRefs.length > nodeIndex) { + if (prevAttributeRefs[nodeIndex] != -1) { + attributes[prevAttributeRefs[nodeIndex]].nextAttributeIndex = index; + } + + prevAttributeRefs[nodeIndex] = index; + } else { + while (prevAttributeRefs.length < nodeIndex) { + prevAttributeRefs.push(-1); + } + + prevAttributeRefs.push(index); + } + + dataOffset += attribute.getLength(); + attributes.push(attribute); + index++; + } + + if (this.debug) { + console.log(" ----- DUMP OF ATTRIBUTE REFERENCES -----"); + for (let i = 0; i < prevAttributeRefs.length; i++) { + console.log(`Node ${i}: last attribute ${prevAttributeRefs[i]}`); + } + + console.log(" ----- DUMP OF V2 ATTRIBUTE TABLE -----"); + for (let i = 0; i < attributes.length; i++) { + var resolved = attributes[i]; + console.log( + `${i}: ${this.getName(resolved)} (offset ${ + resolved.dataOffset + }, typeId ${resolved.getTypeId()}, nextAttribute ${ + resolved.nextAttributeIndex + }), node ${resolved.nodeIndex}` + ); + } + } + + this.attributes = attributes; + } + + /** + * Reads the V3 attribute headers for the LSOF resource + * @param buffer Stream to read the attribute headers from + */ + private readAttributesV3(buffer: Buffer) { + const attributes: Array = []; + const reader = new BufferReader(buffer); + + while (reader.offset < reader.length) { + attributes.push(new LSFAttributeEntryV3(reader)); + } + + if (this.debug) { + console.log(" ----- DUMP OF V3 ATTRIBUTE TABLE -----"); + + for (let i = 0; i < attributes.length; i++) { + var attribute = attributes[i]; + console.log( + `${i}: ${this.getName(attribute)} (offset ${ + attribute.dataOffset + }, typeId ${attribute.getTypeId()}, nextAttribute ${ + attribute.nextAttributeIndex + })` + ); + } + } + + this.attributes = attributes; + } + + private readRegions(resource: Resource, values: Buffer) { + const { nodes } = this; + const reader = new BufferReader(values); + const instances: Array = []; + + for (let i = 0; i < nodes.length; i++) { + const entry = nodes[i]; + + if (entry.parentIndex == -1) { + const region = new Region(); + this.readNode(entry, region, reader); + + resource.regions[region.name] = region; + instances.push(region); + } else { + const node = new Node(); + this.readNode(entry, node, reader); + + node.parent = instances[entry.parentIndex]; + instances.push(node); + instances[entry.parentIndex].append(node); + } + } + } + + private readNode(entry: NodeEntry, node: Node, reader: BufferReader) { + const { attributes } = this; + node.name = this.getName(entry); + + if (entry.firstAttributeIndex != -1) { + let attribute = attributes[entry.firstAttributeIndex]; + + while (attribute) { + reader.offset = attribute.dataOffset; + const value = this.readAttribute( + attribute.getTypeId(), + reader, + attribute.getLength() + ); + + node.attributes[this.getName(attribute)] = value; + + if (attribute.nextAttributeIndex == -1) { + break; + } else { + attribute = attributes[attribute.nextAttributeIndex]; + } + } + } + } + + private readAttribute( + type: DataType, + reader: BufferReader, + length: number + ): NodeAttribute { + // LSF and LSB serialize the buffer types differently, so specialized + // code is added to the LSB and LSf serializers, and the common code is + // available in BinUtils.ReadAttribute() + switch (type) { + case DataType.DT_String: + case DataType.DT_Path: + case DataType.DT_FixedString: + case DataType.DT_LSString: + case DataType.DT_WString: + case DataType.DT_LSWString: { + const attribute = new NodeAttribute(type); + attribute.value = this.readString(reader, length); + return attribute; + } + + case DataType.DT_TranslatedString: { + const attribute = new NodeAttribute(type); + + const valueLength = reader.readInt32(); + const value = this.readString(reader, valueLength); + + const handleLength = reader.readInt32(); + const handle = this.readString(reader, handleLength); + + attribute.value = new TranslatedString(value, handle); + return attribute; + } + + case DataType.DT_TranslatedFSString: { + const attribute = new NodeAttribute(type); + attribute.value = this.readTranslatedFSString(reader); + return attribute; + } + + case DataType.DT_ScratchBuffer: { + const attribute = new NodeAttribute(type); + attribute.value = reader.readBytes(length); + return attribute; + } + + default: + return readAttribute(type, reader); + } + } + + private readTranslatedFSString(reader: BufferReader): TranslatedFSString { + const valueLength = reader.readInt32(); + const value = this.readString(reader, valueLength); + + const handleLength = reader.readInt32(); + const handle = this.readString(reader, handleLength); + + const translatedString = new TranslatedFSString(value, handle); + + const length = reader.readInt32(); + for (let index = 0; index < length; index++) { + const argKeyLength = reader.readInt32(); + const argKey = this.readString(reader, argKeyLength); + + const string = this.readTranslatedFSString(reader); + + const argValueLength = reader.readInt32(); + const argValue = this.readString(reader, argValueLength); + + translatedString.arguments.push( + new TranslatedFSStringArgument(argKey, string, argValue) + ); + } + + return translatedString; + } + + private readString(reader: BufferReader, length: number = -1): string { + if (length === -1) { + const bytes: Array = []; + while (true) { + const b = reader.readByte(); + if (b != 0) { + bytes.push(String.fromCharCode(b)); + } else { + break; + } + } + + return bytes.join(""); + } + + const string = reader.readString(length - 1); + const nullTerminator = reader.readByte(); + if (nullTerminator != 0) { + throw new Error("String is not null-terminated"); + } + + return string; + } +} diff --git a/src/server/parsers/lsf/models/Matrix.ts b/src/server/parsers/lsf/models/Matrix.ts new file mode 100644 index 0000000..7ea058a --- /dev/null +++ b/src/server/parsers/lsf/models/Matrix.ts @@ -0,0 +1,32 @@ +export default class Matrix { + numColumns: number; + numRows: number; + values: Array>; + + constructor(numRows: number, numColumns: number) { + const values: Array> = []; + + for (let y = 0; y < numRows; y++) { + const row = []; + for (let x = 0; x < numColumns; x++) { + row.push(0); + } + + values.push(row); + } + + this.numColumns = numColumns; + this.numRows = numRows; + this.values = values; + } + + each(callback: (value: number, row: number, column: number) => void) { + const { numColumns, numRows, values } = this; + + for (let row = 0; row < numRows; row++) { + for (let column = 0; column < numColumns; column++) { + callback(values[row][column], row, column); + } + } + } +} diff --git a/src/server/parsers/lsf/models/Node.ts b/src/server/parsers/lsf/models/Node.ts new file mode 100644 index 0000000..4b59544 --- /dev/null +++ b/src/server/parsers/lsf/models/Node.ts @@ -0,0 +1,40 @@ +import NodeAttribute from "./NodeAttribute"; + +export default class Node { + attributes: { [name: string]: NodeAttribute } = {}; + children: { [name: string]: Array } = {}; + name: string = ""; + parent: Node | null = null; + + append(child: Node) { + const { name } = child; + let children = this.children[name]; + if (!children) { + this.children[name] = children = []; + } + + children.push(child); + } + + findNode(name: string, ...path: string[]): Node | null { + if (name in this.children) { + const child = this.children[name][0]; + if (path.length) { + const [childName, ...childPath] = path; + return child.findNode(childName, ...childPath); + } else { + return child; + } + } + + return null; + } + + getStringAttribute(name: string): string | null { + const attribute = this.attributes[name]; + if (!attribute) return null; + + const value = attribute.value; + return typeof value === "string" ? value : null; + } +} diff --git a/src/server/parsers/lsf/models/NodeAttribute.ts b/src/server/parsers/lsf/models/NodeAttribute.ts new file mode 100644 index 0000000..a59d686 --- /dev/null +++ b/src/server/parsers/lsf/models/NodeAttribute.ts @@ -0,0 +1,152 @@ +import * as Long from "long"; + +import Matrix from "./Matrix"; + +export enum DataType { + DT_None = 0, + DT_Byte = 1, + DT_Short = 2, + DT_UShort = 3, + DT_Int = 4, + DT_UInt = 5, + DT_Float = 6, + DT_Double = 7, + DT_IVec2 = 8, + DT_IVec3 = 9, + DT_IVec4 = 10, + DT_Vec2 = 11, + DT_Vec3 = 12, + DT_Vec4 = 13, + DT_Mat2 = 14, + DT_Mat3 = 15, + DT_Mat3x4 = 16, + DT_Mat4x3 = 17, + DT_Mat4 = 18, + DT_Bool = 19, + DT_String = 20, + DT_Path = 21, + DT_FixedString = 22, + DT_LSString = 23, + DT_ULongLong = 24, + DT_ScratchBuffer = 25, + DT_Long = 26, + DT_Int8 = 27, + DT_TranslatedString = 28, + DT_WString = 29, + DT_LSWString = 30, + DT_UUID = 31, + DT_Unknown32 = 32, + DT_TranslatedFSString = 33, + // Last supported datatype, always keep this one at the end + DT_Max = DT_TranslatedFSString +} + +export type AttributeValueType = + | boolean + | number + | string + | undefined + | Array + | Buffer + | Long + | Matrix + | TranslatedString; + +export class TranslatedString { + handle: string; + value: string; + + constructor(value: string, handle: string) { + this.value = value; + this.handle = handle; + } +} + +export class TranslatedFSStringArgument { + key: string; + string: TranslatedFSString; + value: string; + + constructor(key: string, string: TranslatedFSString, value: string) { + this.key = key; + this.string = string; + this.value = value; + } +} + +export class TranslatedFSString extends TranslatedString { + arguments: Array = []; +} + +export default class NodeAttribute { + type: DataType; + value: AttributeValueType; + + constructor(type: DataType) { + this.type = type; + } + + getNumRows(): number { + switch (this.type) { + case DataType.DT_IVec2: + case DataType.DT_IVec3: + case DataType.DT_IVec4: + case DataType.DT_Vec2: + case DataType.DT_Vec3: + case DataType.DT_Vec4: + return 1; + + case DataType.DT_Mat2: + return 2; + + case DataType.DT_Mat3: + case DataType.DT_Mat3x4: + return 3; + + case DataType.DT_Mat4x3: + case DataType.DT_Mat4: + return 4; + + default: + return 0; + } + } + + getNumColumns(): number { + switch (this.type) { + case DataType.DT_IVec2: + case DataType.DT_Vec2: + case DataType.DT_Mat2: + return 2; + + case DataType.DT_IVec3: + case DataType.DT_Vec3: + case DataType.DT_Mat3: + case DataType.DT_Mat4x3: + return 3; + + case DataType.DT_IVec4: + case DataType.DT_Vec4: + case DataType.DT_Mat3x4: + case DataType.DT_Mat4: + return 4; + + default: + return 0; + } + } + + isNumeric() { + return ( + this.type == DataType.DT_Byte || + this.type == DataType.DT_Short || + this.type == DataType.DT_Int || + this.type == DataType.DT_UInt || + this.type == DataType.DT_Float || + this.type == DataType.DT_Double || + this.type == DataType.DT_ULongLong || + this.type == DataType.DT_Long || + this.type == DataType.DT_Int8 + ); + } +} diff --git a/src/server/parsers/lsf/models/Region.ts b/src/server/parsers/lsf/models/Region.ts new file mode 100644 index 0000000..745be05 --- /dev/null +++ b/src/server/parsers/lsf/models/Region.ts @@ -0,0 +1,3 @@ +import Node from "./Node"; + +export default class Region extends Node {} diff --git a/src/server/parsers/lsf/models/Resource.ts b/src/server/parsers/lsf/models/Resource.ts new file mode 100644 index 0000000..2da271f --- /dev/null +++ b/src/server/parsers/lsf/models/Resource.ts @@ -0,0 +1,43 @@ +import Node from "./Node"; +import Region from "./Region"; + +export class LSMetadata { + buildNumber: number = 0; + majorVersion: number = 0; + minorVersion: number = 0; + revision: number = 0; + timestamp: number = 0; + + static currentMajorVersion = 33; +} + +export class LSBHeader { + bigEndian: number = 0; + metadata: LSMetadata = new LSMetadata(); + signature: number = 0; + totalSize: number = 0; + unknown: number = 0; + + static signature = 0x40000000; + static currentMajorVersion = 1; + static currentMinorVersion = 3; +} + +export default class Resource { + metadata: LSMetadata = new LSMetadata(); + regions: { [name: string]: Region } = {}; + + findNode(name: string, ...path: string[]): Node | null { + if (name in this.regions) { + const child = this.regions[name]; + if (path.length) { + const [childName, ...childPath] = path; + return child.findNode(childName, ...childPath); + } else { + return child; + } + } + + return null; + } +} diff --git a/src/server/parsers/lsf/structs/LSFAttributeEntryV2.ts b/src/server/parsers/lsf/structs/LSFAttributeEntryV2.ts new file mode 100644 index 0000000..666818d --- /dev/null +++ b/src/server/parsers/lsf/structs/LSFAttributeEntryV2.ts @@ -0,0 +1,64 @@ +import BufferReader from "../BufferReader"; + +/** + * V2 attribute extension in the LSF file + */ +export default class LSFAttributeEntryV2 { + /** + * Name of this attribute + * (16-bit MSB: index into name hash table, 16-bit LSB: offset in hash chain) + */ + nameHashTableIndex: number; + + /** + * 6-bit LSB: Type of this attribute (see NodeAttribute.DataType) + * 26-bit MSB: Length of this attribute + */ + typeAndLength: number; + + /** + * Index of the node that this attribute belongs to + * Note: These indexes are assigned seemingly arbitrarily, and are not neccessarily + * indices into the node list + */ + nodeIndex: number; + + nextAttributeIndex: number = -1; + + dataOffset: number; + + constructor(reader: BufferReader, dataOffset: number) { + this.nameHashTableIndex = reader.readUInt32(); + this.typeAndLength = reader.readUInt32(); + this.nodeIndex = reader.readInt32(); + this.dataOffset = dataOffset; + } + + /** + * Index into name hash table + */ + getNameIndex() { + return this.nameHashTableIndex >> 16; + } + + /** + * Offset in hash chain + */ + getNameOffset() { + return this.nameHashTableIndex & 0xffff; + } + + /** + * Type of this attribute (see NodeAttribute.DataType) + */ + getTypeId() { + return this.typeAndLength & 0x3f; + } + + /** + * Length of this attribute + */ + getLength() { + return this.typeAndLength >> 6; + } +} diff --git a/src/server/parsers/lsf/structs/LSFAttributeEntryV3.ts b/src/server/parsers/lsf/structs/LSFAttributeEntryV3.ts new file mode 100644 index 0000000..726c951 --- /dev/null +++ b/src/server/parsers/lsf/structs/LSFAttributeEntryV3.ts @@ -0,0 +1,65 @@ +import BufferReader from "../BufferReader"; + +/** + * V3 attribute extension in the LSF file + */ +export default class LSFAttributeEntryV3 { + /** + * Name of this attribute + * (16-bit MSB: index into name hash table, 16-bit LSB: offset in hash chain) + */ + nameHashTableIndex: number; + + /** + * 6-bit LSB: Type of this attribute (see NodeAttribute.DataType) + * 26-bit MSB: Length of this attribute + */ + typeAndLength: number; + + /** + * Index of the node that this attribute belongs to + * Note: These indexes are assigned seemingly arbitrarily, and are not neccessarily + * indices into the node list + */ + nextAttributeIndex: number; + + /** + * Absolute position of attribute value in the value stream + */ + dataOffset: number; + + constructor(reader: BufferReader) { + this.nameHashTableIndex = reader.readUInt32(); + this.typeAndLength = reader.readUInt32(); + this.nextAttributeIndex = reader.readInt32(); + this.dataOffset = reader.readUInt32(); + } + + /** + * Index into name hash table + */ + getNameIndex() { + return this.nameHashTableIndex >> 16; + } + + /** + * Offset in hash chain + */ + getNameOffset() { + return this.nameHashTableIndex & 0xffff; + } + + /** + * Type of this attribute (see NodeAttribute.DataType) + */ + getTypeId() { + return this.typeAndLength & 0x3f; + } + + /** + * Length of this attribute + */ + getLength() { + return this.typeAndLength >> 6; + } +} diff --git a/src/server/parsers/lsf/structs/LSFHeader.ts b/src/server/parsers/lsf/structs/LSFHeader.ts new file mode 100644 index 0000000..284eb0c --- /dev/null +++ b/src/server/parsers/lsf/structs/LSFHeader.ts @@ -0,0 +1,122 @@ +import BufferReader from "../BufferReader"; + +export default class LSFHeader { + /** + * LSOF file signature; should be the same as LSFHeader.Signature + */ + magic: string; + + /** + * Version of the LSOF file; D:OS EE is version 1/2, D:OS 2 is version 3 + */ + version: number; + + /** + * Possibly version number? (major, minor, rev, build) + */ + engineVersion: number; + + /** + * Total uncompressed size of the string hash table + */ + stringsUncompressedSize: number; + + /** + * Compressed size of the string hash table + */ + stringsSizeOnDisk: number; + + /** + * Total uncompressed size of the node list + */ + nodesUncompressedSize: number; + + /** + * Compressed size of the node list + */ + nodesSizeOnDisk: number; + + /** + * Total uncompressed size of the attribute list + */ + attributesUncompressedSize: number; + + /** + * Compressed size of the attribute list + */ + attributesSizeOnDisk: number; + + /** + * Total uncompressed size of the raw value buffer + */ + valuesUncompressedSize: number; + + /** + * Compressed size of the raw value buffer + */ + valuesSizeOnDisk: number; + + /** + * Compression method and level used for the string, node, attribute and value buffers. + * Uses the same format as packages (see BinUtils.MakeCompressionFlags) + */ + compressionFlags: number; + + /** + * Possibly unused, always 0 + */ + unknown2: number; + unknown3: number; + + /** + * Extended node/attribute format indicator, 0 for V2, 0/1 for V3 + */ + extended: number; + + /** + * Required header signature. + */ + static headerSignature = "LSOF"; + + /** + * Initial version of the LSF format + */ + static versionInitial = 0x01; + + /** + * LSF version that added chunked compression for substreams + */ + static versionChunkedCompress = 0x02; + + /** + * LSF version that extended the node descriptors + */ + static versionExtendedNodes = 0x03; + + /** + * Latest version supported by this library + */ + static currentVersion = 0x03; + + constructor(reader: BufferReader) { + this.magic = reader.readString(4); + this.version = reader.readUInt32(); + this.engineVersion = reader.readUInt32(); + this.stringsUncompressedSize = reader.readUInt32(); + this.stringsSizeOnDisk = reader.readUInt32(); + this.nodesUncompressedSize = reader.readUInt32(); + this.nodesSizeOnDisk = reader.readUInt32(); + this.attributesUncompressedSize = reader.readUInt32(); + this.attributesSizeOnDisk = reader.readUInt32(); + this.valuesUncompressedSize = reader.readUInt32(); + this.valuesSizeOnDisk = reader.readUInt32(); + this.compressionFlags = reader.readUInt8(); + this.unknown2 = reader.readUInt8(); + this.unknown3 = reader.readUInt16(); + this.extended = reader.readUInt32(); + } + + validate(): boolean { + return this.magic === LSFHeader.headerSignature; + } +} diff --git a/src/server/parsers/lsf/structs/LSFNodeEntryV2.ts b/src/server/parsers/lsf/structs/LSFNodeEntryV2.ts new file mode 100644 index 0000000..b26f4da --- /dev/null +++ b/src/server/parsers/lsf/structs/LSFNodeEntryV2.ts @@ -0,0 +1,44 @@ +import BufferReader from "../BufferReader"; + +/** + * Node (structure) entry in the LSF file + */ +export default class LSFNodeEntryV2 { + /** + * Name of this node + * (16-bit MSB: index into name hash table, 16-bit LSB: offset in hash chain) + */ + nameHashTableIndex: number; + + /** + * Index of the first attribute of this node + * (-1: node has no attributes) + */ + firstAttributeIndex: number; + + /** + * Index of the parent node + * (-1: this node is a root region) + */ + parentIndex: number; + + constructor(reader: BufferReader) { + this.nameHashTableIndex = reader.readUInt32(); + this.firstAttributeIndex = reader.readInt32(); + this.parentIndex = reader.readInt32(); + } + + /** + * Index into name hash table + */ + getNameIndex() { + return this.nameHashTableIndex >> 16; + } + + /** + * Offset in hash chain + */ + getNameOffset() { + return this.nameHashTableIndex & 0xffff; + } +} diff --git a/src/server/parsers/lsf/structs/LSFNodeEntryV3.ts b/src/server/parsers/lsf/structs/LSFNodeEntryV3.ts new file mode 100644 index 0000000..e2e6aed --- /dev/null +++ b/src/server/parsers/lsf/structs/LSFNodeEntryV3.ts @@ -0,0 +1,51 @@ +import BufferReader from "../BufferReader"; + +/** + * Node (structure) entry in the LSF file + */ +export default class LSFNodeEntryV3 { + /** + * Name of this node + * (16-bit MSB: index into name hash table, 16-bit LSB: offset in hash chain) + */ + nameHashTableIndex: number; + + /** + * Index of the parent node + * (-1: this node is a root region) + */ + parentIndex: number; + + /** + * Index of the next sibling of this node + * (-1: this is the last node) + */ + nextSiblingIndex: number; + + /** + * Index of the first attribute of this node + * (-1: node has no attributes) + */ + firstAttributeIndex: number; + + constructor(reader: BufferReader) { + this.nameHashTableIndex = reader.readUInt32(); + this.parentIndex = reader.readInt32(); + this.nextSiblingIndex = reader.readInt32(); + this.firstAttributeIndex = reader.readInt32(); + } + + /** + * Index into name hash table + */ + getNameIndex() { + return this.nameHashTableIndex >> 16; + } + + /** + * Offset in hash chain + */ + getNameOffset() { + return this.nameHashTableIndex & 0xffff; + } +} diff --git a/src/server/parsers/lsf/utils/compressionMethod.ts b/src/server/parsers/lsf/utils/compressionMethod.ts new file mode 100644 index 0000000..07c29ba --- /dev/null +++ b/src/server/parsers/lsf/utils/compressionMethod.ts @@ -0,0 +1,21 @@ +export enum CompressionMethod { + None = 0, + Zlib = 1, + LZ4 = 2 +} + +export function CompressionFlagsToMethod(flags: number): CompressionMethod { + switch (flags & 0x0f) { + case 0: + return CompressionMethod.None; + + case 1: + return CompressionMethod.Zlib; + + case 2: + return CompressionMethod.LZ4; + + default: + throw new Error("Invalid compression method"); + } +} diff --git a/src/server/parsers/lsf/utils/readAttribute.ts b/src/server/parsers/lsf/utils/readAttribute.ts new file mode 100644 index 0000000..6c3697e --- /dev/null +++ b/src/server/parsers/lsf/utils/readAttribute.ts @@ -0,0 +1,111 @@ +import * as Long from "long"; + +import BufferReader from "../BufferReader"; +import Matrix from "../models/Matrix"; +import NodeAttribute, { DataType } from "../models/NodeAttribute"; + +export function readAttribute(type: DataType, reader: BufferReader) { + const attribute = new NodeAttribute(type); + + switch (type) { + case DataType.DT_None: + break; + + case DataType.DT_Byte: + attribute.value = reader.readByte(); + break; + + case DataType.DT_Short: + attribute.value = reader.readInt16(); + break; + + case DataType.DT_UShort: + attribute.value = reader.readUInt16(); + break; + + case DataType.DT_Int: + attribute.value = reader.readInt32(); + break; + + case DataType.DT_UInt: + attribute.value = reader.readUInt32(); + break; + + case DataType.DT_Float: + attribute.value = reader.readFloat(); + break; + + case DataType.DT_Double: + attribute.value = reader.readDouble(); + break; + + case DataType.DT_IVec2: + case DataType.DT_IVec3: + case DataType.DT_IVec4: { + const columns = attribute.getNumColumns(); + const vector: Array = []; + for (let index = 0; index < columns; index++) { + vector[index] = reader.readInt32(); + } + + attribute.value = vector; + break; + } + + case DataType.DT_Vec2: + case DataType.DT_Vec3: + case DataType.DT_Vec4: { + const columns = attribute.getNumColumns(); + const vector: Array = []; + for (let index = 0; index < columns; index++) { + vector[index] = reader.readFloat(); + } + + attribute.value = vector; + break; + } + + case DataType.DT_Mat2: + case DataType.DT_Mat3: + case DataType.DT_Mat3x4: + case DataType.DT_Mat4x3: + case DataType.DT_Mat4: { + const columns = attribute.getNumColumns(); + const rows = attribute.getNumRows(); + const matrix = new Matrix(rows, columns); + matrix.each((value, row, column) => { + matrix.values[row][column] = reader.readFloat(); + }); + + attribute.value = matrix; + break; + } + + case DataType.DT_Bool: + attribute.value = reader.readByte() != 0; + break; + + case DataType.DT_ULongLong: + attribute.value = new Long(reader.readInt32(), reader.readInt32(), true); + break; + + case DataType.DT_Long: + attribute.value = new Long(reader.readInt32(), reader.readInt32()); + break; + + case DataType.DT_Int8: + attribute.value = reader.readInt8(); + break; + + case DataType.DT_UUID: + attribute.value = reader.readString(16); + break; + + default: + // Strings are serialized differently for each file format and should be + // handled by the format-specific ReadAttribute() + throw new Error(`ReadAttribute() not implemented for type ${type}`); + } + + return attribute; +} diff --git a/src/server/parsers/story/GoalParser.ts b/src/server/parsers/story/GoalParser.ts new file mode 100644 index 0000000..420aec5 --- /dev/null +++ b/src/server/parsers/story/GoalParser.ts @@ -0,0 +1,202 @@ +import msgInvalidOptionLocation from "./messages/msgInvalidOptionLocation"; +import msgInvalidOptionName from "./messages/msgInvalidOptionName"; +import msgInvalidOptionValue from "./messages/msgInvalidOptionValue"; +import msgUnexpectedToken from "./messages/msgUnexpectedToken"; +import ParserBase from "./Parser"; +import { Diagnostic } from "./models/diagnostics"; +import { NodeType, StoryGoalNode, AnyNode } from "./models/nodes"; +import { TokenType, Token } from "./Lexer"; + +import isStoryToken, { + StoryToken, + storyTokenTypes +} from "./utils/isStoryToken"; + +export interface ParserResult { + diagnostics: Array; + goal: StoryGoalNode; +} + +export default class GoalParser extends ParserBase { + parse(): ParserResult { + let storyToken: StoryToken | undefined; + const goal: StoryGoalNode = { + endOffset: 0, + endPosition: 0, + startOffset: 0, + startPosition: 0, + type: NodeType.StoryGoal + }; + + this.withBailOutTypes(storyTokenTypes, () => { + do { + switch (storyToken ? storyToken.type : undefined) { + case TokenType.InitSectionKeyword: + storyToken = this.readStoryInit(goal); + break; + case TokenType.KBSectionKeyword: + storyToken = this.readStoryKB(goal); + break; + case TokenType.ExitSectionKeyword: + storyToken = this.readStoryExit(goal); + break; + case TokenType.EndExitSectionKeyword: + storyToken = this.readStoryOptions(goal, false); + break; + default: + storyToken = this.readStoryOptions(goal, true); + } + } while (storyToken); + }); + + return { + diagnostics: this.diagnostics, + goal + }; + } + + readStoryInit = this.withStoryBoundary(TokenType.KBSectionKeyword, goal => { + return (goal.init = this.readActionBlock()); + }); + + readStoryKB = this.withStoryBoundary(TokenType.ExitSectionKeyword, goal => { + return (goal.kb = this.readRuleBlock()); + }); + + readStoryExit = this.withStoryBoundary( + TokenType.EndExitSectionKeyword, + goal => { + return (goal.exit = this.readActionBlock()); + } + ); + + readStoryOptions( + goal: StoryGoalNode, + isHeader: boolean + ): StoryToken | undefined { + let token: Token | undefined; + + while ((token = this.next())) { + if (isStoryToken(token)) { + return token; + } else if (token.type === TokenType.Identifier) { + this.readStoryOption(goal, token, isHeader); + } else { + this.addDiagnostic( + token, + msgUnexpectedToken({ + actualToken: token + }) + ); + } + } + + return undefined; + } + + readStoryOption(goal: StoryGoalNode, token: Token, isHeader: boolean) { + let valueToken: Token | undefined; + const headerCheck = (target: boolean) => + target !== isHeader + ? this.addDiagnostic( + token, + msgInvalidOptionLocation({ + isHeader, + name: token.value + }) + ) + : null; + + switch (token.value) { + case "Version": + headerCheck(true); + valueToken = this.read(TokenType.IntegerLiteral, true); + if (valueToken) { + const version = parseInt(valueToken.value); + if (version !== 1) { + this.addDiagnostic( + token, + msgInvalidOptionValue({ + actualValue: valueToken.value, + expectedValue: "1", + name: "Version" + }) + ); + } + + goal.version = version; + } + break; + + case "SubGoalCombiner": + headerCheck(true); + valueToken = this.read(TokenType.Identifier, true); + if (valueToken) { + if (valueToken.value !== "SGC_AND") { + this.addDiagnostic( + valueToken, + msgInvalidOptionValue({ + actualValue: valueToken.value, + expectedValue: "SGC_AND", + name: "SubGoalCombiner" + }) + ); + } + + goal.subGoalCombiner = valueToken.value; + } + break; + + case "ParentTargetEdge": + headerCheck(false); + const parent = this.readStringLiteral(); + if (parent) { + if (!goal.parentTargetEdges) { + goal.parentTargetEdges = []; + } + goal.parentTargetEdges.push(parent); + } + break; + + default: + this.addDiagnostic(token, msgInvalidOptionName({ name: token.value })); + } + } + + withStoryBoundary( + boundaryType: TokenType, + callback: { + (goal: StoryGoalNode): AnyNode | undefined; + } + ) { + return (goal: StoryGoalNode): StoryToken | undefined => { + const last = this.last(); + let node: AnyNode | undefined; + if (last) { + const startOffset = last.startOffset; + const startPosition = last.startPosition; + + node = callback(goal); + if (node) { + node.startOffset = startOffset; + node.startPosition = startPosition; + } + } else { + node = callback(goal); + } + + const storyToken = this.readStoryBoundary(); + if (storyToken && storyToken.type !== boundaryType) { + this.addDiagnostic( + storyToken, + msgUnexpectedToken({ + actualToken: storyToken, + expectedToken: boundaryType + }) + ); + } + + return storyToken; + }; + } +} diff --git a/src/server/parsers/story/HeaderParser.ts b/src/server/parsers/story/HeaderParser.ts new file mode 100644 index 0000000..a64bed8 --- /dev/null +++ b/src/server/parsers/story/HeaderParser.ts @@ -0,0 +1,271 @@ +import ParserBase from "./Parser"; +import { Diagnostic } from "./models/diagnostics"; +import { Token, TokenType } from "./Lexer"; + +import { + HeaderGoalNode, + NodeType, + HeaderNode, + DefinitionNode +} from "./models/nodes"; +import msgUnexpectedToken from "./messages/msgUnexpectedToken"; +import msgInvalidOptionName from "./messages/msgInvalidOptionName"; +import msgInvalidGoalSection from "./messages/msgInvalidGoalSection"; + +function isAliasTypeToken(token: Token): boolean { + return token.type === TokenType.Identifier && token.value === "alias_type"; +} + +function isConfigToken(token: Token): boolean { + return ( + token.type === TokenType.Identifier && + (token.value === "option" || token.value === "version") + ); +} + +function isGoalToken(token: Token): boolean { + return token.type === TokenType.Identifier && token.value === "Goal"; +} + +function isDefinitionToken(token: Token): boolean { + return ( + token.type === TokenType.Identifier && + (token.value === "call" || + token.value === "event" || + token.value === "query" || + token.value === "syscall" || + token.value === "sysquery") + ); +} + +function range(source: string, start: number, end: number): string { + if (!range) { + return ""; + } + + const sourcePart = source.substring(start, end).trim(); + if (sourcePart === "") return ""; + + const lines = sourcePart.split("\r\n"); + lines.push(""); + lines.unshift(""); + + return lines + .map(line => (line.startsWith("\t\t") ? line.substr(2) : line)) + .join("\r\n"); +} + +export interface ParserResult { + diagnostics: Array; + header: HeaderNode; +} + +export default class HeaderParser extends ParserBase { + parse(): ParserResult { + let token: Token | undefined; + const header: HeaderNode = { + definitions: [], + endOffset: 0, + endPosition: 0, + goals: [], + startOffset: 0, + startPosition: 0, + type: NodeType.Div, + typeAliases: [] + }; + + while ((token = this.next())) { + if (isAliasTypeToken(token)) { + this.consumeAlias(header); + } else if (isConfigToken(token)) { + this.tryReadConstant(); + } else if (isDefinitionToken(token)) { + const definition = this.readDefinition(token); + if (definition) { + header.definitions.push(definition); + } + } else if (isGoalToken(token)) { + this.readGoal(header); + } + } + + return { + diagnostics: this.diagnostics, + header + }; + } + + consumeAlias(header: HeaderNode) { + if (!this.consume(TokenType.CurlyBracketOpen)) { + return; + } + + const type = this.read(TokenType.Identifier); + if (type) { + header.typeAliases.push(type.value); + } + + this.consumeIncluding(TokenType.CurlyBracketClose); + } + + readDefinition(token: Token): DefinitionNode | undefined { + const startOffset = token.startOffset; + const startPosition = token.startPosition; + const definitionType = token.value; + const signature = this.readSignature(); + + if (!signature) return undefined; + + if (this.consume(TokenType.BracketOpen)) { + this.consumeIncluding(TokenType.BracketClose); + } else { + this.addDiagnostic( + undefined, + msgUnexpectedToken({ + actualToken: this.last(), + expectedHint: "definitionMetaData" + }) + ); + } + + return { + definitionType, + endOffset: signature.endOffset, + endPosition: signature.endPosition, + signature, + startOffset, + startPosition, + type: NodeType.Definition + }; + } + + readGoal(header: HeaderNode): undefined { + if (!this.consume(TokenType.BracketOpen)) { + return undefined; + } + + const idToken = this.read(TokenType.IntegerLiteral); + if (!idToken) { + this.consumeIncluding(TokenType.BracketClose); + return undefined; + } + + const id = parseInt(idToken.value); + if (!this.consume(TokenType.BracketClose)) { + return undefined; + } + + let goal = header.goals.find(goal => goal.id === id); + if (!goal) { + goal = { + endOffset: 0, + endPosition: 0, + id, + startPosition: 0, + startOffset: 0, + subGoal: [], + type: NodeType.DivGoal + }; + header.goals.push(goal); + } + + const separator = this.next(); + if (separator && separator.type === TokenType.Dot) { + this.readGoalOption(goal); + } else if (separator && separator.type === TokenType.CurlyBracketOpen) { + this.readGoalContent(goal); + } + + return undefined; + } + + readGoalContent(goal: HeaderGoalNode) { + let token: Token | undefined; + + while ((token = this.next())) { + if (token.type === TokenType.CurlyBracketClose) { + break; + } else if (token.type === TokenType.Identifier) { + const sectionName = token.value; + + token = this.read(TokenType.CurlyBracketOpen); + if (!token) continue; + + const start = token.endOffset; + while ((token = this.next())) { + if (token.type === TokenType.CurlyBracketClose) { + break; + } + } + + if (!token) continue; + const data = range(this.source, start, token.startOffset); + + switch (sectionName) { + case "INIT": + goal.init = data; + break; + case "KB": + goal.kb = data; + break; + case "EXIT": + goal.exit = data; + break; + default: + this.addDiagnostic( + token, + msgInvalidGoalSection({ + name: sectionName + }) + ); + } + } else { + this.addDiagnostic( + token, + msgUnexpectedToken({ + actualToken: token + }) + ); + } + } + } + + readGoalOption(goal: HeaderGoalNode) { + const identifier = this.read(TokenType.Identifier); + if (!identifier) { + return this.consumeIncluding(TokenType.SemiColon); + } + + const option = identifier.value; + if (!this.consume(TokenType.BracketOpen)) { + return this.consumeIncluding(TokenType.SemiColon); + } + + if (option === "Title") { + const title = this.read(TokenType.StringLiteral); + if (title) { + goal.title = title.value.substr(1, title.value.length - 2); + } + } else if (option === "SubGoal") { + const subGoal = this.read(TokenType.IntegerLiteral); + if (subGoal) { + goal.subGoal.push(parseInt(subGoal.value)); + } + } else if (option === "SubGoals") { + this.next(); + } else { + this.addDiagnostic( + identifier, + msgInvalidOptionName({ + name: option + }) + ); + } + + if (!this.consume(TokenType.BracketClose)) { + return this.consumeIncluding(TokenType.SemiColon); + } + + this.ensureSemiColon(); + } +} diff --git a/src/server/parsers/story/Lexer.ts b/src/server/parsers/story/Lexer.ts new file mode 100644 index 0000000..4b90dd9 --- /dev/null +++ b/src/server/parsers/story/Lexer.ts @@ -0,0 +1,526 @@ +import msgNewLineInString from "./messages/msgNewLineInString"; +import msgPrematureRealEnd from "./messages/msgPrematureRealEnd"; +import packPosition from "./utils/packPosition"; +import unpackRange from "./utils/unpackRange"; + +import { + DiagnosticType, + Diagnostic, + DiagnosticMessage +} from "./models/diagnostics"; + +const CHAR_TAB = 9; +const CHAR_NEWLINE = 10; +const CHAR_CARRIAGE_RETURN = 13; +const CHAR_SPACE = 32; +const CHAR_EXCLAMATION = 33; +const CHAR_QUOTE = 34; +const CHAR_BRACKET_OPEN = 40; +const CHAR_BRACKET_CLOSE = 41; +const CHAR_ASTERISK = 42; +const CHAR_PLUS = 43; +const CHAR_COLON = 44; +const CHAR_MINUS = 45; +const CHAR_DOT = 46; +const CHAR_SLASH = 47; +const CHAR_SEMICOLON = 59; +const CHAR_LOWER_THAN = 60; +const CHAR_EQUALS = 61; +const CHAR_GREATER_THAN = 62; +const CHAR_SQUARE_OPEN = 91; +const CHAR_SQUARE_CLOSE = 93; +const CHAR_UNDERSCORE = 95; +const CHAR_CURLY_OPEN = 123; +const CHAR_CURLY_CLOSE = 125; + +const NUM_TOKENS = 5; + +const GUID_REGEXP = /[0-9A-Fa-f]{8}-([0-9A-Fa-f]{4}-){3}[0-9A-Fa-f]{12}$/; + +const VALUE_BUFFER: Buffer = Buffer.alloc(64 * 1024); + +function isIdentifierChar(char: number): boolean { + return ( + isIdentifierStartChar(char) || isNumericChar(char) || char === CHAR_MINUS + ); +} + +function isIdentifierStartChar(char: number): boolean { + return ( + (char >= 65 && char <= 90) || + (char >= 97 && char <= 122) || + char === CHAR_UNDERSCORE + ); +} + +function isNumericChar(char: number): boolean { + return char >= 48 && char <= 57; +} + +function isOperantChar(char: number): boolean { + return ( + char === CHAR_EXCLAMATION || + char === CHAR_LOWER_THAN || + char === CHAR_EQUALS || + char === CHAR_GREATER_THAN + ); +} + +function extractComment(value: string, type: TokenType): string { + while (value.startsWith("/")) { + value = value.substr(1); + } + + if (type === TokenType.LineComment) { + return value; + } + + while (value.endsWith("/") || value.endsWith("*")) { + value = value.substr(0, value.length - 1); + } + + const lines = value.split(/\r\n|\r|\n/); + for (let index = 0; index < lines.length; index++) { + lines[index] = lines[index].replace(/^\s*\*+\s*/, ""); + } + + return lines.join("\n"); +} + +export const enum TokenType { + Empty, + EndOfFile, + Invalid, + + // Common token types + Annotation, + BlockComment, + BracketOpen, + BracketClose, + Colon, + CurlyBracketOpen, + CurlyBracketClose, + Dot, + GuidLiteral, + FloatLiteral, + Identifier, + IntegerLiteral, + LineComment, + Operator, + SemiColon, + StringLiteral, + + // Keyword tokens + AndKeyword, + EndExitSectionKeyword, + ExitSectionKeyword, + GoalCompletedKeyword, + IfKeyword, + InitSectionKeyword, + KBSectionKeyword, + NotKeyword, + ProcKeyword, + ThenKeyword, + QueryKeyword +} + +export type PackedPosition = number; + +export interface TokenRange { + endOffset: number; + endPosition: PackedPosition; + startOffset: number; + startPosition: PackedPosition; +} + +export interface Token extends TokenRange { + comment: string | null; + type: TokenType; + value: string; +} + +export default class Lexer { + diagnostics: Array = []; + source: string; + character: number = -1; + lastComment: string | null = null; + line: number = 0; + offset: number = 0; + region: string | null = null; + private tokenBuffer: Array; + + constructor(source: string) { + const tokenBuffer: Array = []; + this.source = source; + this.tokenBuffer = tokenBuffer; + + for (let index = 0; index < NUM_TOKENS; index++) { + tokenBuffer.push({ + comment: null, + endOffset: 0, + endPosition: 0, + startOffset: 0, + startPosition: 0, + type: TokenType.Empty, + value: "" + }); + + if (index < NUM_TOKENS - 1) { + this.readNextToken(index); + } + } + } + + addDiagnostic( + range: TokenRange | undefined = undefined, + message: DiagnosticMessage + ) { + range = range || this.last() || this.peak(); + + if (range) { + this.diagnostics.push({ + ...message, + range: unpackRange(range), + type: DiagnosticType.Syntax + }); + } + } + + last(): Token | undefined { + const { tokenBuffer } = this; + const result = tokenBuffer[NUM_TOKENS - 1]; + + return result.type === TokenType.EndOfFile || + result.type === TokenType.Empty + ? undefined + : result; + } + + next(): Token | undefined { + const { tokenBuffer } = this; + const result = tokenBuffer[0]; + if (result.type === TokenType.EndOfFile) { + return undefined; + } + + for (let index = 1; index < NUM_TOKENS; index++) { + tokenBuffer[index - 1] = tokenBuffer[index]; + } + + tokenBuffer[NUM_TOKENS - 1] = result; + + do { + this.readNextToken(NUM_TOKENS - 2); + } while ( + tokenBuffer[NUM_TOKENS - 2].type === TokenType.LineComment || + tokenBuffer[NUM_TOKENS - 2].type === TokenType.BlockComment + ); + + return result; + } + + peak(offset: number = 0): Token | undefined { + if (offset > NUM_TOKENS - 2) { + throw new Error("Invalid peak offset."); + } + + const { tokenBuffer } = this; + for (let index = 0; index <= offset; index++) { + if (tokenBuffer[index].type === TokenType.EndOfFile) { + return undefined; + } + } + + return tokenBuffer[offset]; + } + + tokenize() { + const result: Array = []; + let token: Token | undefined; + while ((token = this.next())) { + result.push({ + ...token, + endOffset: token.endOffset, + endPosition: token.endPosition, + startOffset: token.startOffset, + startPosition: token.startPosition + }); + } + + return result; + } + + private readNextToken(bufferIndex: number) { + const { source } = this; + const { length } = source; + let { character, line, offset } = this; + const value = VALUE_BUFFER; + const token = this.tokenBuffer[bufferIndex]; + + let char: number | undefined; + let charLength: number | undefined; + let isNewLine = false; + let nextChar: number | undefined; + let previousCharacter = character; + let previousLine = line; + let previousOffset = offset; + let type: TokenType | undefined; + let valueOffset = 0; + + while (offset < length) { + previousCharacter = character; + previousLine = line; + previousOffset = offset; + + char = source.charCodeAt(offset); + charLength = 1; + + isNewLine = false; + offset += 1; + character += 1; + + // Track line numbers + if (char === CHAR_CARRIAGE_RETURN) { + isNewLine = true; + character = -1; + line += 1; + + if (offset < length - 1) { + nextChar = source.charCodeAt(offset); + if (nextChar === CHAR_NEWLINE) { + charLength += 1; + offset += 1; + } + } + } + + if (char === CHAR_NEWLINE) { + isNewLine = true; + character = -1; + line += 1; + } + + // Consume values of the current token + if (type) { + if (type === TokenType.Annotation) { + // Annotations consume till `]` + if (char === CHAR_SQUARE_CLOSE) { + value[valueOffset++] = char; + break; + } + } else if (type === TokenType.BlockComment) { + // Block comments consume till `*/` + if ( + char === CHAR_SLASH && + valueOffset > 1 && + value[valueOffset - 1] === CHAR_ASTERISK + ) { + value[valueOffset++] = char; + break; + } + } else if (type === TokenType.LineComment) { + // Line comments consume till new line + if (isNewLine) { + // YIELD LineComment + break; + } + } else if (type === TokenType.StringLiteral) { + // String literals consume till `"` + if (char === CHAR_QUOTE) { + value[valueOffset++] = char; + break; + } + } else if (type === TokenType.FloatLiteral) { + // Float literals consume till no more numbers are read + if (!isNumericChar(char)) { + character = previousCharacter; + line = previousLine; + offset = previousOffset; + break; + } + } else if (type === TokenType.IntegerLiteral) { + if (char === CHAR_DOT) { + // If a dot is found, switch to float literal + type = TokenType.FloatLiteral; + } else if (char === CHAR_MINUS) { + // A guid starting with only numbers + type = TokenType.Identifier; + } else if (!isNumericChar(char)) { + // Integer literals consume till no more numbers are read + character = previousCharacter; + line = previousLine; + offset = previousOffset; + break; + } + } else if (type === TokenType.Identifier) { + // Identifiers read all valid characters + if (!isIdentifierChar(char)) { + character = previousCharacter; + line = previousLine; + offset = previousOffset; + break; + } + } + + value[valueOffset++] = char; + continue; + } + + // Just skip whitespace + if (isNewLine || char === CHAR_SPACE || char === CHAR_TAB) { + continue; + } + + token.startOffset = offset - 1; + token.startPosition = packPosition(line, character); + + // Single character tokens + if (char === CHAR_BRACKET_OPEN) { + type = TokenType.BracketOpen; + break; + } else if (char === CHAR_BRACKET_CLOSE) { + type = TokenType.BracketClose; + break; + } else if (char === CHAR_CURLY_OPEN) { + type = TokenType.CurlyBracketOpen; + break; + } else if (char === CHAR_CURLY_CLOSE) { + type = TokenType.CurlyBracketClose; + break; + } else if (char === CHAR_COLON) { + type = TokenType.Colon; + break; + } else if (char === CHAR_SEMICOLON) { + type = TokenType.SemiColon; + break; + } else if (char === CHAR_DOT) { + // Maybe start of float + nextChar = source.charCodeAt(offset); + if (isNumericChar(nextChar)) { + value[valueOffset++] = char; + value[valueOffset++] = nextChar; + type = TokenType.FloatLiteral; + offset += 1; + character += 1; + } else { + type = TokenType.Dot; + break; + } + } else if (char === CHAR_QUOTE) { + value[valueOffset++] = char; + type = TokenType.StringLiteral; + } else if (char === CHAR_SQUARE_OPEN) { + value[valueOffset++] = char; + type = TokenType.Annotation; + } else if ( + isNumericChar(char) || + char === CHAR_PLUS || + char === CHAR_MINUS + ) { + value[valueOffset++] = char; + type = TokenType.IntegerLiteral; + } else if (char === CHAR_SLASH) { + nextChar = source.charCodeAt(offset); + if (nextChar === CHAR_ASTERISK) { + value[valueOffset++] = char; + value[valueOffset++] = nextChar; + type = TokenType.BlockComment; + offset += 1; + character += 1; + } else if (nextChar === CHAR_SLASH) { + value[valueOffset++] = char; + value[valueOffset++] = nextChar; + type = TokenType.LineComment; + offset += 1; + character += 1; + } + } else if (isOperantChar(char)) { + nextChar = source.charCodeAt(offset); + if (nextChar === CHAR_EQUALS) { + value[valueOffset++] = char; + value[valueOffset++] = nextChar; + type = TokenType.Operator; + offset += 1; + character += 1; + break; + } else if (char === CHAR_GREATER_THAN || char === CHAR_LOWER_THAN) { + value[valueOffset++] = char; + type = TokenType.Operator; + break; + } + } else if (isIdentifierStartChar(char)) { + value[valueOffset++] = char; + type = TokenType.Identifier; + } else { + type = TokenType.Invalid; + break; + } + } + + this.character = character; + this.line = line; + this.offset = offset; + + const stringValue = value.toString("utf-8", 0, valueOffset); + if (type === TokenType.Identifier) { + if (stringValue === "AND") { + type = TokenType.AndKeyword; + } else if (stringValue === "ENDEXITSECTION") { + type = TokenType.EndExitSectionKeyword; + } else if (stringValue === "EXITSECTION") { + type = TokenType.ExitSectionKeyword; + } else if (stringValue === "GoalCompleted") { + type = TokenType.GoalCompletedKeyword; + } else if (stringValue === "IF") { + type = TokenType.IfKeyword; + } else if (stringValue === "INITSECTION") { + type = TokenType.InitSectionKeyword; + } else if (stringValue === "KBSECTION") { + type = TokenType.KBSectionKeyword; + } else if (stringValue === "NOT") { + type = TokenType.NotKeyword; + } else if (stringValue === "PROC") { + type = TokenType.ProcKeyword; + } else if (stringValue === "THEN") { + type = TokenType.ThenKeyword; + } else if (stringValue === "QRY") { + type = TokenType.QueryKeyword; + } else if (GUID_REGEXP.test(stringValue)) { + type = TokenType.GuidLiteral; + } + } + + token.endOffset = offset; + token.endPosition = packPosition(line, character + 1); + token.type = type || TokenType.EndOfFile; + token.value = stringValue; + + if ( + token.type === TokenType.LineComment && + stringValue.startsWith("//REGION") + ) { + this.region = stringValue.substr(8).trim(); + } else if ( + token.type === TokenType.LineComment && + stringValue.startsWith("//END_REGION") + ) { + this.region = null; + } else if ( + token.type === TokenType.BlockComment || + token.type === TokenType.LineComment + ) { + this.lastComment = extractComment(stringValue, token.type); + } else { + token.comment = this.lastComment; + this.lastComment = null; + } + + if (token.type === TokenType.FloatLiteral && token.value.endsWith(".")) { + this.addDiagnostic(token, msgPrematureRealEnd()); + } + + if (token.type === TokenType.StringLiteral && /[\r\n]/.test(token.value)) { + this.addDiagnostic(token, msgNewLineInString()); + } + } +} diff --git a/src/server/parsers/story/Parser.ts b/src/server/parsers/story/Parser.ts new file mode 100644 index 0000000..e5a400f --- /dev/null +++ b/src/server/parsers/story/Parser.ts @@ -0,0 +1,721 @@ +import isRuleToken, { ruleTokenTypes } from "./utils/isRuleToken"; +import isStoryToken, { StoryToken } from "./utils/isStoryToken"; +import Lexer, { TokenType, Token, TokenRange } from "./Lexer"; +import msgEmptyRuleBody from "./messages/msgEmptyRuleBody"; +import { ActionNode } from "./utils/isActionNode"; +import { ArgumentNode } from "./utils/isArgumentNode"; +import { ConditionNode } from "./utils/isConditionNode"; + +import msgUnexpectedToken, { + UnexpectedTokenHint +} from "./messages/msgUnexpectedToken"; + +import { + ActionBlockNode, + ConditionBlockNode, + NodeType, + OperatorNode, + ParameterNode, + RuleNode, + SignatureNode, + TypeAnnotationNode, + RuleBlockNode, + SignatureCallNode, + AbstractNode, + ParameterFlow, + IdentifierNode, + StringLiteralNode, + NumericLiteralNode, + GuidLiteralNode, + IdentifierType, + GoalCompletedNode +} from "./models/nodes"; + +type Omit = Pick>; + +type WithoutRange = Omit< + T, + "endOffset" | "endPosition" | "startOffset" | "startPosition" +>; + +const GUID_REGEXP = /(.*?)([0-9A-Fa-f]{8}-([0-9A-Fa-f]{4}-){3}[0-9A-Fa-f]{12})$/; + +export default class Parser extends Lexer { + bailOutTypes: Array = []; + + consume(type: TokenType): boolean { + const token = this.peak(); + if (token && token.type === type) { + this.next(); + return true; + } + + this.addDiagnostic( + token, + msgUnexpectedToken({ + actualToken: token, + expectedToken: type + }) + ); + + return false; + } + + consumeUntil(...types: Array): boolean { + const { bailOutTypes } = this; + + let token: Token | undefined; + while ((token = this.peak())) { + if (bailOutTypes.indexOf(token.type) !== -1) { + return false; + } + + if (types.indexOf(token.type) !== -1) { + return true; + } + + this.next(); + } + + return false; + } + + consumeIncluding(...types: Array): boolean { + const result = this.consumeUntil(...types); + if (result) { + this.next(); + } + + return result; + } + + ensureSemiColon(lastToken: Token | undefined = undefined) { + const nextToken = this.peak(); + + if (nextToken && nextToken.type === TokenType.SemiColon) { + this.next(); + } else { + this.addDiagnostic( + lastToken, + msgUnexpectedToken({ + actualToken: nextToken, + expectedToken: TokenType.SemiColon + }) + ); + } + } + + injectRange(callback: { + (): WithoutRange | undefined; + }): { (): T | undefined } { + return () => { + const first = this.peak(); + if (!first) { + return undefined; + } + + const startOffset = first.startOffset; + const startPosition = first.startPosition; + const result = callback() as T; + + if (result) { + const last = this.last(); + if (!last) { + return undefined; + } + + result.endOffset = last.endOffset; + result.endPosition = last.endPosition; + result.startOffset = startOffset; + result.startPosition = startPosition; + } + + return result; + }; + } + + isBailOutToken(token: Token): boolean { + return this.bailOutTypes.indexOf(token.type) !== -1; + } + + read( + type: TokenType | Array, + error?: boolean, + hint?: UnexpectedTokenHint + ): Token | undefined { + const token = this.peak(); + if ( + token && + (Array.isArray(type) + ? type.indexOf(token.type) !== -1 + : token.type === type) + ) { + this.next(); + return token; + } + + if (error) { + this.addDiagnostic( + token, + msgUnexpectedToken({ + actualToken: token, + expectedHint: hint, + expectedToken: type + }) + ); + } + + return undefined; + } + + readAction(): ActionNode | undefined { + const token = this.peak(); + if (!token) return undefined; + if (token.type == TokenType.GoalCompletedKeyword) { + const result: GoalCompletedNode = { + endOffset: token.endOffset, + endPosition: token.endPosition, + startOffset: token.startOffset, + startPosition: token.startPosition, + type: NodeType.GoalCompletedAction + }; + + this.next(); + this.ensureSemiColon(token); + return result; + } + + const startOffset = token.startOffset; + const startPosition = token.startPosition; + let isInverted: boolean = false; + + if (token.type === TokenType.NotKeyword) { + this.next(); + isInverted = true; + } + + const signature = this.readSignature(); + if (!signature) { + return undefined; + } + + this.ensureSemiColon(); + return { + endOffset: signature.endOffset, + endPosition: signature.endPosition, + isInverted, + signature, + startOffset, + startPosition, + type: NodeType.SignatureAction + }; + } + + readActionBlock = this.injectRange(() => { + const actions: Array = []; + let token: Token | undefined; + let action; + + while ((token = this.peak())) { + if (this.isBailOutToken(token)) { + break; + } + + action = this.readAction(); + if (action) { + actions.push(action); + } else if (this.consumeUntil(TokenType.SemiColon)) { + this.next(); + } else { + break; + } + } + + return { + actions, + type: NodeType.ActionBlock + }; + }); + + readCondition = this.injectRange(() => { + const isInverted = this.tryReadNotToken(); + const maybeBracket = this.peak(); + const maybeOperator = this.peak(1); + + if ( + (maybeBracket && maybeBracket.type === TokenType.BracketOpen) || + (maybeOperator && maybeOperator.type === TokenType.Operator) + ) { + return this.readOperatorCondition(isInverted); + } else { + return this.readSignatureCondition(isInverted); + } + }); + + readConditionBlock = this.injectRange(() => { + const conditions: Array = []; + let token: Token | undefined; + + while ((token = this.peak())) { + if (this.isBailOutToken(token) || token.type === TokenType.ThenKeyword) { + break; + } else if (token.type === TokenType.AndKeyword) { + this.next(); + const condition = this.readCondition(); + if (condition) { + conditions.push(condition); + } + } else { + this.addDiagnostic( + token, + msgUnexpectedToken({ + actualToken: token, + expectedToken: [TokenType.AndKeyword, TokenType.ThenKeyword] + }) + ); + + if (!this.consumeUntil(TokenType.AndKeyword, TokenType.ThenKeyword)) { + break; + } + } + } + + return { + conditions, + type: NodeType.ConditionBlock + }; + }); + + readFloatLiteral(): NumericLiteralNode | undefined { + const token = this.read(TokenType.FloatLiteral); + return token + ? { + endOffset: token.endOffset, + endPosition: token.endPosition, + startOffset: token.startOffset, + startPosition: token.startPosition, + type: NodeType.RealLiteral, + value: parseFloat(token.value) + } + : undefined; + } + + readGuidLiteral(): GuidLiteralNode | undefined { + const token = this.read(TokenType.GuidLiteral); + if (!token) { + return undefined; + } + + const match = GUID_REGEXP.exec(token.value); + if (!match) { + return undefined; + } + + return { + endOffset: token.endOffset, + endPosition: token.endPosition, + guid: match[2].toLowerCase(), + prefix: match[1], + startOffset: token.startOffset, + startPosition: token.startPosition, + type: NodeType.GuidLiteral + }; + } + + readIdentifier(hint?: UnexpectedTokenHint): IdentifierNode | undefined { + const token = this.read(TokenType.Identifier, true, hint); + if (!token) return undefined; + + let identifierType: IdentifierType = IdentifierType.Default; + if (token.value.startsWith("DB_")) { + identifierType = IdentifierType.Database; + } else if (token.value === "_") { + identifierType = IdentifierType.Empty; + } else if (token.value.startsWith("_")) { + identifierType = IdentifierType.Variable; + } + + return { + endOffset: token.endOffset, + endPosition: token.endPosition, + startOffset: token.startOffset, + startPosition: token.startPosition, + identifierType, + name: token.value, + type: NodeType.Identifier + }; + } + + readIntegerLiteral(): NumericLiteralNode | undefined { + const token = this.read(TokenType.IntegerLiteral); + return token + ? { + endOffset: token.endOffset, + endPosition: token.endPosition, + startOffset: token.startOffset, + startPosition: token.startPosition, + value: parseInt(token.value), + type: NodeType.IntegerLiteral + } + : undefined; + } + + readOperatorCondition( + isInverted: boolean + ): WithoutRange | undefined { + const leftType = this.tryReadTypeAnnoation(); + const leftOperant = this.tryReadArgument(); + if (!leftOperant) return undefined; + + const operatorToken = this.read(TokenType.Operator, true); + if (!operatorToken) return undefined; + const operator = operatorToken.value; + + const rightType = this.tryReadTypeAnnoation(); + const rightOperant = this.tryReadArgument(); + if (!rightOperant) return undefined; + + if (leftOperant && operatorToken && rightOperant) { + return { + isInverted, + leftOperant, + leftType, + operator, + rightOperant, + rightType, + type: NodeType.OperatorCondition + }; + } + + return undefined; + } + + readParameter = this.injectRange(() => { + const flow = this.tryReadParameterFlow(); + const valueType = this.tryReadTypeAnnoation(); + const argument = this.tryReadArgument(); + + if (argument) { + return { + argument, + flow, + type: NodeType.Parameter, + valueType + }; + } + + this.addDiagnostic( + argument, + msgUnexpectedToken({ + actualToken: argument, + expectedHint: "parameter" + }) + ); + return undefined; + }); + + readParameters(thisParameter?: IdentifierNode): Array { + const result: Array = []; + + if (thisParameter) { + result.push({ + endOffset: thisParameter.endOffset, + endPosition: thisParameter.endPosition, + startOffset: thisParameter.startOffset, + startPosition: thisParameter.startPosition, + argument: thisParameter, + type: NodeType.Parameter + }); + } + + if (!this.consume(TokenType.BracketOpen)) { + return result; + } + + let token = this.peak(); + if (!token) { + this.addDiagnostic( + undefined, + msgUnexpectedToken({ + expectedHint: "parameterBlockStart" + }) + ); + return result; + } else if (token.type === TokenType.BracketClose) { + this.next(); + return result; + } + + do { + const parameter = this.readParameter(); + if (parameter) { + result.push(parameter); + } else if (!this.consumeUntil(TokenType.Colon, TokenType.BracketClose)) { + return result; + } + + token = this.peak(); + if (token && token.type === TokenType.Colon) { + this.next(); + } else if (token && token.type === TokenType.BracketClose) { + this.next(); + return result; + } else { + this.addDiagnostic( + token, + msgUnexpectedToken({ + actualToken: token, + expectedHint: "parameterBlock" + }) + ); + if (this.consumeUntil(TokenType.Colon, TokenType.BracketClose)) { + continue; + } else { + return result; + } + } + } while (token && (token = this.peak())); + + return result; + } + + readRule = this.injectRange(() => { + const ruleTypeToken = this.read(ruleTokenTypes); + if (!ruleTypeToken) return undefined; + const comment = ruleTypeToken.comment; + const region = this.region; + const ruleType = ruleTypeToken.value as any; + + const signature = this.readSignature(); + if (!signature) return undefined; + + const conditions = this.readConditionBlock(); + if (!conditions) return undefined; + + this.read(TokenType.ThenKeyword, true); + + const body = this.readActionBlock(); + if (!body) return undefined; + if (body.actions.length === 0) { + this.addDiagnostic(undefined, msgEmptyRuleBody()); + } + + return { + body, + comment, + conditions, + ruleType, + region, + signature, + type: NodeType.Rule + }; + }); + + readRuleBlock = this.injectRange(() => { + const rules: Array = []; + let token: Token | undefined; + + while ((token = this.peak())) { + if (this.isBailOutToken(token)) { + break; + } + + let skip: boolean = false; + if (isRuleToken(token)) { + let rule; + this.withBailOutTypes( + [TokenType.IfKeyword, TokenType.ProcKeyword, TokenType.QueryKeyword], + () => { + rule = this.readRule(); + } + ); + + if (!rule) { + skip = true; + } else { + rules.push(rule); + } + } else { + skip = true; + this.addDiagnostic( + token, + msgUnexpectedToken({ + actualToken: token, + expectedToken: ruleTokenTypes + }) + ); + } + + if (skip && !this.consumeUntil(...ruleTokenTypes)) { + break; + } + } + + return { + rules, + type: NodeType.RuleBlock + }; + }); + + readSignature = this.injectRange(() => { + let thisParamater: IdentifierNode | undefined; + let identifier = this.readIdentifier("signature"); + if (!identifier) { + return undefined; + } + + const dotToken = this.peak(); + if (dotToken && dotToken.type === TokenType.Dot) { + this.next(); + thisParamater = identifier; + + identifier = this.readIdentifier("signature"); + if (!identifier) return undefined; + } + + const parameters = this.readParameters(thisParamater); + + return { + identifier, + parameters, + type: NodeType.Signature + }; + }); + + readSignatureCondition( + isInverted: boolean + ): WithoutRange | undefined { + const signature = this.readSignature(); + if (signature) { + return { + isInverted, + signature, + type: NodeType.SignatureCondition + }; + } + + return undefined; + } + + readStringLiteral(): StringLiteralNode | undefined { + const token = this.read(TokenType.StringLiteral); + return token + ? { + endOffset: token.endOffset, + endPosition: token.endPosition, + startOffset: token.startOffset, + startPosition: token.startPosition, + value: token.value.substr(1, token.value.length - 2), + type: NodeType.StringLiteral + } + : undefined; + } + + readStoryBoundary(): StoryToken | undefined { + let token = this.next(); + if (token && isStoryToken(token)) { + return token; + } + + this.addDiagnostic( + token, + msgUnexpectedToken({ + actualToken: token, + expectedHint: "storyBoundary" + }) + ); + + this.next(); + while ((token = this.next())) { + if (isStoryToken(token)) return token; + } + + return undefined; + } + + tryReadArgument(): ArgumentNode | undefined { + const token = this.peak(); + if (!token) return undefined; + + return token.type === TokenType.Identifier + ? this.readIdentifier() + : this.tryReadConstant(); + } + + tryReadConstant() { + const token = this.peak(); + if (!token) return undefined; + + if (token.type === TokenType.FloatLiteral) { + return this.readFloatLiteral(); + } else if (token.type === TokenType.GuidLiteral) { + return this.readGuidLiteral(); + } else if (token.type === TokenType.IntegerLiteral) { + return this.readIntegerLiteral(); + } else if (token.type === TokenType.StringLiteral) { + return this.readStringLiteral(); + } + } + + tryReadNotToken(): boolean { + const notToken = this.peak(); + if (notToken && notToken.type === TokenType.NotKeyword) { + this.next(); + return true; + } + + return false; + } + + tryReadTypeAnnoation = this.injectRange(() => { + if (!this.read(TokenType.BracketOpen)) { + return undefined; + } + + const identifier = this.read(TokenType.Identifier, true, "typeIdentifier"); + + if (!identifier) { + this.consumeIncluding(TokenType.BracketClose); + return { type: NodeType.TypeAnnotation }; + } + + const annotatedType = identifier.value; + if (!this.read(TokenType.BracketClose, true)) { + this.consumeIncluding(TokenType.BracketClose); + return { type: NodeType.TypeAnnotation }; + } + + return { + annotatedType, + type: NodeType.TypeAnnotation + }; + }); + + tryReadParameterFlow(): ParameterFlow | undefined { + const token = this.peak(); + if (token && token.type === TokenType.Annotation) { + this.next(); + return token.value === "[out]" ? ParameterFlow.Out : ParameterFlow.In; + } + + return undefined; + } + + withBailOutTypes( + token: TokenType | Array, + callback: { (): T } + ): T { + const { bailOutTypes } = this; + const { length } = bailOutTypes; + if (Array.isArray(token)) { + this.bailOutTypes.push(...token); + } else { + this.bailOutTypes.push(token); + } + + const result = callback(); + + this.bailOutTypes.length = length; + return result; + } +} diff --git a/src/server/parsers/story/messages/msgEmptyRuleBody.ts b/src/server/parsers/story/messages/msgEmptyRuleBody.ts new file mode 100644 index 0000000..3e8c6a7 --- /dev/null +++ b/src/server/parsers/story/messages/msgEmptyRuleBody.ts @@ -0,0 +1,14 @@ +import { + DiagnosticCode, + DiagnosticMessage, + DiagnosticSeverity +} from "../models/diagnostics"; + +export default function msgEmptyRuleBody(): DiagnosticMessage { + return { + code: DiagnosticCode.EmptyRuleBody, + message: + "Expected at least one action, add `DB_NOOP(1);` if you don't want to perform any actions.", + severity: DiagnosticSeverity.Error + }; +} diff --git a/src/server/parsers/story/messages/msgInvalidGoalSection.ts b/src/server/parsers/story/messages/msgInvalidGoalSection.ts new file mode 100644 index 0000000..189d5c1 --- /dev/null +++ b/src/server/parsers/story/messages/msgInvalidGoalSection.ts @@ -0,0 +1,19 @@ +import { + DiagnosticCode, + DiagnosticMessage, + DiagnosticSeverity +} from "../models/diagnostics"; + +export type Params = { + name: string; +}; + +export default function msgInvalidGoalSection({ + name +}: Params): DiagnosticMessage { + return { + code: DiagnosticCode.InvalidGoalSection, + message: `Invalid goal section "${name}".`, + severity: DiagnosticSeverity.Error + }; +} diff --git a/src/server/parsers/story/messages/msgInvalidOptionLocation.ts b/src/server/parsers/story/messages/msgInvalidOptionLocation.ts new file mode 100644 index 0000000..94417e4 --- /dev/null +++ b/src/server/parsers/story/messages/msgInvalidOptionLocation.ts @@ -0,0 +1,23 @@ +import { + DiagnosticCode, + DiagnosticMessage, + DiagnosticSeverity +} from "../models/diagnostics"; + +export type Params = { + isHeader: boolean; + name: string; +}; + +export default function msgInvalidOptionLocation({ + name, + isHeader +}: Params): DiagnosticMessage { + return { + code: DiagnosticCode.InvalidOptionLocation, + message: `Invalid location of option "${name}": The option must be placed in the ${ + isHeader ? "header" : "footer" + } of the file.`, + severity: DiagnosticSeverity.Error + }; +} diff --git a/src/server/parsers/story/messages/msgInvalidOptionName.ts b/src/server/parsers/story/messages/msgInvalidOptionName.ts new file mode 100644 index 0000000..9e1a8a0 --- /dev/null +++ b/src/server/parsers/story/messages/msgInvalidOptionName.ts @@ -0,0 +1,19 @@ +import { + DiagnosticCode, + DiagnosticMessage, + DiagnosticSeverity +} from "../models/diagnostics"; + +export type Params = { + name: string; +}; + +export default function msgInvalidOptionName({ + name +}: Params): DiagnosticMessage { + return { + code: DiagnosticCode.InvalidOptionName, + message: `Invalid option name "${name}".`, + severity: DiagnosticSeverity.Error + }; +} diff --git a/src/server/parsers/story/messages/msgInvalidOptionValue.ts b/src/server/parsers/story/messages/msgInvalidOptionValue.ts new file mode 100644 index 0000000..2b81e76 --- /dev/null +++ b/src/server/parsers/story/messages/msgInvalidOptionValue.ts @@ -0,0 +1,23 @@ +import { + DiagnosticCode, + DiagnosticMessage, + DiagnosticSeverity +} from "../models/diagnostics"; + +export type Params = { + actualValue: string; + expectedValue: string; + name: string; +}; + +export default function msgInvalidOptionValue({ + actualValue, + expectedValue, + name +}: Params): DiagnosticMessage { + return { + code: DiagnosticCode.InvalidOptionValue, + message: `Invalid value "${actualValue}" for option "${name}", expected "${expectedValue}".`, + severity: DiagnosticSeverity.Error + }; +} diff --git a/src/server/parsers/story/messages/msgNewLineInString.ts b/src/server/parsers/story/messages/msgNewLineInString.ts new file mode 100644 index 0000000..43c1e8e --- /dev/null +++ b/src/server/parsers/story/messages/msgNewLineInString.ts @@ -0,0 +1,13 @@ +import { + DiagnosticCode, + DiagnosticMessage, + DiagnosticSeverity +} from "../models/diagnostics"; + +export default function msgNewLineInString(): DiagnosticMessage { + return { + code: DiagnosticCode.NewLineInString, + message: "String literals cannot contain line breaks.", + severity: DiagnosticSeverity.Error + }; +} diff --git a/src/server/parsers/story/messages/msgPrematureRealEnd.ts b/src/server/parsers/story/messages/msgPrematureRealEnd.ts new file mode 100644 index 0000000..94eace7 --- /dev/null +++ b/src/server/parsers/story/messages/msgPrematureRealEnd.ts @@ -0,0 +1,13 @@ +import { + DiagnosticCode, + DiagnosticMessage, + DiagnosticSeverity +} from "../models/diagnostics"; + +export default function msgPrematureRealEnd(): DiagnosticMessage { + return { + code: DiagnosticCode.PrematureRealEnd, + message: 'Real literals are not allowed to end with ".".', + severity: DiagnosticSeverity.Error + }; +} diff --git a/src/server/parsers/story/messages/msgUnexpectedToken.ts b/src/server/parsers/story/messages/msgUnexpectedToken.ts new file mode 100644 index 0000000..e690cc7 --- /dev/null +++ b/src/server/parsers/story/messages/msgUnexpectedToken.ts @@ -0,0 +1,51 @@ +import printTokenType from "../utils/printTokenType"; +import { Token, TokenType } from "../Lexer"; +import { + DiagnosticCode, + DiagnosticMessage, + DiagnosticSeverity +} from "../models/diagnostics"; + +const hints = { + definitionMetaData: "definition meta data.", + parameter: "a parameter name or constant", + parameterBlock: "a colon or closing bracket for parameters", + parameterBlockStart: `a parameter name, constant or closing bracket ")"`, + signature: "the name of a siganture", + signatureOrThis: "the name of a siganture or a this value", + storyBoundary: "a story boundary token", + typeIdentifier: " a type identifier" +}; + +export type UnexpectedTokenHint = keyof typeof hints; + +export type Params = { + actualToken?: Token | TokenType; + expectedHint?: UnexpectedTokenHint; + expectedToken?: TokenType | Array; +}; + +export default function msgUnexpectedToken({ + actualToken, + expectedHint, + expectedToken +}: Params): DiagnosticMessage { + let message = "Unexpected token"; + if (actualToken) message += `"${printTokenType(actualToken)}"`; + + if (expectedHint) { + message += `, expected ${hints[expectedHint]}`; + } else if (Array.isArray(expectedToken)) { + const args = expectedToken.map(token => `"${printTokenType(token)}"`); + const orArg = args.length > 1 ? ` or ${args.pop()}` : ""; + message += `, expected ${args.join(",")}${orArg}`; + } else if (expectedToken) { + message += `, expected "${printTokenType(expectedToken)}"`; + } + + return { + code: DiagnosticCode.UnexpectedToken, + message, + severity: DiagnosticSeverity.Error + }; +} diff --git a/src/server/parsers/story/models/diagnostics.ts b/src/server/parsers/story/models/diagnostics.ts new file mode 100644 index 0000000..b0ab686 --- /dev/null +++ b/src/server/parsers/story/models/diagnostics.ts @@ -0,0 +1,136 @@ +import { DiagnosticSeverity, Range } from "vscode-languageserver"; + +export { DiagnosticSeverity }; + +export interface DiagnosticMessage { + code: DiagnosticCode; + message: string; + severity: DiagnosticSeverity; +} + +export enum DiagnosticType { + Analyzer, + Syntax +} + +export interface Diagnostic extends DiagnosticMessage { + range: Range; + type: DiagnosticType; +} + +/** + * Keep in sync with + * https://github.com/Norbyte/lslib/blob/master/LSLib/LS/Story/Compiler/Compiler.cs#L159 + */ +export const enum DiagnosticCode { + // Miscellaenous internal error - should not happen. + InternalError = "E00", + + // A type ID was declared multiple times in the story definition file. + TypeIdAlreadyDefined = "E01", + + // A type name (alias) was declared multiple times in the story definition file. + TypeNameAlreadyDefined = "E02", + + // The type ID is either an intrinsic ID or is outside the allowed range. + TypeIdInvalid = "E03", + + // The alias type ID doesn't point to a valid intrinsic type ID + IntrinsicTypeIdInvalid = "E04", + + // A function with the same signature already exists. + SignatureAlreadyDefined = "E05", + + // The type of an argument could not be resolved in a builtin function. + // (This only occurs when parsing story headers, not in goal code) + UnresolvedTypeInSignature = "E06", + + // A goal with the same name was seen earlier. + GoalAlreadyDefined = "E07", + + // The parent goal specified in the goal script was not found. + UnresolvedGoal = "E08", + + // Failed to infer the type of a rule-local variable. + UnresolvedVariableType = "E09", + + // The function signature (full typed parameter list) of a function + // could not be determined. This is likely the result of a failed type inference. + UnresolvedSignature = "E10", + + // The intrinsic type of a function parameter does not match the expected type. + LocalTypeMismatch = "E11", + + // Constant value with unknown type encountered during IR generation. + UnresolvedType = "E12", + + // PROC/QRY declarations must start with a PROC/QRY name as the first condition. + InvalidProcDefinition = "E13", + + // Fact contains a function that is not callable + // (the function is not a call, database or proc). + InvalidSymbolInFact = "E14", + + // Rule action contains a function that is not callable + // (the function is not a call, database or proc). + InvalidSymbolInStatement = "E15", + + // "NOT" action contains a non-database function. + CanOnlyDeleteFromDatabase = "E16", + + // Initial PROC/QRY/IF function type differs from allowed type. + InvalidSymbolInInitialCondition = "E17", + + // Condition contains a function that is not a query or database. + InvalidFunctionTypeInCondition = "E18", + + // Function name could not be resolved. + UnresolvedSymbol = "E19", + + // Use of less/greater operators on strings or guidstrings. + StringLtGtComparison = "W20", + + // The alias type of a function parameter does not match the expected type. + GuidAliasMismatch = "W21", + + // Object name GUID is prefixed with a type that is not known. + GuidPrefixNotKnown = "W22", + + // PROC_/QRY_ naming style violation. + RuleNamingStyle = "W23", + + // A rule variable was used in a read context, but was not yet bound. + ParamNotBound = "E24", + + // The database is likely unused or unpopulated. + // (Written but not read, or vice versa) + UnusedDatabase = "W25", + + // Database "DB_" naming convention violation. + DbNamingStyle = "W26", + + // Object name GUID could not be resolved to a game object. + UnresolvedGameObjectName = "W27", + + // Type of name GUID differs from type of game object. + GameObjectTypeMismatch = "W28", + + // Name part of name GUID differs from name of game object. + GameObjectNameMismatch = "W29", + + // Multiple definitions seen for the same function with different signatures. + ProcTypeMismatch = "E30", + + // Custom + // --------------------------------- + PrematureRealEnd = "E1001", + NewLineInString = "E1002", + UnexpectedToken = "E1003", + EmptyRuleBody = "E1004", + InvalidOptionName = "E1005", + InvalidOptionValue = "E1006", + InvalidOptionLocation = "E1007", + InvalidGoalSection = "E1008", + InvalidVariableName = "E1009", + VariableNotAllowed = "E1010" +} diff --git a/src/server/parsers/story/models/nodes.ts b/src/server/parsers/story/models/nodes.ts new file mode 100644 index 0000000..023c4c2 --- /dev/null +++ b/src/server/parsers/story/models/nodes.ts @@ -0,0 +1,187 @@ +import Symbol from "../../../projects/story/Symbol"; +import { ArgumentNode } from "../utils/isArgumentNode"; +import { ActionNode } from "../utils/isActionNode"; +import { ConditionNode } from "../utils/isConditionNode"; +import { TokenRange } from "../Lexer"; + +export const enum NodeType { + ActionBlock, + ConditionBlock, + Definition, + Div, + DivGoal, + GoalCompletedAction, + GuidLiteral, + Identifier, + IntegerLiteral, + OperatorCondition, + Parameter, + RealLiteral, + Rule, + RuleBlock, + Signature, + SignatureAction, + SignatureCondition, + StoryGoal, + StringLiteral, + TypeAnnotation +} + +export const enum ParameterFlow { + In = 1, + Out = 2 +} + +export const enum IdentifierType { + Default, + Database, + Empty, + Variable +} + +export type AnyNode = + | ActionBlockNode + | ConditionBlockNode + | DefinitionNode + | HeaderNode + | HeaderGoalNode + | GoalCompletedNode + | GuidLiteralNode + | IdentifierNode + | NumericLiteralNode + | OperatorNode + | ParameterNode + | RuleBlockNode + | RuleNode + | SignatureCallNode + | SignatureNode + | StoryGoalNode + | StringLiteralNode + | TypeAnnotationNode; + +export interface AbstractNode extends TokenRange { + type: NodeType; +} + +export interface AbstractGoalNode extends AbstractNode { + exit?: ActionBlockNode; + init?: ActionBlockNode; + kb?: RuleBlockNode; +} + +export interface ActionBlockNode extends AbstractNode { + actions: Array; + type: NodeType.ActionBlock; +} + +export interface ConditionBlockNode extends AbstractNode { + conditions: Array; + type: NodeType.ConditionBlock; +} + +export interface HeaderNode extends AbstractNode { + definitions: Array; + goals: Array; + type: NodeType.Div; + typeAliases: Array; +} + +export interface HeaderGoalNode extends AbstractNode { + exit?: string; + id: number; + init?: string; + kb?: string; + subGoal: Array; + title?: string; + type: NodeType.DivGoal; +} + +export interface DefinitionNode extends AbstractNode { + definitionType: string; + signature: SignatureNode; + type: NodeType.Definition; +} + +export interface GoalCompletedNode extends AbstractNode { + type: NodeType.GoalCompletedAction; +} + +export interface GuidLiteralNode extends AbstractNode { + guid: string; + prefix: string; + type: NodeType.GuidLiteral; +} + +export interface IdentifierNode extends AbstractNode { + identifierType: IdentifierType; + name: string; + type: NodeType.Identifier; +} + +export interface NumericLiteralNode extends AbstractNode { + type: NodeType.IntegerLiteral | NodeType.RealLiteral; + value: number; +} + +export interface OperatorNode extends AbstractNode { + isInverted: boolean; + leftOperant: ArgumentNode; + leftType: TypeAnnotationNode | undefined; + operator: string; + rightOperant: ArgumentNode; + rightType: TypeAnnotationNode | undefined; + type: NodeType.OperatorCondition; +} + +export interface ParameterNode extends AbstractNode { + argument: ArgumentNode; + flow?: ParameterFlow; + type: NodeType.Parameter; + valueType?: TypeAnnotationNode; +} + +export interface RuleNode extends AbstractNode { + body?: ActionBlockNode; + comment: string | null; + conditions?: ConditionBlockNode; + region: string | null; + ruleType: "IF" | "PROC" | "QRY"; + signature: SignatureNode; + symbol?: Symbol; + type: NodeType.Rule; +} + +export interface RuleBlockNode extends AbstractNode { + rules: Array; + type: NodeType.RuleBlock; +} + +export interface SignatureCallNode extends AbstractNode { + isInverted: boolean; + signature: SignatureNode; + symbol?: Symbol; + type: NodeType.SignatureAction | NodeType.SignatureCondition; +} + +export interface SignatureNode extends AbstractNode { + identifier: IdentifierNode; + parameters: Array; + type: NodeType.Signature; +} + +export interface StoryGoalNode extends AbstractGoalNode { + parentTargetEdges?: Array; + subGoalCombiner?: string; + type: NodeType.StoryGoal; + version?: number; +} + +export interface StringLiteralNode extends AbstractNode { + type: NodeType.StringLiteral; + value: string; +} + +export interface TypeAnnotationNode extends AbstractNode { + annotatedType?: string; + type: NodeType.TypeAnnotation; +} diff --git a/src/server/parsers/story/utils/copyTokenRange.ts b/src/server/parsers/story/utils/copyTokenRange.ts new file mode 100644 index 0000000..00ba2d6 --- /dev/null +++ b/src/server/parsers/story/utils/copyTokenRange.ts @@ -0,0 +1,10 @@ +import { TokenRange } from "../Lexer"; + +export default function copyRange(range: TokenRange): TokenRange { + return { + endOffset: range.endOffset, + endPosition: range.endPosition, + startOffset: range.startOffset, + startPosition: range.startPosition + }; +} diff --git a/src/server/parsers/story/utils/eachCaller.ts b/src/server/parsers/story/utils/eachCaller.ts new file mode 100644 index 0000000..b015cc2 --- /dev/null +++ b/src/server/parsers/story/utils/eachCaller.ts @@ -0,0 +1,56 @@ +import eachNodeRecursive from "./eachNodeRecursive"; +import isCallerNode, { CallerNode } from "./isCallerNode"; +import { AnyNode, NodeType, RuleNode } from "../models/nodes"; +import { Variable } from "../../../projects/story/models/symbol"; + +export interface EachCaller { + node: CallerNode; + stack: Array; + type: EachCallerType; + variables?: Array; +} + +export const enum EachCallerType { + Condition, + Definition, + Event, + Fact +} + +export default function* eachCaller( + parent: AnyNode | null +): Iterable { + if (!parent) { + return; + } + + let rule: RuleNode | undefined; + let variables: Array | undefined; + + for (const { node, stack } of eachNodeRecursive(parent)) { + if (!isCallerNode(node)) continue; + + const depth = stack.length; + let type: EachCallerType; + if (node.type === NodeType.Rule) { + rule = node; + variables = []; + if (node.ruleType === "IF") { + type = EachCallerType.Event; + } else { + type = EachCallerType.Definition; + } + } else if (depth < 2 || stack[depth - 2] !== rule) { + rule = undefined; + variables = undefined; + type = EachCallerType.Fact; + } else { + type = + stack[depth - 1].type === NodeType.ConditionBlock + ? EachCallerType.Condition + : EachCallerType.Fact; + } + + yield { node, stack, type, variables }; + } +} diff --git a/src/server/parsers/story/utils/eachNode.ts b/src/server/parsers/story/utils/eachNode.ts new file mode 100644 index 0000000..a25e925 --- /dev/null +++ b/src/server/parsers/story/utils/eachNode.ts @@ -0,0 +1,72 @@ +import { AnyNode, NodeType } from "../models/nodes"; + +export default function* eachNode(parent?: AnyNode): Iterable { + if (!parent) { + return; + } + + switch (parent.type) { + case NodeType.ActionBlock: + for (let index = 0; index < parent.actions.length; index++) { + yield parent.actions[index]; + } + break; + + case NodeType.ConditionBlock: + for (let index = 0; index < parent.conditions.length; index++) { + yield parent.conditions[index]; + } + break; + + case NodeType.OperatorCondition: + yield parent.leftOperant; + yield parent.rightOperant; + break; + + case NodeType.Div: + for (let index = 0; index < parent.definitions.length; index++) { + yield parent.definitions[index]; + } + for (let index = 0; index < parent.goals.length; index++) { + yield parent.goals[index]; + } + break; + + case NodeType.StoryGoal: + if (parent.init) yield parent.init; + if (parent.kb) yield parent.kb; + if (parent.exit) yield parent.exit; + break; + + case NodeType.SignatureAction: + case NodeType.SignatureCondition: { + yield parent.signature; + break; + } + + case NodeType.Parameter: + if (parent.valueType) yield parent.valueType; + if (parent.argument) yield parent.argument; + break; + + case NodeType.Rule: { + yield parent.signature; + if (parent.conditions) yield parent.conditions; + if (parent.body) yield parent.body; + break; + } + + case NodeType.RuleBlock: + for (let index = 0; index < parent.rules.length; index++) { + yield parent.rules[index]; + } + break; + + case NodeType.Signature: + yield parent.identifier; + for (let index = 0; index < parent.parameters.length; index++) { + yield parent.parameters[index]; + } + break; + } +} diff --git a/src/server/parsers/story/utils/eachNodeRecursive.ts b/src/server/parsers/story/utils/eachNodeRecursive.ts new file mode 100644 index 0000000..d9711eb --- /dev/null +++ b/src/server/parsers/story/utils/eachNodeRecursive.ts @@ -0,0 +1,17 @@ +import eachNode from "./eachNode"; +import { AnyNode } from "../models/nodes"; + +export interface NodeWithStack { + node: AnyNode; + stack: Array; +} + +export default function* eachNodeRecursive( + parent?: AnyNode, + stack: Array = [] +): Iterable { + for (const node of eachNode(parent)) { + yield { node, stack }; + yield* eachNodeRecursive(node, [...stack, node]); + } +} diff --git a/src/server/parsers/story/utils/eachRuleNode.ts b/src/server/parsers/story/utils/eachRuleNode.ts new file mode 100644 index 0000000..ff57076 --- /dev/null +++ b/src/server/parsers/story/utils/eachRuleNode.ts @@ -0,0 +1,84 @@ +import { ActionNode } from "./isActionNode"; +import { ConditionNode } from "./isConditionNode"; +import { RuleNode, NodeType, AnyNode } from "../models/nodes"; +import { Variable } from "../../../projects/story/models/symbol"; +import { EachCallerType } from "./eachCaller"; + +export interface Scope { + origin: RuleNode; + variablesBefore: Array; + variables: Array; +} + +export interface EachRuleNode extends Scope { + node: ActionNode | ConditionNode | RuleNode; + type: EachCallerType; +} + +function getRuleCallerType(rule: RuleNode) { + return rule.ruleType === "IF" + ? EachCallerType.Event + : EachCallerType.Definition; +} + +export function isInScope(scope: Scope, stack: Array): boolean { + return stack.indexOf(scope.origin) !== -1; +} + +export function startScope(origin: RuleNode): Scope { + const variables: Array = []; + if (origin.symbol) { + origin.symbol.applyTo(origin, getRuleCallerType(origin), variables); + } + + return { + origin, + variablesBefore: [], + variables + }; +} + +export function tryStartScope(node: AnyNode): Scope | null { + return node.type === NodeType.Rule ? startScope(node) : null; +} + +export function updateScope(scope: Scope, node: AnyNode): Scope { + if (node.type === NodeType.SignatureCondition && node.symbol) { + scope.variablesBefore = [...scope.variables]; + node.symbol.applyTo(node, EachCallerType.Condition, scope.variables); + } else { + scope.variablesBefore = scope.variables; + } + + return scope; +} + +export default function* eachRuleNode(rule: RuleNode): Iterable { + let scope = startScope(rule); + yield { + ...scope, + node: rule, + type: getRuleCallerType(rule) + }; + + if (rule.conditions) { + for (const node of rule.conditions.conditions) { + updateScope(scope, node); + yield { + ...scope, + node, + type: EachCallerType.Condition + }; + } + } + + if (rule.body) { + for (const node of rule.body.actions) { + yield { + ...scope, + node, + type: EachCallerType.Fact + }; + } + } +} diff --git a/src/server/parsers/story/utils/isActionNode.ts b/src/server/parsers/story/utils/isActionNode.ts new file mode 100644 index 0000000..415d9e8 --- /dev/null +++ b/src/server/parsers/story/utils/isActionNode.ts @@ -0,0 +1,16 @@ +import { + AnyNode, + GoalCompletedNode, + NodeType, + SignatureCallNode +} from "../models/nodes"; + +export type ActionNode = GoalCompletedNode | SignatureCallNode; + +export default function isActionNode(node?: AnyNode): node is ActionNode { + return ( + !!node && + (node.type === NodeType.GoalCompletedAction || + node.type === NodeType.SignatureAction) + ); +} diff --git a/src/server/parsers/story/utils/isArgumentNode.ts b/src/server/parsers/story/utils/isArgumentNode.ts new file mode 100644 index 0000000..e0d2a28 --- /dev/null +++ b/src/server/parsers/story/utils/isArgumentNode.ts @@ -0,0 +1,24 @@ +import { + AnyNode, + GuidLiteralNode, + NumericLiteralNode, + StringLiteralNode, + IdentifierNode, + NodeType +} from "../models/nodes"; + +export type ArgumentNode = + | GuidLiteralNode + | IdentifierNode + | NumericLiteralNode + | StringLiteralNode; + +export default function isArgumentNode(node: AnyNode): node is ArgumentNode { + return ( + node.type === NodeType.GuidLiteral || + node.type === NodeType.Identifier || + node.type === NodeType.RealLiteral || + node.type === NodeType.IntegerLiteral || + node.type === NodeType.StringLiteral + ); +} diff --git a/src/server/parsers/story/utils/isCallerNode.ts b/src/server/parsers/story/utils/isCallerNode.ts new file mode 100644 index 0000000..0fd4ec0 --- /dev/null +++ b/src/server/parsers/story/utils/isCallerNode.ts @@ -0,0 +1,17 @@ +import { + RuleNode, + SignatureCallNode, + AnyNode, + NodeType +} from "../models/nodes"; + +export type CallerNode = RuleNode | SignatureCallNode; + +export default function isCallerNode(node?: AnyNode): node is CallerNode { + return ( + !!node && + (node.type === NodeType.Rule || + node.type === NodeType.SignatureAction || + node.type === NodeType.SignatureCondition) + ); +} diff --git a/src/server/parsers/story/utils/isConditionNode.ts b/src/server/parsers/story/utils/isConditionNode.ts new file mode 100644 index 0000000..3b70cde --- /dev/null +++ b/src/server/parsers/story/utils/isConditionNode.ts @@ -0,0 +1,16 @@ +import { + AnyNode, + NodeType, + OperatorNode, + SignatureCallNode +} from "../models/nodes"; + +export type ConditionNode = OperatorNode | SignatureCallNode; + +export default function isConditionNode(node?: AnyNode): node is ConditionNode { + return ( + !!node && + (node.type === NodeType.OperatorCondition || + node.type === NodeType.SignatureCondition) + ); +} diff --git a/src/server/parsers/story/utils/isRuleToken.ts b/src/server/parsers/story/utils/isRuleToken.ts new file mode 100644 index 0000000..4c76df5 --- /dev/null +++ b/src/server/parsers/story/utils/isRuleToken.ts @@ -0,0 +1,19 @@ +import { Token, TokenType } from "../Lexer"; + +export interface RuleToken extends Token { + type: TokenType.IfKeyword | TokenType.ProcKeyword | TokenType.QueryKeyword; +} + +export const ruleTokenTypes = [ + TokenType.IfKeyword, + TokenType.ProcKeyword, + TokenType.QueryKeyword +]; + +export default function isRuleToken(token: Token): token is RuleToken { + return ( + token.type === TokenType.IfKeyword || + token.type === TokenType.ProcKeyword || + token.type === TokenType.QueryKeyword + ); +} diff --git a/src/server/parsers/story/utils/isStoryToken.ts b/src/server/parsers/story/utils/isStoryToken.ts new file mode 100644 index 0000000..4789205 --- /dev/null +++ b/src/server/parsers/story/utils/isStoryToken.ts @@ -0,0 +1,25 @@ +import { Token, TokenType } from "../Lexer"; + +export const storyTokenTypes = [ + TokenType.InitSectionKeyword, + TokenType.KBSectionKeyword, + TokenType.ExitSectionKeyword, + TokenType.EndExitSectionKeyword +]; + +export interface StoryToken extends Token { + type: + | TokenType.InitSectionKeyword + | TokenType.KBSectionKeyword + | TokenType.ExitSectionKeyword + | TokenType.EndExitSectionKeyword; +} + +export default function isStoryToken(token: Token): token is StoryToken { + return ( + token.type === TokenType.InitSectionKeyword || + token.type === TokenType.KBSectionKeyword || + token.type === TokenType.ExitSectionKeyword || + token.type === TokenType.EndExitSectionKeyword + ); +} diff --git a/src/server/parsers/story/utils/packPosition.ts b/src/server/parsers/story/utils/packPosition.ts new file mode 100644 index 0000000..4f93a1e --- /dev/null +++ b/src/server/parsers/story/utils/packPosition.ts @@ -0,0 +1,8 @@ +import { PackedPosition } from "../Lexer"; + +export default function packPosition( + line: number, + character: number +): PackedPosition { + return (character & 0x3fffff) * 0x40000000 + (line & 0x3fffffff); +} diff --git a/src/server/parsers/story/utils/printNode.ts b/src/server/parsers/story/utils/printNode.ts new file mode 100644 index 0000000..1e16d04 --- /dev/null +++ b/src/server/parsers/story/utils/printNode.ts @@ -0,0 +1,59 @@ +import { AnyNode, NodeType, ParameterFlow } from "../models/nodes"; + +export default function printNode(node?: AnyNode | null): string { + if (!node) { + return ""; + } + + switch (node.type) { + case NodeType.SignatureAction: + case NodeType.SignatureCondition: + return printNode(node.signature); + + case NodeType.Parameter: + return [ + node.flow ? `[${node.flow === ParameterFlow.Out ? "out" : "in"}]` : "", + printNode(node.valueType), + printNode(node.argument) + ].join(""); + + case NodeType.Rule: + return `${node.ruleType} ${printNode(node.signature)}`; + + case NodeType.Signature: { + const name = printNode(node.identifier); + const parameters = node.parameters + .map(parameter => printNode(parameter)) + .join(", "); + + return `${name}(${parameters})`; + } + + case NodeType.TypeAnnotation: + return `(${node.annotatedType})`; + + case NodeType.RealLiteral: + case NodeType.IntegerLiteral: + return node.value.toString(); + + case NodeType.GuidLiteral: + return node.guid; + + case NodeType.StringLiteral: + return node.value; + + case NodeType.Identifier: + return node.name; + + case NodeType.GoalCompletedAction: + case NodeType.OperatorCondition: + case NodeType.ActionBlock: + case NodeType.ConditionBlock: + case NodeType.RuleBlock: + case NodeType.Div: + case NodeType.DivGoal: + case NodeType.StoryGoal: + case NodeType.Definition: + return ""; + } +} diff --git a/src/server/parsers/story/utils/printToken.ts b/src/server/parsers/story/utils/printToken.ts new file mode 100644 index 0000000..ebd99b6 --- /dev/null +++ b/src/server/parsers/story/utils/printToken.ts @@ -0,0 +1,22 @@ +import { Token, TokenType } from "../Lexer"; + +export default function printToken(token?: Token | undefined): string { + if (!token) { + return ""; + } + + switch (token.type) { + case TokenType.IfKeyword: + return "IF"; + case TokenType.ProcKeyword: + return "PROC"; + case TokenType.QueryKeyword: + return "QRY"; + case TokenType.StringLiteral: + return `"${token.value}"`; + case TokenType.Identifier: + return token.value; + default: + return ""; + } +} diff --git a/src/server/parsers/story/utils/printTokenType.ts b/src/server/parsers/story/utils/printTokenType.ts new file mode 100644 index 0000000..e3ea8b6 --- /dev/null +++ b/src/server/parsers/story/utils/printTokenType.ts @@ -0,0 +1,80 @@ +import { TokenType, Token } from "../Lexer"; + +export default function printTokenType( + type: Token | TokenType | undefined +): string { + if (!type) { + return ""; + } + + if (typeof type === "object") { + type = type.type; + } + + switch (type) { + case TokenType.Empty: + return ""; + case TokenType.EndOfFile: + return ""; + case TokenType.Invalid: + return ""; + + // Common token types + case TokenType.Annotation: + return "annotion, e.g. '[in]'"; + case TokenType.BlockComment: + return "block comment"; + case TokenType.BracketOpen: + return "opening bracket `(`"; + case TokenType.BracketClose: + return "closing bracket `)`"; + case TokenType.Colon: + return "colon `,`"; + case TokenType.CurlyBracketOpen: + return "opening curly bracket `{`"; + case TokenType.CurlyBracketClose: + return "closing curly bracket `}`"; + case TokenType.Dot: + return "dot `.`"; + case TokenType.GuidLiteral: + return "guid literal"; + case TokenType.FloatLiteral: + return "float literal"; + case TokenType.Identifier: + return "identifier"; + case TokenType.IntegerLiteral: + return "integer literal"; + case TokenType.LineComment: + return "line comment"; + case TokenType.Operator: + return "operator `==`, `!=`, `<=`, `>=`, `>` or `<`"; + case TokenType.SemiColon: + return "semicolon `;`"; + case TokenType.StringLiteral: + return "string literal"; + + // Keyword tokens + case TokenType.AndKeyword: + return "AND"; + case TokenType.EndExitSectionKeyword: + return "ENDEXITSECTION"; + case TokenType.ExitSectionKeyword: + return "EXITSECTION"; + case TokenType.GoalCompletedKeyword: + return "GoalCompleted"; + case TokenType.IfKeyword: + return "IF"; + case TokenType.InitSectionKeyword: + return "INITSECTION"; + case TokenType.KBSectionKeyword: + return "KBSECTION"; + case TokenType.NotKeyword: + return "NOT"; + case TokenType.ProcKeyword: + return "PROC"; + case TokenType.ThenKeyword: + return "THEN"; + case TokenType.QueryKeyword: + return "QRY"; + } +} diff --git a/src/server/parsers/story/utils/unpackPosition.ts b/src/server/parsers/story/utils/unpackPosition.ts new file mode 100644 index 0000000..dfe6896 --- /dev/null +++ b/src/server/parsers/story/utils/unpackPosition.ts @@ -0,0 +1,9 @@ +import { Position } from "vscode-languageserver"; + +import { PackedPosition } from "../Lexer"; + +export default function unpackPosition(value: PackedPosition): Position { + var line = value & 0x3fffffff; + var character = (value - line) / 0x40000000; + return { line, character }; +} diff --git a/src/server/parsers/story/utils/unpackRange.ts b/src/server/parsers/story/utils/unpackRange.ts new file mode 100644 index 0000000..875b8da --- /dev/null +++ b/src/server/parsers/story/utils/unpackRange.ts @@ -0,0 +1,11 @@ +import { Range } from "vscode-languageserver"; + +import unpackPosition from "./unpackPosition"; +import { TokenRange } from "../Lexer"; + +export default function unpackRange(range: TokenRange): Range { + return { + end: unpackPosition(range.endPosition), + start: unpackPosition(range.startPosition) + }; +} diff --git a/src/server/projects/FileWatcher.ts b/src/server/projects/FileWatcher.ts new file mode 100644 index 0000000..661764e --- /dev/null +++ b/src/server/projects/FileWatcher.ts @@ -0,0 +1,171 @@ +import { EventEmitter } from "events"; +import { FSWatcher, watch, existsSync, readdirSync, statSync } from "fs"; +import { join, normalize } from "path"; +import { readDir, stat } from "../../shared/fs"; + +interface PendingEvent { + created: number; + event: string; + path: string; +} + +export interface FileWatcherOptions { + pattern: RegExp; + path: string; + recursive?: boolean; +} + +export default class FileWatcher extends EventEmitter { + readonly path: string; + readonly pattern: RegExp; + readonly recursive: boolean; + + private interval: NodeJS.Timer | null = null; + private pending: Array = []; + private watcher: FSWatcher | null = null; + + constructor(options: FileWatcherOptions) { + super(); + + this.path = options.path; + this.pattern = options.pattern; + this.recursive = !!options.recursive; + } + + dispose() { + if (this.watcher) { + this.watcher.close(); + this.watcher = null; + } + } + + async scan(path: string = this.path) { + this.validate(); + + const { pattern, recursive } = this; + const files = await readDir(path); + + for (const file of files) { + const filePath = normalize(join(path, file)); + const stats = await stat(filePath); + + if (stats.isDirectory()) { + if (recursive) { + await this.scan(filePath); + } + } else if (pattern.test(filePath)) { + this.emit("update", filePath); + } + } + } + + scanSync(path: string = this.path) { + this.validate(); + + const { pattern, recursive } = this; + const files = readdirSync(path); + + for (const file of files) { + const filePath = normalize(join(path, file)); + const stats = statSync(filePath); + + if (stats.isDirectory()) { + if (recursive) { + this.scanSync(filePath); + } + } else if (pattern.test(filePath)) { + this.emit("update", filePath); + } + } + } + + async scanAndStart() { + await this.scan(); + this.start(); + } + + scanAndStartSync() { + this.scanSync(); + this.start(); + } + + start() { + if (this.watcher) return; + this.validate(); + + const { path, recursive } = this; + const watcher = watch(path, { recursive }); + + watcher.on("change", (type, file) => + this.handleFileEvent(type, file as string) + ); + + this.watcher = watcher; + } + + validate() { + if (!existsSync(this.path)) { + throw new Error(`The path ${this.path} does not exist.`); + } + } + + private handleFileEvent(type: string, file: string) { + const { pending } = this; + const path = normalize(join(this.path, file)); + if (!this.pattern.test(path)) { + return; + } + + let event; + if (type === "rename") { + event = existsSync(path) ? "update" : "remove"; + } else { + event = "update"; + } + + let index = 0; + while (index < pending.length) { + if (pending[index].path === path) { + pending.splice(index, 1); + } else { + index++; + } + } + + pending.push({ + created: Date.now(), + event, + path + }); + + this.pending = pending; + this.startInterval(); + } + + private startInterval() { + if (this.interval) return; + + const interval = setInterval(() => { + const { pending } = this; + const now = Date.now(); + let index = 0; + + while (index < pending.length) { + const item = pending[index]; + if (now - item.created > 100) { + pending.splice(index, 1); + this.emit(item.event, item.path); + } else { + index++; + } + } + + if (pending.length === 0) { + clearInterval(interval); + this.interval = null; + } + }, 50); + + this.interval = interval; + } +} diff --git a/src/server/projects/Project.ts b/src/server/projects/Project.ts new file mode 100644 index 0000000..a60945e --- /dev/null +++ b/src/server/projects/Project.ts @@ -0,0 +1,36 @@ +import Levels from "./levels/index"; +import Projects from "."; +import Story from "./story"; +import { ProjectInfo, ProjectMetaInfo } from "../../shared/notifications"; + +export default class Project implements ProjectInfo { + readonly levels: Levels; + readonly meta: ProjectMetaInfo; + readonly path: string; + readonly projects: Projects; + readonly story: Story; + + constructor(projects: Projects, info: ProjectInfo) { + this.levels = new Levels(this); + this.meta = info.meta; + this.path = info.path; + this.projects = projects; + this.story = new Story(this); + } + + dispose() { + this.story.dispose(); + this.levels.dispose(); + } + + getInfo(): ProjectInfo { + return { + meta: this.meta, + path: this.path + }; + } + + initialize() { + this.story.initialize(); + } +} diff --git a/src/server/projects/index.ts b/src/server/projects/index.ts new file mode 100644 index 0000000..e2c4fd6 --- /dev/null +++ b/src/server/projects/index.ts @@ -0,0 +1,120 @@ +import { EventEmitter } from "events"; +import { normalize, join, resolve } from "path"; + +import Documentation from "../documentation/Documentation"; +import parseUri, { ParsedUri } from "../utils/parseUri"; +import Project from "./Project"; +import readProjectMetaInfo from "../utils/readProjectMetaInfo"; +import readXmlFile from "../utils/readXmlFile"; +import Resource from "./story/resources/Resource"; +import { ProjectInfo } from "../../shared/notifications"; + +/** + * @event "diagnostics" (goal: AbstractGoal) + * @event "goalsChanged" (project: Project) + * @event "levelInitStart" (project: Project) + * @event "levelInitReady" (project: Project) + * @event "projectAdded" (project: Project) + * @event "projectReady" (project: Project) + * @event "showError" (string: message) + */ +export default class Projects extends EventEmitter { + readonly docProvider: Documentation = new Documentation(); + readonly projects: Array = []; + + dispose() { + const { projects } = this; + projects.forEach(project => project.dispose()); + projects.length = 0; + } + + findProjectByPath(path: string): Project | null { + path = normalize(path); + return this.projects.find(project => project.path === path) || null; + } + + findProjectByUid(uid: string) { + return this.projects.find(project => project.meta.UUID === uid) || null; + } + + async findResource(uri: string | ParsedUri | null): Promise { + if (typeof uri === "string") { + uri = parseUri(uri); + } + + if (!uri) { + return null; + } + + if (uri.type === "path") { + return this.findResourceByPath(uri.path); + } + + const project = this.findProjectByUid(uri.project); + if (!project) { + return null; + } + + const goal = project.story.findGoal(uri.goal); + return goal ? goal.resource : null; + } + + async findResourceByPath(path: string | null): Promise { + if (!path) { + return null; + } + + for (const project of this.projects) { + const file = project.story.findOrCreateResource(path); + if (file) { + return file; + } + } + + const project = await this.tryCreateForFolder(path); + if (project) { + return project.story.findResource(path); + } + + return null; + } + + async tryGetProjectInfo(path: string): Promise { + path = normalize(path); + try { + const data = await readXmlFile(join(path, "meta.lsx")); + return { + meta: readProjectMetaInfo(data), + path + }; + } catch (e) {} + + return null; + } + + async tryCreateForFolder(initialPath: string): Promise { + let path = resolve(initialPath); + let nextPath = path; + + do { + path = nextPath; + nextPath = resolve(path, ".."); + + const info = await this.tryGetProjectInfo(path); + if (info) { + let project = this.findProjectByPath(info.path); + if (!project) { + project = new Project(this, info); + this.projects.push(project); + this.emit("projectAdded", project); + + project.initialize(); + } + + return project; + } + } while (path !== nextPath); + + return null; + } +} diff --git a/src/server/projects/levels/index.ts b/src/server/projects/levels/index.ts new file mode 100644 index 0000000..c910076 --- /dev/null +++ b/src/server/projects/levels/index.ts @@ -0,0 +1,166 @@ +import LSFParser from "../../parsers/lsf/Parser"; +import Project from "../Project"; +import FileWatcher from "../../projects/FileWatcher"; +import { readFile } from "../../../shared/fs"; + +const allowedTypes = ["character", "item", "leveltemplate", "trigger"]; + +export type InstanceType = "character" | "item" | "leveltemplate" | "trigger"; + +export interface FileInfo { + path: string; + guid: string; + level: string; +} + +export interface InstanceInfo extends FileInfo { + name: string; + type: InstanceType; +} + +export const enum LevelsInitState { + Idle, + Scanning, + Loading, + Ready +} + +export default class Levels { + readonly project: Project; + initState: LevelsInitState = LevelsInitState.Idle; + instances: Array = []; + isProcessing: boolean = false; + lsfReader: LSFParser = new LSFParser(); + pending: Array = []; + watcher: FileWatcher | null = null; + + constructor(project: Project) { + this.project = project; + } + + dispose() { + if (this.watcher) { + this.watcher.dispose(); + this.watcher = null; + } + } + + getFileInfo(path: string): FileInfo | null { + const match = /([^\\\/]+)[\\\/][^\\\/]+[\\\/]([0-9A-Fa-f]{8}-([0-9A-Fa-f]{4}-){3}[0-9A-Fa-f]{12})\.lsf$/.exec( + path + ); + + return match + ? { + path, + guid: match[2], + level: match[1] + } + : null; + } + + initialize() { + try { + const watcher = new FileWatcher({ + path: this.project.path, + pattern: /(Globals|Levels)[\\\/]([^\\\/]+)[\\\/](Characters|Items|LevelTemplates|Splines|Triggers)[\\\/](.*?).lsf$/, + recursive: true + }); + + this.initState = LevelsInitState.Scanning; + + watcher.on("update", this.handleFileUpdate); + watcher.on("remove", this.handleFileRemove); + watcher.scanAndStart().then(() => { + if (!this.isProcessing) { + this.initState = LevelsInitState.Ready; + this.project.projects.emit("levelInitReady", this.project); + } else { + this.initState = LevelsInitState.Loading; + } + }); + + this.project.projects.emit("levelInitStart", this.project); + this.watcher = watcher; + } catch (error) { + this.initState = LevelsInitState.Ready; + this.project.projects.emit("showError", error.message); + } + } + + async process() { + if (this.isProcessing) return; + this.isProcessing = true; + + const { instances, pending } = this; + let info: FileInfo | undefined; + + while ((info = pending.shift())) { + let instance: InstanceInfo | null | undefined; + try { + instance = await this.read(info); + } catch (e) {} + + if (instance) { + const guid = instance.guid; + const index = instances.findIndex(inst => inst.guid === guid); + + if (index === -1) { + instances.push(instance); + } else { + instances[index] = instance; + } + } + } + + this.isProcessing = false; + if (this.initState === LevelsInitState.Loading) { + this.initState = LevelsInitState.Ready; + this.project.projects.emit("levelInitReady", this.project); + } + } + + async read(info: FileInfo): Promise { + const buffer = await readFile(info.path); + const resource = this.lsfReader.read(buffer); + const node = resource.findNode("Templates", "GameObjects"); + let result: InstanceInfo | null = null; + + if (node) { + const name = node.getStringAttribute("Name"); + const type = node.getStringAttribute("Type"); + + if (name && !name.startsWith("S_")) { + return null; + } + + if (name && type && allowedTypes.indexOf(type) !== -1) { + result = { + ...info, + guid: info.guid.toLowerCase(), + name, + type: type as InstanceType + }; + } + } + + return result; + } + + handleFileUpdate = (path: string) => { + const info = this.getFileInfo(path); + if (info) { + this.pending.push(info); + this.process(); + } + }; + + handleFileRemove = (path: string) => { + const info = this.getFileInfo(path); + if (info) { + const { guid } = info; + this.instances = this.instances.filter(info => info.guid !== guid); + this.pending = this.pending.filter(info => info.guid !== guid); + } + }; +} diff --git a/src/server/projects/story/Goal.ts b/src/server/projects/story/Goal.ts new file mode 100644 index 0000000..c2c0037 --- /dev/null +++ b/src/server/projects/story/Goal.ts @@ -0,0 +1,66 @@ +import HeaderGoalResource from "./resources/HeaderGoalResource"; +import Resource from "./resources/Resource"; +import sortGoals from "./utils/sortGoals"; +import Story from "."; + +export default class Goal { + parents: Array = []; + weight: number = 0; + readonly name: string; + readonly resource: Resource; + readonly story: Story; + + constructor(story: Story, name: string, resource: Resource) { + this.resource = resource; + this.name = name; + this.story = story; + } + + getChildren(): Array { + return this.story.getGoalsByParent(this.name); + } + + getSortedChildren(): Array { + return this.story.getGoalsByParent(this.name).sort(sortGoals); + } + + isHeaderGoal(): boolean { + return this.resource instanceof HeaderGoalResource; + } + + setParents(parents: Array) { + const { parents: oldParents } = this; + parents.sort(); + + if ( + oldParents.length === parents.length && + parents.every((parent, index) => parent === oldParents[index]) + ) { + return; + } + + this.parents = parents; + this.story.updateTree(); + } + + setWeight(weight: number) { + if (this.weight === weight) return; + this.weight = weight; + this.story.symbols.notifyGoalChanged(this); + } + + static updateWeights(goals: Array, weight: number = 0): number { + for (const goal of goals) { + goal.setWeight(weight++); + } + + for (const goal of goals) { + const children = goal.getChildren(); + if (children.length) { + weight = this.updateWeights(children, weight); + } + } + + return weight; + } +} diff --git a/src/server/projects/story/Symbol.ts b/src/server/projects/story/Symbol.ts new file mode 100644 index 0000000..00197e9 --- /dev/null +++ b/src/server/projects/story/Symbol.ts @@ -0,0 +1,336 @@ +import getCallerSymbolType from "./utils/getCallerSymbolType"; +import getDefinitionSymbolType from "./utils/getDefinitionSymbolType"; +import getParameterNameScore from "./utils/getParameterNameScore"; +import Goal from "./Goal"; +import parseDocComments from "./utils/parseDocComment"; +import toParameters from "./utils/toParameters"; +import { CallerNode } from "../../parsers/story/utils/isCallerNode"; +import { EachCallerType } from "../../parsers/story/utils/eachCaller"; +import { SymbolType, SymbolDoc, Variable } from "./models/symbol"; +import { TokenRange } from "../../parsers/story/Lexer"; + +import resolveParameters, { + ResolvePrameterResult +} from "./utils/resolveParameters"; + +import { + Parameter, + ParameterType, + ScoredParameterName +} from "./models/parameter"; + +import { + NodeType, + IdentifierType, + DefinitionNode, + ParameterFlow +} from "../../parsers/story/models/nodes"; +import getAnnotatedType from "./utils/getAnnotatedType"; + +function compareDefinition(left: SymbolDefinition, right: SymbolDefinition) { + if (left.goal.weight === right.goal.weight) { + return left.startOffset - right.startOffset; + } + + return left.goal.weight - right.goal.weight; +} + +export interface SymbolDefinition extends TokenRange { + comment: string | null; + goal: Goal; + isInferred: boolean; + isPartial: boolean; + parameters: Array; + type: SymbolType; +} + +export default class Symbol { + category: string | null = null; + dbReads: Array | null = null; + dbWrites: Array | null = null; + definitions: Array = []; + documentation: SymbolDoc | null = null; + isDead: boolean = false; + isSystem: boolean = false; + name: string; + needsUpdate: boolean = false; + numParameters: number; + parameters: Array; + parameterNames: Array; + resolvedDefinition: SymbolDefinition | null = null; + searchName: string; + type: SymbolType = SymbolType.Unknown; + usages: Array = []; + + constructor(name: string, numParameters: number) { + const parameters: Array = []; + const parameterNames: Array = []; + + for (let index = 0; index < numParameters; index++) { + const name = `_Param${index + 1}`; + parameterNames.push({ name, score: 0 }); + parameters.push({ name, type: ParameterType.Unknown }); + } + + this.name = name; + this.numParameters = numParameters; + this.parameters = parameters; + this.parameterNames = parameterNames; + this.searchName = name.toLowerCase(); + } + + applyTo(node: CallerNode, type: EachCallerType, variables: Array) { + if (type === EachCallerType.Fact) { + return; + } + + const { parameters } = node.signature; + if (parameters.length !== this.numParameters) { + throw new Error("Invalid operation."); + } + + for (let index = 0; index < parameters.length; index++) { + const parameter = parameters[index]; + const { argument } = parameter; + if ( + argument.type !== NodeType.Identifier || + argument.identifierType !== IdentifierType.Variable + ) { + continue; + } + + const definition = this.parameters[index]; + if (definition && definition.flow === ParameterFlow.In) { + continue; + } + + const annotatedType = getAnnotatedType(parameter.valueType); + const name = argument.name.toLowerCase(); + const variable = { + displayName: argument.name, + fromIndex: index, + fromSymbol: this, + name, + type: annotatedType || this.parameters[index].type + }; + + const existingIndex = variables.findIndex( + variable => variable.name === name + ); + + if (existingIndex === -1) { + variables.push(variable); + } + } + } + + addReference( + goal: Goal, + node: CallerNode, + type: EachCallerType, + variables?: Array + ) { + const { definitions, parameterNames, usages, dbReads, dbWrites } = this; + const { identifierType } = node.signature.identifier; + const isDefinition = + !this.isSystem && + (identifierType === IdentifierType.Database || + type === EachCallerType.Definition); + + if (usages.indexOf(goal) === -1) { + usages.push(goal); + } + + if ( + identifierType === IdentifierType.Database && + (node.type === NodeType.Rule || + node.type === NodeType.SignatureCondition) && + (!dbReads || dbReads.indexOf(goal) === -1) + ) { + (dbReads || (this.dbReads = [])).push(goal); + } + + if ( + identifierType === IdentifierType.Database && + node.type === NodeType.SignatureAction && + !node.isInverted && + (!dbWrites || dbWrites.indexOf(goal) === -1) + ) { + (dbWrites || (this.dbWrites = [])).push(goal); + } + + const { parameters } = node.signature; + for (let index = 0; index < parameters.length; index++) { + const { argument } = parameters[index]; + if ( + argument.type === NodeType.Identifier && + argument.identifierType === IdentifierType.Variable + ) { + const score = getParameterNameScore(argument.name); + if (score > parameterNames[index].score) { + parameterNames[index].score = score; + parameterNames[index].name = argument.name; + } + } + } + + if (isDefinition && !this.hasCompleteDefinition(goal)) { + const parameters = toParameters(this, node.signature, variables); + definitions.push({ + ...parameters, + comment: node.type === NodeType.Rule ? node.comment : null, + endOffset: node.endOffset, + endPosition: node.endPosition, + goal, + startOffset: node.startOffset, + startPosition: node.startPosition, + type: getCallerSymbolType(node) + }); + + this.needsUpdate = true; + } + } + + hasCompleteDefinition(target: Goal): boolean { + return this.definitions.some( + ({ goal, isInferred, isPartial }) => + goal === target && !isInferred && !isPartial + ); + } + + isDefinedBy(goal: Goal): boolean { + return this.definitions.some(definition => definition.goal === goal); + } + + notifyGoalChanged(goal: Goal) { + if (this.isDefinedBy(goal)) { + this.needsUpdate = true; + } + } + + removeGoal(goal: Goal) { + const { usages, dbReads, dbWrites } = this; + let index = usages.indexOf(goal); + if (index !== -1) { + usages.splice(index, 1); + } + + if (dbWrites) { + index = dbWrites.indexOf(goal); + if (index !== -1) { + dbWrites.splice(index, 1); + } + } + + if (dbReads) { + index = dbReads.indexOf(goal); + if (index !== -1) { + dbReads.splice(index, 1); + } + } + + if (this.isDefinedBy(goal)) { + this.definitions = this.definitions.filter( + definition => definition.goal !== goal + ); + + this.needsUpdate = true; + } + } + + resetParameters() { + if (this.isSystem) { + console.error("Trying to reset system definition."); + return; + } + + const { numParameters, parameterNames } = this; + const parameters: Array = []; + + for (let index = 0; index < numParameters; index++) { + parameters.push({ + name: parameterNames[index].name, + type: ParameterType.Unknown + }); + } + + this.parameters = parameters; + this.resolvedDefinition = null; + } + + toSystemSymbol(definition: DefinitionNode) { + const { parameters } = toParameters(this, definition.signature); + + this.isSystem = true; + this.parameters = parameters; + } + + update() { + const { definitions, isSystem, dbWrites } = this; + if (isSystem) { + console.error("Trying to rebuild system definition."); + this.needsUpdate = false; + return; + } + + definitions.sort(compareDefinition); + let deadCounter = 0; + this.type = SymbolType.Unknown; + + for (const definition of definitions) { + const parameters = resolveParameters(this, definition); + if (Array.isArray(parameters)) { + this.isDead = false; + this.needsUpdate = false; + this.parameters = parameters; + this.resolvedDefinition = definition; + this.type = definition.type; + this.documentation = definition.comment + ? parseDocComments(definition.comment) + : null; + + return; + } + + if (this.type === SymbolType.Unknown) { + this.type = definition.type; + } + + if (parameters === ResolvePrameterResult.Dead) { + deadCounter += 1; + } + } + + if ( + this.type === SymbolType.Database && + (!dbWrites || dbWrites.length === 0 || deadCounter === definitions.length) + ) { + this.isDead = true; + this.needsUpdate = false; + } + + this.resetParameters(); + } + + static fromCaller(caller: CallerNode): Symbol { + const { signature } = caller; + const symbol = new Symbol( + signature.identifier.name, + signature.parameters.length + ); + + return symbol; + } + + static fromDefinition(definition: DefinitionNode): Symbol { + const { signature } = definition; + const symbol = new Symbol( + signature.identifier.name, + signature.parameters.length + ); + + symbol.type = getDefinitionSymbolType(definition); + symbol.toSystemSymbol(definition); + return symbol; + } +} diff --git a/src/server/projects/story/Symbols.ts b/src/server/projects/story/Symbols.ts new file mode 100644 index 0000000..b867879 --- /dev/null +++ b/src/server/projects/story/Symbols.ts @@ -0,0 +1,163 @@ +import { get as levenshtein } from "fast-levenshtein"; + +import eachCaller from "../../parsers/story/utils/eachCaller"; +import Goal from "./Goal"; +import Story from "./index"; +import Symbol from "./Symbol"; +import { SymbolDoc } from "./models/symbol"; + +import { + SignatureNode, + DefinitionNode, + HeaderGoalNode, + StoryGoalNode +} from "../../parsers/story/models/nodes"; + +export default class Symbols { + readonly story: Story; + readonly symbols: Array = []; + + constructor(story: Story) { + this.story = story; + } + + addSystemSymbol(definition: DefinitionNode) { + let symbol = this.findSymbolForSignature(definition.signature); + if (!symbol) { + symbol = Symbol.fromDefinition(definition); + this.symbols.push(symbol); + } else { + symbol.toSystemSymbol(definition); + } + } + + assignSymbols(rootNode: HeaderGoalNode | StoryGoalNode) { + for (const { node } of eachCaller(rootNode)) { + if (node.symbol) { + continue; + } + + let symbol = this.findSymbolForSignature(node.signature); + if (symbol) { + node.symbol = symbol; + } + } + } + + findSimiliarSymbols(name: string): Array { + name = name.toLowerCase(); + return this.symbols.filter( + symbol => levenshtein(symbol.searchName, name) < 4 + ); + } + + findSymbol(name: string, numParameters: number): Symbol | null { + name = name.toLowerCase(); + return ( + this.symbols.find( + symbol => + symbol.searchName === name && symbol.numParameters === numParameters + ) || null + ); + } + + findSymbols(name: string): Array { + name = name.toLowerCase(); + return this.symbols.filter(symbol => symbol.searchName === name); + } + + findSymbolForSignature(signature: SignatureNode): Symbol | null { + return this.findSymbol( + signature.identifier.name, + signature.parameters.length + ); + } + + async getDocumentation(symbol: Symbol): Promise { + if (symbol.documentation) { + return symbol.documentation; + } + + const { docProvider } = this.story.project.projects; + return await docProvider.getDocumentation(symbol.name); + } + + async loadMetaData() { + const { docProvider } = this.story.project.projects; + const categories = await docProvider.getSymbolCategories(); + if (!categories) return; + + for (const symbol of this.symbols) { + if (symbol.searchName in categories) { + symbol.category = categories[symbol.searchName]; + } + } + } + + notifyGoalChanged(goal: Goal) { + for (const symbol of this.symbols) { + symbol.notifyGoalChanged(goal); + } + } + + updateGoal(goal: Goal, rootNode: HeaderGoalNode | StoryGoalNode) { + this.removeByGoal(goal); + + for (const { node, type, variables } of eachCaller(rootNode)) { + let symbol = this.findSymbolForSignature(node.signature); + if (!symbol) { + symbol = Symbol.fromCaller(node); + this.symbols.push(symbol); + } + + symbol.addReference(goal, node, type, variables); + if (variables) { + symbol.applyTo(node, type, variables); + } + + node.symbol = symbol; + } + } + + removeByGoal(goal: Goal) { + const { symbols } = this; + for (const symbol of symbols) { + symbol.removeGoal(goal); + } + } + + update() { + if (this.story.isInitializing) { + return; + } + + const revisit: Array = []; + const { symbols } = this; + let index = 0; + while (index < symbols.length) { + const symbol = symbols[index]; + if ( + !symbol.definitions.length && + !symbol.usages.length && + !symbol.isSystem + ) { + symbols.splice(index, 1); + continue; + } else { + index += 1; + } + + if (symbol.needsUpdate) { + symbol.update(); + } + + if (symbol.needsUpdate) { + revisit.push(symbol); + } + } + + for (const symbol of revisit) { + symbol.update(); + } + } +} diff --git a/src/server/projects/story/analyzers/Analyzer.ts b/src/server/projects/story/analyzers/Analyzer.ts new file mode 100644 index 0000000..9c4b447 --- /dev/null +++ b/src/server/projects/story/analyzers/Analyzer.ts @@ -0,0 +1,56 @@ +import { Range } from "vscode-languageserver"; + +import Analyzers from "./index"; +import isCallerNode from "../../../parsers/story/utils/isCallerNode"; +import Resource from "../resources/Resource"; +import unpackRange from "../../../parsers/story/utils/unpackRange"; +import { Scope } from "../../../parsers/story/utils/eachRuleNode"; +import { StoryGoalNode, AnyNode } from "../../../parsers/story/models/nodes"; + +import { + DiagnosticType, + DiagnosticMessage +} from "../../../parsers/story/models/diagnostics"; + +function resolveRange(node: AnyNode): Range { + if (isCallerNode(node)) { + node = node.signature.identifier; + } + + return unpackRange(node); +} + +export type AnyAnalyzer = SyncAnalyzer | AsyncAnalyzer; + +export interface AnalyzerContext { + node: AnyNode; + resource: Resource; + rootNode: StoryGoalNode; + scope: Scope | null; + stack: Array; +} + +export abstract class Analyzer { + readonly analyzers: Analyzers; + + constructor(analyzers: Analyzers) { + this.analyzers = analyzers; + } + + addDiagnostic(range: AnyNode, message: DiagnosticMessage) { + this.analyzers.diagnostics.push({ + ...message, + range: unpackRange(range), + type: DiagnosticType.Syntax + }); + } +} + +export abstract class SyncAnalyzer extends Analyzer { + abstract analyze(context: AnalyzerContext): void; +} + +export abstract class AsyncAnalyzer extends Analyzer { + abstract async analyze(context: AnalyzerContext): Promise; + abstract canAnalyze(context: AnalyzerContext): boolean; +} diff --git a/src/server/projects/story/analyzers/GuidLiteralAnalyzer.ts b/src/server/projects/story/analyzers/GuidLiteralAnalyzer.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/server/projects/story/analyzers/Parameter.ts b/src/server/projects/story/analyzers/Parameter.ts new file mode 100644 index 0000000..e72aa26 --- /dev/null +++ b/src/server/projects/story/analyzers/Parameter.ts @@ -0,0 +1,386 @@ +import msgComparisonTypeMismatch from "../messages/msgComparisonTypeMismatch"; +import msgInvalidPlaceholder from "../messages/msgInvalidPlaceholder"; +import msgInvalidTypeCast from "../messages/msgInvalidTypeCast"; +import msgInvalidVariableName from "../messages/msgInvalidVariableName"; +import msgParameterTypeMismatch from "../messages/msgParameterTypeMismatch"; +import msgStringLtGtComparison from "../messages/msgStringLtGtComparison"; +import msgVariableNotAllowed from "../messages/msgVariableNotAllowed"; +import msgVariableNotBound from "../messages/msgVariableNotBound"; +import Symbol from "../Symbol"; +import { AnalyzerContext, SyncAnalyzer } from "./Analyzer"; +import { ArgumentNode } from "../../../parsers/story/utils/isArgumentNode"; +import { ParameterType, Parameter } from "../models/parameter"; +import { Scope } from "../../../parsers/story/utils/eachRuleNode"; +import { SymbolType, Variable } from "../models/symbol"; + +import isCallerNode, { + CallerNode +} from "../../../parsers/story/utils/isCallerNode"; + +import { + NodeType, + TypeAnnotationNode, + ParameterFlow, + IdentifierType, + ParameterNode, + OperatorNode +} from "../../../parsers/story/models/nodes"; +import msgDangerousCast from "../messages/msgDangerousCast"; +import getAnnotatedType from "../utils/getAnnotatedType"; + +const enum ArgumentMode { + Constant, + Placeholder, + Variable +} + +function isDangerousCast(from: ParameterType, to: ParameterType): boolean { + return from !== to && isGuidSubtype(from) && isGuidSubtype(to); +} + +function isGuidType(type: ParameterType): boolean { + return type === ParameterType.Guid || isGuidSubtype(type); +} + +function isGuidSubtype(type: ParameterType): boolean { + return ( + type === ParameterType.CharacterGuid || + type === ParameterType.ItemGuid || + type === ParameterType.LevelTemplateGuid || + type === ParameterType.SplineGuid || + type === ParameterType.TriggerGuid + ); +} + +function isIntegerType(type: ParameterType): boolean { + return type === ParameterType.Integer || type === ParameterType.Integer64; +} + +function isNumericType(type: ParameterType): boolean { + return ( + type === ParameterType.Integer || + type === ParameterType.Integer64 || + type === ParameterType.Real + ); +} + +function isNumericOperator(operator: string) { + return ( + operator === ">" || + operator === ">=" || + operator === "<" || + operator === "<=" + ); +} + +function castType( + from: ParameterType, + to: ParameterType, + allowNumericCast?: boolean +): ParameterType { + if (from === to) return from; + if (isGuidType(from) && isGuidType(to)) return to; + if (isIntegerType(from) && isIntegerType(to)) return to; + if (isNumericType(from) && isNumericType(to)) return to; + + return ParameterType.Invalid; +} + +function getArgumentType( + node: ArgumentNode, + scopeOrType: Scope | ParameterType | null +): ParameterType { + switch (node.type) { + case NodeType.RealLiteral: + return ParameterType.Real; + case NodeType.GuidLiteral: + return ParameterType.Guid; + case NodeType.IntegerLiteral: + return ParameterType.Integer; + case NodeType.StringLiteral: + return ParameterType.String; + case NodeType.Identifier: + if (scopeOrType && typeof scopeOrType === "object") { + const variable = scopeOrType.variablesBefore.find( + variable => variable.name === node.name.toLowerCase() + ); + + if (variable && variable.type) { + return variable.type; + } + } else if (typeof scopeOrType === "number") { + return scopeOrType; + } + + return ParameterType.Unknown; + } +} + +export default class ParameterAnalyzer extends SyncAnalyzer { + analyze({ node, scope }: AnalyzerContext) { + if (node.type === NodeType.OperatorCondition) { + this.analyzeOperator(scope, node); + } else if (isCallerNode(node) && node.symbol) { + const { symbol } = node; + const { parameters } = node.signature; + const definitions = symbol.parameters; + + if (definitions.length !== parameters.length) { + return; + } + + for (let index = 0; index < definitions.length; index++) { + this.analyzeParameter( + scope, + node, + symbol, + definitions[index], + parameters[index], + index + ); + } + } + } + + analyzeOperator(scope: Scope | null, operator: OperatorNode) { + const isLeftValid = this.isValidArgument(scope, operator.leftOperant); + const isRightValid = this.isValidArgument(scope, operator.rightOperant); + if (!isLeftValid || !isRightValid) { + return; + } + + const leftType = this.resolveArgumentType( + scope, + operator.leftOperant, + operator.leftType + ); + + const rightType = this.resolveArgumentType( + scope, + operator.rightOperant, + operator.rightType + ); + + if ( + leftType === ParameterType.Invalid || + rightType === ParameterType.Invalid + ) { + return; + } + + if (castType(leftType, rightType, true) === ParameterType.Invalid) { + return this.addDiagnostic( + operator, + msgComparisonTypeMismatch({ leftType, rightType }) + ); + } + + if (isDangerousCast(leftType, rightType)) { + this.addDiagnostic( + operator, + msgDangerousCast({ + sourceType: leftType, + targetType: rightType + }) + ); + } + + if ( + (!isNumericType(leftType) || !isNumericType(rightType)) && + isNumericOperator(operator.operator) + ) { + this.addDiagnostic( + operator, + msgStringLtGtComparison({ + operator: operator.operator + }) + ); + } + } + + analyzeParameter( + scope: Scope | null, + node: CallerNode, + symbol: Symbol, + definition: Parameter, + parameter: ParameterNode, + index: number + ) { + // Fetch inbound variable + let mode: ArgumentMode; + let variableName: string | undefined; + let variable: Variable | undefined; + + if (parameter.argument.type === NodeType.Identifier) { + if (parameter.argument.identifierType === IdentifierType.Empty) { + mode = ArgumentMode.Placeholder; + } else if ( + parameter.argument.identifierType !== IdentifierType.Variable + ) { + return this.addDiagnostic( + parameter, + msgInvalidVariableName({ name: parameter.argument.name }) + ); + } else if (scope) { + const searchName = parameter.argument.name.toLowerCase(); + variableName = parameter.argument.name; + mode = ArgumentMode.Variable; + variable = scope.variablesBefore.find( + variable => variable.name === searchName + ); + } else { + return this.addDiagnostic( + parameter, + msgVariableNotAllowed({ name: parameter.argument.name }) + ); + } + } else { + mode = ArgumentMode.Constant; + } + + // Determine the value flow + let allowPlaceholders = true; + let flow = definition.flow; + if (!flow) { + if (symbol.type === SymbolType.Call || symbol.type === SymbolType.Query) { + if (node.type === NodeType.Rule) { + flow = + mode === ArgumentMode.Variable + ? ParameterFlow.Out + : ParameterFlow.In; + } else { + allowPlaceholders = false; + flow = ParameterFlow.In; + } + } else if (symbol.type === SymbolType.Database) { + if ( + node.type === NodeType.SignatureAction || + (node.type === NodeType.SignatureCondition && node.isInverted) + ) { + flow = ParameterFlow.In; + } else { + flow = variable ? ParameterFlow.In : ParameterFlow.Out; + } + } else { + flow = variable ? ParameterFlow.In : ParameterFlow.Out; + } + } + + // Bail out for placeholders + if (mode === ArgumentMode.Placeholder) { + if (!allowPlaceholders) { + this.addDiagnostic( + parameter, + msgInvalidPlaceholder({ + requiredByIndex: index, + requiredByName: symbol.name + }) + ); + } + + return; + } + + // If the flow is inbound, bail out if no value is given + if ( + flow === ParameterFlow.In && + mode === ArgumentMode.Variable && + !variable + ) { + return this.addDiagnostic( + parameter, + msgVariableNotBound({ + name: `${variableName}`, + requiredByIndex: index, + requiredByName: symbol.name + }) + ); + } + + // Check the type cast + const parameterType = this.resolveArgumentType( + flow === ParameterFlow.Out ? definition.type : scope, + parameter.argument, + parameter.valueType + ); + + // Bail out if the flow is outbound + if (flow === ParameterFlow.Out) { + return; + } + + // Check the type required by the definition + if (castType(parameterType, definition.type) === ParameterType.Invalid) { + this.addDiagnostic( + parameter, + msgParameterTypeMismatch({ + sourceType: parameterType, + symbol, + targetIndex: index, + targetType: definition.type + }) + ); + } else if (isDangerousCast(parameterType, definition.type)) { + this.addDiagnostic( + parameter, + msgDangerousCast({ + sourceType: parameterType, + targetType: definition.type + }) + ); + } + } + + resolveArgumentType( + scope: Scope | ParameterType | null, + argument: ArgumentNode, + typeAnnotation?: TypeAnnotationNode + ): ParameterType { + const argumentType = getArgumentType(argument, scope); + let result = argumentType; + + const annotatedType = getAnnotatedType(typeAnnotation); + if (!typeAnnotation || annotatedType === null) { + return result; + } + + result = castType(result, annotatedType); + if (result === ParameterType.Invalid) { + result = annotatedType; + + this.addDiagnostic( + typeAnnotation, + msgInvalidTypeCast({ + name: + argument.type === NodeType.Identifier ? argument.name : undefined, + sourceType: argumentType, + targetType: annotatedType + }) + ); + } + + return result; + } + + isValidArgument(scope: Scope | null, argument: ArgumentNode): boolean { + if (argument.type === NodeType.Identifier) { + const { name } = argument; + + if (!scope) { + this.addDiagnostic(argument, msgVariableNotAllowed({ name })); + return false; + } + + const variable = scope.variables.find( + variable => variable.name === argument.name.toLowerCase() + ); + + if (!variable) { + this.addDiagnostic(argument, msgVariableNotBound({ name })); + return false; + } + } + + return true; + } +} diff --git a/src/server/projects/story/analyzers/SymbolLocations.ts b/src/server/projects/story/analyzers/SymbolLocations.ts new file mode 100644 index 0000000..b969a9d --- /dev/null +++ b/src/server/projects/story/analyzers/SymbolLocations.ts @@ -0,0 +1,112 @@ +import msgCanOnlyDeleteFromDatabase from "../messages/msgCanOnlyDeleteFromDatabase"; +import msgInvalidSymbolInCondition from "../messages/msgInvalidSymbolInCondition"; +import msgInvalidSymbolInFact from "../messages/msgInvalidSymbolInFact"; +import msgInvalidSymbolInInitialCondition from "../messages/msgInvalidSymbolInInitialCondition"; +import msgInvalidSymbolInStatement from "../messages/msgInvalidSymbolInStatement"; +import Symbol from "../Symbol"; +import { AnalyzerContext, SyncAnalyzer } from "./Analyzer"; +import { SymbolType } from "../models/symbol"; +import { + NodeType, + RuleNode, + SignatureCallNode +} from "../../../parsers/story/models/nodes"; + +import isCallerNode, { + CallerNode +} from "../../../parsers/story/utils/isCallerNode"; + +export default class SymbolLocationsAnalyzer extends SyncAnalyzer { + analyze({ node, stack }: AnalyzerContext) { + if (!isCallerNode(node) || !node.symbol) return; + const { symbol } = node; + const block = stack[stack.length - 1]; + const rule = stack[stack.length - 2]; + + if (node.type === NodeType.Rule) { + return this.analyzeRule(node, symbol); + } + + switch (block ? block.type : undefined) { + case NodeType.ActionBlock: + if (rule && rule.type === NodeType.Rule) { + return this.analyzeRuleAction(node, symbol); + } else { + return this.analyzeFact(node, symbol); + } + case NodeType.ConditionBlock: + return this.analyzeRuleCondition(node, symbol); + } + + // Should never happen + console.error("Caller outside of block"); + } + + analyzeFact(node: SignatureCallNode, symbol: Symbol) { + this.ensureNotOnDatabase(node, symbol); + + if ( + symbol.type !== SymbolType.Database && + symbol.type !== SymbolType.Call + ) { + this.addDiagnostic(node, msgInvalidSymbolInFact({ symbol })); + } + } + + analyzeRule(node: RuleNode, symbol: Symbol) { + const { ruleType } = node; + let isValid: boolean = false; + + switch (ruleType) { + case "PROC": + isValid = symbol.type === SymbolType.Call && !symbol.isSystem; + break; + + case "QRY": + isValid = symbol.type === SymbolType.Query && !symbol.isSystem; + break; + + case "IF": + isValid = + symbol.type === SymbolType.Event || + symbol.type === SymbolType.Database; + break; + } + + if (!isValid) { + this.addDiagnostic( + node, + msgInvalidSymbolInInitialCondition({ ruleType, symbol }) + ); + } + } + + analyzeRuleAction(node: SignatureCallNode, symbol: Symbol) { + this.ensureNotOnDatabase(node, symbol); + + if ( + symbol.type !== SymbolType.Database && + symbol.type !== SymbolType.Call + ) { + this.addDiagnostic(node, msgInvalidSymbolInStatement({ symbol })); + } + } + + analyzeRuleCondition(node: CallerNode, symbol: Symbol) { + if ( + symbol.type !== SymbolType.Database && + symbol.type !== SymbolType.Query + ) { + this.addDiagnostic(node, msgInvalidSymbolInCondition({ symbol })); + } + } + + ensureNotOnDatabase(node: SignatureCallNode, symbol: Symbol) { + if ( + node.isInverted && + !(symbol.type === SymbolType.Database || symbol.type === SymbolType.Query) + ) { + this.addDiagnostic(node, msgCanOnlyDeleteFromDatabase({ symbol })); + } + } +} diff --git a/src/server/projects/story/analyzers/SymbolTypes.ts b/src/server/projects/story/analyzers/SymbolTypes.ts new file mode 100644 index 0000000..63e2a89 --- /dev/null +++ b/src/server/projects/story/analyzers/SymbolTypes.ts @@ -0,0 +1,86 @@ +import isCallerNode from "../../../parsers/story/utils/isCallerNode"; +import msgDatabaseNoWrite from "../messages/msgDatabaseNoWrite"; +import msgDatabaseNoRead from "../messages/msgDatabaseNoRead"; +import msgInvalidDatabasePrefix from "../messages/msgInvalidDatabasePrefix"; +import msgParamaterCountMismatch from "../messages/msgParamaterCountMismatch"; +import msgSigantureTypo from "../messages/msgSigantureTypo"; +import msgUnresolvedSignature from "../messages/msgUnresolvedSignature"; +import msgUnresolvedSymbol from "../messages/msgUnresolvedSymbol"; +import Symbol from "../Symbol"; +import { AnalyzerContext, SyncAnalyzer } from "./Analyzer"; +import { SymbolType } from "../models/symbol"; +import { NodeType } from "../../../parsers/story/models/nodes"; + +export default class SymbolTypesAnalyzer extends SyncAnalyzer { + analyze({ node, resource }: AnalyzerContext) { + if (!isCallerNode(node)) return; + const { symbol } = node; + + // This should actually not happen right now + if (!symbol) { + return this.addDiagnostic(node, msgUnresolvedSymbol({ node })); + } + + // Helper: Filter out invalid symbols + const filterSymbol = (existingSymbol: Symbol) => + existingSymbol !== symbol && existingSymbol.type !== SymbolType.Unknown; + + // Symbol type is unknown + if (symbol.type === SymbolType.Unknown) { + // Check for symbol with different parameter count + const { symbols } = resource.story; + const existingSymbols = symbols + .findSymbols(symbol.name) + .filter(filterSymbol); + + if (existingSymbols.length) { + return this.addDiagnostic( + node, + msgParamaterCountMismatch({ + actualSymbol: symbol, + existingSymbols + }) + ); + } + + // Check for symbols with a similiar name + const similiarSymbols = symbols + .findSimiliarSymbols(symbol.name) + .filter(filterSymbol); + + if (similiarSymbols.length) { + return this.addDiagnostic( + node, + msgSigantureTypo({ + actualSymbol: symbol, + similiarSymbols + }) + ); + } + + // Still no luck - create database error + return this.addDiagnostic(node, msgInvalidDatabasePrefix({ symbol })); + } + + // Procedure / Query definition with undefined parameters + if ( + node.type === NodeType.Rule && + !symbol.resolvedDefinition && + !symbol.isSystem + ) { + return this.addDiagnostic( + node, + msgUnresolvedSignature({ symbol, rule: node }) + ); + } + + // Database warnings + if (symbol.type === SymbolType.Database) { + if (symbol.isDead || !symbol.dbWrites) { + return this.addDiagnostic(node, msgDatabaseNoWrite({ symbol })); + } else if (!symbol.dbReads && symbol.searchName !== "db_noop") { + return this.addDiagnostic(node, msgDatabaseNoRead({ symbol })); + } + } + } +} diff --git a/src/server/projects/story/analyzers/index.ts b/src/server/projects/story/analyzers/index.ts new file mode 100644 index 0000000..94442cc --- /dev/null +++ b/src/server/projects/story/analyzers/index.ts @@ -0,0 +1,67 @@ +import eachNodeRecursive from "../../../parsers/story/utils/eachNodeRecursive"; +import Resource from "../resources/Resource"; +import Story from ".."; +import SymbolLocationsAnalyzer from "./SymbolLocations"; +import SymbolTypesAnalyzer from "./SymbolTypes"; +import { AnyAnalyzer, AnalyzerContext, SyncAnalyzer } from "./Analyzer"; +import { Diagnostic } from "../../../parsers/story/models/diagnostics"; +import { StoryGoalNode } from "../../../parsers/story/models/nodes"; +import { + isInScope, + Scope, + tryStartScope, + updateScope +} from "../../../parsers/story/utils/eachRuleNode"; +import ParameterAnalyzer from "./Parameter"; + +export default class Analyzers { + readonly diagnostics: Array = []; + readonly story: Story; + readonly workers: Array; + + constructor(story: Story) { + this.story = story; + this.workers = [ + new SymbolTypesAnalyzer(this), + new SymbolLocationsAnalyzer(this), + new ParameterAnalyzer(this) + ]; + } + + async apply( + resource: Resource, + rootNode: StoryGoalNode + ): Promise> { + const { workers } = this; + let scope: Scope | null = null; + + const context: AnalyzerContext = { + node: rootNode, + resource, + rootNode, + scope: null, + stack: [] + }; + + this.diagnostics.length = 0; + + for (const { node, stack } of eachNodeRecursive(rootNode)) { + if (scope && !isInScope(scope, stack)) scope = null; + scope = scope ? updateScope(scope, node) : tryStartScope(node); + + context.node = node; + context.scope = scope; + context.stack = stack; + + for (const worker of workers) { + if (worker instanceof SyncAnalyzer) { + worker.analyze(context); + } else if (worker.canAnalyze(context)) { + await worker.analyze(context); + } + } + } + + return this.diagnostics.slice(); + } +} diff --git a/src/server/projects/story/index.ts b/src/server/projects/story/index.ts new file mode 100644 index 0000000..026f4f9 --- /dev/null +++ b/src/server/projects/story/index.ts @@ -0,0 +1,276 @@ +import * as Queue from "promise-queue"; +import { join, normalize } from "path"; + +import Analyzers from "./analyzers"; +import FileResource from "./resources/FileResource"; +import FileWatcher from "../FileWatcher"; +import Goal from "./Goal"; +import GoalResource from "./resources/GoalResource"; +import HeaderResource from "./resources/HeaderResource"; +import HeaderGoalResource from "./resources/HeaderGoalResource"; +import Project from "../Project"; +import Resource from "./resources/Resource"; +import sortGoals from "./utils/sortGoals"; +import Symbols from "./Symbols"; +import { existsSync } from "fs"; + +export default class Story { + isInitializing: boolean = true; + + readonly analyzers: Analyzers = new Analyzers(this); + readonly project: Project; + readonly symbols: Symbols = new Symbols(this); + readonly queue: Queue; + readonly types: Array = [ + "INTEGER", + "INTEGER64", + "REAL", + "STRING", + "GUIDSTRING" + ]; + + private headerResource: HeaderResource | null = null; + private goals: Array = []; + private ignoreHeaderChange: NodeJS.Timer | null = null; + private resources: Array = []; + private watchers: Array = []; + + constructor(project: Project) { + this.project = project; + this.queue = new Queue(1, Number.POSITIVE_INFINITY, { + onEmpty: this.handleQueueEmpty + }); + } + + addGoal(goal: Goal) { + this.removeGoalByName(goal.name); + this.goals.push(goal); + + this.updateTree(); + } + + addIgnoredGoal(name: string) { + if (this.headerResource) { + this.headerResource.addIgnoredGoal(name); + } + } + + addResource(resource: HeaderGoalResource) { + this.resources.push(resource); + } + + async analyzeGoals() { + for (const resource of this.resources) { + if (resource instanceof GoalResource && resource.getDocument()) { + await resource.analyze(); + } + } + } + + dispose() { + this.watchers.forEach(watcher => watcher.dispose()); + this.watchers.length = 0; + } + + findGoal(name: string): Goal | null { + return this.goals.find(goal => goal.name === name) || null; + } + + findOrCreateResource(path: string): FileResource | null { + let resource = this.findResource(path); + if (resource) return resource; + + const rawPath = this.getGoalsPath(); + if (existsSync(rawPath) && path.startsWith(rawPath)) { + resource = new GoalResource({ story: this, path }); + this.resources.push(resource); + return resource; + } + + return null; + } + + findResource(path: string): FileResource | null { + return ( + (this.resources.find( + file => file instanceof FileResource && file.path === path + ) as FileResource) || null + ); + } + + getHeaderGoalResources(): Array { + return this.resources.filter( + file => file instanceof HeaderGoalResource + ) as Array; + } + + getGoal(name: string): Goal | null { + return this.goals.find(goal => goal.name === name) || null; + } + + getGoals(): Array { + return this.goals.slice(); + } + + getGoalsByParent(parent: string): Array { + return this.goals.filter(goal => goal.parents.indexOf(parent) !== -1); + } + + getGoalsPath(): string { + return normalize(join(this.project.path, "Story", "RawFiles", "Goals")); + } + + getRootGoals(): Array { + return this.goals.filter(goal => goal.parents.length === 0); + } + + getSortedRootGoals(): Array { + return this.getRootGoals().sort(sortGoals); + } + + ignoreHeaderFileEvents() { + if (this.ignoreHeaderChange) { + clearTimeout(this.ignoreHeaderChange); + } + + this.ignoreHeaderChange = setTimeout(() => { + this.ignoreHeaderChange = null; + }, 500); + } + + initialize() { + this.watchers = this.createWatchers(); + } + + removeGoal(goal: Goal) { + this.goals = this.goals.filter(existingGoal => existingGoal !== goal); + this.symbols.removeByGoal(goal); + this.updateTree(); + } + + removeGoalByName(name: string) { + this.goals + .filter(goal => goal.name === name) + .forEach(goal => this.removeGoal(goal)); + } + + removeResource(resource: Resource) { + this.resources = this.resources.filter(existing => existing !== resource); + resource.dispose(); + } + + updateTree() { + if (this.isInitializing) { + return; + } + + const { project } = this; + Goal.updateWeights(this.getRootGoals()); + project.projects.emit("goalsChanged", project); + } + + async whenReady(): Promise { + if (this.isInitializing) { + return new Promise(resolve => { + const { projects } = this.project; + const onReady = (project: Project) => { + if (project !== this.project) return; + projects.removeListener("projectReady", onReady); + resolve(); + }; + projects.addListener("projectReady", onReady); + }); + } else { + return Promise.resolve(); + } + } + + private createHeaderWatcher(): FileWatcher { + const watcher = new FileWatcher({ + path: normalize(join(this.project.path, "Story")), + pattern: /story\.div$/ + }); + + let resource: HeaderResource | null = null; + watcher.on("update", path => { + if (!resource) { + resource = new HeaderResource({ story: this, path }); + this.headerResource = resource; + this.resources.push(resource); + } + + if (!this.ignoreHeaderChange) { + resource.loadSync(); + } + }); + + watcher.scanAndStartSync(); + return watcher; + } + + private createGoalWatcher(): FileWatcher { + const watcher = new FileWatcher({ + path: this.getGoalsPath(), + pattern: /\.txt$/ + }); + + watcher.on("update", path => { + let resource = this.findResource(path); + if (!resource) { + resource = new GoalResource({ story: this, path }); + this.resources.push(resource); + } + + this.queue.add(resource.load); + }); + + watcher.on("remove", path => { + const resource = this.findResource(path); + if (resource) { + this.removeResource(resource); + } + }); + + watcher.scanAndStartSync(); + return watcher; + } + + private createWatchers(): Array { + const watchers: Array = []; + + try { + watchers.push(this.createHeaderWatcher()); + } catch (error) { + this.project.projects.emit("showError", error.message); + } + + try { + watchers.push(this.createGoalWatcher()); + } catch (error) { + this.project.projects.emit("showError", error.message); + } + + if (this.queue.getPendingLength() === 0) { + this.handleQueueEmpty(); + } + + return watchers; + } + + private handleQueueEmpty = async () => { + if (this.isInitializing) { + const { project, symbols } = this; + + this.isInitializing = false; + this.updateTree(); + + await symbols.loadMetaData(); + symbols.update(); + + await this.analyzeGoals(); + + project.projects.emit("projectReady", this.project); + project.levels.initialize(); + } + }; +} diff --git a/src/server/projects/story/messages/msgCanOnlyDeleteFromDatabase.ts b/src/server/projects/story/messages/msgCanOnlyDeleteFromDatabase.ts new file mode 100644 index 0000000..6e1215c --- /dev/null +++ b/src/server/projects/story/messages/msgCanOnlyDeleteFromDatabase.ts @@ -0,0 +1,23 @@ +import Symbol from "../Symbol"; +import { + DiagnosticCode, + DiagnosticMessage, + DiagnosticSeverity +} from "../../../parsers/story/models/diagnostics"; +import printSymbolType from "../utils/printSymbolType"; + +export type Params = { + symbol: Symbol; +}; + +export default function msgCanOnlyDeleteFromDatabase({ + symbol +}: Params): DiagnosticMessage { + return { + code: DiagnosticCode.CanOnlyDeleteFromDatabase, + message: `NOT actions can only reference databases; "${ + symbol.name + }" is a ${printSymbolType(symbol)}`, + severity: DiagnosticSeverity.Error + }; +} diff --git a/src/server/projects/story/messages/msgComparisonTypeMismatch.ts b/src/server/projects/story/messages/msgComparisonTypeMismatch.ts new file mode 100644 index 0000000..5039a41 --- /dev/null +++ b/src/server/projects/story/messages/msgComparisonTypeMismatch.ts @@ -0,0 +1,27 @@ +import { ParameterType } from "../models/parameter"; +import { + DiagnosticCode, + DiagnosticMessage, + DiagnosticSeverity +} from "../../../parsers/story/models/diagnostics"; +import printParameterType from "../utils/printParameterType"; + +export type Params = { + leftType: ParameterType; + rightType: ParameterType; +}; + +export default function msgComparisonTypeMismatch({ + leftType, + rightType +}: Params): DiagnosticMessage { + return { + code: DiagnosticCode.LocalTypeMismatch, + message: `Type of left expression (${printParameterType( + leftType + )}) differs from type of right expression (${printParameterType( + rightType + )})`, + severity: DiagnosticSeverity.Error + }; +} diff --git a/src/server/projects/story/messages/msgDangerousCast.ts b/src/server/projects/story/messages/msgDangerousCast.ts new file mode 100644 index 0000000..839a3e6 --- /dev/null +++ b/src/server/projects/story/messages/msgDangerousCast.ts @@ -0,0 +1,25 @@ +import printParameterType from "../utils/printParameterType"; +import { ParameterType } from "../models/parameter"; +import { + DiagnosticCode, + DiagnosticMessage, + DiagnosticSeverity +} from "../../../parsers/story/models/diagnostics"; + +export type Params = { + sourceType: ParameterType; + targetType: ParameterType; +}; + +export default function msgDangerousCast({ + sourceType, + targetType +}: Params): DiagnosticMessage { + return { + code: DiagnosticCode.GuidAliasMismatch, + message: `GUID alias cast: Type ${printParameterType( + sourceType + )} converted to ${printParameterType(targetType)}`, + severity: DiagnosticSeverity.Warning + }; +} diff --git a/src/server/projects/story/messages/msgDatabaseNoRead.ts b/src/server/projects/story/messages/msgDatabaseNoRead.ts new file mode 100644 index 0000000..070437c --- /dev/null +++ b/src/server/projects/story/messages/msgDatabaseNoRead.ts @@ -0,0 +1,22 @@ +import Symbol from "../Symbol"; +import { + DiagnosticCode, + DiagnosticMessage, + DiagnosticSeverity +} from "../../../parsers/story/models/diagnostics"; + +export type Params = { + symbol: Symbol; +}; + +export default function msgDatabaseNoRead({ + symbol +}: Params): DiagnosticMessage { + return { + code: DiagnosticCode.UnusedDatabase, + message: `Database "${ + symbol.name + }" is written to, but is never used in a rule`, + severity: DiagnosticSeverity.Warning + }; +} diff --git a/src/server/projects/story/messages/msgDatabaseNoWrite.ts b/src/server/projects/story/messages/msgDatabaseNoWrite.ts new file mode 100644 index 0000000..c0c72a9 --- /dev/null +++ b/src/server/projects/story/messages/msgDatabaseNoWrite.ts @@ -0,0 +1,22 @@ +import Symbol from "../Symbol"; +import { + DiagnosticCode, + DiagnosticMessage, + DiagnosticSeverity +} from "../../../parsers/story/models/diagnostics"; + +export type Params = { + symbol: Symbol; +}; + +export default function msgDatabaseNoWrite({ + symbol +}: Params): DiagnosticMessage { + return { + code: DiagnosticCode.UnusedDatabase, + message: `Database "${ + symbol.name + }" is used in a rule, but is never written to`, + severity: DiagnosticSeverity.Warning + }; +} diff --git a/src/server/projects/story/messages/msgInvalidDatabasePrefix.ts b/src/server/projects/story/messages/msgInvalidDatabasePrefix.ts new file mode 100644 index 0000000..0b6dd3d --- /dev/null +++ b/src/server/projects/story/messages/msgInvalidDatabasePrefix.ts @@ -0,0 +1,22 @@ +import Symbol from "../Symbol"; +import { + DiagnosticCode, + DiagnosticMessage, + DiagnosticSeverity +} from "../../../parsers/story/models/diagnostics"; + +export type Params = { + symbol: Symbol; +}; + +export default function msgInvalidDatabasePrefix({ + symbol +}: Params): DiagnosticMessage { + return { + code: DiagnosticCode.DbNamingStyle, + message: `Name of database "${ + symbol.name + }" should start with the prefix "DB"`, + severity: DiagnosticSeverity.Error + }; +} diff --git a/src/server/projects/story/messages/msgInvalidPlaceholder.ts b/src/server/projects/story/messages/msgInvalidPlaceholder.ts new file mode 100644 index 0000000..97e0eac --- /dev/null +++ b/src/server/projects/story/messages/msgInvalidPlaceholder.ts @@ -0,0 +1,22 @@ +import { + DiagnosticCode, + DiagnosticMessage, + DiagnosticSeverity +} from "../../../parsers/story/models/diagnostics"; + +export type Params = { + requiredByIndex: number; + requiredByName: string; +}; + +export default function msgInvalidPlaceholder({ + requiredByIndex, + requiredByName +}: Params): DiagnosticMessage { + return { + code: DiagnosticCode.ParamNotBound, + message: `Placeholder used but parameter ${requiredByIndex + + 1} of ${requiredByName} is required`, + severity: DiagnosticSeverity.Error + }; +} diff --git a/src/server/projects/story/messages/msgInvalidSymbolInCondition.ts b/src/server/projects/story/messages/msgInvalidSymbolInCondition.ts new file mode 100644 index 0000000..e2a1d35 --- /dev/null +++ b/src/server/projects/story/messages/msgInvalidSymbolInCondition.ts @@ -0,0 +1,23 @@ +import printSymbolType from "../utils/printSymbolType"; +import Symbol from "../Symbol"; +import { + DiagnosticCode, + DiagnosticMessage, + DiagnosticSeverity +} from "../../../parsers/story/models/diagnostics"; + +export type Params = { + symbol: Symbol; +}; + +export default function msgInvalidSymbolInCondition({ + symbol +}: Params): DiagnosticMessage { + return { + code: DiagnosticCode.InvalidFunctionTypeInCondition, + message: `Subsequent rule conditions can only be queries or DBs; "${ + symbol.name + }" is a ${printSymbolType(symbol)}`, + severity: DiagnosticSeverity.Error + }; +} diff --git a/src/server/projects/story/messages/msgInvalidSymbolInFact.ts b/src/server/projects/story/messages/msgInvalidSymbolInFact.ts new file mode 100644 index 0000000..08f6d68 --- /dev/null +++ b/src/server/projects/story/messages/msgInvalidSymbolInFact.ts @@ -0,0 +1,23 @@ +import printSymbolType from "../utils/printSymbolType"; +import Symbol from "../Symbol"; +import { + DiagnosticCode, + DiagnosticMessage, + DiagnosticSeverity +} from "../../../parsers/story/models/diagnostics"; + +export type Params = { + symbol: Symbol; +}; + +export default function msgInvalidSymbolInFact({ + symbol +}: Params): DiagnosticMessage { + return { + code: DiagnosticCode.InvalidSymbolInFact, + message: `Init/Exit actions can only reference databases, calls and PROCs; "${ + symbol.name + }" is a ${printSymbolType(symbol)}`, + severity: DiagnosticSeverity.Error + }; +} diff --git a/src/server/projects/story/messages/msgInvalidSymbolInInitialCondition.ts b/src/server/projects/story/messages/msgInvalidSymbolInInitialCondition.ts new file mode 100644 index 0000000..81b362f --- /dev/null +++ b/src/server/projects/story/messages/msgInvalidSymbolInInitialCondition.ts @@ -0,0 +1,48 @@ +import printSymbolType from "../utils/printSymbolType"; +import Symbol from "../Symbol"; +import { + DiagnosticCode, + DiagnosticMessage, + DiagnosticSeverity +} from "../../../parsers/story/models/diagnostics"; + +export type Params = { + ruleType: "IF" | "PROC" | "QRY"; + symbol: Symbol; +}; + +export default function msgInvalidSymbolInInitialCondition({ + ruleType, + symbol +}: Params): DiagnosticMessage { + let message: string; + switch (ruleType) { + case "IF": + message = `Initial rule condition can only be an event or a DB; "${ + symbol.name + }" is a ${printSymbolType(symbol)}`; + break; + + case "PROC": + message = `Initial proc condition can only be a PROC name; "${ + symbol.name + }" is a ${printSymbolType(symbol)}`; + break; + + case "QRY": + message = `Initial query condition can only be a user-defined QRY name; "${ + symbol.name + }" is a ${printSymbolType(symbol)}`; + break; + + default: + message = `Invalid initial condition`; + break; + } + + return { + code: DiagnosticCode.InvalidSymbolInInitialCondition, + message, + severity: DiagnosticSeverity.Error + }; +} diff --git a/src/server/projects/story/messages/msgInvalidSymbolInStatement.ts b/src/server/projects/story/messages/msgInvalidSymbolInStatement.ts new file mode 100644 index 0000000..75ed357 --- /dev/null +++ b/src/server/projects/story/messages/msgInvalidSymbolInStatement.ts @@ -0,0 +1,23 @@ +import printSymbolType from "../utils/printSymbolType"; +import Symbol from "../Symbol"; +import { + DiagnosticCode, + DiagnosticMessage, + DiagnosticSeverity +} from "../../../parsers/story/models/diagnostics"; + +export type Params = { + symbol: Symbol; +}; + +export default function msgInvalidSymbolInStatement({ + symbol +}: Params): DiagnosticMessage { + return { + code: DiagnosticCode.InvalidSymbolInFact, + message: `KB rule actions can only reference databases, calls and PROCs; "${ + symbol.name + }" is a ${printSymbolType(symbol)}`, + severity: DiagnosticSeverity.Error + }; +} diff --git a/src/server/projects/story/messages/msgInvalidTypeCast.ts b/src/server/projects/story/messages/msgInvalidTypeCast.ts new file mode 100644 index 0000000..7c962aa --- /dev/null +++ b/src/server/projects/story/messages/msgInvalidTypeCast.ts @@ -0,0 +1,36 @@ +import { ParameterType } from "../models/parameter"; +import { + DiagnosticCode, + DiagnosticMessage, + DiagnosticSeverity +} from "../../../parsers/story/models/diagnostics"; +import printParameterType from "../utils/printParameterType"; + +export type Params = { + name?: string; + sourceType: ParameterType; + targetType: ParameterType; +}; + +export default function msgInvalidTypeCast({ + name, + sourceType, + targetType +}: Params): DiagnosticMessage { + let message: string; + if (name) { + message = `Rule variable "${name}" of type ${printParameterType( + sourceType + )} cannot be converted to ${printParameterType(targetType)}`; + } else { + message = `Type ${printParameterType( + sourceType + )} cannot be converted to ${printParameterType(targetType)}`; + } + + return { + code: DiagnosticCode.LocalTypeMismatch, + message, + severity: DiagnosticSeverity.Error + }; +} diff --git a/src/server/projects/story/messages/msgInvalidVariableName.ts b/src/server/projects/story/messages/msgInvalidVariableName.ts new file mode 100644 index 0000000..81a116d --- /dev/null +++ b/src/server/projects/story/messages/msgInvalidVariableName.ts @@ -0,0 +1,20 @@ +import Symbol from "../Symbol"; +import { + DiagnosticCode, + DiagnosticMessage, + DiagnosticSeverity +} from "../../../parsers/story/models/diagnostics"; + +export type Params = { + name: string; +}; + +export default function msgInvalidVariableName({ + name +}: Params): DiagnosticMessage { + return { + code: DiagnosticCode.InvalidVariableName, + message: `Variable name "${name}" must start with an underscore.`, + severity: DiagnosticSeverity.Error + }; +} diff --git a/src/server/projects/story/messages/msgParamaterCountMismatch.ts b/src/server/projects/story/messages/msgParamaterCountMismatch.ts new file mode 100644 index 0000000..0c9bd29 --- /dev/null +++ b/src/server/projects/story/messages/msgParamaterCountMismatch.ts @@ -0,0 +1,34 @@ +import Symbol from "../Symbol"; +import { + DiagnosticCode, + DiagnosticMessage, + DiagnosticSeverity +} from "../../../parsers/story/models/diagnostics"; + +function printNumParameters(symbols: Array) { + const nums = symbols.map(symbol => symbol.numParameters).sort(); + if (nums.length === 1 && nums[0] === 1) { + return `1 parameter`; + } + + const orNum = nums.length > 1 ? nums.pop() : undefined; + return `${nums.join(", ")}${orNum ? ` or ${orNum}` : ""} parameters`; +} + +export type Params = { + actualSymbol: Symbol; + existingSymbols: Array; +}; + +export default function msgParamaterCountMismatch({ + actualSymbol, + existingSymbols +}: Params): DiagnosticMessage { + return { + code: DiagnosticCode.UnresolvedSymbol, + message: `Symbol "${actualSymbol.name}" could not be resolved: "${ + actualSymbol.name + }" requires ${printNumParameters(existingSymbols)}.`, + severity: DiagnosticSeverity.Error + }; +} diff --git a/src/server/projects/story/messages/msgParameterTypeMismatch.ts b/src/server/projects/story/messages/msgParameterTypeMismatch.ts new file mode 100644 index 0000000..a6ba3c1 --- /dev/null +++ b/src/server/projects/story/messages/msgParameterTypeMismatch.ts @@ -0,0 +1,33 @@ +import printParameterType from "../utils/printParameterType"; +import printSymbolType from "../utils/printSymbolType"; +import Symbol from "../Symbol"; +import { ParameterType } from "../models/parameter"; +import { + DiagnosticCode, + DiagnosticMessage, + DiagnosticSeverity +} from "../../../parsers/story/models/diagnostics"; + +export type Params = { + sourceType: ParameterType; + symbol: Symbol; + targetIndex: number; + targetType: ParameterType; +}; + +export default function msgParameterTypeMismatch({ + sourceType, + symbol, + targetIndex, + targetType +}: Params): DiagnosticMessage { + return { + code: DiagnosticCode.LocalTypeMismatch, + message: `Parameter ${targetIndex + 1} of ${printSymbolType(symbol)} "${ + symbol.name + }" expects ${printParameterType(targetType)}; ${printParameterType( + sourceType + )} specified`, + severity: DiagnosticSeverity.Error + }; +} diff --git a/src/server/projects/story/messages/msgSigantureTypo.ts b/src/server/projects/story/messages/msgSigantureTypo.ts new file mode 100644 index 0000000..3087bc8 --- /dev/null +++ b/src/server/projects/story/messages/msgSigantureTypo.ts @@ -0,0 +1,38 @@ +import Symbol from "../Symbol"; +import { + DiagnosticCode, + DiagnosticMessage, + DiagnosticSeverity +} from "../../../parsers/story/models/diagnostics"; + +function printSimiliars(symbols: Array): string { + const names = symbols + .map(symbol => `"${symbol.name}"`) + .reduce( + (result, name) => + result.indexOf(name) === -1 ? [...result, name] : result, + [] as Array + ) + .sort(); + + const orName = names.length > 1 ? names.pop() : undefined; + return `${names.join(", ")}${orName ? ` or ${orName}` : ""}`; +} + +export type Params = { + actualSymbol: Symbol; + similiarSymbols: Array; +}; + +export default function msgSigantureTypo({ + actualSymbol, + similiarSymbols +}: Params): DiagnosticMessage { + return { + code: DiagnosticCode.UnresolvedSymbol, + message: `Symbol "${ + actualSymbol.name + }" could not be resolved: did you mean ${printSimiliars(similiarSymbols)}?`, + severity: DiagnosticSeverity.Error + }; +} diff --git a/src/server/projects/story/messages/msgStringLtGtComparison.ts b/src/server/projects/story/messages/msgStringLtGtComparison.ts new file mode 100644 index 0000000..a7fa05c --- /dev/null +++ b/src/server/projects/story/messages/msgStringLtGtComparison.ts @@ -0,0 +1,20 @@ +import { CallerNode } from "../../../parsers/story/utils/isCallerNode"; +import { + DiagnosticCode, + DiagnosticMessage, + DiagnosticSeverity +} from "../../../parsers/story/models/diagnostics"; + +export type Params = { + operator: string; +}; + +export default function msgStringLtGtComparison({ + operator +}: Params): DiagnosticMessage { + return { + code: DiagnosticCode.StringLtGtComparison, + message: `String comparison using operator ${operator} - probably a mistake?`, + severity: DiagnosticSeverity.Error + }; +} diff --git a/src/server/projects/story/messages/msgUnresolvedSignature.ts b/src/server/projects/story/messages/msgUnresolvedSignature.ts new file mode 100644 index 0000000..04a67cf --- /dev/null +++ b/src/server/projects/story/messages/msgUnresolvedSignature.ts @@ -0,0 +1,59 @@ +import Symbol from "../Symbol"; +import toParameters from "../utils/toParameters"; +import { ParameterType } from "../models/parameter"; +import { RuleNode } from "../../../parsers/story/models/nodes"; +import { + DiagnosticCode, + DiagnosticMessage, + DiagnosticSeverity +} from "../../../parsers/story/models/diagnostics"; + +function printDefinitionError(rule: RuleNode, symbol: Symbol): string { + const { parameters } = toParameters(symbol, rule.signature); + + const unknowns: Array = []; + for (let index = 0; index < parameters.length; index++) { + const parameter = parameters[index]; + if ( + parameter.type === ParameterType.Unknown || + parameter.type === ParameterType.Invalid + ) { + unknowns.push(`#${index + 1} "${parameter.name}"`); + } + } + + if (unknowns.length) { + let parameters: string; + + if (unknowns.length === 1) { + parameters = `parameter ${unknowns[0]}`; + } else { + const andUnknown = unknowns.length > 1 ? unknowns.pop() : undefined; + parameters = `parameters ${unknowns.join(", ")}${ + andUnknown ? ` and ${andUnknown}` : "" + }`; + } + + return `: the type of ${parameters} could not be resolved`; + } + + return `.`; +} + +export type Params = { + rule: RuleNode; + symbol: Symbol; +}; + +export default function msgUnresolvedSignature({ + rule, + symbol +}: Params): DiagnosticMessage { + return { + code: DiagnosticCode.UnresolvedSignature, + message: `Signature of "${ + symbol.name + }" could not be determined${printDefinitionError(rule, symbol)}`, + severity: DiagnosticSeverity.Error + }; +} diff --git a/src/server/projects/story/messages/msgUnresolvedSymbol.ts b/src/server/projects/story/messages/msgUnresolvedSymbol.ts new file mode 100644 index 0000000..77d793b --- /dev/null +++ b/src/server/projects/story/messages/msgUnresolvedSymbol.ts @@ -0,0 +1,20 @@ +import { CallerNode } from "../../../parsers/story/utils/isCallerNode"; +import { + DiagnosticCode, + DiagnosticMessage, + DiagnosticSeverity +} from "../../../parsers/story/models/diagnostics"; + +export type Params = { + node: CallerNode; +}; + +export default function msgUnresolvedSymbol({ + node +}: Params): DiagnosticMessage { + return { + code: DiagnosticCode.UnresolvedSymbol, + message: `Symbol "${node.signature.identifier.name}" could not be resolved`, + severity: DiagnosticSeverity.Error + }; +} diff --git a/src/server/projects/story/messages/msgVariableNotAllowed.ts b/src/server/projects/story/messages/msgVariableNotAllowed.ts new file mode 100644 index 0000000..8f24e2a --- /dev/null +++ b/src/server/projects/story/messages/msgVariableNotAllowed.ts @@ -0,0 +1,19 @@ +import { + DiagnosticCode, + DiagnosticMessage, + DiagnosticSeverity +} from "../../../parsers/story/models/diagnostics"; + +export type Params = { + name: string; +}; + +export default function msgVariableNotAllowed({ + name +}: Params): DiagnosticMessage { + return { + code: DiagnosticCode.VariableNotAllowed, + message: `Variable "${name}" is not allowed in this context.`, + severity: DiagnosticSeverity.Error + }; +} diff --git a/src/server/projects/story/messages/msgVariableNotBound.ts b/src/server/projects/story/messages/msgVariableNotBound.ts new file mode 100644 index 0000000..b4bdd03 --- /dev/null +++ b/src/server/projects/story/messages/msgVariableNotBound.ts @@ -0,0 +1,29 @@ +import { + DiagnosticCode, + DiagnosticMessage, + DiagnosticSeverity +} from "../../../parsers/story/models/diagnostics"; + +export type Params = { + name: string; + requiredByIndex?: number; + requiredByName?: string; +}; + +export default function msgVariableNotBound({ + name, + requiredByIndex, + requiredByName +}: Params): DiagnosticMessage { + let message = `Variable ${name} is not bound`; + if (requiredByIndex && requiredByName) { + message += ` but parameter ${requiredByIndex + + 1} of ${requiredByName} is required`; + } + + return { + code: DiagnosticCode.ParamNotBound, + message, + severity: DiagnosticSeverity.Error + }; +} diff --git a/src/server/projects/story/models/parameter.ts b/src/server/projects/story/models/parameter.ts new file mode 100644 index 0000000..6674161 --- /dev/null +++ b/src/server/projects/story/models/parameter.ts @@ -0,0 +1,30 @@ +import Symbol from "../Symbol"; +import { ParameterFlow } from "../../../parsers/story/models/nodes"; + +export enum ParameterType { + Unknown, + Invalid, + Integer, + Integer64, + Real, + String, + Guid, + CharacterGuid, + ItemGuid, + TriggerGuid, + SplineGuid, + LevelTemplateGuid +} + +export interface Parameter { + flow?: ParameterFlow; + fromIndex?: number; + fromSymbol?: Symbol; + name: string; + type: ParameterType; +} + +export interface ScoredParameterName { + name: string; + score: number; +} diff --git a/src/server/projects/story/models/symbol.ts b/src/server/projects/story/models/symbol.ts new file mode 100644 index 0000000..902e251 --- /dev/null +++ b/src/server/projects/story/models/symbol.ts @@ -0,0 +1,35 @@ +import Goal from "../Goal"; +import Symbol from "../Symbol"; +import { AnyNode } from "../../../parsers/story/models/nodes"; +import { ParameterType } from "./parameter"; + +export interface SymbolParameterDoc { + description?: string; + name?: string; +} + +export interface SymbolDoc { + description?: string; + parameters?: Array; +} + +export const enum SymbolType { + Unknown, + Call, + Database, + Event, + Query +} + +export interface SymbolLocation { + goal: Goal; + node: AnyNode; +} + +export interface Variable { + displayName: string; + fromIndex: number; + fromSymbol: Symbol; + name: string; + type?: ParameterType; +} diff --git a/src/server/projects/story/resources/FileResource.ts b/src/server/projects/story/resources/FileResource.ts new file mode 100644 index 0000000..83133ce --- /dev/null +++ b/src/server/projects/story/resources/FileResource.ts @@ -0,0 +1,32 @@ +import { normalize } from "path"; +import { readFileSync } from "fs"; + +import Resource, { ResourceOptions } from "./Resource"; +import { AnyNode } from "../../../parsers/story/models/nodes"; +import { readFile } from "../../../../shared/fs"; + +export interface FileResourceOptions extends ResourceOptions { + path: string; +} + +export default abstract class FileResource< + T extends AnyNode = AnyNode +> extends Resource { + readonly path: string; + constructor(options: FileResourceOptions) { + super(options); + this.path = normalize(options.path); + } + + async getSource(): Promise { + return readFile(this.path, { encoding: "utf-8" }); + } + + getSourceSync(): string { + return readFileSync(this.path, { encoding: "utf-8" }); + } + + getUri(): string { + return `file:///${encodeURIComponent(this.path.replace(/\\/g, "/"))}`; + } +} diff --git a/src/server/projects/story/resources/GoalResource.ts b/src/server/projects/story/resources/GoalResource.ts new file mode 100644 index 0000000..b16d870 --- /dev/null +++ b/src/server/projects/story/resources/GoalResource.ts @@ -0,0 +1,79 @@ +import { basename } from "path"; + +import GoalParser from "../../../parsers/story/GoalParser"; +import { DiagnosticType } from "../../../parsers/story/models/diagnostics"; +import { StoryGoalNode } from "../../../parsers/story/models/nodes"; + +import FileResource, { FileResourceOptions } from "./FileResource"; +import Goal from "../Goal"; +import Story from ".."; + +export default class GoalResource extends FileResource { + readonly goal: Goal; + readonly story: Story; + + constructor(options: FileResourceOptions) { + super(options); + + const { path, story } = options; + const goal = new Goal(story, basename(path, ".txt"), this); + story.addIgnoredGoal(goal.name); + story.addGoal(goal); + + this.goal = goal; + this.story = story; + } + + async analyze() { + const node = await this.getRootNode(true); + + this.setDiagnostics( + DiagnosticType.Analyzer, + await this.story.analyzers.apply(this, node) + ); + } + + dispose() { + super.dispose(); + this.story.removeGoal(this.goal); + } + + async getSource(): Promise { + const { document } = this; + if (document) { + return Promise.resolve(document.getText()); + } + + return super.getSource(); + } + + protected async parse( + source: string, + noAnalysis?: boolean + ): Promise { + const { goal, story } = this; + const parser = new GoalParser(source); + const { diagnostics, goal: node } = parser.parse(); + + if (noAnalysis) { + story.symbols.assignSymbols(node); + return node; + } + + this.setDiagnostics(DiagnosticType.Syntax, diagnostics); + story.symbols.updateGoal(goal, node); + + const parents = node.parentTargetEdges; + goal.setParents(parents ? parents.map(parentEdge => parentEdge.value) : []); + story.symbols.update(); + + if (!story.isInitializing) { + this.setDiagnostics( + DiagnosticType.Analyzer, + await story.analyzers.apply(this, node) + ); + } + + return node; + } +} diff --git a/src/server/projects/story/resources/HeaderGoalResource.ts b/src/server/projects/story/resources/HeaderGoalResource.ts new file mode 100644 index 0000000..1625c64 --- /dev/null +++ b/src/server/projects/story/resources/HeaderGoalResource.ts @@ -0,0 +1,80 @@ +import Goal from "../Goal"; +import goalTemplate from "../../../../shared/goalTemplate"; +import GoalParser from "../../../parsers/story/GoalParser"; +import Resource, { ResourceOptions } from "./Resource"; +import { + HeaderGoalNode, + StoryGoalNode +} from "../../../parsers/story/models/nodes"; + +export interface HeaderGoalResourceOptions extends ResourceOptions { + name: string; +} + +export default class HeaderGoalResource extends Resource { + readonly goal: Goal; + private source: string = ""; + + constructor(options: HeaderGoalResourceOptions) { + super(options); + + const { name, story } = options; + const goal = new Goal(story, name, this); + story.addGoal(goal); + + this.goal = goal; + } + + dispose() { + super.dispose(); + this.source = ""; + this.story.removeGoal(this.goal); + } + + async getSource(): Promise { + return Promise.resolve(this.source); + } + + getUri(): string { + const { goal, story } = this; + const { UUID } = story.project.meta; + return `divinity:///${UUID}/${goal.name}.divGoal`; + } + + update(allNodes: Array, rootNode: HeaderGoalNode) { + const id = rootNode.id; + const parents: Array = []; + + for (const { title, subGoal } of allNodes) { + if (title && subGoal.some(subGoal => subGoal === id)) { + parents.push(title); + } + } + + this.source = goalTemplate({ + exit: rootNode.exit, + init: rootNode.init, + kb: rootNode.kb, + parents + }); + + const parser = new GoalParser(this.source); + const { goal: node } = parser.parse(); + const { goal, story } = this; + + story.symbols.updateGoal(goal, node); + goal.setParents(parents); + } + + protected async parse( + source: string, + noAnalysis?: boolean + ): Promise { + const parser = new GoalParser(source); + const { goal: node } = parser.parse(); + + this.story.symbols.assignSymbols(node); + + return node; + } +} diff --git a/src/server/projects/story/resources/HeaderResource.ts b/src/server/projects/story/resources/HeaderResource.ts new file mode 100644 index 0000000..ad12ce0 --- /dev/null +++ b/src/server/projects/story/resources/HeaderResource.ts @@ -0,0 +1,117 @@ +import { join } from "path"; +import { readFileSync, writeFileSync } from "fs"; + +import FileResource, { FileResourceOptions } from "./FileResource"; +import HeaderGoalResource from "./HeaderGoalResource"; +import HeaderParser from "../../../parsers/story/HeaderParser"; + +import { + HeaderNode, + HeaderGoalNode, + DefinitionNode +} from "../../../parsers/story/models/nodes"; + +export default class HeaderResource extends FileResource { + ignoredGoals: Array; + ignoredPath: string; + + constructor(options: FileResourceOptions) { + super(options); + + const ignoredPath = join( + options.story.project.path, + "Story", + "story_custom_goals.txt" + ); + + let ignoredGoals: Array; + try { + const data = readFileSync(ignoredPath, "utf-8"); + ignoredGoals = JSON.parse(data); + } catch (error) { + ignoredGoals = []; + } + + this.ignoredGoals = ignoredGoals; + this.ignoredPath = ignoredPath; + } + + addIgnoredGoal(name: string) { + const { ignoredGoals, ignoredPath } = this; + if (ignoredGoals.indexOf(name) !== -1) return; + ignoredGoals.push(name); + + try { + writeFileSync(ignoredPath, JSON.stringify(ignoredGoals)); + } catch (error) {} + } + + loadSync(noAnalysis?: boolean) { + const source = this.getSourceSync(); + this.parse(source, noAnalysis); + } + + protected async parse( + source: string, + noAnalysis?: boolean + ): Promise { + const parser: HeaderParser = new HeaderParser(source); + const { header } = parser.parse(); + + this.syncDefinitions(header.definitions); + this.syncGoals(header.goals); + this.syncTypeAliases(header.typeAliases); + + this.story.symbols.update(); + return header; + } + + private syncDefinitions(definitions: Array) { + const { symbols } = this.story; + + for (const definition of definitions) { + symbols.addSystemSymbol(definition); + } + } + + private syncGoals(nodes: Array) { + const { story } = this; + const existingResources = story.getHeaderGoalResources(); + const resources: Array = []; + + for (const node of nodes) { + if (!node.title || this.ignoredGoals.indexOf(node.title) !== -1) continue; + const name = node.title; + let resource = existingResources.find( + resource => resource.goal.name === name + ); + + if (!resource) { + if (story.getGoal(name) !== null) { + continue; + } + + resource = new HeaderGoalResource({ name, story }); + story.addResource(resource); + } + + if (resource) { + resource.update(nodes, node); + resources.push(resource); + } + } + + existingResources + .filter(resource => resources.indexOf(resource) === -1) + .forEach(resource => story.removeResource(resource)); + } + + private syncTypeAliases(aliases: Array) { + const { types } = this.story; + for (const alias of aliases) { + if (types.indexOf(alias) === -1) { + types.push(alias); + } + } + } +} diff --git a/src/server/projects/story/resources/Resource.ts b/src/server/projects/story/resources/Resource.ts new file mode 100644 index 0000000..9e87075 --- /dev/null +++ b/src/server/projects/story/resources/Resource.ts @@ -0,0 +1,137 @@ +import { + Position, + TextDocument, + TextDocumentEdit +} from "vscode-languageserver"; + +import eachNode from "../../../parsers/story/utils/eachNode"; +import Projects from "../.."; +import Story from ".."; +import { AnyNode } from "../../../parsers/story/models/nodes"; + +import { + Diagnostic, + DiagnosticType +} from "../../../parsers/story/models/diagnostics"; + +export interface ResourceOptions { + story: Story; +} + +export default abstract class Resource { + readonly story: Story; + + protected diagnostics: Array = []; + protected document: TextDocument | null = null; + protected rootNode: T | null = null; + + constructor(options: ResourceOptions) { + this.load = this.load.bind(this); + this.story = options.story; + } + + abstract async getSource(): Promise; + + abstract getUri(): string; + + protected abstract async parse( + source: string, + noAnalysis?: boolean + ): Promise; + + dispose() { + if (this.document) this.document = null; + if (this.rootNode) this.rootNode = null; + this.diagnostics.length = 0; + } + + async load(noAnalysis?: boolean): Promise { + const buffer = await this.getSource(); + + const rootNode = await this.parse(buffer, noAnalysis); + if (this.document) { + this.rootNode = rootNode; + } + + return rootNode; + } + + getDocument(): TextDocument | null { + return this.document; + } + + getDiagnostics(): Array { + return this.diagnostics; + } + + async getNodesAt(position: Position) { + const { document } = this; + const rootNode = await this.getRootNode(); + + if (!rootNode || !document) { + return null; + } + + function findIn(parent: AnyNode) { + for (const node of eachNode(parent)) { + if (node.endOffset <= offset) continue; + if (node.startOffset > offset) break; + nodes.push(node); + findIn(node); + break; + } + } + + const offset = document.offsetAt(position); + const nodes: Array = []; + findIn(rootNode); + + return nodes; + } + + getProjects(): Projects { + return this.story.project.projects; + } + + async getRootNode(noAnalysis?: boolean): Promise { + if (this.rootNode) { + return Promise.resolve(this.rootNode); + } + + return this.load(noAnalysis); + } + + getTextEdit(): TextDocumentEdit { + return { + edits: [], + textDocument: { + uri: this.getUri(), + version: this.document ? this.document.version : null + } + }; + } + + invalidate() { + this.story.queue.add(this.load); + } + + setDocument(document: TextDocument | null) { + this.document = document; + + if (document == null) { + this.rootNode = null; + } + } + + protected setDiagnostics( + type: DiagnosticType, + diagnostics: Array + ) { + this.diagnostics = [ + ...this.diagnostics.filter(diagnostics => diagnostics.type !== type), + ...diagnostics + ]; + + this.getProjects().emit("diagnostics", this); + } +} diff --git a/src/server/projects/story/utils/getAnnotatedType.ts b/src/server/projects/story/utils/getAnnotatedType.ts new file mode 100644 index 0000000..8baaf3b --- /dev/null +++ b/src/server/projects/story/utils/getAnnotatedType.ts @@ -0,0 +1,14 @@ +import { TypeAnnotationNode } from "../../../parsers/story/models/nodes"; +import { ParameterType } from "../models/parameter"; +import toParameterType from "./toParameterType"; + +export default function getAnnotatedType( + node?: TypeAnnotationNode +): ParameterType | null { + if (!node || !node.annotatedType) { + return null; + } + + const result = toParameterType(node.annotatedType); + return result === ParameterType.Unknown ? null : result; +} diff --git a/src/server/projects/story/utils/getCallerSymbolType.ts b/src/server/projects/story/utils/getCallerSymbolType.ts new file mode 100644 index 0000000..eef6acf --- /dev/null +++ b/src/server/projects/story/utils/getCallerSymbolType.ts @@ -0,0 +1,21 @@ +import { CallerNode } from "../../../parsers/story/utils/isCallerNode"; +import { NodeType, IdentifierType } from "../../../parsers/story/models/nodes"; +import { SymbolType } from "../models/symbol"; + +export default function getCallerSymbolType(caller: CallerNode): SymbolType { + if (caller.type === NodeType.Rule) { + const { ruleType } = caller; + if (ruleType === "PROC") { + return SymbolType.Call; + } else if (ruleType === "QRY") { + return SymbolType.Query; + } + } + + const { identifierType } = caller.signature.identifier; + if (identifierType === IdentifierType.Database) { + return SymbolType.Database; + } + + return SymbolType.Unknown; +} diff --git a/src/server/projects/story/utils/getConstantParameterType.ts b/src/server/projects/story/utils/getConstantParameterType.ts new file mode 100644 index 0000000..402d972 --- /dev/null +++ b/src/server/projects/story/utils/getConstantParameterType.ts @@ -0,0 +1,22 @@ +import { ParameterNode, NodeType } from "../../../parsers/story/models/nodes"; +import { ParameterType } from "../models/parameter"; + +export default function getConstantParameterType( + node: ParameterNode +): ParameterType | null { + switch (node.argument.type) { + case NodeType.RealLiteral: + return ParameterType.Real; + + case NodeType.GuidLiteral: + return ParameterType.Guid; + + case NodeType.IntegerLiteral: + return ParameterType.Integer; + + case NodeType.StringLiteral: + return ParameterType.String; + } + + return null; +} diff --git a/src/server/projects/story/utils/getDefinitionSymbolType.ts b/src/server/projects/story/utils/getDefinitionSymbolType.ts new file mode 100644 index 0000000..d9aead7 --- /dev/null +++ b/src/server/projects/story/utils/getDefinitionSymbolType.ts @@ -0,0 +1,17 @@ +import { DefinitionNode } from "../../../parsers/story/models/nodes"; +import { SymbolType } from "../models/symbol"; + +export default function getDefinitionSymbolType( + definition: DefinitionNode +): SymbolType { + switch (definition.definitionType.toLowerCase()) { + case "event": + return SymbolType.Event; + case "sysquery": + case "query": + case "qry": + return SymbolType.Query; + } + + return SymbolType.Call; +} diff --git a/src/server/projects/story/utils/getExplicitParameterType.ts b/src/server/projects/story/utils/getExplicitParameterType.ts new file mode 100644 index 0000000..deefda2 --- /dev/null +++ b/src/server/projects/story/utils/getExplicitParameterType.ts @@ -0,0 +1,14 @@ +import toParameterType from "./toParameterType"; +import { ParameterNode } from "../../../parsers/story/models/nodes"; +import { ParameterType } from "../models/parameter"; +import getConstantParameterType from "./getConstantParameterType"; + +export default function getExplicitParameterType( + node: ParameterNode +): ParameterType | null { + if (node.valueType) { + return toParameterType(node.valueType.annotatedType); + } + + return getConstantParameterType(node); +} diff --git a/src/server/projects/story/utils/getParameterNameScore.ts b/src/server/projects/story/utils/getParameterNameScore.ts new file mode 100644 index 0000000..6a836a6 --- /dev/null +++ b/src/server/projects/story/utils/getParameterNameScore.ts @@ -0,0 +1,7 @@ +export default function getParameterNameScoe(value: string): number { + if (value.startsWith("_Param")) { + return 0; + } + + return Math.abs(value.length); +} diff --git a/src/server/projects/story/utils/parseDocComment.ts b/src/server/projects/story/utils/parseDocComment.ts new file mode 100644 index 0000000..5ee167f --- /dev/null +++ b/src/server/projects/story/utils/parseDocComment.ts @@ -0,0 +1,226 @@ +import { SymbolDoc, SymbolParameterDoc } from "../models/symbol"; + +/** + * The doc parser is an adaption of the module djsdoc + * https://github.com/EYHN/djsdoc + * + * + * The MIT License (MIT) + * + * Copyright (c) 2016-present Zeit, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +const NON_ASCII_WHITESPACES = [ + 0x1680, + 0x2000, + 0x2001, + 0x2002, + 0x2003, + 0x2004, + 0x2005, + 0x2006, + 0x2007, + 0x2008, + 0x2009, + 0x200a, + 0x202f, + 0x205f, + 0x3000, + 0xfeff +]; + +/** + * Return true if provided code is line terminator. Line terminator characters are formally defined in ECMA262. + * @param {number} ch + */ +function isLineTerminator(ch: number): boolean { + return ch === 0x0a || ch === 0x0d || ch === 0x2028 || ch === 0x2029; +} + +/** + * Return true if provided code is white space. White space characters are formally defined in ECMA262. + * @param {number} ch + */ +function isWhiteSpace(ch: number): boolean { + return ( + ch === 0x20 || + ch === 0x09 || + ch === 0x0b || + ch === 0x0c || + ch === 0xa0 || + (ch >= 0x1680 && NON_ASCII_WHITESPACES.indexOf(ch) >= 0) + ); +} + +export interface DocCommentTag { + title: string; + content: string; +} + +export class DocComment { + readonly description: string; + readonly source: string; + readonly tags: Array; + private index: number; + + constructor(source: string) { + this.source = source; + this.index = 0; + + const description = this.readDescription(); + const tags = []; + + while (true) { + const tag = this.readTag(); + if (!tag) break; + tags.push(tag); + } + + this.description = description; + this.tags = tags; + } + + private advance(): string { + return String.fromCharCode(this.source.charCodeAt(this.index++)); + } + + private readContent() { + const { source } = this; + let content = ""; + let waiting = false; + + while (this.index < source.length) { + const ch = source.charCodeAt(this.index); + if ( + isLineTerminator(ch) && + !( + ch === 0x0d /* '\r' */ && source.charCodeAt(this.index + 1) === 0x0a + ) /* '\n' */ + ) { + waiting = true; + } else if (waiting) { + if (ch === 0x40 /* '@' */) { + break; + } + + if (!isWhiteSpace(ch)) { + waiting = false; + } + } + + content += this.advance(); + } + + return content.trim(); + } + + private readDescription(): string { + const { source } = this; + let description = ""; + let atAllowed = true; + + while (this.index < source.length) { + let ch = source.charCodeAt(this.index); + if (atAllowed && ch === 0x40 /* '@' */) { + break; + } + + if (isLineTerminator(ch)) { + atAllowed = true; + } else if (atAllowed && !isWhiteSpace(ch)) { + atAllowed = false; + } + + description += this.advance(); + } + + return description.trim(); + } + + private readTag(): DocCommentTag | null { + if (!this.skipToTag()) { + return null; + } + + const title = this.readTitle(); + const content = this.readContent(); + return { content, title }; + } + + private readTitle(): string { + const { source } = this; + let title = ""; + + // waste '@' + this.advance(); + + while ( + this.index < source.length && + !isWhiteSpace(source.charCodeAt(this.index)) && + !isLineTerminator(source.charCodeAt(this.index)) + ) { + title += this.advance(); + } + + return title; + } + + private skipToTag(): boolean { + const { source } = this; + + while ( + this.index < source.length && + source.charCodeAt(this.index) !== 0x40 /* '@' */ + ) { + this.advance(); + } + + if (this.index >= source.length) { + return false; + } + + return true; + } +} + +export default function parseDocComments(source: string): SymbolDoc { + const comment = new DocComment(source); + + let description: string | undefined = comment.description.trim(); + if (description === "") description = undefined; + + const parameters: Array = []; + for (const tag of comment.tags) { + if (tag.title.toLowerCase() !== "param") continue; + + const firstSpace = tag.content.indexOf(" "); + if (firstSpace === -1) continue; + + const name = tag.content.substr(0, firstSpace).trim(); + const description = tag.content.substr(firstSpace).trim(); + parameters.push({ description, name }); + } + + return { + description, + parameters + }; +} diff --git a/src/server/projects/story/utils/printParameterFlow.ts b/src/server/projects/story/utils/printParameterFlow.ts new file mode 100644 index 0000000..23625dd --- /dev/null +++ b/src/server/projects/story/utils/printParameterFlow.ts @@ -0,0 +1,12 @@ +import { ParameterFlow } from "../../../parsers/story/models/nodes"; + +export default function printParameterFlow(value?: ParameterFlow): string { + switch (value) { + case ParameterFlow.In: + return "IN"; + case ParameterFlow.Out: + return "OUT"; + default: + return ""; + } +} diff --git a/src/server/projects/story/utils/printParameterType.ts b/src/server/projects/story/utils/printParameterType.ts new file mode 100644 index 0000000..11e3e60 --- /dev/null +++ b/src/server/projects/story/utils/printParameterType.ts @@ -0,0 +1,28 @@ +import { ParameterType } from "../models/parameter"; + +export default function printParameterType(value?: ParameterType): string { + switch (value) { + case ParameterType.Integer: + return "INTEGER"; + case ParameterType.Integer64: + return "INTEGER64"; + case ParameterType.Real: + return "REAL"; + case ParameterType.String: + return "STRING"; + case ParameterType.Guid: + return "GUIDSTRING"; + case ParameterType.CharacterGuid: + return "CHARACTERGUID"; + case ParameterType.ItemGuid: + return "ITEMGUID"; + case ParameterType.TriggerGuid: + return "TRIGGERGUID"; + case ParameterType.SplineGuid: + return "SPLINEGUID"; + case ParameterType.LevelTemplateGuid: + return "LEVELTEMPLATEGUID"; + default: + return "Unknown"; + } +} diff --git a/src/server/projects/story/utils/printSymbol.ts b/src/server/projects/story/utils/printSymbol.ts new file mode 100644 index 0000000..07d6bb9 --- /dev/null +++ b/src/server/projects/story/utils/printSymbol.ts @@ -0,0 +1,26 @@ +import printParameterType from "./printParameterType"; +import Symbol from "../Symbol"; +import { ParameterFlow } from "../../../parsers/story/models/nodes"; + +export default function printSymbol( + symbol: Symbol, + useLineBreaks?: boolean +): string { + let parameters = symbol.parameters + .map(({ flow, name, type }) => { + const parts: Array = []; + if (useLineBreaks) parts.push(" "); + if (flow) parts.push(`[${flow === ParameterFlow.In ? "in" : "out"}]`); + if (type) parts.push(`(${printParameterType(type)})`); + parts.push(name); + + return parts.join(""); + }) + .join(useLineBreaks ? ",\n" : ", "); + + if (useLineBreaks && parameters.length) { + parameters = `\n${parameters}\n`; + } + + return `${symbol.name}(${parameters})`; +} diff --git a/src/server/projects/story/utils/printSymbolType.ts b/src/server/projects/story/utils/printSymbolType.ts new file mode 100644 index 0000000..da88e04 --- /dev/null +++ b/src/server/projects/story/utils/printSymbolType.ts @@ -0,0 +1,23 @@ +import Symbol from "../Symbol"; +import { SymbolType } from "../models/symbol"; + +export default function printSymbolType(type: Symbol | SymbolType): string { + let prefix = ""; + if (type instanceof Symbol) { + if (type.isSystem) prefix = "system "; + type = type.type; + } + + switch (type) { + case SymbolType.Call: + return prefix + "call"; + case SymbolType.Database: + return "database"; + case SymbolType.Event: + return "event"; + case SymbolType.Query: + return prefix + "query"; + default: + return ""; + } +} diff --git a/src/server/projects/story/utils/resolveParameters.ts b/src/server/projects/story/utils/resolveParameters.ts new file mode 100644 index 0000000..3ec85ec --- /dev/null +++ b/src/server/projects/story/utils/resolveParameters.ts @@ -0,0 +1,70 @@ +import Symbol, { SymbolDefinition } from "../Symbol"; +import { Parameter, ParameterType } from "../models/parameter"; + +export const enum ResolvePrameterResult { + Dead, + Partial, + Unresolved +} + +export default function resolveParameters( + symbol: Symbol, + definition: SymbolDefinition, + unresolved?: Array +): Array | ResolvePrameterResult { + const { isPartial, parameters } = definition; + const { parameterNames: names } = symbol; + const resolved: Array = []; + + if (isPartial) { + return ResolvePrameterResult.Partial; + } + + for (let index = 0; index < parameters.length; index++) { + const { flow, fromIndex, fromSymbol, type } = parameters[index]; + const name = names[index].name; + + if (type !== ParameterType.Unknown) { + resolved.push({ flow, name, type }); + continue; + } + + if (fromSymbol && fromIndex !== undefined) { + if (fromSymbol.isDead) { + return ResolvePrameterResult.Dead; + } else if (fromSymbol.needsUpdate) { + // Check for loops - If the other symbol is only waiting for + // definitions from us, it's a deadlock + if ( + fromSymbol.definitions.every(otherDefinition => + otherDefinition.parameters.some( + otherParameter => otherParameter.fromSymbol === symbol + ) + ) + ) { + return ResolvePrameterResult.Dead; + } + } + + const type = fromSymbol.parameters[fromIndex].type; + if (type === ParameterType.Unknown) { + if (unresolved) { + unresolved.push(index); + } else { + return ResolvePrameterResult.Unresolved; + } + } + + resolved.push({ flow, name, type }); + continue; + } + + if (unresolved) { + unresolved.push(index); + } else { + return ResolvePrameterResult.Unresolved; + } + } + + return resolved; +} diff --git a/src/server/projects/story/utils/sortGoals.ts b/src/server/projects/story/utils/sortGoals.ts new file mode 100644 index 0000000..2023ca1 --- /dev/null +++ b/src/server/projects/story/utils/sortGoals.ts @@ -0,0 +1,5 @@ +import Goal from "../Goal"; + +export default function sortGoals(left: Goal, right: Goal): number { + return left.name.localeCompare(right.name); +} diff --git a/src/server/projects/story/utils/toParameterType.ts b/src/server/projects/story/utils/toParameterType.ts new file mode 100644 index 0000000..8a8238d --- /dev/null +++ b/src/server/projects/story/utils/toParameterType.ts @@ -0,0 +1,28 @@ +import { ParameterType } from "../models/parameter"; + +export default function toParameterType(value?: string): ParameterType { + switch (value ? value.toUpperCase() : undefined) { + case "INTEGER": + return ParameterType.Integer; + case "INTEGER64": + return ParameterType.Integer64; + case "REAL": + return ParameterType.Real; + case "STRING": + return ParameterType.String; + case "GUIDSTRING": + return ParameterType.Guid; + case "CHARACTERGUID": + return ParameterType.CharacterGuid; + case "ITEMGUID": + return ParameterType.ItemGuid; + case "TRIGGERGUID": + return ParameterType.TriggerGuid; + case "SPLINEGUID": + return ParameterType.SplineGuid; + case "LEVELTEMPLATEGUID": + return ParameterType.LevelTemplateGuid; + default: + return ParameterType.Unknown; + } +} diff --git a/src/server/projects/story/utils/toParameters.ts b/src/server/projects/story/utils/toParameters.ts new file mode 100644 index 0000000..b679b68 --- /dev/null +++ b/src/server/projects/story/utils/toParameters.ts @@ -0,0 +1,75 @@ +import getExplicitParameterType from "./getExplicitParameterType"; +import Symbol from "../Symbol"; +import { Parameter, ParameterType } from "../models/parameter"; +import { Variable } from "../models/symbol"; + +import { + SignatureNode, + NodeType, + IdentifierType +} from "../../../parsers/story/models/nodes"; + +export default function toParameters( + symbol: Symbol, + signature: SignatureNode, + variables?: Array +) { + const positions = signature.parameters; + const parameters: Array = []; + let isInferred: boolean = false; + let isPartial: boolean = false; + + for (let index = 0; index < positions.length; index++) { + const position = positions[index]; + const { argument, flow } = position; + + let type = getExplicitParameterType(position); + let name: string; + if (argument.type === NodeType.Identifier) { + name = argument.name; + } else { + name = `_Param${index + 1}`; + } + + if ( + !type && + variables && + argument.type === NodeType.Identifier && + argument.identifierType === IdentifierType.Variable + ) { + const variable = variables.find( + variable => variable.name === name.toLowerCase() + ); + + // Make sure we don't wait for variables from ourself + if (variable && variable.fromSymbol !== symbol) { + isInferred = true; + parameters.push({ + flow, + fromIndex: variable.fromIndex, + fromSymbol: variable.fromSymbol, + name, + type: variable.type || ParameterType.Unknown + }); + continue; + } + } + + if (!type) { + isPartial = true; + type = ParameterType.Invalid; + } + + parameters.push({ + flow, + name, + type + }); + } + + return { + isInferred, + isPartial, + parameters + }; +} diff --git a/src/server/projects/story/utils/toSymbolTypeFromString.ts b/src/server/projects/story/utils/toSymbolTypeFromString.ts new file mode 100644 index 0000000..e702764 --- /dev/null +++ b/src/server/projects/story/utils/toSymbolTypeFromString.ts @@ -0,0 +1,20 @@ +import { SymbolType } from "../models/symbol"; + +export default function toSymbolTypeFromString( + value: string +): SymbolType | null { + switch (value.toLowerCase()) { + case "call": + case "proc": + case "syscall": + return SymbolType.Call; + case "event": + return SymbolType.Event; + case "sysquery": + case "query": + case "qry": + return SymbolType.Query; + } + + return null; +} diff --git a/src/server/utils/debounce.ts b/src/server/utils/debounce.ts new file mode 100644 index 0000000..2cadfd8 --- /dev/null +++ b/src/server/utils/debounce.ts @@ -0,0 +1,13 @@ +export default function debounce( + callback: T, + time: number +) { + let interval: NodeJS.Timer | undefined; + return (...args: Array) => { + if (interval) clearTimeout(interval); + interval = setTimeout(() => { + interval = undefined; + callback(...args); + }, time); + }; +} diff --git a/src/server/utils/fetch.ts b/src/server/utils/fetch.ts new file mode 100644 index 0000000..e4b00bf --- /dev/null +++ b/src/server/utils/fetch.ts @@ -0,0 +1,30 @@ +import * as https from "https"; + +export interface FetchResult { + code: number; + content: string; + error?: Error; +} + +export default function fetch(url: string): Promise { + return new Promise(resolve => { + https + .get(url, resp => { + let content = ""; + + resp.on("data", chunk => { + content += chunk; + }); + + resp.on("end", () => { + resolve({ + code: resp.statusCode || -1, + content + }); + }); + }) + .on("error", error => { + resolve({ code: -1, content: "", error }); + }); + }); +} diff --git a/src/server/utils/parseUri.ts b/src/server/utils/parseUri.ts new file mode 100644 index 0000000..63dd2a1 --- /dev/null +++ b/src/server/utils/parseUri.ts @@ -0,0 +1,34 @@ +import { normalize } from "path"; + +export type ParsedUri = + | { + type: "path"; + path: string; + } + | { + goal: string; + project: string; + type: "header"; + } + | null; + +export default function parseUri(uri: string): ParsedUri { + uri = decodeURIComponent(uri); + if (uri.startsWith("file:///")) { + return { + type: "path", + path: normalize(uri.substr(8)) + }; + } + + const match = /divinity:\/([^/]+)\/(.*?)\.divGoal/.exec(uri); + if (match) { + return { + goal: match[2], + project: match[1], + type: "header" + }; + } + + return null; +} diff --git a/src/server/utils/readProjectMetaInfo.ts b/src/server/utils/readProjectMetaInfo.ts new file mode 100644 index 0000000..222c480 --- /dev/null +++ b/src/server/utils/readProjectMetaInfo.ts @@ -0,0 +1,21 @@ +import { ProjectMetaInfo } from "../../shared/notifications"; + +export default function readProjectMetaInfo(data: any): ProjectMetaInfo { + const params = data.save.region[0].node[0].children[0].node; + const result: any = {}; + + for (const param of params) { + if (param.$.id === "ModuleInfo") { + for (const { $ } of param.attribute) { + const { id, value } = $; + result[id] = value; + } + } + } + + if (!("UUID" in result) || !("Name" in result)) { + throw new Error("Missing project metadata."); + } + + return result as ProjectMetaInfo; +} diff --git a/src/server/utils/readXmlFile.ts b/src/server/utils/readXmlFile.ts new file mode 100644 index 0000000..dcde18f --- /dev/null +++ b/src/server/utils/readXmlFile.ts @@ -0,0 +1,17 @@ +import { parseString } from "xml2js"; +import { readFile } from "../../shared/fs"; + +export default async function readXmlFile(path: string): Promise { + return readFile(path, { encoding: "utf-8" }).then( + source => + new Promise((resolve, reject) => { + parseString(source, (error, data) => { + if (error) { + reject(error); + } else { + resolve(data); + } + }); + }) + ); +} diff --git a/src/server/utils/runSafe.ts b/src/server/utils/runSafe.ts new file mode 100644 index 0000000..eb1fc61 --- /dev/null +++ b/src/server/utils/runSafe.ts @@ -0,0 +1,55 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { + CancellationToken, + ResponseError, + ErrorCodes +} from "vscode-languageserver/lib/main"; + +export function formatError(message: string, err: any): string { + if (err instanceof Error) { + let error = err; + return `${message}: ${error.message}\n${error.stack}`; + } else if (typeof err === "string") { + return `${message}: ${err}`; + } else if (err) { + return `${message}: ${err.toString()}`; + } + return message; +} + +export function cancelValue() { + console.log("cancelled"); + return new ResponseError(ErrorCodes.RequestCancelled, "Request cancelled"); +} + +export default function runSafe( + func: () => T, + errorVal: T, + errorMessage: string, + token: CancellationToken +): Thenable> { + return new Promise>((resolve, reject) => { + setImmediate(() => { + if (token.isCancellationRequested) { + resolve(cancelValue()); + } else { + try { + let result = func(); + if (token.isCancellationRequested) { + resolve(cancelValue()); + return; + } else { + resolve(result); + } + } catch (e) { + console.error(formatError(errorMessage, e)); + resolve(errorVal); + } + } + }); + }); +} diff --git a/src/server/utils/runSafeAsync.ts b/src/server/utils/runSafeAsync.ts new file mode 100644 index 0000000..456e836 --- /dev/null +++ b/src/server/utils/runSafeAsync.ts @@ -0,0 +1,40 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { + CancellationToken, + ResponseError +} from "vscode-languageserver/lib/main"; + +import { cancelValue, formatError } from "./runSafe"; + +export default function runSafeAsync( + func: () => Thenable, + errorVal: T, + errorMessage: string, + token: CancellationToken +): Thenable> { + return new Promise>((resolve, reject) => { + setImmediate(() => { + if (token.isCancellationRequested) { + resolve(cancelValue()); + } + return func().then( + result => { + if (token.isCancellationRequested) { + resolve(cancelValue()); + return; + } else { + resolve(result); + } + }, + e => { + console.error(formatError(errorMessage, e)); + resolve(errorVal); + } + ); + }); + }); +} diff --git a/src/server/utils/sleep.ts b/src/server/utils/sleep.ts new file mode 100644 index 0000000..13150a9 --- /dev/null +++ b/src/server/utils/sleep.ts @@ -0,0 +1,5 @@ +export default async function sleep(duration: number = 500): Promise { + return new Promise(resolve => { + setTimeout(() => resolve(), duration); + }); +} diff --git a/src/server/utils/toLocation.ts b/src/server/utils/toLocation.ts new file mode 100644 index 0000000..8a3a250 --- /dev/null +++ b/src/server/utils/toLocation.ts @@ -0,0 +1,13 @@ +import { Location, TextDocument } from "vscode-languageserver/lib/main"; +import { TokenRange } from "../parsers/story/Lexer"; +import unpackRange from "../parsers/story/utils/unpackRange"; + +export default function toLocation( + document: TextDocument, + range: TokenRange +): Location { + return { + uri: document.uri, + range: unpackRange(range) + }; +} diff --git a/src/server/utils/ucfirst.ts b/src/server/utils/ucfirst.ts new file mode 100644 index 0000000..7bf7d5a --- /dev/null +++ b/src/server/utils/ucfirst.ts @@ -0,0 +1,3 @@ +export default function ucfirst(value: string): string { + return value.substr(0, 1).toUpperCase() + value.substr(1); +} diff --git a/src/shared/fs.ts b/src/shared/fs.ts new file mode 100644 index 0000000..cde6d84 --- /dev/null +++ b/src/shared/fs.ts @@ -0,0 +1,11 @@ +import * as fs from "fs"; +import { promisify } from "util"; + +const readDir = promisify(fs.readdir); +const readFile = promisify(fs.readFile); +const rename = promisify(fs.rename); +const stat = promisify(fs.stat); +const unlink = promisify(fs.unlink); +const writeFile = promisify(fs.writeFile); + +export { readDir, readFile, rename, stat, unlink, writeFile }; diff --git a/src/shared/getPackagePath.ts b/src/shared/getPackagePath.ts new file mode 100644 index 0000000..666d84f --- /dev/null +++ b/src/shared/getPackagePath.ts @@ -0,0 +1,5 @@ +import { join, normalize } from "path"; + +export default function getPackagePath() { + return normalize(join(__dirname, "..", "..")); +} diff --git a/src/shared/goalTemplate.ts b/src/shared/goalTemplate.ts new file mode 100644 index 0000000..4482a8d --- /dev/null +++ b/src/shared/goalTemplate.ts @@ -0,0 +1,35 @@ +export interface TemplateOptions { + exit?: string; + init?: string; + kb?: string; + parents?: Array; + subGoalCombiner?: string; + version?: number; +} + +export default function goalTemplate({ + exit = "", + init = "", + kb = "", + parents = [], + subGoalCombiner = "SGC_AND", + version = 1 +}: TemplateOptions): string { + const lines = [ + `Version ${version}`, + `SubGoalCombiner ${subGoalCombiner}`, + "INITSECTION", + init, + "KBSECTION", + kb, + "EXITSECTION", + exit, + "ENDEXITSECTION" + ]; + + for (const parent of parents) { + lines.push(`ParentTargetEdge "${parent}"`); + } + + return lines.join("\r\n"); +} diff --git a/src/shared/notifications.ts b/src/shared/notifications.ts new file mode 100644 index 0000000..2107f9f --- /dev/null +++ b/src/shared/notifications.ts @@ -0,0 +1,66 @@ +export const apiShowEvent = "divinity/apiShow"; +export const divRequestEvent = "divinity/divRequest"; +export const divRequestResultEvent = "divinity/divRequestResult"; +export const goalsChangedEvent = "divinity/goalsChanged"; +export const readyEvent = "divinity/ready"; + +export interface DivRequestResult { + content: string | null; + uri: string; +} + +export interface GoalsChanged { + goals: Array; + project: ProjectInfo; + treeVersion: number; +} + +export interface ProjectInfo { + meta: ProjectMetaInfo; + path: string; +} + +export interface ProjectMetaInfo { + Author?: string; + CharacterCreationLevelName?: string; + Description?: string; + Folder: string; + GMTemplate?: string; + LobbyLevelName?: string; + MD5?: string; + MenuLevelName?: string; + Name: string; + NumPlayers?: string; + PhotoBooth?: string; + StartupLevelName?: string; + Tags?: string; + Type?: string; + UUID: string; + Version?: string; +} + +export interface GoalInfo { + children: Array; + isShared: boolean; + name: string; + uri: string; +} + +// Error event + +export const showErrorEvent = "divinity/showError"; + +export interface ShowErrorArgs { + message: string; +} + +// Project events + +export const projectAddedEvent = "divinity/projectAdded"; +export const projectReadyEvent = "divinity/projectReady"; +export const levelIndexStartEvent = "divinity/levelIndexStart"; +export const levelIndexReadyEvent = "divinity/levelIndexReady"; + +export interface ProjectEventArgs { + project: ProjectInfo; +} diff --git a/src/shared/requests.ts b/src/shared/requests.ts new file mode 100644 index 0000000..58255b6 --- /dev/null +++ b/src/shared/requests.ts @@ -0,0 +1,38 @@ +import { WorkspaceEdit } from "vscode-languageclient"; + +// API request + +export const apiRequest = "divinity/api"; + +export interface ApiResult { + content: string; + location: string; +} + +// Rename goal + +export const renameGoalRequest = "divinity/renameGoal"; + +export interface RenameGoalParams { + goalName: string; + newName: string; + projectUid: string; +} + +export interface RenameGoalResult extends WorkspaceEdit { + error?: string; +} + +// Move goal + +export const moveGoalRequest = "divinity/moveGoal"; + +export interface MoveGoalParams { + goalName: string; + newParent: string; + projectUid: string; +} + +export interface MoveGoalResult extends WorkspaceEdit { + error?: string; +} diff --git a/syntaxes/divinity-story-div.language-configuration.json b/syntaxes/divinity-story-div.language-configuration.json new file mode 100644 index 0000000..b6b70e0 --- /dev/null +++ b/syntaxes/divinity-story-div.language-configuration.json @@ -0,0 +1,16 @@ +{ + "comments": { + "lineComment": "//", + "blockComment": ["/*", "*/"] + }, + "brackets": [["{", "}"], ["[", "]"], ["(", ")"]], + "autoClosingPairs": [["{", "}"], ["[", "]"], ["(", ")"], ["\"", "\""]], + "surroundingPairs": [["{", "}"], ["[", "]"], ["(", ")"], ["\"", "\""]], + "folding": { + "offSide": true, + "markers": { + "start": "^\\s*//REGION", + "end": "^\\s*//END_REGION" + } + } +} diff --git a/syntaxes/divinity-story-div.tmLanguage.json b/syntaxes/divinity-story-div.tmLanguage.json new file mode 100644 index 0000000..8588d98 --- /dev/null +++ b/syntaxes/divinity-story-div.tmLanguage.json @@ -0,0 +1,96 @@ +{ + "$schema": + "https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json", + "name": "Divinity story div", + "scopeName": "text.divinity.storydiv", + "patterns": [ + { + "include": "#comments" + }, + { + "include": "#keywords" + }, + { + "include": "#strings" + } + ], + "repository": { + "comments": { + "patterns": [ + { + "name": "markup.bold.osiris", + "match": "^\\s*//(REGION|END_REGION).*" + }, + { + "name": "comment.line.double-slash.osiris", + "match": "//.*" + }, + { + "name": "comment.block.osiris", + "begin": "/\\*", + "end": "\\*/" + } + ] + }, + "keywords": { + "patterns": [ + { + "name": "support.other.osiris", + "match": "^(Version|SubGoalCombiner|ParentTargetEdge).*$" + }, + { + "name": "support.type.osiris", + "match": "\\([A-Z]+\\)" + }, + { + "name": "keyword.control.osiris", + "match": "\\b(AND|IF|NOT|PROC|THEN|QRY)\\b" + }, + { + "name": "keyword.control.osiris", + "match": "\\bNOT\\b" + }, + { + "name": "keyword.other.osiris", + "match": "^(ENDEXITSECTION|EXITSECTION|INITSECTION|KBSECTION)$" + }, + { + "name": "constant.numeric.osiris", + "match": "\\b([+-]?[0-9]*\\.[0-9]*)|([+-]?[0-9]+)\\b" + }, + { + "name": "constant.character.osiris", + "match": + "\\b(CHARACTERGUID|GUIDSTRING|ITEMGUID|SPLINEGUID|TRIGGERGUID)_[A-Za-z0-9_-]+\\b" + }, + { + "name": "variable.parameter.osiris", + "match": "\\b_[A-Za-z0-9_]*\\b" + }, + { + "name": "variable.other.db.osiris", + "match": "\\bDB_[A-Za-z0-9_-]+\\b" + }, + { + "match": "\\b([A-Za-z_][A-Za-z0-9_]*)\\(", + "captures": { + "1": { + "name": "entity.name.function.osiris" + } + } + } + ] + }, + "strings": { + "name": "string.quoted.double.osiris", + "begin": "\"", + "end": "\"", + "patterns": [ + { + "name": "constant.character.escape.osiris", + "match": "\\\\." + } + ] + } + } +} diff --git a/syntaxes/divinity-story-goal.language-configuration.json b/syntaxes/divinity-story-goal.language-configuration.json new file mode 100644 index 0000000..b6b70e0 --- /dev/null +++ b/syntaxes/divinity-story-goal.language-configuration.json @@ -0,0 +1,16 @@ +{ + "comments": { + "lineComment": "//", + "blockComment": ["/*", "*/"] + }, + "brackets": [["{", "}"], ["[", "]"], ["(", ")"]], + "autoClosingPairs": [["{", "}"], ["[", "]"], ["(", ")"], ["\"", "\""]], + "surroundingPairs": [["{", "}"], ["[", "]"], ["(", ")"], ["\"", "\""]], + "folding": { + "offSide": true, + "markers": { + "start": "^\\s*//REGION", + "end": "^\\s*//END_REGION" + } + } +} diff --git a/syntaxes/divinity-story-goal.snippets.json b/syntaxes/divinity-story-goal.snippets.json new file mode 100644 index 0000000..bbcf0be --- /dev/null +++ b/syntaxes/divinity-story-goal.snippets.json @@ -0,0 +1,22 @@ +{ + "AND": { + "prefix": "AND", + "body": ["AND", "$0"], + "description": "And condition" + }, + "IF": { + "prefix": "IF", + "body": ["IF", "$0", "THEN"], + "description": "Rule definition" + }, + "PROC": { + "prefix": "PROC", + "body": ["PROC", "$0", "THEN"], + "description": "Procedure definition" + }, + "QRY": { + "prefix": "QRY", + "body": ["QRY", "$0", "THEN", "DB_NOOP(1);"], + "description": "Query definition" + } +} diff --git a/syntaxes/divinity-story-goal.tmLanguage.json b/syntaxes/divinity-story-goal.tmLanguage.json new file mode 100644 index 0000000..732246c --- /dev/null +++ b/syntaxes/divinity-story-goal.tmLanguage.json @@ -0,0 +1,90 @@ +{ + "$schema": "https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json", + "name": "Divinity story goal", + "scopeName": "text.divinity.storygoal", + "patterns": [ + { + "include": "#comments" + }, + { + "include": "#keywords" + }, + { + "include": "#strings" + } + ], + "repository": { + "comments": { + "patterns": [ + { + "name": "markup.bold.osiris", + "match": "^\\s*//(REGION|END_REGION).*" + }, + { + "name": "comment.line.double-slash.osiris", + "match": "//.*" + }, + { + "name": "comment.block.osiris", + "begin": "/\\*", + "end": "\\*/" + } + ] + }, + "keywords": { + "patterns": [ + { + "name": "support.other.osiris", + "match": "^(Version|SubGoalCombiner|ParentTargetEdge).*$" + }, + { + "name": "support.type.osiris", + "match": "\\([A-Z]+\\)" + }, + { + "name": "keyword.control.osiris", + "match": "\\b(AND|IF|NOT|PROC|THEN|QRY)\\b" + }, + { + "name": "keyword.other.osiris", + "match": "^(ENDEXITSECTION|EXITSECTION|INITSECTION|KBSECTION)$" + }, + { + "name": "constant.numeric.osiris", + "match": "\\b([+-]?[0-9]*\\.[0-9]*)|([+-]?[0-9]+)\\b" + }, + { + "name": "variable.parameter.osiris", + "match": "\\b_[A-Za-z0-9_]*\\b" + }, + { + "name": "variable.other.db.osiris", + "match": "\\bDB_[A-Za-z0-9_-]+\\b" + }, + { + "match": "\\b([A-Za-z_][A-Za-z0-9_]*)\\(", + "captures": { + "1": { + "name": "entity.name.function.osiris" + } + } + }, + { + "name": "constant.character.osiris", + "match": "\\b[A-Za-z0-9][A-Za-z0-9_-]*\\b" + } + ] + }, + "strings": { + "name": "string.quoted.double.osiris", + "begin": "\"", + "end": "\"", + "patterns": [ + { + "name": "constant.character.escape.osiris", + "match": "\\\\." + } + ] + } + } +} diff --git a/test/fixtures/analyzer.txt b/test/fixtures/analyzer.txt new file mode 100644 index 0000000..051a081 --- /dev/null +++ b/test/fixtures/analyzer.txt @@ -0,0 +1,242 @@ +Version 1 +SubGoalCombiner SGC_AND + +INITSECTION + +DB_String("IAmAGUID_12345678-1234-1234-1234-12345678abcd"); +// DB_StringGuid(IAmAGUIDToo_55555555-5555-5555-5555-555555555555); +DB_Int(1234); +// DB_Float(123.456); + +// ERR! [11] Intrinsic type of column 1 of DB_BadInference differs: Integer vs Integer64 +DB_BadInference(444); +DB_BadInference((INTEGER64)444); + +// WARN [23] Name of database "BaaadDB" should start with the prefix "DB" +BaaadDB(123); + +// ERR! [11] Intrinsic type of column 1 of Call "CharacterFreeze" differs: GuidString vs String +CharacterFreeze("hahah"); + +// WARN [25] Database "DB_Unused1(1)" is written to, but is never used in a rule +DB_Unused1(1); + +DB_CharacterGuid((CHARACTERGUID)55555555-5555-5555-5555-555555555555); +DB_ItemGuid((ITEMGUID)55555555-5555-5555-5555-555555555555); + +KBSECTION + +IF +DB_String(_Str) +AND +// ERR! [11] Rule variable _Str of type String cannot be casted to Integer +DB_Int(_Str) +THEN +DB_NOOP(1); + +// Call rule test +// ERR! [17] Initial rule condition can only be an event or a DB; "CharacterFreeze(1)" is a Call +IF +CharacterFreeze(_) +THEN +// OK +DB_NOOP(1); +// OK +NOT DB_NOOP(1); +// OK +CharacterFreeze(55555555-5555-5555-5555-555555555555); +// ERR! [16] KB rule NOT actions can only reference databases; "CharacterFreeze(1)" is a Call +NOT CharacterFreeze(55555555-5555-5555-5555-555555555555); +// ERR! [15] KB rule actions can only reference databases, calls and PROCs; "RealMin(3)" is a Query +RealMin(1.0, 1.0, 1.0); + +// OK +IF +TradeEnds(_CharA, _CharB) +THEN +DB_NOOP(1); + +// Event in 2nd condition check +IF +TradeEnds(_CharA, _CharB) +// ERR! [18] Subsequent rule conditions can only be queries or DBs; "TradeEnds(2)" is a Event +AND +TradeEnds(_CharA, _CharB) +THEN +DB_NOOP(1); + +// OK +PROC +ProcName((STRING)_Asd) +THEN +DB_NOOP(1); + +// ERR! [17] Initial proc condition can only be a PROC name; "CharacterFreeze(1)" is a Call +PROC +CharacterFreeze((STRING)_Asd) +THEN +DB_NOOP(1); + +// OK +QRY +QryName((STRING)_Asd) +THEN +DB_NOOP(1); + +// BAD, incompatible with first declaration +QRY +// ERR! [11] Intrinsic type of parameter 1 of UserQuery "QryName" differs: String vs Integer +QryName((INTEGER)_Asd) +THEN +DB_NOOP(1); + +// ERR! [17] Initial query condition can only be a user-defined QRY name; "RealMin(3)" is a Query +QRY +RealMin(_Arg1, _Arg2, _Arg3) +THEN +DB_NOOP(1); + +// Alias type cast warning check +IF +DB_CharacterGuid(_CG) +AND +DB_ItemGuid(_IG) +AND +// WARN [11] GUID alias cast - LHS/RHS differs: 6 vs 7 +_CG == _IG +THEN +DB_NOOP(1); + +// Alias type cast warning check 2 +IF +DB_CharacterGuid(_CG) +AND +// WARN [11] GUID alias cast: Rule variable _CG of type 6 casted to 7 +DB_ItemGuid(_CG) +THEN +DB_NOOP(1); + +// Alias type cast warning check 3 +IF +DB_CharacterGuid(_CG) +THEN +// WARN [11] GUID alias cast: Rule variable _CG of type 6 casted to 7 +NOT DB_ItemGuid(_CG); + +// Illogical string comparison warning +IF +DB_String(_CG) +AND +// WARN [20] String comparison using operator Greater - probably a mistake? +_CG > "asdf" +THEN +DB_NOOP(1); + +// Bad constant type +IF +// WARN [21] GUID constant "ITEMGUID_12341234-1234-1234-1234-123412341234" has inferred type CHARACTERGUID +DB_CharacterGuid((CHARACTERGUID)ITEMGUID_12341234-1234-1234-1234-123412341234) +THEN +DB_NOOP(1); + +// OK +PROC +ProcGoodName(_ASD) +THEN +DB_NOOP(_ASD); + +// Bad PROC naming convention +PROC +// WARN [23] Name of PROC "BadProcName(1)" should start with the prefix "PROC" +BadProcName(_ASD) +THEN +DB_NOOP(_ASD); + +// OK +QRY +QryGoodName((INTEGER)_ASD) +THEN +DB_NOOP(1); + +// Bad QUERY naming convention +QRY +// WARN [23] Name of Query "BadQryName(1)" should start with the prefix "QRY" +BadQryName((INTEGER)_ASD) +THEN +DB_NOOP(1); + +// Bad DB naming convention +IF +// WARN [23] Name of database "BaaadDB" should start with the prefix "DB" +BaaadDB((INTEGER)_Arg) +THEN +DB_NOOP(1); + +// Using unbound names 1 +IF +DB_CharacterGuid(_Asd) +AND +// ERR! [24] Variable _Undef11 was never bound, but was used as parameter _A in Query "IntegerSum(3)" +// ERR! [24] Variable _Undef12 was never bound, but was used as parameter _B in Query "IntegerSum(3)" +IntegerSum(_Undef11, _Undef12, _C) +THEN +DB_NOOP(1); + +// Using unbound names 2 +IF +DB_CharacterGuid(_Asd) +AND +// ERR! [24] Variable _Undef2 was unbound when used in a binary expression +_Asd != (CHARACTERGUID)_Undef2 +THEN +DB_NOOP(1); + +// Using unbound names 3 +IF +DB_CharacterGuid(_Asd) +THEN +// ERR! [24] Variable _Undef3 was never bound, but was used as parameter 0 in Database "DB_NOOP(1)" +DB_NOOP((INTEGER)_Undef3); + +// Using late-bound names 1 +IF +DB_CharacterGuid(_Asd) +AND +// ERR! [24] Variable _LateBound11 was not bound when used as parameter _A in Query "IntegerSum(3)" +// ERR! [24] Variable _LateBound12 was not bound when used as parameter _B in Query "IntegerSum(3)" +IntegerSum(_LateBound11, _LateBound12, _C) +AND +DB_Int(_LateBound11) +AND +DB_Int(_LateBound12) +THEN +DB_NOOP(1); + +// Using late-bound names 2 +IF +DB_CharacterGuid(_Asd) +AND +// ERR! [24] Variable _LateBound2 was unbound when used in a binary expression +_Asd != (CHARACTERGUID)_LateBound2 +AND +DB_CharacterGuid(_LateBound2) +THEN +DB_NOOP(1); + +// Check write-only/read-only DBs +IF +// WARN [25] Database "DB_Unused3(1)" is used in a rule, but is never written to +DB_Unused3((STRING)_Unused) +THEN +// WARN [25] Database "DB_Unused4(1)" is written to, but is never used in a rule +DB_Unused4(1); + + +EXITSECTION + +// WARN [25] Database "DB_Unused2(1)" is written to, but is never used in a rule +DB_Unused2(1); + +ENDEXITSECTION +// ERR! [08] Parent goal of "testbench" could not be resolved: "Nonexistent" +ParentTargetEdge "Nonexistent" diff --git a/test/fixtures/headers.div b/test/fixtures/headers.div new file mode 100644 index 0000000..7d9a88a --- /dev/null +++ b/test/fixtures/headers.div @@ -0,0 +1,19785 @@ +//Story header automatically generated. Do not modify! +option compile_trace +option debug_trace + +//Registered types: +// type {INTEGER, 1} // osiris predefined type +// type {INTEGER64, 2} // osiris predefined type +// type {REAL, 3} // osiris predefined type +// type {STRING, 4} // osiris predefined type +// type {GUIDSTRING, 5} // osiris predefined type +alias_type {CHARACTERGUID, 6, 5} +alias_type {ITEMGUID, 7, 5} +alias_type {TRIGGERGUID, 8, 5} +alias_type {SPLINEGUID, 9, 5} +alias_type {LEVELTEMPLATEGUID, 10, 5} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////Built-in calls and queries///////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +syscall SysCompleteGoal((STRING)_GoalTitle) (1,0,0,0) +syscall SysActivateGoal((STRING)_GoalTitle) (2,0,0,0) +syscall SysSetGoalSleeping((STRING)_GoalTitle) (3,0,0,0) +syscall SysClear((STRING)_Predicate,(INTEGER)_Arity) (5,0,0,0) +syscall SysLog((STRING)_Predicate,(INTEGER)_Arity) (4,0,0,0) + + +// _Status can have these values: +// INTERNAL_QUERY_GoalStatus_Sleeping 1 // goal is not yet active, but can become so +// INTERNAL_QUERY_GoalStatus_Active 2 // goal is active +// INTERNAL_QUERY_GoalStatus_Completed 3 // goal is completed (hence, not active) +sysquery SysStatus([in](STRING)_GoalTitle,[out](INTEGER)_Status) (100,0,0,0) +sysquery SysIsCompleted([in](STRING)_GoalTitle) (101,0,0,0) +sysquery SysIsActive([in](STRING)_GoalTitle) (102,0,0,0) +sysquery SysIsSleeping([in](STRING)_GoalTitle) (103,0,0,0) +// Was... functions are only valid if WasDefined() is true. +sysquery SysWasCompleted([in](STRING)_GoalTitle) (104,0,0,0) +sysquery SysWasActive([in](STRING)_GoalTitle) (105,0,0,0) +sysquery SysWasSleeping([in](STRING)_GoalTitle) (106,0,0,0) +sysquery SysWasDefined([in](STRING)_GoalTitle) (107,0,0,0) +sysquery SysCount([in](STRING)_Predicate,[in](INTEGER)_Arity,[out](INTEGER)_Count) (108,0,0,0) +sysquery SysStoryVersion([out](INTEGER)_Major,[out](INTEGER)_Minor,[out](INTEGER)_V3,[out](INTEGER)_V4) (109,0,0,0) + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +query IntegerSum([in](INTEGER)_A, [in](INTEGER)_B, [out](INTEGER)_Sum) (2,0,0,1) +query IntegerSubtract([in](INTEGER)_A, [in](INTEGER)_B, [out](INTEGER)_Result) (2,0,1,1) +query IntegerProduct([in](INTEGER)_A, [in](INTEGER)_B, [out](INTEGER)_Product) (2,0,2,1) +query IntegerDivide([in](INTEGER)_A, [in](INTEGER)_B, [out](INTEGER)_Quotient) (2,0,3,1) +query IntegerMin([in](INTEGER)_A, [in](INTEGER)_B, [out](INTEGER)_Minimum) (2,0,4,1) +query IntegerMax([in](INTEGER)_A, [in](INTEGER)_B, [out](INTEGER)_Maximum) (2,0,5,1) +query IntegerModulo([in](INTEGER)_Num, [in](INTEGER)_Mod, [out](INTEGER)_Return) (2,0,6,1) +query RealSum([in](REAL)_A, [in](REAL)_B, [out](REAL)_Sum) (2,0,7,1) +query RealSubtract([in](REAL)_A, [in](REAL)_B, [out](REAL)_Result) (2,0,8,1) +query RealProduct([in](REAL)_A, [in](REAL)_B, [out](REAL)_Product) (2,0,9,1) +query RealDivide([in](REAL)_A, [in](REAL)_B, [out](REAL)_Quotient) (2,0,10,1) +query RealMin([in](REAL)_A, [in](REAL)_B, [out](REAL)_Minimum) (2,0,11,1) +query RealMax([in](REAL)_A, [in](REAL)_B, [out](REAL)_Maximum) (2,0,12,1) +query Integer([in](REAL)_R, [out](INTEGER)_I) (2,0,13,1) +query Real([in](INTEGER)_I, [out](REAL)_R) (2,0,14,1) +query Random([in](INTEGER)_Modulo, [out](INTEGER)_Random) (2,0,15,1) +query CharacterHasTalent([in](CHARACTERGUID)_Character, [in](STRING)_Talent, [out](INTEGER)_Bool) (2,0,16,1) +call CharacterAddTalent((CHARACTERGUID)_Character, (STRING)_Talent) (1,0,17,1) +call CharacterRemoveTalent((CHARACTERGUID)_Character, (STRING)_Talent) (1,0,18,1) +query CharacterGetLevel([in](CHARACTERGUID)_Character, [out](INTEGER)_Level) (2,0,19,1) +query CharacterCanFight([in](CHARACTERGUID)_Character, [out](INTEGER)_Bool) (2,0,20,1) +call CharacterFreeze((CHARACTERGUID)_Character) (1,0,21,1) +call CharacterUnfreeze((CHARACTERGUID)_Character) (1,0,22,1) +call CharacterCreateAtTrigger((TRIGGERGUID)_Trigger, (STRING)_TemplateId, (INTEGER)_PlaySpawn) (1,0,23,1) +call TemporaryCharacterCreateAtTrigger((TRIGGERGUID)_Trigger, (STRING)_TemplateId, (INTEGER)_PlaySpawn) (1,0,24,1) +query CharacterCreateAtPosition([in](REAL)_X, [in](REAL)_Y, [in](REAL)_Z, [in](STRING)_TemplateId, [in](INTEGER)_PlaySpawn, [out](CHARACTERGUID)_Created) (2,0,25,1) +query TemporaryCharacterCreateAtPosition([in](REAL)_X, [in](REAL)_Y, [in](REAL)_Z, [in](STRING)_TemplateId, [in](INTEGER)_PlaySpawn, [out](CHARACTERGUID)_Created) (2,0,26,1) +query CharacterCreateAtPositionOutOfSightTo([in](REAL)_X, [in](REAL)_Y, [in](REAL)_Z, [in](STRING)_TemplateId, [in](INTEGER)_Angle, [in](INTEGER)_PlaySpawn, [in](STRING)_Event, [out](CHARACTERGUID)_Created) (2,0,27,1) +query TemporaryCharacterCreateAtPositionOutOfSightTo([in](REAL)_X, [in](REAL)_Y, [in](REAL)_Z, [in](STRING)_TemplateId, [in](INTEGER)_Angle, [in](INTEGER)_PlaySpawn, [in](STRING)_Event, [out](CHARACTERGUID)_Created) (2,0,28,1) +query CharacterCreateOutOfSightToObject([in](STRING)_TemplateId, [in](CHARACTERGUID)_ToTarget, [in](GUIDSTRING)_FromObject, [in](INTEGER)_PlaySpawn, [in](STRING)_Event, [out](CHARACTERGUID)_Created) (2,0,29,1) +query TemporaryCharacterCreateOutOfSightToObject([in](STRING)_TemplateId, [in](CHARACTERGUID)_ToTarget, [in](GUIDSTRING)_FromObject, [in](INTEGER)_PlaySpawn, [in](STRING)_Event, [out](CHARACTERGUID)_Created) (2,0,30,1) +call OpenMessageBox((CHARACTERGUID)_Character, (STRING)_Message) (1,0,31,1) +call ShowCredits((CHARACTERGUID)_Character) (1,0,32,1) +call TeleportToPosition((GUIDSTRING)_SourceObject, (REAL)_X, (REAL)_Y, (REAL)_Z, (STRING)_Event, (INTEGER)_TeleportLinkedCharacters, (INTEGER)_ExcludePartyFollowers) (1,0,33,1) +call TeleportTo((GUIDSTRING)_SourceObject, (GUIDSTRING)_TargetObject, (STRING)_Event, (INTEGER)_TeleportLinkedCharacters, (INTEGER)_ExcludePartyFollowers) (1,0,34,1) +call CharacterMoveToPosition((CHARACTERGUID)_Character, (REAL)_X, (REAL)_Y, (REAL)_Z, (INTEGER)_Running, (STRING)_Event) (1,0,35,1) +call CharacterMoveTo((CHARACTERGUID)_Character, (GUIDSTRING)_Target, (INTEGER)_Running, (STRING)_Event, (INTEGER)_IncreaseSpeed) (1,0,36,1) +call CharacterLookFromTrigger((CHARACTERGUID)_Character, (TRIGGERGUID)_Trigger, (INTEGER)_SnapToTarget) (1,0,37,1) +call CharacterEquipItem((CHARACTERGUID)_Character, (ITEMGUID)_Item) (1,0,38,1) +call TransferItemsToCharacter((CHARACTERGUID)_Character, (CHARACTERGUID)_ToCharacter) (1,0,39,1) +call TransferItemsToParty((CHARACTERGUID)_Character) (1,0,40,1) +call TransferItemsToUser((CHARACTERGUID)_Character) (1,0,41,1) +call CharacterUnequipItem((CHARACTERGUID)_Character, (ITEMGUID)_Item) (1,0,42,1) +call CharacterFollowCharacter((CHARACTERGUID)_Character, (CHARACTERGUID)_ToCharacter) (1,0,43,1) +call CharacterStopFollow((CHARACTERGUID)_Character) (1,0,44,1) +call CharacterTeleportPartiesToTriggerWithMovie((TRIGGERGUID)_Trigger, (STRING)_Event, (STRING)_Movie) (1,0,45,1) +call CharacterTeleportPartiesToTriggerWithMovieRequestCallback((TRIGGERGUID)_Trigger, (STRING)_Event) (1,0,46,1) +call CharacterSetTeleportMovie((INTEGER)_UserId, (STRING)_Movie) (1,0,47,1) +call CharacterTeleportPartiesToTrigger((TRIGGERGUID)_Trigger, (STRING)_Event) (1,0,48,1) +call CharacterClearTradeGeneratedItems((CHARACTERGUID)_Character) (1,0,49,1) +call CharacterSetCustomTradeTreasure((CHARACTERGUID)_Character, (STRING)_Treasure) (1,0,50,1) +call GenerateItems((CHARACTERGUID)_Player, (CHARACTERGUID)_Trader) (1,0,51,1) +call CharacterGiveReward((CHARACTERGUID)_Player, (STRING)_Treasure, (INTEGER)_Identified) (1,0,52,1) +call CharacterGiveQuestReward((CHARACTERGUID)_Player, (STRING)_Quest, (STRING)_RewardState) (1,0,53,1) +call CharacterDie((CHARACTERGUID)_Character, (INTEGER)_GenerateTreasure, (STRING)_DeathType, (GUIDSTRING)_Source) (1,0,54,1) +call CharacterDieImmediate((CHARACTERGUID)_Character, (INTEGER)_GenerateTreasure, (STRING)_DeathType, (GUIDSTRING)_Source) (1,0,55,1) +call CharacterAddSkill((CHARACTERGUID)_Character, (STRING)_Skill, (INTEGER)_ShowNotification) (1,0,56,1) +call CharacterRemoveSkill((CHARACTERGUID)_Character, (STRING)_Skill) (1,0,57,1) +query CharacterHasSkill([in](CHARACTERGUID)_Character, [in](STRING)_Skill, [out](INTEGER)_Bool) (2,0,58,1) +call CharacterAddAttributePoint((CHARACTERGUID)_Character, (INTEGER)_Amount) (1,0,59,1) +query CharacterGetAttributePoints([in](CHARACTERGUID)_Character, [out](INTEGER)_Amount) (2,0,60,1) +call CharacterAddAbilityPoint((CHARACTERGUID)_Character, (INTEGER)_Amount) (1,0,61,1) +call CharacterAddCivilAbilityPoint((CHARACTERGUID)_Character, (INTEGER)_Amount) (1,0,62,1) +call CharacterAddActionPoints((CHARACTERGUID)_Character, (INTEGER)_Amount) (1,0,63,1) +query CharacterGetAbilityPoints([in](CHARACTERGUID)_Character, [out](INTEGER)_Amount) (2,0,64,1) +query CharacterGetCivilAbilityPoints([in](CHARACTERGUID)_Character, [out](INTEGER)_Amount) (2,0,65,1) +call CharacterAddTalentPoint((CHARACTERGUID)_Character, (INTEGER)_Amount) (1,0,66,1) +query CharacterGetTalentPoints([in](CHARACTERGUID)_Character, [out](INTEGER)_Amount) (2,0,67,1) +query CharacterGetBaseSourcePoints([in](CHARACTERGUID)_Character, [out](INTEGER)_Amount) (2,0,68,1) +query CharacterGetSourcePoints([in](CHARACTERGUID)_Character, [out](INTEGER)_Amount) (2,0,69,1) +query CharacterGetMaxSourcePoints([in](CHARACTERGUID)_Character, [out](INTEGER)_Amount) (2,0,70,1) +call CharacterResurrect((CHARACTERGUID)_Character) (1,0,71,1) +call CharacterResurrectAndResetXPReward((CHARACTERGUID)_Character) (1,0,72,1) +call CharacterResurrectCustom((CHARACTERGUID)_Character, (STRING)_ResurrectAnimation) (1,0,73,1) +query CharacterGetReservedUserID([in](CHARACTERGUID)_Character, [out](INTEGER)_User) (2,0,74,1) +query GetCurrentCharacter([in](INTEGER)_User, [out](CHARACTERGUID)_Character) (2,0,75,1) +query CharacterIsControlled([in](CHARACTERGUID)_Character, [out](INTEGER)_IsControlled) (2,0,76,1) +query CharacterGetGold([in](CHARACTERGUID)_Character, [out](INTEGER)_Count) (2,0,77,1) +call CharacterAddGold((CHARACTERGUID)_Character, (INTEGER)_Count) (1,0,78,1) +call PartyAddGold((CHARACTERGUID)_Character, (INTEGER)_Count) (1,0,79,1) +query PartyGetGold([in](CHARACTERGUID)_Character, [out](INTEGER)_Gold) (2,0,80,1) +call UserAddGold((CHARACTERGUID)_Character, (INTEGER)_Count) (1,0,81,1) +query UserGetGold([in](CHARACTERGUID)_Character, [out](INTEGER)_Gold) (2,0,82,1) +call CharacterIncreaseSocialStat((CHARACTERGUID)_Character, (STRING)_Id) (1,0,83,1) +call CharacterDecreaseSocialStat((CHARACTERGUID)_Character, (STRING)_Id) (1,0,84,1) +call CharacterSetSpectating((CHARACTERGUID)_Character, (INTEGER)_Spectating) (1,0,85,1) +query CharacterIsSpectating([in](CHARACTERGUID)_Character, [out](INTEGER)_Bool) (2,0,86,1) +query CharacterCanSee([in](CHARACTERGUID)_Character, [in](GUIDSTRING)_Target, [out](INTEGER)_Bool) (2,0,87,1) +call CharacterSetCustomName((CHARACTERGUID)_Character, (STRING)_Text) (1,0,88,1) +call CharacterAppear((CHARACTERGUID)_Character, (INTEGER)_PlaySpawn, (STRING)_Event) (1,0,89,1) +call CharacterAppearCustom((CHARACTERGUID)_Character, (STRING)_Animation, (STRING)_Event) (1,0,90,1) +call CharacterAppearAt((CHARACTERGUID)_Character, (GUIDSTRING)_Target, (INTEGER)_PlaySpawn, (STRING)_Event) (1,0,91,1) +call CharacterAppearAtCustom((CHARACTERGUID)_Character, (GUIDSTRING)_Target, (STRING)_Animation, (STRING)_Event) (1,0,92,1) +call CharacterAppearAtPosition((CHARACTERGUID)_Character, (REAL)_X, (REAL)_Y, (REAL)_Z, (INTEGER)_PlaySpawn, (STRING)_Event) (1,0,93,1) +call CharacterAppearAtPositionCustom((CHARACTERGUID)_Character, (REAL)_X, (REAL)_Y, (REAL)_Z, (STRING)_Animation, (STRING)_Event) (1,0,94,1) +call CharacterAppearOutOfSightTo((CHARACTERGUID)_Character, (GUIDSTRING)_Target, (INTEGER)_Angle, (INTEGER)_PlaySpawn, (STRING)_Event) (1,0,95,1) +call CharacterAppearOutOfSightToCustom((CHARACTERGUID)_Character, (GUIDSTRING)_Target, (INTEGER)_Angle, (STRING)_Animation, (STRING)_Event) (1,0,96,1) +call CharacterAppearOutOfSightToObject((CHARACTERGUID)_Character, (GUIDSTRING)_Target, (GUIDSTRING)_Object, (INTEGER)_PlaySpawn, (STRING)_Event) (1,0,97,1) +call CharacterAppearOutOfSightToObjectCustom((CHARACTERGUID)_Character, (GUIDSTRING)_Target, (GUIDSTRING)_Object, (STRING)_Animation, (STRING)_Event) (1,0,98,1) +call CharacterAppearOnTrailOutOfSightTo((CHARACTERGUID)_Character, (CHARACTERGUID)_Target, (INTEGER)_Angle, (INTEGER)_PlaySpawn, (STRING)_Event) (1,0,99,1) +call CharacterAppearOnTrailOutOfSightToCustom((CHARACTERGUID)_Character, (CHARACTERGUID)_Target, (INTEGER)_Angle, (STRING)_Animation, (STRING)_Event) (1,0,100,1) +call CharacterAppearOnTrailOutOfSightToObject((CHARACTERGUID)_Character, (CHARACTERGUID)_Target, (GUIDSTRING)_Object, (INTEGER)_PlaySpawn, (STRING)_Event) (1,0,101,1) +call CharacterAppearOnTrailOutOfSightToObjectCustom((CHARACTERGUID)_Character, (CHARACTERGUID)_Target, (GUIDSTRING)_Object, (STRING)_Animation, (STRING)_Event) (1,0,102,1) +call CharacterAppearAtPositionOutOfSightTo((CHARACTERGUID)_Character, (REAL)_X, (REAL)_Y, (REAL)_Z, (INTEGER)_Angle, (INTEGER)_PlaySpawn, (STRING)_Event) (1,0,103,1) +call CharacterAppearAtPositionOutOfSightToCustom((CHARACTERGUID)_Character, (REAL)_X, (REAL)_Y, (REAL)_Z, (INTEGER)_Angle, (STRING)_Animation, (STRING)_Event) (1,0,104,1) +call CharacterAppearAtPositionOutOfSightToObject((CHARACTERGUID)_Character, (REAL)_X, (REAL)_Y, (REAL)_Z, (GUIDSTRING)_Object, (INTEGER)_PlaySpawn, (STRING)_Event) (1,0,105,1) +call CharacterAppearAtPositionOutOfSightToObjectCustom((CHARACTERGUID)_Character, (REAL)_X, (REAL)_Y, (REAL)_Z, (GUIDSTRING)_Object, (STRING)_Animation, (STRING)_Event) (1,0,106,1) +call CharacterDisappearOutOfSight((CHARACTERGUID)_Character, (INTEGER)_Angle, (INTEGER)_Running, (STRING)_Event, (INTEGER)_IncreaseSpeed) (1,0,107,1) +call CharacterDisappearOutOfSightToObject((CHARACTERGUID)_Character, (GUIDSTRING)_Object, (INTEGER)_Running, (STRING)_Event, (INTEGER)_IncreaseSpeed) (1,0,108,1) +call CharacterFleeOutOfSight((CHARACTERGUID)_Character, (STRING)_Event) (1,0,109,1) +call CharacterAttack((CHARACTERGUID)_Character, (GUIDSTRING)_Target) (1,0,110,1) +query CharacterAddToCharacterCreation([in](CHARACTERGUID)_Character, [in](INTEGER)_Respec, [out](INTEGER)_Success) (2,0,111,1) +query GameMasterAddToCharacterCreation([in](CHARACTERGUID)_Character, [in](INTEGER)_Respec, [out](INTEGER)_Success) (2,0,112,1) +call CharacterMakePlayer((CHARACTERGUID)_TargetCharacter, (CHARACTERGUID)_OwnerCharacter) (1,0,113,1) +call CharacterRecruitCharacter((CHARACTERGUID)_Character, (CHARACTERGUID)_Character) (1,0,114,1) +call CharacterAssign((INTEGER)_UserID) (1,0,115,1) +call CharacterAssignToUser((INTEGER)_UserID, (CHARACTERGUID)_Character) (1,0,116,1) +call CharacterMakeCompanion((CHARACTERGUID)_Character, (CHARACTERGUID)_Character) (1,0,117,1) +call CharacterMakeNPC((CHARACTERGUID)_Character) (1,0,118,1) +call CharacterAddToParty((CHARACTERGUID)_Character, (CHARACTERGUID)_Character) (1,0,119,1) +call CharacterRemoveFromParty((CHARACTERGUID)_Character) (1,0,120,1) +call CharacterAddToPlayerCharacter((CHARACTERGUID)_Character, (CHARACTERGUID)_Owner) (1,0,121,1) +call CharacterRemoveFromPlayerCharacter((CHARACTERGUID)_Character, (CHARACTERGUID)_Owner) (1,0,122,1) +call CharacterRemoveAllPartyFollowers((CHARACTERGUID)_Character) (1,0,123,1) +query CharacterIsPartyMember([in](CHARACTERGUID)_Character, [out](INTEGER)_Bool) (2,0,124,1) +query CharacterIsInPartyWith([in](CHARACTERGUID)_Character, [in](CHARACTERGUID)_Target, [out](INTEGER)_Bool) (2,0,125,1) +query CharacterGetRelationToCharacter([in](CHARACTERGUID)_Character, [in](CHARACTERGUID)_OtherCharacter, [out](INTEGER)_Relation) (2,0,126,1) +call CharacterSetRelationIndivFactionToIndivFaction((CHARACTERGUID)_Character, (CHARACTERGUID)_OtherCharacter, (INTEGER)_Relation) (1,0,127,1) +call CharacterSetRelationIndivFactionToFaction((CHARACTERGUID)_Character, (STRING)_OtherFaction, (INTEGER)_Relation) (1,0,128,1) +call CharacterSetRelationFactionToIndivFaction((STRING)_Faction, (CHARACTERGUID)_OtherCharacter, (INTEGER)_Relation) (1,0,129,1) +call CharacterSetRelationFactionToFaction((STRING)_Faction, (STRING)_otherFaction, (INTEGER)_Relation) (1,0,130,1) +call CharacterSetTemporaryHostileRelation((CHARACTERGUID)_Character, (CHARACTERGUID)_OtherCharacter) (1,0,131,1) +call CharacterSetReactionPriority((CHARACTERGUID)_Character, (STRING)_Reaction, (INTEGER)_Priority) (1,0,132,1) +query CharacterGetAttitudeTowardsPlayer([in](CHARACTERGUID)_Character, [in](CHARACTERGUID)_Player, [out](INTEGER)_Attitude) (2,0,133,1) +query CharacterGetHitpointsPercentage([in](CHARACTERGUID)_Character, [out](INTEGER)_Percentage) (2,0,134,1) +call CharacterSetHitpointsPercentage((CHARACTERGUID)_Character, (INTEGER)_Percentage) (1,0,135,1) +query CharacterGetArmorPercentage([in](CHARACTERGUID)_Character, [out](INTEGER)_Percentage) (2,0,136,1) +call CharacterSetArmorPercentage((CHARACTERGUID)_Character, (INTEGER)_Percentage) (1,0,137,1) +query CharacterGetMagicArmorPercentage([in](CHARACTERGUID)_Character, [out](INTEGER)_Percentage) (2,0,138,1) +call CharacterSetMagicArmorPercentage((CHARACTERGUID)_Character, (INTEGER)_Percentage) (1,0,139,1) +call CharacterLookAt((CHARACTERGUID)_Character, (GUIDSTRING)_Target, (INTEGER)_SnapToTarget) (1,0,140,1) +call CharacterLevelUp((CHARACTERGUID)_Character) (1,0,141,1) +call CharacterLevelUpTo((CHARACTERGUID)_Character, (INTEGER)_Level) (1,0,142,1) +call PartyAddActualExperience((CHARACTERGUID)_Character, (INTEGER)_XP) (1,0,143,1) +call PartyAddExperience((CHARACTERGUID)_Character, (INTEGER)_Act, (INTEGER)_ActPart, (INTEGER)_Gain) (1,0,144,1) +call PartyAddExplorationExperience((CHARACTERGUID)_Character, (INTEGER)_Act, (INTEGER)_ActPart, (INTEGER)_Gain) (1,0,145,1) +call CharacterAddExplorationExperience((CHARACTERGUID)_Character, (INTEGER)_Act, (INTEGER)_ActPart, (INTEGER)_Gain) (1,0,146,1) +call PartyAddCharismaExperience((CHARACTERGUID)_Character, (INTEGER)_Act, (INTEGER)_ActPart, (INTEGER)_Gain) (1,0,147,1) +call CharacterStatusText((CHARACTERGUID)_Character, (STRING)_Text) (1,0,148,1) +call CharacterEnteredSubRegion((CHARACTERGUID)_Character, (STRING)_Text) (1,0,149,1) +call CharacterDisplayTextWithParam((CHARACTERGUID)_Character, (STRING)_Text, (INTEGER)_Parameter) (1,0,150,1) +call CharacterSetImmortal((CHARACTERGUID)_Character, (INTEGER)_Bool) (1,0,151,1) +query CharacterGetHostCharacter([out](CHARACTERGUID)_Character) (2,0,152,1) +query CharacterGetDisplayName([in](CHARACTERGUID)_Character, [out](STRING)_stringHandle, [out](STRING)_referenceString) (2,0,153,1) +call CharacterFlushQueue((CHARACTERGUID)_Character) (1,0,154,1) +call CharacterPurgeQueue((CHARACTERGUID)_Character) (1,0,155,1) +call CharacterLaunchIterator((STRING)_Event) (1,0,156,1) +call CharacterLaunchIteratorAroundCharacter((CHARACTERGUID)_Character, (REAL)_Radius, (STRING)_Event) (1,0,157,1) +call CharacterSetCanTrade((CHARACTERGUID)_Trader, (INTEGER)_Bool) (1,0,158,1) +query CharacterCanTrade([in](CHARACTERGUID)_Trader, [out](INTEGER)_Bool) (2,0,159,1) +call CharacterSetStill((CHARACTERGUID)_Character) (1,0,160,1) +query CharacterIsInCombat([in](CHARACTERGUID)_Character, [out](INTEGER)_Bool) (2,0,161,1) +query CharacterIsMoving([in](CHARACTERGUID)_Character, [out](INTEGER)_Bool) (2,0,162,1) +query CharacterIsInFightMode([in](CHARACTERGUID)_Character, [out](INTEGER)_Bool) (2,0,163,1) +call CharacterSetFightMode((CHARACTERGUID)_Character, (INTEGER)_Enabled, (INTEGER)_Immediately) (1,0,164,1) +call CharacterMakeStoryNpc((CHARACTERGUID)_Character, (INTEGER)_Bool) (1,0,165,1) +call CharacterStopAllEffectsWithName((CHARACTERGUID)_Character, (STRING)_FxName) (1,0,166,1) +call CharacterPickupItem((CHARACTERGUID)_Character, (ITEMGUID)_Item, (STRING)_Event) (1,0,167,1) +call CharacterItemSetEvent((CHARACTERGUID)_Character, (ITEMGUID)_Item, (STRING)_Event) (1,0,168,1) +call CharacterCharacterSetEvent((CHARACTERGUID)_Character, (CHARACTERGUID)_Character, (STRING)_Event) (1,0,169,1) +call CharacterUseItem((CHARACTERGUID)_Character, (ITEMGUID)_Item, (STRING)_Event) (1,0,170,1) +call CharacterMoveItemToTrigger((CHARACTERGUID)_Character, (ITEMGUID)_Item, (TRIGGERGUID)_Trigger, (INTEGER)_Amount, (STRING)_Event) (1,0,171,1) +query CharacterConsume([in](CHARACTERGUID)_Character, [in](STRING)_Potion, [out](INTEGER64)_ConsumeHandle) (2,0,172,1) +call CharacterUnconsume((CHARACTERGUID)_Character, (INTEGER64)_ConsumeHandle) (1,0,173,1) +call CharacterAddAttribute((CHARACTERGUID)_Character, (STRING)_Attribute, (INTEGER)_Value) (1,0,174,1) +call CharacterRemoveAttribute((CHARACTERGUID)_Character, (STRING)_Attribute, (INTEGER)_Value) (1,0,175,1) +query CharacterGetBaseAttribute([in](CHARACTERGUID)_Character, [in](STRING)_Attribute, [out](INTEGER)_Value) (2,0,176,1) +query CharacterGetAttribute([in](CHARACTERGUID)_Character, [in](STRING)_Attribute, [out](INTEGER)_Value) (2,0,177,1) +call CharacterAddAbility((CHARACTERGUID)_Character, (STRING)_Ability, (INTEGER)_Value) (1,0,178,1) +call CharacterRemoveAbility((CHARACTERGUID)_Character, (STRING)_Ability, (INTEGER)_Value) (1,0,179,1) +query CharacterIsIncapacitated([in](CHARACTERGUID)_Character, [out](INTEGER)_Incapacitated) (2,0,180,1) +query CharacterGetAbility([in](CHARACTERGUID)_Character, [in](STRING)_Ability, [out](INTEGER)_Value) (2,0,181,1) +query CharacterGetBaseAbility([in](CHARACTERGUID)_Character, [in](STRING)_Ability, [out](INTEGER)_Value) (2,0,182,1) +query CharacterGetOwner([in](CHARACTERGUID)_Character, [out](CHARACTERGUID)_Owner) (2,0,183,1) +call ActivateTrade((CHARACTERGUID)_Player, (CHARACTERGUID)_Trader, (INTEGER)_CanRepair, (INTEGER)_CanIdentify, (INTEGER)_CanSell) (1,0,184,1) +call StartPickpocket((CHARACTERGUID)_Player, (CHARACTERGUID)_NPC, (INTEGER)_Success) (1,0,185,1) +call ExecuteDeal((CHARACTERGUID)_Character, (INTEGER)_Deal, (INTEGER)_AttitudeDiff) (1,0,186,1) +query CharacterGetEquippedWeapon([in](CHARACTERGUID)_Character, [out](GUIDSTRING)_ItemGUID) (2,0,187,1) +query CharacterGetEquippedShield([in](CHARACTERGUID)_Character, [out](GUIDSTRING)_ItemGUID) (2,0,188,1) +query CharacterGetEquippedItem([in](CHARACTERGUID)_Character, [in](STRING)_Slotname, [out](GUIDSTRING)_ItemGUID) (2,0,189,1) +call CharacterSetFollowCharacter((CHARACTERGUID)_Character, (CHARACTERGUID)_CharacterToFollow) (1,0,190,1) +call CharacterAttachToGroup((CHARACTERGUID)_Src, (CHARACTERGUID)_Target) (1,0,191,1) +call CharacterDetachFromGroup((CHARACTERGUID)_Character) (1,0,192,1) +query CharactersAreGrouped([in](CHARACTERGUID)_Character1, [in](CHARACTERGUID)_Character2, [out](INTEGER)_Bool) (2,0,193,1) +query CharacterGetInventoryGoldValue([in](CHARACTERGUID)_Character, [out](INTEGER)_Value) (2,0,194,1) +query CharacterGetItemTemplateCount([in](CHARACTERGUID)_Character, [in](STRING)_ItemTemplate, [out](INTEGER)_Value) (2,0,195,1) +call CharacterAddAttitudeTowardsPlayer((CHARACTERGUID)_Character, (CHARACTERGUID)_Player, (INTEGER)_Delta) (1,0,196,1) +query CharacterIsFemale([in](CHARACTERGUID)_Character, [out](INTEGER)_Bool) (2,0,197,1) +call CharacterSetCanSpotSneakers((CHARACTERGUID)_Character, (INTEGER)_CanSpotSneakers) (1,0,198,1) +query CharacterCanSpotSneakers([in](CHARACTERGUID)_Character, [out](INTEGER)_Bool) (2,0,199,1) +call CharacterMoveWeaponsToContainer((CHARACTERGUID)_Character, (ITEMGUID)_Container) (1,0,200,1) +call CharacterLockAbility((CHARACTERGUID)_Character, (STRING)_Ability) (1,0,201,1) +call CharacterUnlockAbility((CHARACTERGUID)_Character, (STRING)_Ability) (1,0,202,1) +call CharacterUnlockRecipe((CHARACTERGUID)_Character, (STRING)_RecipeID, (INTEGER)_ShowNotification) (1,0,203,1) +query CharacterIsDead([in](CHARACTERGUID)_Character, [out](INTEGER)_Bool) (2,0,204,1) +query CharacterIsDeadOrFeign([in](CHARACTERGUID)_Character, [out](INTEGER)_Bool) (2,0,205,1) +call CharacterSetAnimationOverride((CHARACTERGUID)_Character, (STRING)_Animation) (1,0,206,1) +call CharacterSetAnimationSetOverride((CHARACTERGUID)_Character, (STRING)_AnimationSetResource) (1,0,207,1) +call PartySetIdentifyPriceModifier((CHARACTERGUID)_Character, (CHARACTERGUID)_PartyMember, (INTEGER)_Modifier) (1,0,208,1) +call PartySetRepairPriceModifier((CHARACTERGUID)_Character, (CHARACTERGUID)_PartyMember, (INTEGER)_Modifier) (1,0,209,1) +call PartySetShopPriceModifier((CHARACTERGUID)_Character, (CHARACTERGUID)_PartyMember, (INTEGER)_Modifier) (1,0,210,1) +call SetTagPriceModifier((CHARACTERGUID)_Character, (STRING)_Tag, (INTEGER)_Modifier) (1,0,211,1) +call CharacterResetCooldowns((CHARACTERGUID)_Character) (1,0,212,1) +call CharacterShowStoryElementUI((CHARACTERGUID)_Character, (INTEGER)_Type, (STRING)_UIInstance) (1,0,213,1) +call CharacterCloseStoryElementUI((CHARACTERGUID)_Character, (INTEGER)_Type, (STRING)_UIInstance) (1,0,214,1) +call CharacterSendGlobalCombatCounter((CHARACTERGUID)_Character, (INTEGER)_Turn) (1,0,215,1) +call CharacterPlayHUDSound((CHARACTERGUID)_Character, (STRING)_Sound) (1,0,216,1) +call CharacterRegisterCrime((CHARACTERGUID)_Player, (STRING)_CrimeType, (GUIDSTRING)_Evidence, (CHARACTERGUID)_Witness, (INTEGER)_CrimeID) (1,0,217,1) +call CharacterRegisterCrimeWithPosition((CHARACTERGUID)_Player, (STRING)_CrimeType, (GUIDSTRING)_Evidence, (CHARACTERGUID)_Witness, (REAL)_X, (REAL)_Y, (REAL)_Z, (INTEGER)_CrimeID) (1,0,218,1) +call CharacterStopCrime((CHARACTERGUID)_Player, (STRING)_CrimeType, (GUIDSTRING)_Evidence) (1,0,219,1) +call CharacterStopCrimeWithID((CHARACTERGUID)_Player, (INTEGER)_Crime) (1,0,220,1) +call CharacterIgnoreCharacterActiveCrimes((CHARACTERGUID)_Character, (CHARACTERGUID)_Player, (REAL)_Timer) (1,0,221,1) +call CharacterRemoveSummons((CHARACTERGUID)_Character, (INTEGER)_Die) (1,0,222,1) +call CharacterLinkGhost((CHARACTERGUID)_Character, (CHARACTERGUID)_Ghost) (1,0,223,1) +call CharacterUnlinkGhost((CHARACTERGUID)_Character, (CHARACTERGUID)_Ghost) (1,0,224,1) +call DestroyGhost((CHARACTERGUID)_Ghost) (1,0,225,1) +call CharacterUseSkill((CHARACTERGUID)_Character, (STRING)_SkillID, (GUIDSTRING)_Target, (INTEGER)_ForceResetCooldown, (INTEGER)_IgnoreHasSkill, (INTEGER)_IgnoreChecks) (1,0,226,1) +call CharacterUseSkillAtPosition((CHARACTERGUID)_Character, (STRING)_SkillID, (REAL)_X, (REAL)_Y, (REAL)_Z, (INTEGER)_ForceResetCooldown, (INTEGER)_IgnoreHasSkill) (1,0,227,1) +call CharacterDisableCrime((CHARACTERGUID)_Character, (STRING)_Crime) (1,0,228,1) +query CharacterIsCrimeEnabled([in](CHARACTERGUID)_Character, [in](STRING)_Crime, [out](INTEGER)_Bool) (2,0,229,1) +query CharacterGetCrimeRegion([in](CHARACTERGUID)_Character, [out](STRING)_Region) (2,0,230,1) +call CharacterEnableCrime((CHARACTERGUID)_Character, (STRING)_Crime) (1,0,231,1) +call CharacterDisableAllCrimes((CHARACTERGUID)_Character) (1,0,232,1) +call CharacterEnableAllCrimes((CHARACTERGUID)_Character) (1,0,233,1) +call CharacterEnableCrimeWarnings((CHARACTERGUID)_Character, (INTEGER)_Enable) (1,0,234,1) +query CharacterGetCrimeDialog([in](CHARACTERGUID)_Character, [out](INTEGER)_InstanceID) (2,0,235,1) +call CharacterRemoveTension((CHARACTERGUID)_Player) (1,0,236,1) +query CharacterIsEnemy([in](CHARACTERGUID)_Character, [in](CHARACTERGUID)_OtherCharacter, [out](INTEGER)_Bool) (2,0,237,1) +query CharacterIsAlly([in](CHARACTERGUID)_Character, [in](CHARACTERGUID)_OtherCharacter, [out](INTEGER)_Bool) (2,0,238,1) +query CharacterIsNeutral([in](CHARACTERGUID)_Character, [in](CHARACTERGUID)_OtherCharacter, [out](INTEGER)_Bool) (2,0,239,1) +call CharacterAddSourcePoints((CHARACTERGUID)_Character, (INTEGER)_Amount) (1,0,240,1) +call CharacterOverrideMaxSourcePoints((CHARACTERGUID)_Character, (INTEGER)_Amount) (1,0,241,1) +call CharacterRemoveMaxSourcePointsOverride((CHARACTERGUID)_Character) (1,0,242,1) +call CharacterApplyPreset((CHARACTERGUID)_Character, (STRING)_Preset) (1,0,243,1) +call CharacterApplyHenchmanPreset((CHARACTERGUID)_Character, (STRING)_Preset) (1,0,244,1) +call CharacterApplyRacePreset((CHARACTERGUID)_Character, (STRING)_Preset) (1,0,245,1) +call CharacterMoveToAndTalk((CHARACTERGUID)_Character, (GUIDSTRING)_Target, (STRING)_DialogID, (INTEGER)_IsAutomated, (STRING)_MoveID, (INTEGER)_Running, (REAL)_Timeout) (1,0,246,1) +call CharacterMoveToAndTalkRequestDialogFailed((CHARACTERGUID)_Character, (GUIDSTRING)_Target, (STRING)_MoveId) (1,0,247,1) +call CharacterEnableWaypointUsage((CHARACTERGUID)_Character, (INTEGER)_Bool) (1,0,248,1) +query CharacterCanUseWaypoints([in](CHARACTERGUID)_Character, [out](INTEGER)_Bool) (2,0,249,1) +query CharacterCanSeeGhost([in](CHARACTERGUID)_Character, [in](GUIDSTRING)_Ghost, [out](INTEGER)_Bool) (2,0,250,1) +query CharacterIsSummon([in](CHARACTERGUID)_Character, [out](INTEGER)_Bool) (2,0,251,1) +query CharacterIsPartyFollower([in](CHARACTERGUID)_Character, [out](INTEGER)_Bool) (2,0,252,1) +query CharacterIsPolymorphedInto([in](CHARACTERGUID)_Character, [in](STRING)_TargetRace, [out](INTEGER)_Bool) (2,0,253,1) +query CharacterIsPolymorphInteractionDisabled([in](CHARACTERGUID)_Character, [out](INTEGER)_Bool) (2,0,254,1) +query CharacterGameMaster([in](CHARACTERGUID)_Character, [out](INTEGER)_Bool) (2,0,255,1) +query CharacterIsPlayer([in](CHARACTERGUID)_Character, [out](INTEGER)_Bool) (2,0,256,1) +query CharacterHasLinkedGhost([in](CHARACTERGUID)_Character, [out](INTEGER)_Bool) (2,0,257,1) +call CharacterReservePolymorphShape((CHARACTERGUID)_Character, (STRING)_Race) (1,0,258,1) +call CharacterSetForceSynch((CHARACTERGUID)_Character, (INTEGER)_Bool) (1,0,259,1) +call CharacterSetForceUpdate((CHARACTERGUID)_Character, (INTEGER)_Bool) (1,0,260,1) +call CharacterTransform((CHARACTERGUID)_Character, (STRING)_ObjectTemplate, (INTEGER)_ReplaceScripts, (INTEGER)_ReplaceScale, (INTEGER)_ReplaceStats, (INTEGER)_ReplaceEquipment, (INTEGER)_ReplaceSkills, (INTEGER)_UseCustomLooks, (INTEGER)_ReleasePlayerData) (1,0,261,1) +call CharacterTransformAppearanceTo((CHARACTERGUID)_Character, (CHARACTERGUID)_Target, (INTEGER)_CopyEquipment, (INTEGER)_CopyDisplayNameAndIcon) (1,0,262,1) +call CharacterTransformAppearanceToWithEquipmentSet((CHARACTERGUID)_Character, (CHARACTERGUID)_Target, (STRING)_EquipmentSet, (INTEGER)_CopyDisplayNameAndIcon) (1,0,263,1) +query CharacterCanSpotCrimes([in](CHARACTERGUID)_Character, [out](INTEGER)_Bool) (2,0,264,1) +call CharacterReceivedTag((CHARACTERGUID)_Character, (STRING)_Tag) (1,0,265,1) +call CharacterAddPreferredAiTargetTag((CHARACTERGUID)_Character, (STRING)_Tag) (1,0,266,1) +call CharacterRemovePreferredAiTargetTag((CHARACTERGUID)_Character, (STRING)_Tag) (1,0,267,1) +query CharacterGetRace([in](CHARACTERGUID)_Player, [in](INTEGER)_CanPolymorphOverride, [out](STRING)_Race) (2,0,268,1) +query CharacterGetOrigin([in](CHARACTERGUID)_Player, [in](INTEGER)_CanPolymorphOverride, [out](STRING)_Origin) (2,0,269,1) +query CharacterGetInstrument([in](CHARACTERGUID)_Player, [out](STRING)_Instrument) (2,0,270,1) +query CharacterGetHenchmanPresetPrice([in](CHARACTERGUID)_Owner, [in](STRING)_Preset, [out](INTEGER)_Price) (2,0,271,1) +call CharacterOriginIntroStopped((CHARACTERGUID)_Character) (1,0,272,1) +call CharacterSetDoNotFaceFlag((CHARACTERGUID)_Character, (INTEGER)_Value) (1,0,273,1) +call CharacterSetReadyCheckBlocked((CHARACTERGUID)_Character, (INTEGER)_Value) (1,0,274,1) +call CharacterSetCorpseLootable((CHARACTERGUID)_Character, (INTEGER)_Value) (1,0,275,1) +call CharacterSetDetached((CHARACTERGUID)_Character, (INTEGER)_Value) (1,0,276,1) +event CharacterDied((CHARACTERGUID)_Character) (3,0,277,1) +event CharacterResurrected((CHARACTERGUID)_Character) (3,0,278,1) +event CharacterDying((CHARACTERGUID)_Character) (3,0,279,1) +event CharacterTemplateDied((STRING)_TemplateName) (3,0,280,1) +event CharacterStatusAttempt((CHARACTERGUID)_Character, (STRING)_Status, (GUIDSTRING)_Causee) (3,0,281,1) +event CharacterStatusApplied((CHARACTERGUID)_Character, (STRING)_Status, (GUIDSTRING)_Causee) (3,0,282,1) +event CharacterStatusRemoved((CHARACTERGUID)_Character, (STRING)_Status, (GUIDSTRING)_Causee) (3,0,283,1) +event CharacterUsedItem((CHARACTERGUID)_Character, (ITEMGUID)_Item) (3,0,284,1) +event CharacterUsedItemTemplate((CHARACTERGUID)_Character, (STRING)_Template, (ITEMGUID)_Item) (3,0,285,1) +event CharacterUsedItemFailed((CHARACTERGUID)_Character, (ITEMGUID)_Item) (3,0,286,1) +event CharacterPreMovedItem((CHARACTERGUID)_Character, (ITEMGUID)_Item) (3,0,287,1) +event CharacterMovedItem((CHARACTERGUID)_Character, (ITEMGUID)_Item) (3,0,288,1) +event CharacterMovedItemTemplate((CHARACTERGUID)_Character, (STRING)_Template) (3,0,289,1) +event CharacterEnteredRegion((CHARACTERGUID)_Character, (STRING)_Region) (3,0,290,1) +event CharacterLeftRegion((CHARACTERGUID)_Character, (STRING)_Region) (3,0,291,1) +event CharacterSawCharacter((CHARACTERGUID)_Character, (CHARACTERGUID)_OtherCharacter) (3,0,292,1) +event CharacterSawSneakingCharacter((CHARACTERGUID)_Character, (CHARACTERGUID)_OtherCharacter) (3,0,293,1) +event CharacterLostSightOfCharacter((CHARACTERGUID)_Character, (CHARACTERGUID)_OtherCharacter) (3,0,294,1) +event CharacterBlockedBy((CHARACTERGUID)_Defender, (CHARACTERGUID)_AttackOwner, (CHARACTERGUID)_Attacker) (3,0,295,1) +event CharacterMissedBy((CHARACTERGUID)_Defender, (CHARACTERGUID)_AttackOwner, (CHARACTERGUID)_Attacker) (3,0,296,1) +event CharacterCriticalHitBy((CHARACTERGUID)_Defender, (CHARACTERGUID)_AttackOwner, (CHARACTERGUID)_Attacker) (3,0,297,1) +event CharacterKilledBy((CHARACTERGUID)_Defender, (CHARACTERGUID)_AttackOwner, (CHARACTERGUID)_Attacker) (3,0,298,1) +event CharacterTemplateKilledByCharacter((STRING)_CharacterTemplate, (CHARACTERGUID)_Killer) (3,0,299,1) +event CharacterStartAttackObject((GUIDSTRING)_Defender, (CHARACTERGUID)_AttackOwner, (CHARACTERGUID)_Attacker) (3,0,300,1) +event CharacterStartAttackPosition((REAL)_x, (REAL)_y, (REAL)_z, (CHARACTERGUID)_AttackOwner, (CHARACTERGUID)_Attacker) (3,0,301,1) +event CharacterChangedAlginmentToCharacter((CHARACTERGUID)_Character, (CHARACTERGUID)_OtherCharacter, (INTEGER)_NewRelation) (3,0,302,1) +event CharacterEnteredTrigger((CHARACTERGUID)_Character, (TRIGGERGUID)_Trigger) (3,0,303,1) +event CharacterReceivedDamage((CHARACTERGUID)_Character, (INTEGER)_Percentage, (GUIDSTRING)_Source) (3,0,304,1) +event CharacterVitalityChanged((CHARACTERGUID)_Character, (INTEGER)_Percentage) (3,0,305,1) +event CharacterLeftTrigger((CHARACTERGUID)_Character, (TRIGGERGUID)_Trigger) (3,0,306,1) +event CharacterPickpocketSuccess((CHARACTERGUID)_Player, (CHARACTERGUID)_NPC, (ITEMGUID)_Item, (INTEGER)_Amount) (3,0,307,1) +event CharacterAttitudeTowardsPlayerChanged((CHARACTERGUID)_Character, (CHARACTERGUID)_Player, (INTEGER)_NewAttitude) (3,0,308,1) +event ItemSendToHomesteadEvent((CHARACTERGUID)_Player, (ITEMGUID)_Item) (3,0,309,1) +event CharacterItemEvent((CHARACTERGUID)_Character, (ITEMGUID)_Item, (STRING)_Event) (3,0,310,1) +event CharacterUsedSkill((CHARACTERGUID)_Character, (STRING)_Skill, (STRING)_SkillType) (3,0,311,1) +event CharacterUsedSkillAtPosition((CHARACTERGUID)_Character, (REAL)_X, (REAL)_Y, (REAL)_Z, (STRING)_Skill, (STRING)_SkillType) (3,0,312,1) +event CharacterUsedSkillOnTarget((CHARACTERGUID)_Character, (GUIDSTRING)_Target, (STRING)_Skill, (STRING)_SkillType) (3,0,313,1) +event CharacterUsedSkillOnZoneWithTarget((CHARACTERGUID)_Character, (GUIDSTRING)_Target, (STRING)_Skill, (STRING)_SkillType) (3,0,314,1) +event CharacterUsedSkillInTrigger((CHARACTERGUID)_Character, (STRING)_Skill, (STRING)_SkillElement, (TRIGGERGUID)_Trigger) (3,0,315,1) +event CharacterLearnedSkill((CHARACTERGUID)_Character, (STRING)_Skill) (3,0,316,1) +event SkillCast((CHARACTERGUID)_Character, (STRING)_Skill, (STRING)_SkillElement) (3,0,317,1) +event RequestPickpocket((CHARACTERGUID)_Player, (CHARACTERGUID)_NPC) (3,0,318,1) +event CharacterPickpocketFailed((CHARACTERGUID)_Player, (CHARACTERGUID)_NPC) (3,0,319,1) +event CharacterLootedCharacterCorpse((CHARACTERGUID)_Player, (CHARACTERGUID)_Corpse) (3,0,320,1) +event RequestTrade((CHARACTERGUID)_Character, (CHARACTERGUID)_Trader) (3,0,321,1) +event HappyWithDeal((CHARACTERGUID)_Character, (CHARACTERGUID)_Trader, (INTEGER)_CharacterValue, (INTEGER)_TraderValue) (3,0,322,1) +event TradeEnds((CHARACTERGUID)_Character, (CHARACTERGUID)_Trader) (3,0,323,1) +event CharacterScriptFrameFinished((CHARACTERGUID)_Character, (STRING)_FrameID) (3,0,324,1) +event CharacterCharacterEvent((CHARACTERGUID)_Character1, (CHARACTERGUID)_Character2, (STRING)_Event) (3,0,325,1) +event CharacterRelationChangedTo((CHARACTERGUID)_Character, (CHARACTERGUID)_OtherCharacter, (INTEGER)_Attitude) (3,0,326,1) +event CharacterDestroyedItem((CHARACTERGUID)_Character, (ITEMGUID)_Item) (3,0,327,1) +event CharacterDestroyedItemTemplate((CHARACTERGUID)_Character, (STRING)_ItemTemplate) (3,0,328,1) +event CharacterDisplayTextEnded((CHARACTERGUID)_Character, (STRING)_DisplayText) (3,0,329,1) +event CharacterSetTemporaryRelationsFailed((CHARACTERGUID)_Character1, (CHARACTERGUID)_Character2) (3,0,330,1) +event CharacterMadePlayer((CHARACTERGUID)_Character) (3,0,331,1) +event CharacterLeveledUp((CHARACTERGUID)_Character) (3,0,332,1) +event CharacterUnlockedTalent((CHARACTERGUID)_Character, (STRING)_Talent) (3,0,333,1) +event CharacterLockedTalent((CHARACTERGUID)_Character, (STRING)_Talent) (3,0,334,1) +event MessageBoxClosed((CHARACTERGUID)_Character, (STRING)_Message) (3,0,335,1) +event TutorialBoxClosed((CHARACTERGUID)_Character, (STRING)_Message) (3,0,336,1) +event CharacterTraitChanged((CHARACTERGUID)_Character, (STRING)_Trait) (3,0,337,1) +event CharacterGuarded((CHARACTERGUID)_Character) (3,0,338,1) +event CharacterWentOnStage((CHARACTERGUID)_Character, (INTEGER)_Bool) (3,0,339,1) +event CharacterReservedUserIDChanged((CHARACTERGUID)_Character, (INTEGER)_UserID) (3,0,340,1) +event CharacterAddedToGroup((CHARACTERGUID)_Character) (3,0,341,1) +event CharacterDetachedFromGroup((CHARACTERGUID)_Character) (3,0,342,1) +event CharacterStartLockpickingItem((CHARACTERGUID)_Character, (ITEMGUID)_Item) (3,0,343,1) +event CharacterStoppedUsingItem((CHARACTERGUID)_Character, (ITEMGUID)_Item) (3,0,344,1) +event CharacterStoppedCombiningItems((CHARACTERGUID)_Character, (ITEMGUID)_ItemA, (ITEMGUID)_ItemB, (ITEMGUID)_ItemC, (ITEMGUID)_ItemD, (ITEMGUID)_ItemE) (3,0,345,1) +event CharacterTurnedToGhost((CHARACTERGUID)_Character, (CHARACTERGUID)_Ghost) (3,0,346,1) +event CharacterGhostDestroyed((CHARACTERGUID)_Character, (CHARACTERGUID)_Ghost) (3,0,347,1) +event CharacterGhostRevealed((CHARACTERGUID)_Character, (CHARACTERGUID)_Ghost) (3,0,348,1) +event CharacterOnCrimeSensibleActionNotification((CHARACTERGUID)_Character, (STRING)_CrimeRegion, (INTEGER)_CrimeID, (STRING)_PriortiyName, (STRING)_PrimaryDialog, (CHARACTERGUID)_Criminal1, (CHARACTERGUID)_Criminal2, (CHARACTERGUID)_Criminal3, (CHARACTERGUID)_Criminal4, (INTEGER)_IsPrimary) (3,0,349,1) +event CharacterSelectedAsBestUnavailableFallbackLead((CHARACTERGUID)_Character, (STRING)_CrimeRegion, (INTEGER)_UnavailableForCrimeID, (INTEGER)_BusyCrimeID, (CHARACTERGUID)_Criminal1, (CHARACTERGUID)_Criminal2, (CHARACTERGUID)_Criminal3, (CHARACTERGUID)_Criminal4) (3,0,350,1) +event CrimeIsRegistered((CHARACTERGUID)_Victim, (STRING)_CrimeType, (INTEGER)_CrimeID, (GUIDSTRING)_Evidence, (CHARACTERGUID)_Criminal1, (CHARACTERGUID)_Criminal2, (CHARACTERGUID)_Criminal3, (CHARACTERGUID)_Criminal4) (3,0,351,1) +event CharacterSelectedInCharCreation((CHARACTERGUID)_Character, (INTEGER)_UserID) (3,0,352,1) +event CharacterStartOriginIntroduction((CHARACTERGUID)_Character, (INTEGER)_UserID, (STRING)_Origin) (3,0,353,1) +event CharacterStopOriginIntroduction((CHARACTERGUID)_Character, (INTEGER)_UserID) (3,0,354,1) +event CharacterCreatedInArena((CHARACTERGUID)_Character, (INTEGER)_Team) (3,0,355,1) +event CharacterUsedSourcePoint((CHARACTERGUID)_Character) (3,0,356,1) +event SkillAdded((CHARACTERGUID)_Character, (STRING)_Skill, (INTEGER)_Learned) (3,0,357,1) +event SkillActivated((CHARACTERGUID)_Character, (STRING)_Skill) (3,0,358,1) +event SkillDeactivated((CHARACTERGUID)_Character, (STRING)_Skill) (3,0,359,1) +event CharacterTeleported((CHARACTERGUID)_Target, (CHARACTERGUID)_Cause, (REAL)_OldX, (REAL)_OldY, (REAL)_OldZ, (REAL)_NewX, (REAL)_NewY, (REAL)_NewZ, (STRING)_Skill) (3,0,360,1) +event CharacterMoveToAndTalkFailed((CHARACTERGUID)_Character, (GUIDSTRING)_Target, (STRING)_MoveID) (3,0,361,1) +event CharacterMoveToAndTalkRequestDialog((CHARACTERGUID)_Character, (GUIDSTRING)_Target, (STRING)_DialogID, (INTEGER)_IsAutomated, (STRING)_MoveID) (3,0,362,1) +event CharacterMoveToAndTalkRequestDialogFailedEvent((CHARACTERGUID)_Character, (GUIDSTRING)_Target, (STRING)_MoveId) (3,0,363,1) +event CharacterJoinedParty((CHARACTERGUID)_Character) (3,0,364,1) +event CharacterLeftParty((CHARACTERGUID)_Character) (3,0,365,1) +event CharacterLoadedInPreset((CHARACTERGUID)_Character) (3,0,366,1) +event CharacterPolymorphedInto((CHARACTERGUID)_Character, (STRING)_Race) (3,0,367,1) +event CharacterStoppedPolymorph((CHARACTERGUID)_Character) (3,0,368,1) +event CharacterTeleportToPyramid((CHARACTERGUID)_Character, (ITEMGUID)_Pyramid) (3,0,369,1) +event CharacterTeleportToWaypoint((CHARACTERGUID)_Character, (TRIGGERGUID)_Trigger) (3,0,370,1) +event CharacterTeleportToFleeWaypoint((CHARACTERGUID)_Character, (TRIGGERGUID)_Trigger) (3,0,371,1) +event CharacterStoleItem((CHARACTERGUID)_Character, (ITEMGUID)_Item, (REAL)_X, (REAL)_Y, (REAL)_Z, (CHARACTERGUID)_OldOwner, (ITEMGUID)_SrcContainer, (INTEGER)_Amount) (3,0,372,1) +event CharacterRequestsHomestead((CHARACTERGUID)_Character) (3,0,373,1) +call ItemDragToTrigger((ITEMGUID)_Item, (TRIGGERGUID)_Trigger) (1,0,374,1) +call ItemDragToPosition((ITEMGUID)_Item, (REAL)_X, (REAL)_Y, (REAL)_Z) (1,0,375,1) +call ItemMoveToTrigger((ITEMGUID)_Item, (TRIGGERGUID)_Trigger, (REAL)_Speed, (REAL)_Acceleration, (INTEGER)_UseRotation, (STRING)_Event, (INTEGER)_DoHits) (1,0,376,1) +call ItemMoveToPosition((ITEMGUID)_Item, (REAL)_X, (REAL)_Y, (REAL)_Z, (REAL)_Speed, (REAL)_Acceleration, (STRING)_Event, (INTEGER)_DoHits) (1,0,377,1) +call ItemToTransform((GUIDSTRING)_UUID, (REAL)_X, (REAL)_Y, (REAL)_Z, (REAL)_Pitch, (REAL)_Yaw, (REAL)_Roll, (INTEGER)_Amount, (CHARACTERGUID)_OwnerCharacter) (1,0,378,1) +call ItemToInventory((ITEMGUID)_Item, (GUIDSTRING)_TargetObject, (INTEGER)_Amount, (INTEGER)_ShowNotification, (INTEGER)_ClearOriginalOwner) (1,0,379,1) +call ItemTemplateDropFromCharacter((STRING)_ItemTemplate, (CHARACTERGUID)_Character, (INTEGER)_Count) (1,0,380,1) +call ItemScatterAt((ITEMGUID)_Item, (REAL)_X, (REAL)_Y, (REAL)_Z) (1,0,381,1) +call ItemTemplateRemoveFrom((STRING)_ItemTemplate, (GUIDSTRING)_Object, (INTEGER)_Count) (1,0,382,1) +call ItemTemplateRemoveFromParty((STRING)_ItemTemplate, (CHARACTERGUID)_Character, (INTEGER)_Count) (1,0,383,1) +call ItemTemplateRemoveFromUser((STRING)_ItemTemplate, (CHARACTERGUID)_Character, (INTEGER)_Count) (1,0,384,1) +call ItemTemplateAddTo((STRING)_ItemTemplate, (GUIDSTRING)_Object, (INTEGER)_Count, (INTEGER)_ShowNotification) (1,0,385,1) +query CreateItemTemplateAtPosition([in](STRING)_ItemTemplate, [in](REAL)_X, [in](REAL)_Y, [in](REAL)_Z, [out](ITEMGUID)_Item) (2,0,386,1) +query GetDebugItem([in](INTEGER)_Index, [out](STRING)_Template) (2,0,387,1) +call ItemDrop((ITEMGUID)_Item) (1,0,388,1) +call ItemRemove((ITEMGUID)_Item) (1,0,389,1) +call MoveAllItemsTo((GUIDSTRING)_FromObject, (GUIDSTRING)_ToObject, (INTEGER)_MoveEquippedArmor, (INTEGER)_MoveEquippedWeapons, (INTEGER)_ClearOriginalOwner) (1,0,390,1) +call ContainerIdentifyAll((ITEMGUID)_FromContainer) (1,0,391,1) +query ItemIsInCharacterInventory([in](ITEMGUID)_Item, [in](CHARACTERGUID)_Character, [out](INTEGER)_Bool) (2,0,392,1) +query ItemIsInPartyInventory([in](ITEMGUID)_Item, [in](CHARACTERGUID)_Character, [in](INTEGER)_MoveAndReport, [out](INTEGER)_Bool) (2,0,393,1) +query ItemIsInUserInventory([in](ITEMGUID)_Item, [in](CHARACTERGUID)_Character, [in](INTEGER)_MoveAndReport, [out](INTEGER)_Bool) (2,0,394,1) +query ItemIsInInventory([in](ITEMGUID)_Item, [out](INTEGER)_Bool) (2,0,395,1) +query ItemTemplateIsInCharacterInventory([in](CHARACTERGUID)_Character, [in](STRING)_Template, [out](INTEGER)_Count) (2,0,396,1) +query ItemTagIsInCharacterInventory([in](CHARACTERGUID)_Character, [in](STRING)_Tag, [out](INTEGER)_Count) (2,0,397,1) +query ItemTemplateTagIsInCharacterInventory([in](CHARACTERGUID)_Character, [in](STRING)_Template, [in](STRING)_Tag, [out](INTEGER)_Count) (2,0,398,1) +query ItemTemplateIsInPartyInventory([in](CHARACTERGUID)_Character, [in](STRING)_Template, [in](INTEGER)_MoveAndReport, [out](INTEGER)_Count) (2,0,399,1) +query ItemTemplateIsInUserInventory([in](CHARACTERGUID)_Character, [in](STRING)_Template, [in](INTEGER)_MoveAndReport, [out](INTEGER)_Count) (2,0,400,1) +query ItemTemplateGetDisplayString([in](GUIDSTRING)_Template, [out](STRING)_stringHandle, [out](STRING)_referenceString) (2,0,401,1) +query ItemTemplateIsInContainer([in](ITEMGUID)_Item, [in](STRING)_Template, [out](INTEGER)_Count) (2,0,402,1) +call ItemLock((ITEMGUID)_Item, (STRING)_Key) (1,0,403,1) +call ItemUnLock((ITEMGUID)_Item) (1,0,404,1) +query ItemIsOpened([in](ITEMGUID)_Item, [out](INTEGER)_Opened) (2,0,405,1) +query DoorIsOpening([in](ITEMGUID)_Door, [out](INTEGER)_Opening) (2,0,406,1) +query ItemIsClosed([in](ITEMGUID)_Item, [out](INTEGER)_Closed) (2,0,407,1) +query DoorIsClosing([in](ITEMGUID)_Door, [out](INTEGER)_Closing) (2,0,408,1) +query ItemIsLocked([in](ITEMGUID)_Item, [out](INTEGER)_Locked) (2,0,409,1) +query ItemIsContainer([in](ITEMGUID)_Item, [out](INTEGER)_IsContainer) (2,0,410,1) +query ItemHasOnUse([in](ITEMGUID)_Item, [in](STRING)_OnUse, [out](INTEGER)_Bool) (2,0,411,1) +call ItemLockUnEquip((ITEMGUID)_Item, (INTEGER)_lock) (1,0,412,1) +call ItemIsPoisoned((CHARACTERGUID)_Item, (INTEGER)_IsPoisoned) (1,0,413,1) +query ItemIsTorch([in](ITEMGUID)_Item, [out](INTEGER)_Bool) (2,0,414,1) +query ItemIsEquipable([in](ITEMGUID)_Item, [out](INTEGER)_Bool) (2,0,415,1) +call ItemOpen((ITEMGUID)_Item) (1,0,416,1) +call ItemClose((ITEMGUID)_Item) (1,0,417,1) +call ItemDestroy((ITEMGUID)_Item) (1,0,418,1) +query ItemIsDestroyed([in](ITEMGUID)_Item, [out](INTEGER)_Destroyed) (2,0,419,1) +call ItemClearOwner((ITEMGUID)_Item) (1,0,420,1) +query ItemGetOwner([in](ITEMGUID)_Item, [out](CHARACTERGUID)_Character) (2,0,421,1) +query ItemGetOriginalOwner([in](ITEMGUID)_Item, [out](CHARACTERGUID)_Character) (2,0,422,1) +query GetInventoryOwner([in](ITEMGUID)_Item, [out](GUIDSTRING)_Owner) (2,0,423,1) +call ItemSetCanInteract((ITEMGUID)_Item, (INTEGER)_bool) (1,0,424,1) +call ItemSetCanPickUp((ITEMGUID)_Item, (INTEGER)_bool) (1,0,425,1) +call ItemSetCanMove((ITEMGUID)_Item, (INTEGER)_bool) (1,0,426,1) +call ItemSetOwner((ITEMGUID)_Item, (CHARACTERGUID)_NewOwner) (1,0,427,1) +call ItemSetOriginalOwner((ITEMGUID)_Item, (CHARACTERGUID)_NewOwner) (1,0,428,1) +call ItemSetOnlyOwnerCanUse((ITEMGUID)_Item, (INTEGER)_bool) (1,0,429,1) +call ItemSetStoryItem((ITEMGUID)_Item, (INTEGER)_bool) (1,0,430,1) +call ItemLaunchIterator((STRING)_Event) (1,0,431,1) +query ItemIsStoryItem([in](ITEMGUID)_Item, [out](INTEGER)_Bool) (2,0,432,1) +call ItemCreateAtTrigger((TRIGGERGUID)_Trigger, (STRING)_TemplateId) (1,0,433,1) +call CreateKickstarterMessageInABottleItemAtTrigger((TRIGGERGUID)_Trigger, (STRING)_TemplateId) (1,0,434,1) +call ItemRotateY((ITEMGUID)_Item, (REAL)_Angle, (REAL)_Speed) (1,0,435,1) +call ItemRotateToAngleY((ITEMGUID)_Item, (REAL)_Angle, (REAL)_Speed) (1,0,436,1) +call ItemAddCharges((ITEMGUID)_Item, (INTEGER)_Charges) (1,0,437,1) +query ItemGetCharges([in](ITEMGUID)_Item, [out](INTEGER)_Charges) (2,0,438,1) +call ItemResetChargesToMax((ITEMGUID)_Item) (1,0,439,1) +query ItemGetMaxCharges([in](ITEMGUID)_Item, [out](INTEGER)_InitialCharges) (2,0,440,1) +call ItemSetDurability((ITEMGUID)_Item, (INTEGER)_Durability) (1,0,441,1) +query ItemGetDurability([in](ITEMGUID)_Item, [out](INTEGER)_Durability) (2,0,442,1) +query ItemGetAmount([in](ITEMGUID)_Item, [out](INTEGER)_Amount) (2,0,443,1) +query ItemGetHealthPoints([in](ITEMGUID)_Item, [out](INTEGER)_HP) (2,0,444,1) +query ItemCanSitOn([in](ITEMGUID)_Item, [out](INTEGER)_Bool) (2,0,445,1) +query ItemIsLadder([in](ITEMGUID)_Item, [out](INTEGER)_Bool) (2,0,446,1) +query ItemTemplateCanSitOn([in](STRING)_Template, [out](INTEGER)_Bool) (2,0,447,1) +query ContainerGetGoldValue([in](ITEMGUID)_Container, [out](INTEGER)_Value) (2,0,448,1) +query ItemGetGoldValue([in](ITEMGUID)_Item, [out](INTEGER)_Value) (2,0,449,1) +call ItemSetForceSynch((ITEMGUID)_Item, (INTEGER)_Bool) (1,0,450,1) +call ItemSetKnown((ITEMGUID)_Item, (INTEGER)_Bool) (1,0,451,1) +query GetItemForItemTemplateInInventory([in](CHARACTERGUID)_Character, [in](STRING)_Template, [out](ITEMGUID)_Item) (2,0,452,1) +query GetItemForItemTemplateInPartyInventory([in](CHARACTERGUID)_Character, [in](STRING)_Template, [out](ITEMGUID)_Item) (2,0,453,1) +query CharacterFindTaggedItem([in](CHARACTERGUID)_Character, [in](STRING)_Tag, [out](ITEMGUID)_Item) (2,0,454,1) +query PartyFindTaggedItem([in](CHARACTERGUID)_Character, [in](STRING)_Tag, [in](INTEGER)_MoveAndReport, [out](ITEMGUID)_Item) (2,0,455,1) +query UserFindTaggedItem([in](CHARACTERGUID)_Character, [in](STRING)_Tag, [in](INTEGER)_MoveAndReport, [out](ITEMGUID)_Item) (2,0,456,1) +query CharacterRemoveTaggedLocalItems([in](CHARACTERGUID)_Character, [in](STRING)_Tag, [in](INTEGER)_Amount, [out](INTEGER)_AmountRemoved) (2,0,457,1) +query PartyRemoveTaggedLocalItems([in](CHARACTERGUID)_Character, [in](STRING)_Tag, [in](INTEGER)_Amount, [out](INTEGER)_AmountRemoved) (2,0,458,1) +query UserRemoveTaggedLocalItems([in](CHARACTERGUID)_Character, [in](STRING)_Tag, [in](INTEGER)_Amount, [out](INTEGER)_AmountRemoved) (2,0,459,1) +query UserTransferTaggedLocalItems([in](CHARACTERGUID)_Character, [in](GUIDSTRING)_ToObject, [in](STRING)_Tag, [in](INTEGER)_Amount, [out](INTEGER)_AmountTransfered) (2,0,460,1) +query ItemIsPublicDomain([in](ITEMGUID)_Item, [out](INTEGER)_Bool) (2,0,461,1) +event ItemAddedToCharacter((ITEMGUID)_Item, (CHARACTERGUID)_Character) (3,0,462,1) +event ItemOpened((ITEMGUID)_Item) (3,0,463,1) +event ItemTemplateOpening((STRING)_ItemTemplate, (ITEMGUID)_Item, (CHARACTERGUID)_Character) (3,0,464,1) +event ItemDestroying((ITEMGUID)_Item) (3,0,465,1) +event ItemDestroyed((ITEMGUID)_Item) (3,0,466,1) +event ItemTemplateDestroyed((STRING)_TemplateID, (ITEMGUID)_Item) (3,0,467,1) +event ItemCreatedAtTrigger((TRIGGERGUID)_Trigger, (STRING)_TemplateID, (ITEMGUID)_Item) (3,0,468,1) +event ItemClosed((ITEMGUID)_Item) (3,0,469,1) +event ItemDropped((ITEMGUID)_Item) (3,0,470,1) +event ItemEnteredTrigger((ITEMGUID)_Item, (TRIGGERGUID)_Trigger, (CHARACTERGUID)_Mover) (3,0,471,1) +event ItemTemplateEnteredTrigger((STRING)_ItemTemplate, (ITEMGUID)_Item, (TRIGGERGUID)_Trigger, (CHARACTERGUID)_Owner, (CHARACTERGUID)_Mover) (3,0,472,1) +event ItemLeftTrigger((ITEMGUID)_Item, (TRIGGERGUID)_Trigger, (CHARACTERGUID)_Mover) (3,0,473,1) +event ItemTemplateLeftTrigger((STRING)_ItemTemplate, (ITEMGUID)_Item, (TRIGGERGUID)_Trigger, (CHARACTERGUID)_Owner, (CHARACTERGUID)_Mover) (3,0,474,1) +event ItemAddedToContainer((ITEMGUID)_Item, (ITEMGUID)_Container) (3,0,475,1) +event ItemTemplateAddedToCharacter((GUIDSTRING)_ItemTemplate, (ITEMGUID)_Item, (CHARACTERGUID)_Character) (3,0,476,1) +event ItemTemplateAddedToContainer((STRING)_ItemTemplate, (ITEMGUID)_Item, (ITEMGUID)_Container) (3,0,477,1) +event ItemRemovedFromCharacter((ITEMGUID)_Item, (CHARACTERGUID)_Character) (3,0,478,1) +event ItemTemplateRemovedFromCharacter((STRING)_ItemTemplate, (ITEMGUID)_Item, (CHARACTERGUID)_Character) (3,0,479,1) +event ItemRemovedFromContainer((ITEMGUID)_Item, (ITEMGUID)_Container) (3,0,480,1) +event ItemTemplateRemovedFromContainer((STRING)_ItemTemplate, (ITEMGUID)_Item, (ITEMGUID)_Container) (3,0,481,1) +event ItemEquipped((ITEMGUID)_Item, (CHARACTERGUID)_Character) (3,0,482,1) +event ItemUnEquipped((ITEMGUID)_Item, (CHARACTERGUID)_Character) (3,0,483,1) +event ItemTemplateEquipped((STRING)_ItemTemplate, (CHARACTERGUID)_Character) (3,0,484,1) +event ItemTemplateUnEquipped((STRING)_ItemTemplate, (CHARACTERGUID)_Character) (3,0,485,1) +event ItemTransformed((ITEMGUID)_Item, (STRING)_ItemTemplate) (3,0,486,1) +event ItemMoved((ITEMGUID)_Item) (3,0,487,1) +event ItemTemplateMoved((STRING)_ItemTemplate, (ITEMGUID)_Item) (3,0,488,1) +event ItemDisplayTextEnded((ITEMGUID)_Item, (STRING)_DisplayText) (3,0,489,1) +event ItemTemplateCombinedWithItemTemplate((STRING)_FirstItemTemplate, (STRING)_SecondItemTemplate, (STRING)_ThirdItemTemplate, (STRING)_ForthItemTemplate, (STRING)_FifthItemTemplate, (CHARACTERGUID)_Character, (ITEMGUID)_NewItem) (3,0,490,1) +event ItemStatusAttempt((ITEMGUID)_Item, (STRING)_Status, (GUIDSTRING)_Causee) (3,0,491,1) +event ItemStatusChange((ITEMGUID)_Item, (STRING)_Status, (GUIDSTRING)_Causee) (3,0,492,1) +event ItemStatusRemoved((ITEMGUID)_Item, (STRING)_Status, (GUIDSTRING)_Causee) (3,0,493,1) +event ItemStackedWith((ITEMGUID)_Item, (ITEMGUID)_StackedWithItem) (3,0,494,1) +event ItemMovedFromTo((ITEMGUID)_Item, (GUIDSTRING)_FromObject, (GUIDSTRING)_ToObject, (INTEGER)_IsTrade) (3,0,495,1) +event ItemsScatteredAt((REAL)_X, (REAL)_Y, (REAL)_Z) (3,0,496,1) +event TradeGenerationStarted((CHARACTERGUID)_Trader) (3,0,497,1) +event TradeGenerationEnded((CHARACTERGUID)_Trader) (3,0,498,1) +event ItemUnlocked((ITEMGUID)_Item, (CHARACTERGUID)_Character, (ITEMGUID)_Key) (3,0,499,1) +event ItemUnEquipFailed((ITEMGUID)_Item, (CHARACTERGUID)_Character) (3,0,500,1) +event ItemReceivedDamage((ITEMGUID)_Item) (3,0,501,1) +event ItemWentOnStage((GUIDSTRING)_Item, (INTEGER)_Bool) (3,0,502,1) +event ItemGhostRevealed((ITEMGUID)_Item) (3,0,503,1) +event ItemEnteredRegion((ITEMGUID)_Item, (STRING)_Region) (3,0,504,1) +event ItemLeftRegion((ITEMGUID)_Item, (STRING)_Region) (3,0,505,1) +call ItemLevelUp((ITEMGUID)_Item) (1,0,506,1) +call ItemLevelUpTo((ITEMGUID)_Item, (INTEGER)_Level) (1,0,507,1) +event RuneInserted((CHARACTERGUID)_Character, (ITEMGUID)_Item, (STRING)_RuneItemTemplate, (INTEGER)_Slot) (3,0,508,1) +event RuneRemoved((CHARACTERGUID)_Character, (ITEMGUID)_Item, (ITEMGUID)_Rune, (INTEGER)_Slot) (3,0,509,1) +query ItemGetRuneItemTemplate([in](ITEMGUID)_Item, [in](INTEGER)_Slot, [out](STRING)_Template) (2,0,510,1) +call ItemInsertRune((CHARACTERGUID)_Character, (ITEMGUID)_Item, (STRING)_RuneTemplate, (INTEGER)_Slot) (1,0,511,1) +query ItemRemoveRune([in](CHARACTERGUID)_Character, [in](ITEMGUID)_Item, [in](INTEGER)_Slot, [out](ITEMGUID)_Rune) (2,0,512,1) +call ItemAddDeltaModifier((ITEMGUID)_Item, (STRING)_Boost) (1,0,513,1) +query ItemHasDeltaModifier([in](ITEMGUID)_Item, [in](STRING)_Boost, [out](INTEGER)_Count) (2,0,514,1) +call DialogRequestStopForDialog((STRING)_Dialog, (GUIDSTRING)_Speaker) (1,0,515,1) +call DialogRequestStop((GUIDSTRING)_Speaker) (1,0,516,1) +query DialogGetNumberOfInvolvedPlayers([in](INTEGER)_InstanceID, [out](INTEGER)_Count) (2,0,517,1) +query DialogGetInvolvedPlayer([in](INTEGER)_InstanceID, [in](INTEGER)_Index, [out](GUIDSTRING)_Player) (2,0,518,1) +query DialogGetCategory([in](INTEGER)_InstanceID, [out](STRING)_Category) (2,0,519,1) +query DialogGetNumberOfInvolvedNPCs([in](INTEGER)_InstanceID, [out](INTEGER)_NumberOfNPCs) (2,0,520,1) +query DialogGetInvolvedNPC([in](INTEGER)_InstanceID, [in](INTEGER)_Index, [out](GUIDSTRING)_NPC) (2,0,521,1) +call GlobalSetFlag((STRING)_Flag) (1,0,522,1) +call GlobalClearFlag((STRING)_Flag) (1,0,523,1) +query GlobalGetFlag([in](STRING)_Flag, [out](INTEGER)_FlagState) (2,0,524,1) +call ObjectSetFlag((GUIDSTRING)_Target, (STRING)_Flag, (INTEGER)_DialogInstance) (1,0,525,1) +call ObjectShareFlag((GUIDSTRING)_Target, (STRING)_Flag) (1,0,526,1) +call GlobalShareFlag((STRING)_Flag) (1,0,527,1) +call ObjectClearFlag((GUIDSTRING)_Target, (STRING)_Flag, (INTEGER)_DialogInstance) (1,0,528,1) +query ObjectGetFlag([in](GUIDSTRING)_Target, [in](STRING)_Flag, [out](INTEGER)_FlagState) (2,0,529,1) +call ObjectSetDialogFlag((GUIDSTRING)_Target, (STRING)_Flag, (INTEGER)_DialogInstance) (1,0,530,1) +call ObjectClearDialogFlag((GUIDSTRING)_Target, (STRING)_Flag, (INTEGER)_DialogInstance) (1,0,531,1) +query ObjectGetDialogFlag([in](GUIDSTRING)_Target, [in](STRING)_Flag, [out](INTEGER)_FlagState) (2,0,532,1) +call UserSetFlag((CHARACTERGUID)_Character, (STRING)_Flag, (INTEGER)_DialogInstance) (1,0,533,1) +call UserClearFlag((CHARACTERGUID)_Character, (STRING)_Flag, (INTEGER)_DialogInstance) (1,0,534,1) +query UserGetFlag([in](CHARACTERGUID)_Character, [in](STRING)_Flag, [out](INTEGER)_FlagState) (2,0,535,1) +call PartySetFlag((CHARACTERGUID)_Character, (STRING)_Flag, (INTEGER)_DialogInstance) (1,0,536,1) +call PartyClearFlag((CHARACTERGUID)_Character, (STRING)_Flag, (INTEGER)_DialogInstance) (1,0,537,1) +query PartyGetFlag([in](CHARACTERGUID)_Character, [in](STRING)_Flag, [out](INTEGER)_FlagState) (2,0,538,1) +query DialogGetLocalFlag([in](INTEGER)_DialogInstance, [in](STRING)_LocalEvent, [out](INTEGER)_Value) (2,0,539,1) +event DialogStarted((STRING)_Dialog, (INTEGER)_InstanceID) (3,0,540,1) +event AutomatedDialogStarted((STRING)_Dialog, (INTEGER)_InstanceID) (3,0,541,1) +event DualDialogStart((STRING)_Dialog, (INTEGER)_InstanceID) (3,0,542,1) +event AutomatedDialogEnded((STRING)_Dialog, (INTEGER)_InstanceID) (3,0,543,1) +event DialogEnded((STRING)_Dialog, (INTEGER)_InstanceID) (3,0,544,1) +event DialogRequestFailed((STRING)_Dialog, (INTEGER)_InstanceID) (3,0,545,1) +event DialogActorJoined((STRING)_Dialog, (INTEGER)_InstanceID, (GUIDSTRING)_Actor) (3,0,546,1) +event DialogActorLeft((STRING)_Dialog, (INTEGER)_InstanceID, (GUIDSTRING)_Actor) (3,0,547,1) +event AutomatedDialogRequestFailed((STRING)_Dialog, (INTEGER)_InstanceID) (3,0,548,1) +event DialogStartRequested((GUIDSTRING)_Target, (GUIDSTRING)_Player) (3,0,549,1) +event GlobalFlagSet((STRING)_FlagName) (3,0,550,1) +event GlobalFlagCleared((STRING)_FlagName) (3,0,551,1) +event ObjectFlagSet((STRING)_FlagName, (GUIDSTRING)_Speaker, (INTEGER)_DialogInstance) (3,0,552,1) +event ObjectFlagShared((STRING)_FlagName, (GUIDSTRING)_Speaker, (INTEGER)_NewValue) (3,0,553,1) +event ObjectFlagCleared((STRING)_FlagName, (GUIDSTRING)_Speaker, (INTEGER)_DialogInstance) (3,0,554,1) +event PersuasionResult((CHARACTERGUID)_Character, (INTEGER)_Success, (STRING)_Dialog) (3,0,555,1) +query ObjectIsCharacter([in](GUIDSTRING)_Object, [out](INTEGER)_Bool) (2,0,556,1) +query GetStatString([in](GUIDSTRING)_Object, [out](STRING)_Statname) (2,0,557,1) +query ObjectExists([in](GUIDSTRING)_Object, [out](INTEGER)_Exists) (2,0,558,1) +query ObjectIsGlobal([in](GUIDSTRING)_Object, [out](INTEGER)_IsGlobal) (2,0,559,1) +query ObjectIsItem([in](GUIDSTRING)_Object, [out](INTEGER)_Bool) (2,0,560,1) +query ObjectIsInTrigger([in](GUIDSTRING)_Object, [in](TRIGGERGUID)_Trigger, [out](INTEGER)_Bool) (2,0,561,1) +query PositionIsInTrigger([in](REAL)_x, [in](REAL)_Y, [in](REAL)_Z, [in](TRIGGERGUID)_Trigger, [out](INTEGER)_Bool) (2,0,562,1) +query ObjectIsOnStage([in](GUIDSTRING)_Object, [out](INTEGER)_Bool) (2,0,563,1) +call PlayEffect((GUIDSTRING)_Object, (STRING)_FxName, (STRING)_Bone) (1,0,564,1) +query PlayLoopEffect([in](GUIDSTRING)_Object, [in](STRING)_FxName, [in](STRING)_BoneName, [out](INTEGER64)_FxHandle) (2,0,565,1) +call PlayBeamEffect((GUIDSTRING)_Object, (GUIDSTRING)_Target, (STRING)_FxName, (STRING)_SourceBone, (STRING)_TargetBone) (1,0,566,1) +query PlayLoopBeamEffect([in](GUIDSTRING)_Object, [in](GUIDSTRING)_Target, [in](STRING)_FxName, [in](STRING)_SourceBone, [in](STRING)_TargetBone, [out](INTEGER64)_FxHandle) (2,0,567,1) +call PlaySound((GUIDSTRING)_Object, (STRING)_SoundEvent) (1,0,568,1) +call DebugText((GUIDSTRING)_Object, (STRING)_Text) (1,0,569,1) +call DisplayText((GUIDSTRING)_Object, (STRING)_Text) (1,0,570,1) +call SetOnStage((GUIDSTRING)_Object, (INTEGER)_Bool) (1,0,571,1) +call SetVisible((GUIDSTRING)_Object, (INTEGER)_Bool) (1,0,572,1) +call ShowMapMarker((CHARACTERGUID)_Character, (STRING)_MarkerID, (INTEGER)_Show) (1,0,573,1) +call SetCameraDistanceOverride((REAL)_Distance) (1,0,574,1) +query GetMaxCameraDistance([out](REAL)_Distance) (2,0,575,1) +call TimerLaunch((STRING)_Timer, (INTEGER)_Time) (1,0,576,1) +call TimerCancel((STRING)_Timer) (1,0,577,1) +call TimerPause((STRING)_Timer) (1,0,578,1) +call TimerUnpause((STRING)_Timer) (1,0,579,1) +call MusicPlayForPeer((CHARACTERGUID)_Character, (STRING)_EventName) (1,0,580,1) +call MusicPlayForPeerWithInstrument((CHARACTERGUID)_Character, (CHARACTERGUID)_Character, (STRING)_EventName) (1,0,581,1) +call MusicPlayOnCharacter((CHARACTERGUID)_Character, (STRING)_EventName) (1,0,582,1) +call MusicPlayGeneral((STRING)_EventName) (1,0,583,1) +call MoviePlay((CHARACTERGUID)_Character, (STRING)_Event) (1,0,584,1) +call PlayMovieForDialog((CHARACTERGUID)_Character, (STRING)_DialogName, (STRING)_NodePrefix) (1,0,585,1) +call CameraActivate((CHARACTERGUID)_Character, (STRING)_Name, (REAL)_Time, (INTEGER)_HideUI, (INTEGER)_Smooth, (INTEGER)_HideShroud) (1,0,586,1) +call QuestUpdate((CHARACTERGUID)_Character, (STRING)_Quest, (STRING)_Status) (1,0,587,1) +call QuestReceiveSharedUpdate((CHARACTERGUID)_SrcCharacter, (CHARACTERGUID)_Character, (STRING)_Quest, (STRING)_Status) (1,0,588,1) +query QuestUpdateExists([in](STRING)_Quest, [in](STRING)_Status, [out](INTEGER)_Result) (2,0,589,1) +call QuestAdd((CHARACTERGUID)_Character, (STRING)_Quest) (1,0,590,1) +query QuestAccepted([in](CHARACTERGUID)_Character, [in](STRING)_Quest, [out](INTEGER)_Bool) (2,0,591,1) +call QuestClose((CHARACTERGUID)_Character, (STRING)_Quest) (1,0,592,1) +call QuestCloseAll((STRING)_Quest) (1,0,593,1) +query QuestIsShared([in](CHARACTERGUID)_Character, [in](STRING)_Quest, [out](INTEGER)_Bool) (2,0,594,1) +call SetInArena((GUIDSTRING)_Target, (INTEGER)_InArena) (1,0,595,1) +query IsInArena([in](GUIDSTRING)_Target, [out](INTEGER)_Bool) (2,0,596,1) +query QuestIsClosed([in](CHARACTERGUID)_Character, [in](STRING)_Quest, [out](INTEGER)_Bool) (2,0,597,1) +query QuestGetBroadcastLevel([in](STRING)_Quest, [out](STRING)_Level) (2,0,598,1) +call QuestSetCategory((STRING)_Quest, (STRING)_CategoryID) (1,0,599,1) +call QuestArchive((CHARACTERGUID)_Character, (STRING)_Quest, (INTEGER)_DoArchive) (1,0,600,1) +query QuestHasUpdate([in](CHARACTERGUID)_Character, [in](STRING)_Quest, [in](STRING)_Update, [out](INTEGER)_Bool) (2,0,601,1) +call QuestArchiveCategory((CHARACTERGUID)_Character, (STRING)_CategoryID, (INTEGER)_DoArchive) (1,0,602,1) +call AddSecret((CHARACTERGUID)_Character, (STRING)_Secret) (1,0,603,1) +call UnlockJournalRecipe((STRING)_RecipeID) (1,0,604,1) +call GameEnd() (1,0,605,1) +call GameEndWithMovie((STRING)_Movie) (1,0,606,1) +call GameEndWithMovieRequestCallback((STRING)_CallbackID) (1,0,607,1) +call EnqueueGameEndMovie((INTEGER)_UserID, (STRING)_Movie, (STRING)_Music) (1,0,608,1) +call EnqueueGameEndDialogMovie((CHARACTERGUID)_Character, (STRING)_DialogName, (STRING)_PlaylistId, (STRING)_Music) (1,0,609,1) +call FinalizeGameEndMovieQueue((INTEGER)_UserID) (1,0,610,1) +call SetGameEndMovie((INTEGER)_UserID, (STRING)_Movie) (1,0,611,1) +call ShroudRender((INTEGER)_Enable) (1,0,612,1) +call DebugBreak((STRING)_Message) (1,0,613,1) +call FadeToBlack((CHARACTERGUID)_Character, (REAL)_Seconds, (INTEGER)_ToBlack, (STRING)_FadeID) (1,0,614,1) +call FadeToWhite((CHARACTERGUID)_Character, (REAL)_Seconds, (INTEGER)_ToWhite, (STRING)_FadeID) (1,0,615,1) +call FadeOutBlack((CHARACTERGUID)_Character, (REAL)_Seconds, (STRING)_FadeID) (1,0,616,1) +call FadeOutWhite((CHARACTERGUID)_Character, (REAL)_Seconds, (STRING)_FadeID) (1,0,617,1) +call FadeIn((CHARACTERGUID)_Character, (REAL)_Seconds, (STRING)_FadeID) (1,0,618,1) +call OpenCustomBookUI((CHARACTERGUID)_Character, (STRING)_Bookname) (1,0,619,1) +call AddEntryToCustomBook((STRING)_Bookname, (STRING)_Entryname) (1,0,620,1) +call RemoveEntryFromCustomBook((STRING)_Bookname, (STRING)_Entryname) (1,0,621,1) +call OpenWaypointUI((CHARACTERGUID)_Character, (STRING)_CurrentWaypoint, (ITEMGUID)_Item, (INTEGER)_IsFleeing) (1,0,622,1) +call CloseUI((CHARACTERGUID)_Character, (STRING)_UIName) (1,0,623,1) +call OpenCraftUI((CHARACTERGUID)_Character, (ITEMGUID)_Item) (1,0,624,1) +call UnlockWaypoint((STRING)_WaypointName, (TRIGGERGUID)_Trigger, (CHARACTERGUID)_Character) (1,0,625,1) +call LockWaypoint((STRING)_WaypointName, (CHARACTERGUID)_Character) (1,0,626,1) +call RegisterWaypoint((STRING)_WaypointName, (ITEMGUID)_Item) (1,0,627,1) +call UnlockSecretRegion((TRIGGERGUID)_SecretRegionTrigger) (1,0,628,1) +call LockSecretRegion((TRIGGERGUID)_SecretRegionTrigger) (1,0,629,1) +call CreateSurface((GUIDSTRING)_Source, (STRING)_SurfaceType, (REAL)_Radius, (REAL)_Lifetime) (1,0,630,1) +call CreateSurfaceAtPosition((REAL)_x, (REAL)_Y, (REAL)_Z, (STRING)_SurfaceType, (REAL)_Radius, (REAL)_Lifetime) (1,0,631,1) +call CreatePuddle((GUIDSTRING)_Source, (STRING)_SurfaceType, (INTEGER)_CellAmountMin, (INTEGER)_CellAmountMax, (INTEGER)_GrowAmountMin, (INTEGER)_GrowAmountMax, (REAL)_GrowTime) (1,0,632,1) +call TransformSurface((GUIDSTRING)_Source, (STRING)_TransformType, (STRING)_TransformLayer, (REAL)_Radius, (REAL)_Lifetime, (GUIDSTRING)_Owner) (1,0,633,1) +call TransformSurfaceAtPosition((REAL)_X, (REAL)_Y, (REAL)_Z, (STRING)_TransformType, (STRING)_TransformLayer, (REAL)_Radius, (REAL)_Lifetime, (GUIDSTRING)_Owner) (1,0,634,1) +query GetSurfaceGroundAt([in](GUIDSTRING)_Target, [out](STRING)_Surface) (2,0,635,1) +query GetSurfaceCloudAt([in](GUIDSTRING)_Target, [out](STRING)_Surface) (2,0,636,1) +query GetSurfaceGroundOwnerAt([in](GUIDSTRING)_Target, [out](CHARACTERGUID)_OwnerCharacter, [out](ITEMGUID)_OwnerItem) (2,0,637,1) +query GetSurfaceCloudOwnerAt([in](GUIDSTRING)_Target, [out](GUIDSTRING)_OwnerCharacter, [out](GUIDSTRING)_OwnerItem) (2,0,638,1) +query GetSurfaceTypeIndex([in](STRING)_Surface, [out](INTEGER)_Index) (2,0,639,1) +query GetSurfaceNameByTypeIndex([in](INTEGER)_SurfaceIndex, [out](STRING)_SurfaceName) (2,0,640,1) +query DrawSurfaceOnPath([in](GUIDSTRING)_OwnerObject, [in](GUIDSTRING)_FollowObject, [in](STRING)_SurfaceType, [in](REAL)_Radius, [in](REAL)_LifeTime, [out](INTEGER64)_SurfaceActionHandle) (2,0,641,1) +call StopDrawSurfaceOnPath((INTEGER64)_SurfaceActionHandle) (1,0,642,1) +query GetSurfaceSize([in](GUIDSTRING)_Target, [in](INTEGER)_SurfaceLayer, [out](INTEGER)_SurfaceSize) (2,0,643,1) +query GetSurfaceTurns([in](GUIDSTRING)_Target, [in](INTEGER)_SurfaceLayer, [out](INTEGER)_SurfaceTurns) (2,0,644,1) +call PlayEffectAtPosition((STRING)_FxName, (REAL)_X, (REAL)_Y, (REAL)_Z) (1,0,645,1) +call PlayEffectAtPositionAndRotation((STRING)_FxName, (REAL)_X, (REAL)_Y, (REAL)_Z, (REAL)_Yangle) (1,0,646,1) +call PlayScaledEffectAtPosition((STRING)_FxName, (REAL)_Scale, (REAL)_X, (REAL)_Y, (REAL)_Z) (1,0,647,1) +call EndCombat((INTEGER)_CombatID) (1,0,648,1) +call StopLoopEffect((INTEGER64)_FxHandle) (1,0,649,1) +call MakePlayerActive((CHARACTERGUID)_Target) (1,0,650,1) +query CombatGetNumberOfInvolvedPlayers([in](INTEGER)_CombatID, [out](INTEGER)_NumPlayers) (2,0,651,1) +query CombatGetNumberOfInvolvedPartyMembers([in](INTEGER)_CombatID, [out](INTEGER)_NumPartyMembers) (2,0,652,1) +query CombatGetInvolvedPlayer([in](INTEGER)_CombatID, [in](INTEGER)_PlayerIndex, [out](CHARACTERGUID)_Player) (2,0,653,1) +query CombatGetInvolvedPartyMember([in](INTEGER)_CombatID, [in](INTEGER)_PartyMemberIndex, [out](CHARACTERGUID)_PartyMember) (2,0,654,1) +query CombatGetIDForCharacter([in](CHARACTERGUID)_Player, [out](INTEGER)_CombatID) (2,0,655,1) +query IsCombatActive([in](INTEGER)_CombatID, [out](INTEGER)_Active) (2,0,656,1) +query CombatGetActiveEntity([in](INTEGER)_CombatID, [out](GUIDSTRING)_CurrentEntity) (2,0,657,1) +call CreateExplosion((GUIDSTRING)_Target, (STRING)_SkillID, (INTEGER)_CasterLevel) (1,0,658,1) +call CreateExplosionAtPosition((REAL)_X, (REAL)_Y, (REAL)_Z, (STRING)_SkillID, (INTEGER)_CasterLevel) (1,0,659,1) +query GetMultiplayerCharacter([in](CHARACTERGUID)_Character, [out](CHARACTERGUID)_MultiplayerCharacter) (2,0,660,1) +call AutoSave() (1,0,661,1) +call ShowGameOverMenu() (1,0,662,1) +call OnCompanionDismissed((CHARACTERGUID)_Character) (1,0,663,1) +call NotifyGameProgress((REAL)_Progress) (1,0,664,1) +call ShowNotification((STRING)_Text) (1,0,665,1) +call ShowError((CHARACTERGUID)_Character, (STRING)_Error) (1,0,666,1) +call ShowTutorial((CHARACTERGUID)_Character, (STRING)_Text, (STRING)_Category, (STRING)_Title, (INTEGER)_ControllerType, (INTEGER)_ModalType, (INTEGER)_Duration, (INTEGER)_Priority, (INTEGER)_Flags, (INTEGER)_MinimumPlaytimeInMinutes) (1,0,667,1) +call CompleteTutorial((CHARACTERGUID)_Character, (STRING)_Text) (1,0,668,1) +call UnlockAchievement((STRING)_AchievementID, (CHARACTERGUID)_Character) (1,0,669,1) +call ProgressAchievement((STRING)_AchievementID, (CHARACTERGUID)_Character, (INTEGER)_Progress) (1,0,670,1) +call SetAchievementProgress((STRING)_AchievementID, (INTEGER)_Value) (1,0,671,1) +call SetHomesteadKeyState((INTEGER)_State, (CHARACTERGUID)_Character) (1,0,672,1) +call EnableSendToHomestead((INTEGER)_Bool) (1,0,673,1) +call KillCombatFor((CHARACTERGUID)_Character) (1,0,674,1) +query HasKickstarterDialogReward([out](INTEGER)_Bool) (2,0,675,1) +query IsHardcoreMode([out](INTEGER)_Bool) (2,0,676,1) +query CharacterHasDLC([in](CHARACTERGUID)_Character, [in](INTEGER)_DLCId, [out](INTEGER)_HasDLC) (2,0,677,1) +query GetUserCount([out](INTEGER)_UserCount) (2,0,678,1) +call GenerateTreasure((ITEMGUID)_Item, (STRING)_TreasureID, (INTEGER)_Level, (CHARACTERGUID)_Character) (1,0,679,1) +call PlayAnimation((GUIDSTRING)_SourceObject, (STRING)_Animation, (STRING)_Event) (1,0,680,1) +query GetVarObject([in](GUIDSTRING)_Source, [in](STRING)_VarName, [out](GUIDSTRING)_UUID) (2,0,681,1) +call SetVarObject((GUIDSTRING)_Source, (STRING)_VarName, (GUIDSTRING)_Object) (1,0,682,1) +call ClearVarObject((GUIDSTRING)_Source, (STRING)_VarName) (1,0,683,1) +call SetScriptframe((GUIDSTRING)_Target, (STRING)_Scriptframe) (1,0,684,1) +call ClearScriptframe((GUIDSTRING)_Target) (1,0,685,1) +call SetTag((GUIDSTRING)_Source, (STRING)_Tag) (1,0,686,1) +call ClearTag((GUIDSTRING)_Source, (STRING)_Tag) (1,0,687,1) +query GetVarInteger([in](GUIDSTRING)_Source, [in](STRING)_VarName, [out](INTEGER)_VarValue) (2,0,688,1) +query GetVarFloat([in](GUIDSTRING)_Source, [in](STRING)_VarName, [out](REAL)_VarValue) (2,0,689,1) +query GetVarString([in](GUIDSTRING)_Source, [in](STRING)_VarName, [out](STRING)_VarValue) (2,0,690,1) +query GetVarFixedString([in](GUIDSTRING)_Source, [in](STRING)_VarName, [out](STRING)_VarValue) (2,0,691,1) +call SetVarInteger((GUIDSTRING)_Target, (STRING)_VarName, (INTEGER)_VarValue) (1,0,692,1) +call SetVarFloat((GUIDSTRING)_Target, (STRING)_VarName, (REAL)_VarValue) (1,0,693,1) +call SetVarString((GUIDSTRING)_Target, (STRING)_VarName, (STRING)_VarValue) (1,0,694,1) +call SetVarFixedString((GUIDSTRING)_Target, (STRING)_VarName, (STRING)_VarValue) (1,0,695,1) +call SetVarFloat3((GUIDSTRING)_Target, (STRING)_VarName, (REAL)_X, (REAL)_Y, (REAL)_Z) (1,0,696,1) +query GetVarFloat3([in](GUIDSTRING)_Target, [in](STRING)_VarName, [out](REAL)_X, [out](REAL)_Y, [out](REAL)_Z) (2,0,697,1) +query IsTagged([in](GUIDSTRING)_Target, [in](STRING)_Tag, [out](INTEGER)_Bool) (2,0,698,1) +call RemoveStatus((GUIDSTRING)_Target, (STRING)_Status) (1,0,699,1) +call RemoveHarmfulStatuses((GUIDSTRING)_Target) (1,0,700,1) +query HasActiveStatus([in](GUIDSTRING)_Target, [in](STRING)_Status, [out](INTEGER)_Bool) (2,0,701,1) +query HasAppliedStatus([in](GUIDSTRING)_Target, [in](STRING)_Status, [out](INTEGER)_Bool) (2,0,702,1) +query GetUUID([in](GUIDSTRING)_Target, [out](STRING)_UUID) (2,0,703,1) +query GetPosition([in](GUIDSTRING)_Target, [out](REAL)_X, [out](REAL)_Y, [out](REAL)_Z) (2,0,704,1) +query GetRotation([in](GUIDSTRING)_Target, [out](REAL)_X, [out](REAL)_Y, [out](REAL)_Z) (2,0,705,1) +query GetClosestPlayer([in](GUIDSTRING)_Target, [out](CHARACTERGUID)_Player, [out](REAL)_Distance) (2,0,706,1) +query GetClosestPlayerWithTalent([in](GUIDSTRING)_Target, [in](STRING)_Talent, [out](CHARACTERGUID)_Player, [out](REAL)_Distance) (2,0,707,1) +query GetClosestAlivePlayer([in](GUIDSTRING)_Target, [out](CHARACTERGUID)_Player, [out](REAL)_Distance) (2,0,708,1) +query GetClosestAliveUserPlayer([in](GUIDSTRING)_Target, [in](INTEGER)_UserID, [out](CHARACTERGUID)_Player, [out](REAL)_Distance) (2,0,709,1) +query GetClosestPlayerToPosition([in](REAL)_X, [in](REAL)_Y, [in](REAL)_Z, [out](CHARACTERGUID)_Player, [out](REAL)_Distance) (2,0,710,1) +call Transform((GUIDSTRING)_Object, (STRING)_ObjectTemplate, (INTEGER)_ReplaceScripts, (INTEGER)_ReplaceScale, (INTEGER)_ReplaceStats) (1,0,711,1) +call ApplyStatus((GUIDSTRING)_Object, (STRING)_Status, (REAL)_Duration, (INTEGER)_Force, (GUIDSTRING)_Source) (1,0,712,1) +query GetRegion([in](GUIDSTRING)_Object, [out](STRING)_Region) (2,0,713,1) +call SetStoryEvent((GUIDSTRING)_Object, (STRING)_Event) (1,0,714,1) +call RequestProcessed((CHARACTERGUID)_Character, (INTEGER)_RequestId, (INTEGER)_RequestAccepted) (1,0,715,1) +call IterateParty((CHARACTERGUID)_Character, (STRING)_Event) (1,0,716,1) +call IterateParties((STRING)_Event) (1,0,717,1) +call IterateUsers((STRING)_Event) (1,0,718,1) +query GetUserName([in](INTEGER)_UserId, [out](STRING)_UserName) (2,0,719,1) +query GetUserProfileID([in](INTEGER)_UserId, [out](STRING)_UserProfileID) (2,0,720,1) +call LoadGame((STRING)_Savegame) (1,0,721,1) +call LeaveParty((INTEGER)_UserId) (1,0,722,1) +call AddToParty((INTEGER)_Source, (INTEGER)_Target) (1,0,723,1) +call LoadPartyPreset((STRING)_Preset, (GUIDSTRING)_TeleportToTarget) (1,0,724,1) +query CrimeGetType([in](INTEGER)_CrimeID, [out](STRING)_Type) (2,0,725,1) +query CrimeGetDetectionRange([in](INTEGER)_CrimeID, [out](REAL)_Range) (2,0,726,1) +query CrimeGetTension([in](INTEGER)_CrimeID, [out](INTEGER)_Tension) (2,0,727,1) +query CrimeGetNewID([out](INTEGER)_CrimeID) (2,0,728,1) +query CrimeIsTensionOverWarningTreshold([in](CHARACTERGUID)_Character, [out](INTEGER)_Bool) (2,0,729,1) +call CrimeClearAll() (1,0,730,1) +query CrimeFindEvidence([in](INTEGER)_CrimeID, [in](CHARACTERGUID)_Searcher, [in](CHARACTERGUID)_Criminal1, [in](CHARACTERGUID)_Criminal2, [in](CHARACTERGUID)_Criminal3, [in](CHARACTERGUID)_Criminal4, [out](INTEGER)_EvidenceFoundForCurrentCrime, [out](INTEGER)_EvidenceFound, [out](INTEGER)_GuiltyFound) (2,0,731,1) +call CrimeInterrogationDone((INTEGER)_CrimeID, (CHARACTERGUID)_Interrogator, (INTEGER)_FoundEvidence, (CHARACTERGUID)_Criminal1, (CHARACTERGUID)_Criminal2, (CHARACTERGUID)_Criminal3, (CHARACTERGUID)_Criminal4) (1,0,732,1) +call CrimeConfrontationDone((INTEGER)_CrimeID, (CHARACTERGUID)_Interrogator) (1,0,733,1) +query CrimeGetLeadInvestigator([in](INTEGER)_CrimeID, [out](CHARACTERGUID)_LeadInvestigator) (2,0,734,1) +call SetCanFight((GUIDSTRING)_Target, (INTEGER)_Enabled) (1,0,735,1) +call SetCanJoinCombat((GUIDSTRING)_Target, (INTEGER)_Enabled) (1,0,736,1) +call InventoryLaunchIterator((GUIDSTRING)_InventoryHolder, (STRING)_Event, (STRING)_CompletionEvent) (1,0,737,1) +call InventoryLaunchTagIterator((GUIDSTRING)_InventoryHolder, (STRING)_TagA, (STRING)_OptionalTagB, (STRING)_Event, (STRING)_CompletionEvent) (1,0,738,1) +call InventoryLaunchTemplateIterator((GUIDSTRING)_InventoryHolder, (STRING)_Template, (STRING)_Event, (STRING)_CompletionEvent) (1,0,739,1) +call SetIsBoss((GUIDSTRING)_Target, (INTEGER)_Enabled) (1,0,740,1) +query IsBoss([in](GUIDSTRING)_Target, [out](INTEGER)_Bool) (2,0,741,1) +query GetDistanceTo([in](GUIDSTRING)_object1, [in](GUIDSTRING)_object2, [out](REAL)_Dist) (2,0,742,1) +query GetDistanceToPosition([in](GUIDSTRING)_Object, [in](REAL)_X, [in](REAL)_Y, [in](REAL)_Z, [out](REAL)_Dist) (2,0,743,1) +query GetAngleTo([in](REAL)_x0, [in](REAL)_z0, [in](REAL)_x1, [in](REAL)_z1, [out](INTEGER)_Angle) (2,0,744,1) +call SetCombatGroupID((GUIDSTRING)_Target, (STRING)_GroupID) (1,0,745,1) +call EndTurn((GUIDSTRING)_Target) (1,0,746,1) +call CrimeAreaSetTensionModifier((TRIGGERGUID)_CrimeArea, (INTEGER)_Modifier) (1,0,747,1) +query CrimeAreaGetTensionModifier([in](TRIGGERGUID)_CrimeArea, [out](INTEGER)_Modifier) (2,0,748,1) +call CrimeAreaResetTensionModifier((TRIGGERGUID)_CrimeArea) (1,0,749,1) +query CrimeGetVictim([in](INTEGER)_CrimeID, [out](CHARACTERGUID)_CrimeVictim) (2,0,750,1) +call CrimeTransferEvidenceTo((INTEGER)_CrimeID, (GUIDSTRING)_Target) (1,0,751,1) +query CrimeGetEvidence([in](INTEGER)_CrimeID, [in](INTEGER)_Index, [out](GUIDSTRING)_Evidence) (2,0,752,1) +query CrimeGetNumberOfEvidence([in](INTEGER)_CrimeID, [out](INTEGER)_NumEvidence) (2,0,753,1) +query CrimeIsContinuous([in](INTEGER)_CrimeID, [out](INTEGER)_Bool) (2,0,754,1) +call ShutdownCrimeSystem() (1,0,755,1) +query CrimeGetCriminals([in](INTEGER)_CrimeID, [out](CHARACTERGUID)_Criminal1, [out](CHARACTERGUID)_Criminal2, [out](CHARACTERGUID)_Criminal3, [out](CHARACTERGUID)_Criminal4) (2,0,756,1) +query CrimeIsAnyNPCGoingToReact([in](GUIDSTRING)_Criminal, [in](STRING)_CrimeType, [in](REAL)_X, [in](REAL)_Y, [in](REAL)_Z, [out](INTEGER)_Bool) (2,0,757,1) +query CrimeTransferLeadershipTo([in](GUIDSTRING)_OldLead, [in](INTEGER)_CrimeID, [in](GUIDSTRING)_NewLead, [out](INTEGER)_Bool) (2,0,758,1) +call CrimeEnableInterrogation((GUIDSTRING)_NPC, (INTEGER)_Bool) (1,0,759,1) +call CrimeIgnoreAllCrimesForCriminal((GUIDSTRING)_Criminal, (GUIDSTRING)_NPC, (INTEGER)_IgnoreDuration) (1,0,760,1) +call CrimeResetInterrogationForCriminals((INTEGER)_CrimeID, (GUIDSTRING)_Criminal1, (GUIDSTRING)_Criminal2, (GUIDSTRING)_Criminal3, (GUIDSTRING)_Criminal4) (1,0,761,1) +call JumpToTurn((GUIDSTRING)_Target) (1,0,762,1) +call CrimeIgnoreCrime((INTEGER)_CrimeID, (GUIDSTRING)_NPC) (1,0,763,1) +query CrimeAddCriminal([in](INTEGER)_CrimeID, [in](GUIDSTRING)_Criminal, [out](INTEGER)_Bool) (2,0,764,1) +call NotifyCharacterCreationFinished() (1,0,765,1) +call SetHasDialog((GUIDSTRING)_Speaker, (INTEGER)_Dialog) (1,0,766,1) +call ShowArenaResult((INTEGER)_WinnerTeamId) (1,0,767,1) +call EnterCombat((GUIDSTRING)_Source, (GUIDSTRING)_Target) (1,0,768,1) +call LeaveCombat((GUIDSTRING)_Target) (1,0,769,1) +call SetFaction((GUIDSTRING)_Target, (STRING)_Faction) (1,0,770,1) +query GetFaction([in](GUIDSTRING)_Target, [out](STRING)_Faction) (2,0,771,1) +query GetTemplate([in](GUIDSTRING)_Target, [out](STRING)_Template) (2,0,772,1) +call SetInvulnerable_UseProcSetInvulnerable((GUIDSTRING)_Target, (INTEGER)_Bool) (1,0,773,1) +call UserRest((CHARACTERGUID)_Character, (STRING)_Restconsumable, (REAL)_PartyRadius, (REAL)_MinSafeDistance) (1,0,774,1) +call FireOsirisEvents() (1,0,775,1) +query IsSkillActive([in](GUIDSTRING)_Character, [in](STRING)_SkillID, [out](INTEGER)_Bool) (2,0,776,1) +call StartCameraSpline((SPLINEGUID)_Spline, (CHARACTERGUID)_Character, (REAL)_FadeTime, (INTEGER)_HideUI, (INTEGER)_Freeze, (INTEGER)_StartIndex) (1,0,777,1) +call StopCameraSpline((SPLINEGUID)_Spline, (CHARACTERGUID)_Character) (1,0,778,1) +call ApplyDamage((GUIDSTRING)_Object, (INTEGER)_Damage, (STRING)_DamageType, (GUIDSTRING)_Source) (1,0,779,1) +query IsSourceSkill([in](STRING)_SkillID, [out](INTEGER)_Bool) (2,0,780,1) +call MakePeace((CHARACTERGUID)_Source, (CHARACTERGUID)_Target, (INTEGER)_IgnoreVote) (1,0,781,1) +call MakeWar((CHARACTERGUID)_Source, (CHARACTERGUID)_Target, (INTEGER)_IgnoreVote) (1,0,782,1) +call ActivatePersistentLevelTemplateWithCombat((LEVELTEMPLATEGUID)_LevelTemplate) (1,0,783,1) +call ActivatePersistentLevelTemplate((LEVELTEMPLATEGUID)_LevelTemplate) (1,0,784,1) +query GetStatusType([in](STRING)_StatusID, [out](STRING)_StatusType) (2,0,785,1) +query GetStatusTurns([in](GUIDSTRING)_Source, [in](STRING)_StatusID, [out](INTEGER)_Turns) (2,0,786,1) +query GetHealStat([in](STRING)_StatusID, [out](STRING)_HealStat) (2,0,787,1) +call ReadyCheckStart((GUIDSTRING)_Player, (STRING)_Id) (1,0,788,1) +query IsGameLevel([in](STRING)_LevelName, [out](INTEGER)_Bool) (2,0,789,1) +query IsCharacterCreationLevel([in](STRING)_LevelName, [out](INTEGER)_Bool) (2,0,790,1) +query HasRecipeUnlockedWithIngredient([in](CHARACTERGUID)_Player, [in](GUIDSTRING)_ItemTemplate, [out](INTEGER)_Bool) (2,0,791,1) +query GetGlobalPriceModifier([out](INTEGER)_Modifier) (2,0,792,1) +call SetGlobalPriceModifier((INTEGER)_Modifier) (1,0,793,1) +event TimerFinished((STRING)_Timer) (3,0,794,1) +event RegionStarted((STRING)_NewRegion) (3,0,795,1) +event GameStarted((STRING)_LevelName, (INTEGER)_IsEditorMode) (3,0,796,1) +event RegionEnded((STRING)_Region) (3,0,797,1) +event CreditsEnded() (3,0,798,1) +event DLCUnlocked((INTEGER)_DLCId) (3,0,799,1) +event CharacterCreationStarted((STRING)_Level) (3,0,800,1) +event CharacterCreationFinished((CHARACTERGUID)_Character) (3,0,801,1) +event GameBookInterfaceClosed((ITEMGUID)_Item, (CHARACTERGUID)_Character) (3,0,802,1) +event SavegameLoaded((INTEGER)_Major, (INTEGER)_Minor, (INTEGER)_Revision, (INTEGER)_Build) (3,0,803,1) +event MovieFinished((STRING)_MovieName) (3,0,804,1) +event MoviePlaylistFinished((STRING)_MovieName) (3,0,805,1) +event DifficultyChanged((INTEGER)_DifficultyLevel) (3,0,806,1) +event GameModeStarted((STRING)_GameMode, (INTEGER)_IsEditorMode) (3,0,807,1) +event StoryEvent((GUIDSTRING)_Object, (STRING)_Event) (3,0,808,1) +event CanMoveItem((CHARACTERGUID)_Character, (ITEMGUID)_Item, (INTEGER)_RequestID) (3,0,809,1) +event CanPickupItem((CHARACTERGUID)_Character, (ITEMGUID)_Item, (INTEGER)_RequestID) (3,0,810,1) +event CanUseItem((CHARACTERGUID)_Character, (ITEMGUID)_Item, (INTEGER)_RequestID) (3,0,811,1) +event CanLockpickItem((CHARACTERGUID)_Character, (ITEMGUID)_Item, (INTEGER)_RequestID) (3,0,812,1) +event CanCombineItem((CHARACTERGUID)_Character, (ITEMGUID)_ItemA, (ITEMGUID)_ItemB, (ITEMGUID)_ItemC, (ITEMGUID)_ItemD, (ITEMGUID)_ItemE, (INTEGER)_RequestID) (3,0,813,1) +event UserConnected((INTEGER)_UserID, (STRING)_UserName, (STRING)_UserProfileID) (3,0,814,1) +event FadeOutDone((INTEGER)_UserID, (STRING)_FadeID) (3,0,815,1) +event FadeInDone((INTEGER)_UserID, (STRING)_FadeID) (3,0,816,1) +event FadeDone((INTEGER)_UserID, (STRING)_FadeID) (3,0,817,1) +event ClearFadeDone((INTEGER)_UserID, (STRING)_FadeID) (3,0,818,1) +event UserDisconnected((INTEGER)_UserID, (STRING)_UserName, (STRING)_UserProfileID) (3,0,819,1) +event UserEvent((INTEGER)_UserID, (STRING)_UserEvent) (3,0,820,1) +event UserMakeWar((INTEGER)_SourceUserID, (INTEGER)_TargetUserID, (INTEGER)_War) (3,0,821,1) +event AttackedByObject((GUIDSTRING)_Defender, (GUIDSTRING)_AttackerOwner, (GUIDSTRING)_Attacker, (STRING)_DamageType, (STRING)_DamageSource) (3,0,822,1) +event FleeCombat((CHARACTERGUID)_Character) (3,0,823,1) +event CrimeInterrogationRequest((STRING)_CrimeRegion, (INTEGER)_CrimeID, (CHARACTERGUID)_Interrogator, (CHARACTERGUID)_Criminal1, (CHARACTERGUID)_Criminal2, (CHARACTERGUID)_Criminal3, (CHARACTERGUID)_Criminal4, (STRING)_InterrogateDialog) (3,0,824,1) +event OnCrimeResolved((INTEGER)_CrimeID, (CHARACTERGUID)_Victim, (CHARACTERGUID)_Criminal1, (CHARACTERGUID)_Criminal2, (CHARACTERGUID)_Criminal3, (CHARACTERGUID)_Criminal4) (3,0,825,1) +event OnCrimeRemoved((INTEGER)_CrimeID, (CHARACTERGUID)_Victim, (CHARACTERGUID)_Criminal1, (CHARACTERGUID)_Criminal2, (CHARACTERGUID)_Criminal3, (CHARACTERGUID)_Criminal4) (3,0,826,1) +event OnCrimeConfrontationDone((INTEGER)_CrimeID, (CHARACTERGUID)_Investigator, (INTEGER)_WasLead, (CHARACTERGUID)_Criminal1, (CHARACTERGUID)_Criminal2, (CHARACTERGUID)_Criminal3, (CHARACTERGUID)_Criminal4) (3,0,827,1) +event OnCriminalMergedWithCrime((INTEGER)_CrimeID, (CHARACTERGUID)_Criminal) (3,0,828,1) +event OnCrimeMergedWith((INTEGER)_OldCrimeID, (INTEGER)_NewCrimeID) (3,0,829,1) +event OnCrimeSawCriminalInCombat((INTEGER)_CrimeID, (CHARACTERGUID)_Character, (CHARACTERGUID)_Criminal1) (3,0,830,1) +event OnArenaRoundStarted((STRING)_ArenaMode) (3,0,831,1) +event OnArenaRoundForceEnded() (3,0,832,1) +event ObjectWasTagged((GUIDSTRING)_Object, (STRING)_Tag) (3,0,833,1) +event ObjectLostTag((GUIDSTRING)_Object, (STRING)_Tag) (3,0,834,1) +event ObjectEnteredCombat((GUIDSTRING)_Object, (INTEGER)_CombatID) (3,0,835,1) +event CombatStarted((INTEGER)_CombatID) (3,0,836,1) +event CombatRoundStarted((INTEGER)_CombatID, (INTEGER)_Round) (3,0,837,1) +event CombatEnded((INTEGER)_CombatID) (3,0,838,1) +event ObjectSwitchedCombat((GUIDSTRING)_Object, (INTEGER)_OldCombatID, (INTEGER)_NewCombatID) (3,0,839,1) +event ObjectLeftCombat((GUIDSTRING)_Object, (INTEGER)_CombatID) (3,0,840,1) +event ObjectReadyInCombat((GUIDSTRING)_Object, (INTEGER)_CombatID) (3,0,841,1) +event ObjectTurnStarted((GUIDSTRING)_Object) (3,0,842,1) +event ObjectTurnEnded((GUIDSTRING)_Object) (3,0,843,1) +event CrimeDisabled((CHARACTERGUID)_Character, (STRING)_Crime) (3,0,844,1) +event CrimeEnabled((CHARACTERGUID)_Character, (STRING)_Crime) (3,0,845,1) +event GMCampaignModeStarted((STRING)_CampaignMode) (3,0,846,1) +event PartyPresetLoaded((STRING)_PartyPreset) (3,0,847,1) +event QuestShared((CHARACTERGUID)_Character, (STRING)_Quest, (INTEGER)_IsShared) (3,0,848,1) +event QuestCategoryChanged((STRING)_Quest, (STRING)_NewCategory) (3,0,849,1) +event ReadyCheckPassed((STRING)_Id) (3,0,850,1) +event ReadyCheckFailed((STRING)_Id) (3,0,851,1) +event ObjectSourcePointAddRequest((GUIDSTRING)_Target, (INTEGER)_AddSP, (INTEGER)_WasSP) (3,0,852,1) +event TeleportRequestMovie((INTEGER)_UserId, (STRING)_EventId) (3,0,853,1) +event EndGameRequestMovie((INTEGER)_UserId, (STRING)_EventId) (3,0,854,1) +event CustomBookUIClosed((CHARACTERGUID)_Character, (STRING)_BookName) (3,0,855,1) +event PuzzleUIUsed((CHARACTERGUID)_Character, (STRING)_UIInstance, (INTEGER)_Type, (STRING)_Command, (INTEGER)_ElementId) (3,0,856,1) +event PuzzleUIClosed((CHARACTERGUID)_Character, (STRING)_UIInstance, (INTEGER)_Type) (3,0,857,1) +event CameraReachedNode((SPLINEGUID)_Spline, (CHARACTERGUID)_Character, (STRING)_event, (INTEGER)_index, (INTEGER)_last) (3,0,858,1) +call TriggerLaunchIterator((TRIGGERGUID)_Trigger, (STRING)_Event) (1,0,859,1) +call TriggerRemoveAllItemTemplates((TRIGGERGUID)_Trigger, (STRING)_ItemTemplate) (1,0,860,1) +call TriggerRegisterForCharacter((TRIGGERGUID)_Trigger, (CHARACTERGUID)_Character) (1,0,861,1) +call TriggerUnregisterForCharacter((TRIGGERGUID)_Trigger, (CHARACTERGUID)_Character) (1,0,862,1) +call TriggerRegisterForPlayers((TRIGGERGUID)_Trigger) (1,0,863,1) +call TriggerUnregisterForPlayers((TRIGGERGUID)_Trigger) (1,0,864,1) +call TriggerRegisterForItems((TRIGGERGUID)_Trigger) (1,0,865,1) +call TriggerUnregisterForItems((TRIGGERGUID)_Trigger) (1,0,866,1) +query CharacterTemplatesInTrigger([in](STRING)_Template, [in](TRIGGERGUID)_Trigger, [out](INTEGER)_Count) (2,0,867,1) +call TriggerSetAtmosphere((TRIGGERGUID)_Trigger, (STRING)_AtmosphereUUID) (1,0,868,1) +call TriggerResetAtmosphere((TRIGGERGUID)_Trigger) (1,0,869,1) +call TriggerSetSoundState((TRIGGERGUID)_Trigger, (STRING)_StateGroup, (STRING)_State, (INTEGER)_Recursive) (1,0,870,1) +call TriggerSetSoundRTPC((TRIGGERGUID)_Trigger, (STRING)_Name, (REAL)_Value, (INTEGER)_Recursive) (1,0,871,1) +call TriggerSetItemOwner((TRIGGERGUID)_AreaTrigger, (CHARACTERGUID)_Owner) (1,0,872,1) +call TriggerClearItemOwner((TRIGGERGUID)_AreaTrigger) (1,0,873,1) +call TriggerClearItemTemplateOwners((TRIGGERGUID)_Trigger, (STRING)_ItemTemplate) (1,0,874,1) +query GetRandomPositionInTrigger([in](TRIGGERGUID)_Trigger, [out](REAL)_X, [out](REAL)_Y, [out](REAL)_Z) (2,0,875,1) +query StringConcatenate([in](STRING)_StringA, [in](STRING)_StringB, [out](STRING)_Result) (2,0,876,1) +query StringContains([in](STRING)_StringA, [in](STRING)_StringB, [out](INTEGER)_Bool) (2,0,877,1) +query StringSub([in](STRING)_String, [in](INTEGER)_Start, [in](INTEGER)_Count, [out](STRING)_Result) (2,0,878,1) +query IntegertoString([in](INTEGER)_Integer, [out](STRING)_Result) (2,0,879,1) +query String([in](GUIDSTRING)_GUIDstring, [out](STRING)_Result) (2,0,880,1) +query StartDialog_Internal([in](STRING)_Dialog, [in](INTEGER)_MarkForInteractiveDialog, [in](GUIDSTRING)_Speaker1, [in](GUIDSTRING)_Speaker2, [in](GUIDSTRING)_Speaker3, [in](GUIDSTRING)_Speaker4, [in](GUIDSTRING)_Speaker5, [in](GUIDSTRING)_Speaker6, [out](INTEGER)_Success) (2,0,881,1) +query StartDialog_Internal_NoDeadCheck([in](STRING)_Dialog, [in](INTEGER)_MarkForInteractiveDialog, [in](GUIDSTRING)_Speaker1, [in](GUIDSTRING)_Speaker2, [in](GUIDSTRING)_Speaker3, [in](GUIDSTRING)_Speaker4, [in](GUIDSTRING)_Speaker5, [in](GUIDSTRING)_Speaker6, [out](INTEGER)_Success) (2,0,882,1) +query DialogStartCrimeDialog([in](INTEGER)_CrimeID, [in](STRING)_Dialog, [in](INTEGER)_MarkForInteractiveDialog, [in](GUIDSTRING)_NPC, [in](GUIDSTRING)_Criminal1, [in](GUIDSTRING)_Criminal2, [in](GUIDSTRING)_Criminal3, [in](GUIDSTRING)_Criminal4, [out](INTEGER)_success) (2,0,883,1) +call DialogAddCharacter((INTEGER)_InstanceID, (CHARACTERGUID)_Player) (1,0,884,1) +call DialogAddCharacterAt((INTEGER)_InstanceID, (CHARACTERGUID)_Player, (INTEGER)_Index) (1,0,885,1) +query DialogRemoveCharacter([in](INTEGER)_InstanceID, [in](CHARACTERGUID)_Player, [out](INTEGER)_success) (2,0,886,1) +query DialogStartPartyDialog([in](STRING)_Dialog, [in](INTEGER)_ParentInstanceID, [in](INTEGER)_NewInstanceID, [in](CHARACTERGUID)_Player1, [in](CHARACTERGUID)_Player2, [in](CHARACTERGUID)_Player3, [in](CHARACTERGUID)_Player4, [out](INTEGER)_success) (2,0,887,1) +query DialogStartChildDialog([in](STRING)_Dialog, [in](INTEGER)_ParentInstanceID, [in](INTEGER)_NewInstanceID, [in](CHARACTERGUID)_Player1, [in](CHARACTERGUID)_Player2, [in](CHARACTERGUID)_Player3, [in](CHARACTERGUID)_Player4, [out](INTEGER)_success) (2,0,888,1) +call DialogResume((INTEGER)_InstanceID) (1,0,889,1) +query IsSpeakerReserved([in](GUIDSTRING)_Speaker, [out](INTEGER)_success) (2,0,890,1) +call StartVoiceBark((STRING)_Bark, (CHARACTERGUID)_Source) (1,0,891,1) +event VoiceBarkStarted((STRING)_Bark, (INTEGER)_InstanceID) (3,0,892,1) +event VoiceBarkFailed((STRING)_Bark) (3,0,893,1) +event VoiceBarkEnded((STRING)_Bark, (INTEGER)_InstanceID) (3,0,894,1) +event DualDialogRequested((STRING)_Dialog, (INTEGER)_InstanceID, (INTEGER)_TargetInstanceID) (3,0,895,1) +event ChildDialogRequested((STRING)_Dialog, (INTEGER)_InstanceID, (INTEGER)_TargetInstanceID) (3,0,896,1) +call DialogSetVariableString((STRING)_Dialog, (STRING)_Variable, (STRING)_Value) (1,0,897,1) +call DialogSetVariableInt((STRING)_Dialog, (STRING)_Variable, (INTEGER)_Value) (1,0,898,1) +call DialogSetVariableFloat((STRING)_Dialog, (STRING)_Variable, (REAL)_Value) (1,0,899,1) +call DialogSetVariableFixedString((STRING)_Dialog, (STRING)_Variable, (STRING)_Value) (1,0,900,1) +call DialogSetVariableTranslatedString((STRING)_Dialog, (STRING)_Variable, (STRING)_StringHandleValue, (STRING)_ReferenceStringValue) (1,0,901,1) +call DialogSetVariableStringForInstance((INTEGER)_InstanceID, (STRING)_Variable, (STRING)_Value) (1,0,902,1) +call DialogSetVariableIntForInstance((INTEGER)_InstanceID, (STRING)_Variable, (INTEGER)_Value) (1,0,903,1) +call DialogSetVariableFloatForInstance((INTEGER)_InstanceID, (STRING)_Variable, (REAL)_Value) (1,0,904,1) +call DialogSetVariableFixedStringForInstance((INTEGER)_InstanceID, (STRING)_Variable, (STRING)_Value) (1,0,905,1) +call DialogSetVariableTranslatedStringForInstance((INTEGER)_InstanceID, (STRING)_Variable, (STRING)_StringHandleValue, (STRING)_ReferenceStringValue) (1,0,906,1) +query HasDefaultDialog([in](CHARACTERGUID)_Character, [out](INTEGER)_HasDefaultDialog) (2,0,907,1) +query StartDefaultDialog([in](CHARACTERGUID)_Character, [in](CHARACTERGUID)_Player, [out](STRING)_Dialog, [out](INTEGER)_Automated) (2,0,908,1) +event GameEventSet((STRING)_Event) (3,0,909,1) +event GameEventCleared((STRING)_Event) (3,0,910,1) +event TextEventSet((STRING)_Event) (3,0,911,1) +query GetTextEventParamString([in](INTEGER)_Number, [out](STRING)_Value) (2,0,912,1) +query GetTextEventParamInteger([in](INTEGER)_Number, [out](INTEGER)_Value) (2,0,913,1) +query GetTextEventParamReal([in](INTEGER)_Number, [out](REAL)_Value) (2,0,914,1) +query GetTextEventParamUUID([in](INTEGER)_Number, [out](GUIDSTRING)_Value) (2,0,915,1) + + +version "0.0.6.1" +Goal(1).Title("__Combat"); +Goal(1) +{ + INIT + { + + } + KB + { + PROC + ProcClearPreviousCombat((GUIDSTRING)_Char) + AND + DB_WasInCombat(_Char,(INTEGER)_Combat) + THEN + NOT DB_WasInCombat(_Char,_Combat); + + PROC + ProcAddToCharacterCombatList((GUIDSTRING)_Char,(INTEGER)_Combat) + AND + DB_CombatCharacters((CHARACTERGUID)_Char,(INTEGER)_Old) + THEN + NOT DB_CombatCharacters((CHARACTERGUID)_Char,_Old); + + PROC + ProcAddToCharacterCombatList((GUIDSTRING)_Char,(INTEGER)_Combat) + AND + ObjectIsCharacter(_Char,1) + THEN + DB_CombatCharacters((CHARACTERGUID)_Char,_Combat); + + IF + ObjectEnteredCombat(_Character,_Combat) + THEN + ProcClearPreviousCombat((GUIDSTRING)_Character); + DB_CombatObjects(_Character,_Combat); + ProcAddToCharacterCombatList(_Character,_Combat); + + IF + ObjectSwitchedCombat(_Character,_Old,_New) + THEN + NOT DB_CombatObjects(_Character,_Old); + DB_CombatObjects(_Character,_New); + ProcAddToCharacterCombatList(_Character,_New); + + PROC + Proc_ObjectLeftCombat((GUIDSTRING)_Char,(INTEGER)_Combat) + THEN + DB_NOOP(1); + + IF + ObjectLeftCombat(_Character,_Combat) + THEN + DB_WasInCombat(_Character,_Combat); + NOT DB_CombatObjects(_Character,_Combat); + NOT DB_CombatCharacters((CHARACTERGUID)_Character,_Combat); + Proc_ObjectLeftCombat(_Character,_Combat); + + IF + FleeCombat(_Player) + AND + DB_IsPlayer(_Player) + AND + NOT DB_BlockWaypointUsage(_Player) + THEN + OpenWaypointUI(_Player,"",NULL_00000000-0000-0000-0000-000000000000,1); + + } + EXIT + { + + } +} +Goal(2).Title("__GLO_Shovel"); +Goal(2) +{ + INIT + { + DB_LizardFirstShovelUses(0); + + } + KB + { + //REGION CHECK FOR TREASURE + IF + DB_ShovelArea((TRIGGERGUID)_Trigger,(STRING)_,(ITEMGUID)_) + THEN + ProcTriggerRegisterForPlayers(_Trigger); + + IF + DB_ShovelRewardItemAppear(_,_Item,_) + THEN + SetOnStage(_Item,0); + + IF + DB_ShovelRewardItemScatter(_,_Item) + THEN + SetOnStage(_Item,0); + + IF + DB_ShovelRewardItemSpawn(_,_Item) + THEN + SetOnStage(_Item,0); + + IF + DB_ShovelRewardCharacterAppear(_,_Character) + THEN + SetOnStage(_Character,0); + + //Dig while in a trigger + IF + CharacterUsedItem(_Player, _Shovel) + AND + ObjectExists(_Shovel,1) //ignore consumables that were destroyed + AND + IsTagged(_Shovel, "SHOVEL", 1) + AND + DB_ShovelArea(_Trigger,_,_DirtPile) + AND + DB_InRegion(_Player,_Trigger) + AND + NOT DB_Shovelling_Mound(_,_DirtPile) + AND + CharacterIsInFightMode(_Player,_WasInFightMode) + THEN + DB_Shovelling_Mound(_Player,_DirtPile); + CharacterSetFightMode(_Player,0,1); + PlayAnimation(_Player,"use_dig","digmound"); + DB_Shovel_PlayerHadWeaponDrawn(_Player,_WasInFightMode); + + //Dig outside a trigger + IF + CharacterUsedItem(_Player, _Shovel) + AND + ObjectExists(_Shovel,1) //ignore consumables that were destroyed + AND + IsTagged(_Shovel, "SHOVEL", 1) + AND + NOT DB_Shovelling_Mound(_Player,_) + AND + CharacterIsInFightMode(_Player,_WasInFightMode) + THEN + CharacterSetFightMode(_Player,0,1); + PlayAnimation(_Player,"use_dig","digmound"); + DB_Shovel_PlayerHadWeaponDrawn(_Player,_WasInFightMode); + + //Dig by interacting with pile without a shovel + + IF + CharacterUsedItem(_Player,_DirtPile) + AND + DB_ShovelArea(_,_,_DirtPile) + AND + NOT PartyFindTaggedItem(_Player, "SHOVEL", 0, _) + AND + IsTagged(_Player,"LIZARD",0) + THEN + Proc_StartDialog(1,"GLO_AD_ShovelRequired", _Player); + + IF + CharacterUsedItem(_Player,_DirtPile) + AND + DB_ShovelArea(_,_,_DirtPile) + AND + NOT DB_Shovelling_Mound(_,_DirtPile) + AND + IsTagged(_Player,"LIZARD",1) + AND + CharacterIsInFightMode(_Player,_WasInFightMode) + THEN + DB_Shovelling_Mound(_Player,_DirtPile); + CharacterSetFightMode(_Player,0,1); + PlayAnimation(_Player,"use_dig","digmound"); + DB_Shovel_PlayerHadWeaponDrawn(_Player,_WasInFightMode); + proc_LizardFirstShovelUse((CHARACTERGUID)_Player); + + //Dig by interacting with pile with a shovel + IF + CharacterUsedItem(_Player,_DirtPile) + AND + DB_ShovelArea(_,_,_DirtPile) + AND + NOT DB_Shovelling_Mound(_,_DirtPile) + AND + PartyFindTaggedItem(_Player, "SHOVEL", 1, _) + AND + CharacterIsInFightMode(_Player,_WasInFightMode) + THEN + DB_Shovelling_Mound(_Player,_DirtPile); + CharacterSetFightMode(_Player,0,1); + PlayAnimation(_Player,"use_dig","digmound"); + DB_Shovel_PlayerHadWeaponDrawn(_Player,_WasInFightMode); + + //After Dig + IF + StoryEvent(_Player,"digmound") + AND + NOT DB_Shovelling_Mound((CHARACTERGUID)_Player,_) + THEN + Proc_StartDialog(1,"GLO_AD_ShovelFailed", _Player); + + IF + StoryEvent(_Player,"digmound") + AND + DB_Shovelling_Mound((CHARACTERGUID)_Player,_DirtMound) + AND + DB_ShovelArea(_Trigger,_Reward,_DirtMound) + THEN + NOT DB_ShovelArea(_Trigger,_Reward,_DirtMound); + NOT DB_Shovelling_Mound(_Player,_DirtMound); + SetOnStage(_DirtMound,0); + ProcShovelRewards(_Player,_Reward); + CharacterItemSetEvent(_Player,_DirtMound,"clearedMound"); + + IF + StoryEvent(_Player,"digmound") + AND + DB_Shovel_PlayerHadWeaponDrawn((CHARACTERGUID)_Player,1) + THEN + CharacterSetFightMode(_Player,1,0); + + IF + StoryEvent(_Player,"digmound") + AND + DB_Shovel_PlayerHadWeaponDrawn((CHARACTERGUID)_Player,_Value) + THEN + NOT DB_Shovel_PlayerHadWeaponDrawn(_Player,_Value); + + //END_REGION + + //REGION Lizard Dig AD + + PROC + proc_LizardFirstShovelUse((CHARACTERGUID)_Lizrd) + AND + IsTagged(_Lizrd,"LIZARD",1) + AND + DB_LizardFirstShovelUses(_Int) + AND + _Int < 3 + AND + IntegerSum(_Int,1,_NewInt) + THEN + DB_LizardFirstShovelUses(_NewInt); + NOT DB_LizardFirstShovelUses(_Int); + Proc_StartDialog(1,"GEN_AD_Lizrd_Dig",_Lizrd); + + + + //END_REGION + + //REGION SHOVEL REWARDS + PROC + ProcShovelRewards((CHARACTERGUID)_Player,(STRING)_Reward) + AND + DB_ShovelRewardItemAdd(_Reward,(ITEMGUID)_Item) + THEN + ItemToInventory(_Item,_Player,-1); + + PROC + ProcShovelRewards((CHARACTERGUID)_Player,(STRING)_Reward) + AND + DB_ShovelRewardItemAppear(_Reward,(ITEMGUID)_Item,(TRIGGERGUID)_Trigger) + THEN + SetOnStage(_Item,1); + ItemDragToTrigger(_Item,_Trigger); + + PROC + ProcShovelRewards((CHARACTERGUID)_Player,(STRING)_Reward) + AND + DB_ShovelRewardCharacterAppear(_Reward,(CHARACTERGUID)_Character) + THEN + CharacterAppear(_Character,1,""); + + PROC + ProcShovelRewards((CHARACTERGUID)_Player,(STRING)_Reward) + AND + DB_ShovelRewardItemSpawn(_Reward,(ITEMGUID)_Item) + THEN + SetOnStage(_Item,1); + + PROC + ProcShovelRewards((CHARACTERGUID)_Player,(STRING)_Reward) + AND + DB_ShovelRewardItemTemplate(_Reward,(STRING)_ItemTemplate,(INTEGER)_Amount) + THEN + ItemTemplateAddTo(_ItemTemplate,_Player,_Amount); + + PROC + ProcShovelRewards((CHARACTERGUID)_Player,(STRING)_Reward) + AND + DB_ShovelRewardEvent(_Reward,(STRING)_Event) + THEN + GlobalSetFlag(_Event); + + PROC + ProcShovelRewards((CHARACTERGUID)_Player,(STRING)_Reward) + AND + DB_ShovelRewardVoiceBark(_Reward, (STRING)_VoiceBark) + THEN + StartVoiceBark(_VoiceBark, _Player); + + PROC + ProcShovelRewards((CHARACTERGUID)_Player,(STRING)_Reward) + AND + DB_ShovelRewardSurface(_Reward,(TRIGGERGUID)_Trigger, (STRING)_Type, (REAL)_Radius, (REAL)_Lifetime) + THEN + CreateSurface(_Trigger, _Type, _Radius, _Lifetime); + + PROC + ProcShovelRewards((CHARACTERGUID)_Player,(STRING)_Reward) + AND + DB_ShovelRewardItemScatter(_Reward,(ITEMGUID)_Item) + AND + GetPosition(_Item, _X, _Y, _Z) + THEN + SetOnStage(_Item, 1); + ItemScatterAt(_Item, _X, _Y, _Z); + + //END_REGION + + //REGION Tombstone AD + + IF + CharacterItemEvent(_Player,_Tombstone,"GEN_UsedTombstone") + AND + GetVarFixedString(_Tombstone,"Dialog",_Dialog) + AND + _Dialog != "" + THEN + Proc_StartDialog(1,_Dialog,_Tombstone,_Player); + + //END_REGION + + } + EXIT + { + + } +} +Goal(3).Title("__GLOBAL_Dialogs"); +Goal(3) +{ + INIT + { + // To intercept a dialog request, define one of these: + // PROC_GLOBAL_DialogStartRequested((GUIDSTRING)_Target,(GUIDSTRING)_Source) + // PROC_GLOBAL_DialogStartRequested_AfterGenerics((GUIDSTRING)_Target,(GUIDSTRING)_Source) + // The former is called before any generics (low attitude etc) are checked, but still after + // speaker availability checks are confirmed (not dead, not in combat). + // Start your own dialog in one of those PROCs if the conditions are right, and set + // DB_FoundDialog(_Target,_Source) if you want to prevent the default dialog to be searched/started + // + + DB_AnimalFoodVars("FoodTemplate1"); + DB_AnimalFoodVars("FoodTemplate2"); + DB_AnimalFoodVars("FoodTemplate3"); + DB_AnimalFoodVars("FoodTemplate4"); + DB_AnimalFoodVars("FoodTemplate5"); + DB_AnimalFoodVars("FoodTemplate6"); + + } + KB + { + //REGION HasMet + IF + DB_HasMetCharactersToCheck(_NPC,_PC) + THEN + ProcSetHasMetTag(_Npc,_PC); + + IF + DB_HasMetCharactersToCheck(_NPC,_PC) + THEN + NOT DB_HasMetCharactersToCheck(_NPC,_PC); + + PROC + Proc_Dialogs_CharactersHaveMetInThisShape((GUIDSTRING)_Npc,(CHARACTERGUID)_Player) + THEN + SetTag(_NPC,"HasMet"); + + PROC + ProcSetHasMetTag((GUIDSTRING)_Npc,(CHARACTERGUID)_Player) + AND + DB_CharacterPolymorphedInto(_Player,(STRING)_Race) + AND + DB_HasMet(_Npc,_Player,(STRING)_Race) + THEN + Proc_Dialogs_CharactersHaveMetInThisShape(_Npc,_Player); + + PROC + ProcSetHasMetTag((GUIDSTRING)_Npc,(CHARACTERGUID)_Player) + AND + NOT DB_CharacterPolymorphedInto(_Player,_) + AND + DB_HasMet(_Npc,_Player,"") + THEN + Proc_Dialogs_CharactersHaveMetInThisShape(_Npc,_Player); + + IF + DialogEnded(_Diag,_Inst) + AND + DB_DialogNPCs(_Inst,_Npc,_) + AND + DB_DialogPlayers(_Inst,_Player,_) + THEN + ProcSetHasMetDBEntry(_Diag,(GUIDSTRING)_Npc,(CHARACTERGUID)_Player); + + PROC + ProcSetHasMetDBEntry((STRING)_Diag,(GUIDSTRING)_Npc,(CHARACTERGUID)_Player) + AND + DB_CharacterPolymorphedInto(_Player,_Race) + THEN + DB_HasMet(_Npc,_Player,_Race); + + PROC + ProcSetHasMetDBEntry((STRING)_Diag,(GUIDSTRING)_Npc,(CHARACTERGUID)_Player) + AND + NOT DB_CharacterPolymorphedInto(_Player,_) + THEN + DB_HasMet(_Npc,_Player,""); + + + IF + DialogEnded(_Diag,_Inst) + AND + DB_DialogNPCs(_Inst,_Npc,_) + THEN + ClearTag(_Npc,"HasMet"); + //END_REGION + + //REGION Tag Priority Dialogs + PROC + PROC_GLOBAL_DialogStartRequested((GUIDSTRING)_NPC,(GUIDSTRING)_Player) + THEN + PROC_GLO_Origins_CheckTagPriorityDialogs(_NPC,_Player); + + PROC + PROC_GLO_Origins_CheckTagPriorityDialogs((GUIDSTRING)_NPC,(GUIDSTRING)_Player) + AND + DB_TagPriorityDialog(_NPC, (STRING)_Dialog, (STRING)_PriorityTag) + AND + NOT DB_GLO_OriginsFoundTagPriorityPlayer(1) + AND + IsTagged(_Player,_PriorityTag,0) + AND + DB_IsPlayer(_OtherPlayer) + AND + QRY_SpeakerIsAvailableAndInDialogRange(_OtherPlayer, _Player) + AND + _OtherPlayer != _Player + AND + CharacterGetReservedUserID((CHARACTERGUID)_Player,_ID) + AND + CharacterGetReservedUserID((CHARACTERGUID)_OtherPlayer,_ID) + AND + IsTagged(_OtherPlayer,_PriorityTag,1) + THEN + DB_GLO_OriginsFoundTagPriorityPlayer(1); + ProcForceStopDialog(_NPC); + Proc_StartDialog(0,_Dialog,_NPC,_OtherPlayer); + PROC_GLO_Origins_ClearTaggedPrioritiesForDialog(_Dialog); + + PROC + PROC_GLO_Origins_ClearTaggedPrioritiesForDialog((STRING)_Dialog) + AND + DB_TagPriorityDialog(_NPC,_Dialog,_PriorityTag) + THEN + NOT DB_TagPriorityDialog(_NPC,_Dialog,_PriorityTag); + + PROC + PROC_GLO_Origins_CheckTagPriorityDialogs((GUIDSTRING)_NPC,(GUIDSTRING)_Player) + THEN + NOT DB_GLO_OriginsFoundTagPriorityPlayer(1); + + //END_REGION + + + //REGION The Only Allowed Dialog Starting + PROC + Proc_StartDialog((INTEGER)_Automated,(STRING)_Dialog,(GUIDSTRING)_Speaker1) + AND + QRY_StartDialog(_Automated,_Dialog,_Speaker1) + THEN + DB_NOOP(1); + + PROC + Proc_StartDialog((INTEGER)_Automated,(STRING)_Dialog,(GUIDSTRING)_Speaker1,(GUIDSTRING)_Speaker2) + AND + NOT DB_OriginMomentTag(_Dialog,(STRING)_,(STRING)_) + AND + NOT DB_OriginMomentTag_HighPriority(_Dialog,(STRING)_,(STRING)_) + AND + NOT DB_OriginRecruitmentDialog((CHARACTERGUID)_,_Dialog) + AND + QRY_StartDialog(_Automated,_Dialog,_Speaker1,_Speaker2) + THEN + DB_NOOP(1); + + PROC + Proc_StartDialog((INTEGER)_Automated,(STRING)_Dialog,(GUIDSTRING)_Speaker1,(GUIDSTRING)_Speaker2,(GUIDSTRING)_Speaker3) + AND + NOT DB_OriginMomentTag_3SP(_Dialog,(STRING)_,(STRING)_) + AND + NOT DB_OriginMomentTag_HighPriority_3SP(_Dialog,(STRING)_,(STRING)_) + AND + QRY_StartDialog(_Automated,_Dialog,_Speaker1,_Speaker2,_Speaker3) + THEN + DB_NOOP(1); + + PROC + Proc_StartDialog((INTEGER)_Automated,(STRING)_Dialog,(GUIDSTRING)_Speaker1,(GUIDSTRING)_Speaker2,(GUIDSTRING)_Speaker3,(GUIDSTRING)_Speaker4) + AND + QRY_StartDialog(_Automated,_Dialog,_Speaker1,_Speaker2,_Speaker3,_Speaker4) + THEN + DB_NOOP(1); + + PROC + Proc_StartDialog((INTEGER)_Automated,(STRING)_Dialog,(GUIDSTRING)_Speaker1,(GUIDSTRING)_Speaker2,(GUIDSTRING)_Speaker3,(GUIDSTRING)_Speaker4,(GUIDSTRING)_Speaker5) + AND + QRY_StartDialog(_Automated,_Dialog,_Speaker1,_Speaker2,_Speaker3,_Speaker4,_Speaker5) + THEN + DB_NOOP(1); + + PROC + Proc_StartDialog((INTEGER)_Automated,(STRING)_Dialog,(GUIDSTRING)_Speaker1,(GUIDSTRING)_Speaker2,(GUIDSTRING)_Speaker3,(GUIDSTRING)_Speaker4,(GUIDSTRING)_Speaker5,(GUIDSTRING)_Speaker6) + AND + QRY_StartDialog(_Automated,_Dialog,_Speaker1,_Speaker2,_Speaker3,_Speaker4,_Speaker5,_Speaker6) + THEN + DB_NOOP(1); + + QRY + QRY_StartDialog(1,(STRING)_Dialog,(GUIDSTRING)_Speaker1) + AND + QRY_SpeakerIsAvailable(_Speaker1,1) + AND + StartDialog_Internal(_Dialog,0,_Speaker1,NULL_00000000-0000-0000-0000-000000000000,NULL_00000000-0000-0000-0000-000000000000,NULL_00000000-0000-0000-0000-000000000000,NULL_00000000-0000-0000-0000-000000000000,NULL_00000000-0000-0000-0000-000000000000,1) + THEN + Proc_DialogFlagSetup((STRING)_Dialog,(GUIDSTRING)_Speaker1); + + QRY + QRY_StartDialog(1,(STRING)_Dialog,(GUIDSTRING)_Speaker1,(GUIDSTRING)_Speaker2) + AND + QRY_SpeakerIsAvailable(_Speaker1,1) + AND + QRY_SpeakerIsAvailable(_Speaker2,1) + AND + StartDialog_Internal(_Dialog,0,_Speaker1,_Speaker2,NULL_00000000-0000-0000-0000-000000000000,NULL_00000000-0000-0000-0000-000000000000,NULL_00000000-0000-0000-0000-000000000000,NULL_00000000-0000-0000-0000-000000000000,1) + THEN + Proc_DialogFlagSetup((STRING)_Dialog,(GUIDSTRING)_Speaker1,(GUIDSTRING)_Speaker2); + + QRY + QRY_StartDialog(1,(STRING)_Dialog,(GUIDSTRING)_Speaker1,(GUIDSTRING)_Speaker2,(GUIDSTRING)_Speaker3) + AND + QRY_SpeakerIsAvailable(_Speaker1,1) + AND + QRY_SpeakerIsAvailable(_Speaker2,1) + AND + QRY_SpeakerIsAvailable(_Speaker3,1) + AND + StartDialog_Internal(_Dialog,0,_Speaker1,_Speaker2,_Speaker3,NULL_00000000-0000-0000-0000-000000000000,NULL_00000000-0000-0000-0000-000000000000,NULL_00000000-0000-0000-0000-000000000000,1) + THEN + Proc_DialogFlagSetup((STRING)_Dialog,(GUIDSTRING)_Speaker1,(GUIDSTRING)_Speaker2, (GUIDSTRING)_Speaker3); + + QRY + QRY_StartDialog(1,(STRING)_Dialog,(GUIDSTRING)_Speaker1,(GUIDSTRING)_Speaker2,(GUIDSTRING)_Speaker3,(GUIDSTRING)_Speaker4) + AND + QRY_SpeakerIsAvailable(_Speaker1,1) + AND + QRY_SpeakerIsAvailable(_Speaker2,1) + AND + QRY_SpeakerIsAvailable(_Speaker3,1) + AND + QRY_SpeakerIsAvailable(_Speaker4,1) + AND + StartDialog_Internal(_Dialog,0,_Speaker1,_Speaker2,_Speaker3,_Speaker4,NULL_00000000-0000-0000-0000-000000000000,NULL_00000000-0000-0000-0000-000000000000,1) + THEN + Proc_DialogFlagSetup((STRING)_Dialog,(GUIDSTRING)_Speaker1,(GUIDSTRING)_Speaker2, (GUIDSTRING)_Speaker3, (GUIDSTRING)_Speaker4); + + QRY + QRY_StartDialog(1,(STRING)_Dialog,(GUIDSTRING)_Speaker1,(GUIDSTRING)_Speaker2,(GUIDSTRING)_Speaker3,(GUIDSTRING)_Speaker4,(GUIDSTRING)_Speaker5) + AND + QRY_SpeakerIsAvailable(_Speaker1,1) + AND + QRY_SpeakerIsAvailable(_Speaker2,1) + AND + QRY_SpeakerIsAvailable(_Speaker3,1) + AND + QRY_SpeakerIsAvailable(_Speaker4,1) + AND + QRY_SpeakerIsAvailable(_Speaker5,1) + AND + StartDialog_Internal(_Dialog,0,_Speaker1,_Speaker2,_Speaker3,_Speaker4,_Speaker5,NULL_00000000-0000-0000-0000-000000000000,1) + THEN + Proc_DialogFlagSetup((STRING)_Dialog,(GUIDSTRING)_Speaker1,(GUIDSTRING)_Speaker2, (GUIDSTRING)_Speaker3, (GUIDSTRING)_Speaker4, (GUIDSTRING)_Speaker5,NULL_00000000-0000-0000-0000-000000000000); + + QRY + QRY_StartDialog(1,(STRING)_Dialog,(GUIDSTRING)_Speaker1,(GUIDSTRING)_Speaker2,(GUIDSTRING)_Speaker3,(GUIDSTRING)_Speaker4,(GUIDSTRING)_Speaker5,(GUIDSTRING)_Speaker6) + AND + QRY_SpeakerIsAvailable(_Speaker1,1) + AND + QRY_SpeakerIsAvailable(_Speaker2,1) + AND + QRY_SpeakerIsAvailable(_Speaker3,1) + AND + QRY_SpeakerIsAvailable(_Speaker4,1) + AND + QRY_SpeakerIsAvailable(_Speaker5,1) + AND + QRY_SpeakerIsAvailable(_Speaker6,1) + AND + StartDialog_Internal(_Dialog,0,_Speaker1,_Speaker2,_Speaker3,_Speaker4,_Speaker5,_Speaker6,1) + THEN + Proc_DialogFlagSetup((STRING)_Dialog,(GUIDSTRING)_Speaker1,(GUIDSTRING)_Speaker2, (GUIDSTRING)_Speaker3, (GUIDSTRING)_Speaker4, (GUIDSTRING)_Speaker5, (GUIDSTRING)_Speaker6); + + QRY + QRY_StartDialog(0,(STRING)_Dialog,(GUIDSTRING)_Speaker1) + AND + QRY_SpeakerIsAvailable(_Speaker1) + AND + QRY_PrepForInteractiveDialog(_Speaker1) + AND + StartDialog_Internal(_Dialog,1,_Speaker1,NULL_00000000-0000-0000-0000-000000000000,NULL_00000000-0000-0000-0000-000000000000,NULL_00000000-0000-0000-0000-000000000000,NULL_00000000-0000-0000-0000-000000000000,NULL_00000000-0000-0000-0000-000000000000,1) + THEN + Proc_DialogFlagSetup((STRING)_Dialog,(GUIDSTRING)_Speaker1); + CharacterMakeStoryNpc((CHARACTERGUID)_Speaker1,1); + ProcItemSetInvulnerableForDialog(_Speaker1); + + QRY + QRY_StartDialog(0,(STRING)_Dialog,(GUIDSTRING)_Speaker1,(GUIDSTRING)_Speaker2) + AND + QRY_SpeakerIsAvailable(_Speaker1) + AND + QRY_SpeakerIsAvailable(_Speaker2) + AND + QRY_PrepForInteractiveDialog(_Speaker1) + AND + QRY_PrepForInteractiveDialog(_Speaker2) + AND + StartDialog_Internal(_Dialog,1,_Speaker1,_Speaker2,NULL_00000000-0000-0000-0000-000000000000,NULL_00000000-0000-0000-0000-000000000000,NULL_00000000-0000-0000-0000-000000000000,NULL_00000000-0000-0000-0000-000000000000,1) + THEN + Proc_DialogFlagSetup((STRING)_Dialog,(GUIDSTRING)_Speaker1,(GUIDSTRING)_Speaker2); + ProcFaceCharacter(_Speaker1,_Speaker2); + ProcFaceCharacter(_Speaker2,_Speaker1); + ProcItemSetInvulnerableForDialog(_Speaker1); + ProcItemSetInvulnerableForDialog(_Speaker2); + CharacterMakeStoryNpc((CHARACTERGUID)_Speaker1,1); + CharacterMakeStoryNpc((CHARACTERGUID)_Speaker2,1); + DB_HasMetCharactersToCheck(_Speaker1,_Speaker2); + + QRY + QRY_StartDialog(0,(STRING)_Dialog,(GUIDSTRING)_Speaker1,(GUIDSTRING)_Speaker2,(GUIDSTRING)_Speaker3) + AND + QRY_SpeakerIsAvailable(_Speaker1) + AND + QRY_SpeakerIsAvailable(_Speaker2) + AND + QRY_SpeakerIsAvailable(_Speaker3) + AND + QRY_PrepForInteractiveDialog(_Speaker1) + AND + QRY_PrepForInteractiveDialog(_Speaker2) + AND + QRY_PrepForInteractiveDialog(_Speaker3) + AND + StartDialog_Internal(_Dialog,1,_Speaker1,_Speaker2,_Speaker3,NULL_00000000-0000-0000-0000-000000000000,NULL_00000000-0000-0000-0000-000000000000,NULL_00000000-0000-0000-0000-000000000000,1) + THEN + Proc_DialogFlagSetup((STRING)_Dialog,(GUIDSTRING)_Speaker1,(GUIDSTRING)_Speaker2, (GUIDSTRING)_Speaker3); + ProcFaceCharacter(_Speaker1,_Speaker3); + ProcFaceCharacter(_Speaker2,_Speaker3); + ProcFaceCharacter(_Speaker3,_Speaker1); + ProcItemSetInvulnerableForDialog(_Speaker1); + ProcItemSetInvulnerableForDialog(_Speaker2); + ProcItemSetInvulnerableForDialog(_Speaker3); + CharacterMakeStoryNpc((CHARACTERGUID)_Speaker1,1); + CharacterMakeStoryNpc((CHARACTERGUID)_Speaker2,1); + CharacterMakeStoryNpc((CHARACTERGUID)_Speaker3,1); + DB_HasMetCharactersToCheck(_Speaker1,_Speaker2); + DB_HasMetCharactersToCheck(_Speaker1,_Speaker3); + + + QRY + QRY_StartDialog(0,(STRING)_Dialog,(GUIDSTRING)_Speaker1,(GUIDSTRING)_Speaker2,(GUIDSTRING)_Speaker3,(GUIDSTRING)_Speaker4) + AND + QRY_SpeakerIsAvailable(_Speaker1) + AND + QRY_SpeakerIsAvailable(_Speaker2) + AND + QRY_SpeakerIsAvailable(_Speaker3) + AND + QRY_SpeakerIsAvailable(_Speaker4) + AND + QRY_PrepForInteractiveDialog(_Speaker1) + AND + QRY_PrepForInteractiveDialog(_Speaker2) + AND + QRY_PrepForInteractiveDialog(_Speaker3) + AND + QRY_PrepForInteractiveDialog(_Speaker4) + AND + StartDialog_Internal(_Dialog,1,_Speaker1,_Speaker2,_Speaker3,_Speaker4,NULL_00000000-0000-0000-0000-000000000000,NULL_00000000-0000-0000-0000-000000000000,1) + THEN + Proc_DialogFlagSetup((STRING)_Dialog,(GUIDSTRING)_Speaker1,(GUIDSTRING)_Speaker2, (GUIDSTRING)_Speaker3, (GUIDSTRING)_Speaker4); + ProcFaceCharacter(_Speaker1,_Speaker4); + ProcFaceCharacter(_Speaker2,_Speaker4); + ProcFaceCharacter(_Speaker3,_Speaker4); + ProcFaceCharacter(_Speaker4,_Speaker1); + ProcItemSetInvulnerableForDialog(_Speaker1); + ProcItemSetInvulnerableForDialog(_Speaker2); + ProcItemSetInvulnerableForDialog(_Speaker3); + ProcItemSetInvulnerableForDialog(_Speaker4); + CharacterMakeStoryNpc((CHARACTERGUID)_Speaker1,1); + CharacterMakeStoryNpc((CHARACTERGUID)_Speaker2,1); + CharacterMakeStoryNpc((CHARACTERGUID)_Speaker3,1); + CharacterMakeStoryNpc((CHARACTERGUID)_Speaker4,1); + DB_HasMetCharactersToCheck(_Speaker1,_Speaker2); + DB_HasMetCharactersToCheck(_Speaker1,_Speaker3); + DB_HasMetCharactersToCheck(_Speaker1,_Speaker4); + + + QRY + QRY_StartDialog(0,(STRING)_Dialog,(GUIDSTRING)_Speaker1,(GUIDSTRING)_Speaker2,(GUIDSTRING)_Speaker3,(GUIDSTRING)_Speaker4,(GUIDSTRING)_Speaker5) + AND + QRY_SpeakerIsAvailable(_Speaker1) + AND + QRY_SpeakerIsAvailable(_Speaker2) + AND + QRY_SpeakerIsAvailable(_Speaker3) + AND + QRY_SpeakerIsAvailable(_Speaker4) + AND + QRY_SpeakerIsAvailable(_Speaker5) + AND + QRY_PrepForInteractiveDialog(_Speaker1) + AND + QRY_PrepForInteractiveDialog(_Speaker2) + AND + QRY_PrepForInteractiveDialog(_Speaker3) + AND + QRY_PrepForInteractiveDialog(_Speaker4) + AND + QRY_PrepForInteractiveDialog(_Speaker5) + AND + StartDialog_Internal(_Dialog,1,_Speaker1,_Speaker2,_Speaker3,_Speaker4,_Speaker5,NULL_00000000-0000-0000-0000-000000000000,1) + THEN + Proc_DialogFlagSetup((STRING)_Dialog,(GUIDSTRING)_Speaker1,(GUIDSTRING)_Speaker2, (GUIDSTRING)_Speaker3, (GUIDSTRING)_Speaker4, (GUIDSTRING)_Speaker5); + ProcFaceCharacter(_Speaker1,_Speaker5); + ProcFaceCharacter(_Speaker2,_Speaker5); + ProcFaceCharacter(_Speaker3,_Speaker5); + ProcFaceCharacter(_Speaker4,_Speaker5); + ProcFaceCharacter(_Speaker5,_Speaker1); + ProcItemSetInvulnerableForDialog(_Speaker1); + ProcItemSetInvulnerableForDialog(_Speaker2); + ProcItemSetInvulnerableForDialog(_Speaker3); + ProcItemSetInvulnerableForDialog(_Speaker4); + ProcItemSetInvulnerableForDialog(_Speaker5); + CharacterMakeStoryNpc((CHARACTERGUID)_Speaker1,1); + CharacterMakeStoryNpc((CHARACTERGUID)_Speaker2,1); + CharacterMakeStoryNpc((CHARACTERGUID)_Speaker3,1); + CharacterMakeStoryNpc((CHARACTERGUID)_Speaker4,1); + CharacterMakeStoryNpc((CHARACTERGUID)_Speaker5,1); + DB_HasMetCharactersToCheck(_Speaker1,_Speaker2); + DB_HasMetCharactersToCheck(_Speaker1,_Speaker3); + DB_HasMetCharactersToCheck(_Speaker1,_Speaker4); + DB_HasMetCharactersToCheck(_Speaker1,_Speaker5); + + QRY + QRY_StartDialog(0,(STRING)_Dialog,(GUIDSTRING)_Speaker1,(GUIDSTRING)_Speaker2,(GUIDSTRING)_Speaker3,(GUIDSTRING)_Speaker4,(GUIDSTRING)_Speaker5,(GUIDSTRING)_Speaker6) + AND + QRY_SpeakerIsAvailable(_Speaker1) + AND + QRY_SpeakerIsAvailable(_Speaker2) + AND + QRY_SpeakerIsAvailable(_Speaker3) + AND + QRY_SpeakerIsAvailable(_Speaker4) + AND + QRY_SpeakerIsAvailable(_Speaker5) + AND + QRY_SpeakerIsAvailable(_Speaker6) + AND + QRY_PrepForInteractiveDialog(_Speaker1) + AND + QRY_PrepForInteractiveDialog(_Speaker2) + AND + QRY_PrepForInteractiveDialog(_Speaker3) + AND + QRY_PrepForInteractiveDialog(_Speaker4) + AND + QRY_PrepForInteractiveDialog(_Speaker5) + AND + QRY_PrepForInteractiveDialog(_Speaker6) + AND + StartDialog_Internal(_Dialog,1,_Speaker1,_Speaker2,_Speaker3,_Speaker4,_Speaker5,_Speaker6,1) + THEN + Proc_DialogFlagSetup((STRING)_Dialog,(GUIDSTRING)_Speaker1,(GUIDSTRING)_Speaker2, (GUIDSTRING)_Speaker3, (GUIDSTRING)_Speaker4, (GUIDSTRING)_Speaker5, (GUIDSTRING)_Speaker6); + ProcFaceCharacter(_Speaker1,_Speaker6); + ProcFaceCharacter(_Speaker2,_Speaker6); + ProcFaceCharacter(_Speaker3,_Speaker6); + ProcFaceCharacter(_Speaker4,_Speaker6); + ProcFaceCharacter(_Speaker5,_Speaker6); + ProcFaceCharacter(_Speaker6,_Speaker1); + ProcItemSetInvulnerableForDialog(_Speaker1); + ProcItemSetInvulnerableForDialog(_Speaker2); + ProcItemSetInvulnerableForDialog(_Speaker3); + ProcItemSetInvulnerableForDialog(_Speaker4); + ProcItemSetInvulnerableForDialog(_Speaker5); + ProcItemSetInvulnerableForDialog(_Speaker6); + CharacterMakeStoryNpc((CHARACTERGUID)_Speaker1,1); + CharacterMakeStoryNpc((CHARACTERGUID)_Speaker2,1); + CharacterMakeStoryNpc((CHARACTERGUID)_Speaker3,1); + CharacterMakeStoryNpc((CHARACTERGUID)_Speaker4,1); + CharacterMakeStoryNpc((CHARACTERGUID)_Speaker5,1); + CharacterMakeStoryNpc((CHARACTERGUID)_Speaker6,1); + DB_HasMetCharactersToCheck(_Speaker1,_Speaker2); + DB_HasMetCharactersToCheck(_Speaker1,_Speaker3); + DB_HasMetCharactersToCheck(_Speaker1,_Speaker4); + DB_HasMetCharactersToCheck(_Speaker1,_Speaker5); + DB_HasMetCharactersToCheck(_Speaker1,_Speaker6); + + QRY + QRY_PrepForInteractiveDialog((GUIDSTRING)_Speaker) + THEN + DialogRequestStop(_Speaker); + //END_REGION + + //REGION Setting Items in Dialog Invulnerable + PROC + ProcItemSetInvulnerableForDialog((GUIDSTRING)_Speaker) + AND + ObjectIsItem(_Speaker,1) + THEN + SetInvulnerable_UseProcSetInvulnerable(_Speaker,1); + //END_REGION + + //REGION Flags Set Up The Start Of Dialog + PROC + Proc_DialogFlagSetup((STRING)_Dialog,(GUIDSTRING)_Speaker1) + THEN + DB_NOOP(1); + + PROC + Proc_DialogFlagSetup((STRING)_Dialog,(GUIDSTRING)_Speaker1,(GUIDSTRING)_Speaker2) + THEN + DB_NOOP(1); + + PROC + Proc_DialogFlagSetup((STRING)_Dialog,(GUIDSTRING)_Speaker1,(GUIDSTRING)_Speaker2,(GUIDSTRING)_Speaker3) + THEN + DB_NOOP(1); + + PROC + Proc_DialogFlagSetup((STRING)_Dialog,(GUIDSTRING)_Speaker1,(GUIDSTRING)_Speaker2,(GUIDSTRING)_Speaker3,(GUIDSTRING)_Speaker4) + THEN + DB_NOOP(1); + + PROC + Proc_DialogFlagSetup((STRING)_Dialog,(GUIDSTRING)_Speaker1,(GUIDSTRING)_Speaker2,(GUIDSTRING)_Speaker3,(GUIDSTRING)_Speaker4,(GUIDSTRING)_Speaker5) + THEN + DB_NOOP(1); + + PROC + Proc_DialogFlagSetup((STRING)_Dialog,(GUIDSTRING)_Speaker1,(GUIDSTRING)_Speaker2,(GUIDSTRING)_Speaker3,(GUIDSTRING)_Speaker4,(GUIDSTRING)_Speaker5,(GUIDSTRING)_Speaker6) + THEN + DB_NOOP(1); + //END_REGION + + IF + DialogEnded(_,_Inst) + THEN + DB_MarkedForDelete(_Inst); + ProcClearDialogFlagsForPlayers(_Inst); + ProcClearDialogFlagsForNPCs(_Inst); + + //REGION Animal Food Dialogs + PROC + ProcSetAnimalFoodEvents((CHARACTERGUID)_Player,_) + THEN + SetVarInteger(_Player,"GEN_HasAnimalFood",0); + + PROC + ProcSetAnimalFoodEvents((CHARACTERGUID)_Player,(CHARACTERGUID)_Npc) + AND + DB_AnimalFoodVars(_Var) + AND + GetVarFixedString(_Npc,_Var,_TempVal) + AND + _TempVal!="DONTEAT" + AND + QryItemTemplateInMagicPockets(_Player,_tempVal) + THEN + SetVarInteger(_Player,"GEN_HasAnimalFood",1); + + PROC + ProcGiveAnimalFood((CHARACTERGUID)_Player,(CHARACTERGUID)_Npc) + AND + DB_AnimalFoodVars(_Var) + AND + GetVarFixedString(_Npc,_Var,_TempVal) + AND + _TempVal!="DONTEAT" + AND + NOT DB_FoodGiven(_Player) + AND + QryRemoveItemTemplateFromMagicPockets(_Player,_TempVal,1) + THEN + DB_FoodGiven(_Player); + CharacterAddAttitudeTowardsPlayer(_Npc,_Player,5); + + PROC + ProcGiveAnimalFood(_Player,_Npc) + THEN + NOT DB_FoodGiven(_Player); + + IF + ObjectFlagSet("GEN_PlayerGivesFood",_Player,_Inst) + AND + DB_DialogNPCs(_Inst,_Npc,1) + THEN + ProcGiveAnimalFood((CHARACTERGUID)_Player,(CHARACTERGUID)_Npc); + //END_REGION + + //REGION Dialogs that must not be interceptable by custom scripts + //END_REGION + + //REGION Custom global overrides + PROC + PROC_GLOBAL_DialogCustomOverride((GUIDSTRING)_Npc,(GUIDSTRING)_Player) + THEN + DB_NOOP(1); + + PROC + SelectAndStartDialog((GUIDSTRING)_Player,(GUIDSTRING)_Npc) + THEN + PROC_GLOBAL_DialogCustomOverride(_Npc,_Player); + + //END_REGION + + //REGION Custom script dialog Intercepts + PROC + PROC_GLOBAL_DialogStartRequested((GUIDSTRING)_Npc,(GUIDSTRING)_Player) + THEN + DB_NOOP(1); + + PROC + SelectAndStartDialog((GUIDSTRING)_Player,(GUIDSTRING)_Npc) + AND + NOT DB_FoundDialog(_Npc,_Player) + THEN + // Reverse order of parameters to be consisted with DialogStartRequested() + PROC_GLOBAL_DialogStartRequested(_Npc,_Player); + //END_REGION + + //REGION Hostile Dialog + PROC + StartHostileDialog((GUIDSTRING)_Player,(GUIDSTRING)_Npc) + AND + IsTagged(_Npc,"ANIMAL",1) + THEN + DB_FoundDialog(_Npc,_Player); + ProcSetAnimalFoodEvents((CHARACTERGUID)_Player,(CHARACTERGUID)_Npc); + Proc_StartDialog(0,"GEB_Default_AnimalHostile",_Npc,_Player); //TODO Change this Dialog to a new Style Dialog + + PROC + StartHostileDialog((GUIDSTRING)_Player,(GUIDSTRING)_Npc) + AND + IsTagged(_Npc,"ANIMAL",0) + THEN + StartHostileDialog_1(_Player,_Npc); + + PROC + StartHostileDialog_1((GUIDSTRING)_Player,(GUIDSTRING)_Npc) + AND + DB_HostileDialog(_Npc,(STRING)_Dialog) + THEN + DB_FoundDialog(_Npc,_Player); + Proc_StartDialog(0,_Dialog,_Npc,_Player); + + PROC + StartHostileDialog_1((GUIDSTRING)_Player,(GUIDSTRING)_Npc) + AND + NOT DB_FoundDialog(_Npc,_Player) + THEN + DB_FoundDialog(_Npc,_Player); + Proc_StartDialog(0,"GEB_Default_Hostile",_Npc,_Player); + //END_REGION + + //REGION Companion Redirects & ADs + PROC + SelectAndStartDialog((GUIDSTRING)_Player,(GUIDSTRING)_OtherPlayer) + AND + NOT DB_FoundDialog(_OtherPlayer,_Player) + AND + DB_IsPlayer((CHARACTERGUID)_Player) + AND + DB_IsPlayer((CHARACTERGUID)_OtherPlayer) + AND + CharacterGetReservedUserID(_Player,_PID) + AND + CharacterGetReservedUserID(_OtherPlayer,_OPID) + AND + _PID != _OPID + THEN + Proc_StartDialog(1,"GLO_AD_CompanionCantTalk",_OtherPlayer); + DB_FoundDialog(_OtherPlayer,_Player); + + PROC + SelectAndStartDialog((GUIDSTRING)_Player,(GUIDSTRING)_OtherPlayer) + AND + NOT DB_FoundDialog(_OtherPlayer,_Player) + AND + DB_IsPlayer((CHARACTERGUID)_Player) + AND + DB_IsPlayer((CHARACTERGUID)_OtherPlayer) + AND + IsTagged(_Player,"AVATAR",0) + AND + IsTagged(_OtherPlayer,"AVATAR",0) + AND + DB_CompanionAvatarBond(_OtherPlayer,_Avatar) + AND + IsTagged(_Avatar,"AVATAR",1) + AND + CharacterGetReservedUserID(_Avatar,_PID) + AND + CharacterGetReservedUserID(_OtherPlayer,_PID) + AND + QRY_SpeakerIsAvailableAndInDialogRange(_Avatar,_OtherPlayer) + THEN + SelectAndStartDialog(_Avatar,_OtherPlayer); + MakePlayerActive(_Avatar); + DB_FoundDialog(_OtherPlayer,_Player); + + PROC + SelectAndStartDialog((GUIDSTRING)_Player,(GUIDSTRING)_OtherPlayer) + AND + NOT DB_FoundDialog(_OtherPlayer,_Player) + AND + DB_IsPlayer((CHARACTERGUID)_Player) + AND + DB_IsPlayer((CHARACTERGUID)_OtherPlayer) + AND + IsTagged(_Player,"AVATAR",0) + AND + IsTagged(_OtherPlayer,"AVATAR",0) + THEN + Proc_StartDialog(1,"GLO_AD_CompanionCantTalk",_OtherPlayer); + DB_FoundDialog(_OtherPlayer,_Player); + + PROC + SelectAndStartDialog((GUIDSTRING)_Player,(GUIDSTRING)_OtherPlayer) + AND + NOT DB_FoundDialog(_OtherPlayer,_Player) + AND + DB_IsPlayer((CHARACTERGUID)_Player) + AND + DB_IsPlayer((CHARACTERGUID)_OtherPlayer) + AND + ObjectGetFlag(_OtherPlayer,"GLO_Polymorphed",1) + THEN + Proc_StartDialog(1,"GLO_AD_CompanionCantTalk",_OtherPlayer); + DB_FoundDialog(_OtherPlayer,_Player); + + PROC + SelectAndStartDialog((GUIDSTRING)_Player,(GUIDSTRING)_Companion) + AND + NOT DB_FoundDialog(_Companion,_Player) + AND + DB_GLO_PartyMembers_RecruiteeAvatarBond((CHARACTERGUID)_Companion,(CHARACTERGUID)_Player2) + AND + _Player != _Player2 + THEN + Proc_StartDialog(0,"GLO_NonBondedCompanionDialog",_Companion,_Player); + DB_FoundDialog(_Companion,_Player); + + PROC + SelectAndStartDialog((GUIDSTRING)_Player,(GUIDSTRING)_Companion) + AND + NOT DB_FoundDialog(_Companion,_Player) + AND + DB_RelationshipDialogs((CHARACTERGUID)_Companion,_Dialog) + AND + DB_CompanionAvatarBond((CHARACTERGUID)_Companion,(CHARACTERGUID)_Player) + AND + QRY_StartDialog(0,_Dialog,_Companion,_Player) + THEN + NOT DB_RelationshipDialogs(_Companion,_Dialog); + DB_FoundDialog(_Companion,_Player); + + PROC + SelectAndStartDialog((GUIDSTRING)_Player,(GUIDSTRING)_Npc) + AND + IsTagged(_NPC,"ANIMAL",0) + AND + NOT DB_FoundDialog(_Npc,_Player) + AND + CharacterGetAttitudeTowardsPlayer((CHARACTERGUID)_Npc,(CHARACTERGUID)_Player,_Att) + AND + _Att <= -45 + AND + NOT _Npc.DB_IsPlayer() + AND + NOT DB_NoLowAttitudeDialog(_Npc) + THEN + StartHostileDialog(_Player,_Npc); + DB_FoundDialog(_Npc,_Player); + //END_REGION + + //REGION Threatened Dialog + PROC + SelectAndStartDialog((GUIDSTRING)_Player,(GUIDSTRING)_NPC) + AND + NOT DB_FoundDialog(_NPC,_Player) + AND + CharacterIsInFightMode((CHARACTERGUID)_Player,1) + AND + NOT DB_IsPlayer((CHARACTERGUID)_NPC) + AND + NOT DB_BlockThreatenedDialog(_NPC) + AND + IsTagged(_NPC,"ANIMAL",0) + AND + NOT DB_CombatCharacters(_Player,_) + THEN + StartThreatenedDialog(_NPC,_Player); + + PROC + StartThreatenedDialog((GUIDSTRING)_NPC,(GUIDSTRING)_Player) + AND + CharacterCanTrade((CHARACTERGUID)_NPC,_CanTrade) + THEN + DB_FoundDialog(_NPC,_Player); + Proc_StartDialog(0,"GEB_Warning_Weapons_StartDialog",_NPC,_Player); + CharacterSetCanTrade(_NPC,0); + DB_CouldTrade(_NPC,_CanTrade); + + IF + DialogEnded("GEB_Warning_Weapons_StartDialog",_Inst) + AND + DB_DialogNPCs(_Inst,_NPC,1) + AND + DB_CouldTrade((CHARACTERGUID)_NPC,_CanTrade) + THEN + NOT DB_CouldTrade(_NPC,_CanTrade); + CharacterSetCanTrade((CHARACTERGUID)_NPC,_CanTrade); + + //END_REGION + + //REGION Script intercept after generics + PROC + PROC_GLOBAL_DialogStartRequested_AfterGenerics((GUIDSTRING)_Npc,(GUIDSTRING)_Player) + THEN + DB_NOOP(1); + + PROC + SelectAndStartDialog((GUIDSTRING)_Player,(GUIDSTRING)_Npc) + AND + NOT DB_FoundDialog(_Npc,_Player) + THEN + PROC_GLOBAL_DialogStartRequested_AfterGenerics(_Npc,_Player); + //END_REGION + + //REGION Dialog Start by clicking on NPC + PROC + SelectAndStartDialog((GUIDSTRING)_Player,(GUIDSTRING)_Npc) + AND + NOT DB_FoundDialog(_Npc,_Player) + THEN + DB_FoundDialog(_Npc,_Player); + NPCDialogStartRequested(_Npc,_Player);// start the default dialog + + PROC + SelectAndStartDialog((GUIDSTRING)_Player,(GUIDSTRING)_Npc) + THEN + NOT DB_FoundDialog(_Npc,_Player); + + IF + DialogStartRequested(_Npc2,_Npc1) + AND + QRY_SpeakerIsAvailable(_Npc1) + AND + QRY_SpeakerIsAvailable(_Npc2) + THEN + SelectAndStartDialog(_Npc1,_Npc2); + + //END_REGION + + IF + CharacterDying(_Char) + THEN + DialogRequestStop(_Char); + + IF + CharacterUnlockedTalent(_Char,"AnimalEmpathy") + THEN + SetTag(_char,"PETPAL"); + + IF + CharacterLockedTalent(_Char,"AnimalEmpathy") + THEN + ClearTag(_char,"PETPAL"); + + IF + DB_IsPlayer(_Char) + AND + CharacterHasTalent(_Char,"AnimalEmpathy",1) + THEN + SetTag(_char,"PETPAL"); + + IF + CharacterCreationFinished(_Char) + AND + _Char != NULL_00000000-0000-0000-0000-000000000000 + AND + CharacterHasTalent(_Char,"AnimalEmpathy",0) + THEN + ClearTag(_char,"PETPAL"); + + //REGION Child dialogs + IF + ChildDialogRequested(_ChildDialog,_ParentInstance,_TargetInstance) + AND + DB_DialogPlayers(_ParentInstance,_Player,1) + THEN + ProcStartChildDialog(_ChildDialog,_ParentInstance,_TargetInstance); + + PROC + ProcStartChildDialog((STRING)_ChildDialog,(INTEGER)_ParentInstance,(INTEGER)_TargetInstance) + THEN + DB_TargetInstancespeakers(_TargetInstance,1,(CHARACTERGUID)NULL_00000000-0000-0000-0000-000000000000); + DB_TargetInstancespeakers(_TargetInstance,2,(CHARACTERGUID)NULL_00000000-0000-0000-0000-000000000000); + DB_TargetInstancespeakers(_TargetInstance,3,(CHARACTERGUID)NULL_00000000-0000-0000-0000-000000000000); + DB_TargetInstancespeakers(_TargetInstance,4,(CHARACTERGUID)NULL_00000000-0000-0000-0000-000000000000); + + PROC + ProcStartChildDialog((STRING)_ChildDialog,(INTEGER)_ParentInstance,(INTEGER)_TargetInstance) + AND + DB_DialogNumPlayers(_ParentInstance,_NumPlayers) + AND + DB_DialogNumNPCs(_ParentInstance,_NumNPCs) + AND + IntegerSum(_NumPlayers,_NumNPCs,_Total) + AND + _Total > 4 + THEN + DebugBreak("too many speakers to fit in the child dialog! Need custom scripting and/or extension on speaker limit"); + + PROC + ProcStartChildDialog(_,(INTEGER)_ParentInstance,(INTEGER)_TargetInstance) + AND + DB_DialogNPCs(_ParentInstance,_NPC,_Index) + THEN + NOT DB_TargetInstancespeakers(_TargetInstance,_Index,NULL_00000000-0000-0000-0000-000000000000); + DB_TargetInstancespeakers(_TargetInstance,_Index,(CHARACTERGUID)_NPC); + + PROC + ProcStartChildDialog(_,(INTEGER)_ParentInstance,(INTEGER)_TargetInstance) + AND + DB_DialogPlayers(_ParentInstance,_Player,_Index) + AND + DB_DialogNumNPCs(_ParentInstance,_NumNPCs) + AND + IntegerSum(_NumNPCs,_Index,_PlayerIndex) + THEN + NOT DB_TargetInstancespeakers(_TargetInstance,_PlayerIndex,NULL_00000000-0000-0000-0000-000000000000); + DB_TargetInstancespeakers(_TargetInstance,_PlayerIndex,(CHARACTERGUID)_Player); + + PROC + ProcStartChildDialog(_ChildDialog,(INTEGER)_ParentInstance,(INTEGER)_TargetInstance) + AND + DB_TargetInstancespeakers(_TargetInstance,1,_Speaker1) + AND + DB_TargetInstancespeakers(_TargetInstance,2,_Speaker2) + AND + DB_TargetInstancespeakers(_TargetInstance,3,_Speaker3) + AND + DB_TargetInstancespeakers(_TargetInstance,4,_Speaker4) + AND + DialogStartChildDialog(_ChildDialog,_ParentInstance,_TargetInstance,_Speaker1,_Speaker2,_Speaker3,_Speaker4,_) + THEN + DB_NOOP(1); + + PROC + ProcStartChildDialog(_ChildDialog,(INTEGER)_ParentInstance,(INTEGER)_TargetInstance) + AND + DB_TargetInstancespeakers(_TargetInstance,_Index,_Speaker) + THEN + NOT DB_TargetInstancespeakers(_TargetInstance,_Index,_Speaker); + //END_REGION + + } + EXIT + { + + } +} +Goal(4).Title("__GLOBAL_ExplorationBonus"); +Goal(4) +{ + INIT + { + + } + KB + { + IF + DB_ExplorationZones((TRIGGERGUID)_Trigger,(INTEGER)_Act,(INTEGER)_ActPArt,(INTEGER)_Gain) + AND + NOT DB_Subregion(_Trigger,_,_) //Subregions can give XP without being Oneshot + THEN + ProcTriggerRegisterForPlayers(_Trigger); + + IF + CharacterEnteredTrigger((CHARACTERGUID)_Player,(TRIGGERGUID)_Trigger) + AND + DB_ExplorationZones(_Trigger,(INTEGER)_Act,(INTEGER)_ActPart,(INTEGER)_Gain) + THEN + ProcAddXPToParty(_Player,_Trigger); + ProcCheckRemoveExplorationZone(_Trigger); + + + PROC + ProcAddXPToParty((CHARACTERGUID)_Player,(TRIGGERGUID)_Trigger) + AND + DB_ExplorationZones(_Trigger,_Act,_ActPart,_Gain) + AND + DB_IsPlayer(_OtherPlayer) + AND + NOT DB_ExplorationXPGiven(_OtherPlayer,_Trigger) + AND + CharacterIsInPartyWith(_OtherPlayer,_Player,1) + THEN + DB_ExplorationXPGiven(_OtherPlayer,_Trigger); + CharacterAddExplorationExperience(_OtherPlayer,_Act,_ActPart,_Gain); + + //Dont unregister if also a Subregion + IF + DB_ExplorationXPGiven(_OtherPlayer,_Trigger) + AND + NOT DB_Subregion(_Trigger,_,_) + THEN + TriggerUnregisterForCharacter(_Trigger,_OtherPlayer); + + PROC + ProcCheckRemoveExplorationZone((TRIGGERGUID)_Trigger) + AND + DB_IsPlayer(_Player) + AND + NOT DB_ExplorationXPGiven(_Player,_Trigger) + THEN + DB_ExplorationZoneStillOpen(1); + + PROC + ProcCheckRemoveExplorationZone(_Trigger) + AND + NOT DB_ExplorationZoneStillOpen(1) + AND + DB_ExplorationZones(_Trigger,_Act,_ActPart,_Gain) + THEN + NOT DB_ExplorationZones(_Trigger,_Act,_ActPart,_Gain); + + PROC + ProcCheckRemoveExplorationZone(_Trigger) + THEN + NOT DB_ExplorationZoneStillOpen(1); + + IF + CharacterJoinedParty(_Char) + AND + DB_ExplorationZones(_Trigger,_Act,_ActPart,_Gain) + AND + DB_ExplorationXPGiven(_Player,_Trigger) + AND + CharacterIsInPartyWith(_Player,_Char,1) + THEN + DB_ExplorationXPGiven(_Char,_Trigger); + ProcCheckRemoveExplorationZone(_Trigger); + + } + EXIT + { + + } +} +Goal(5).Title("__GLOBAL_HiddenWalls"); +Goal(5) +{ + INIT + { + DB_HiddenWallCount(0); + + } + KB + { + //REGION Use event to open hidden wall + + IF + GlobalFlagSet(_flag) + AND + DB_HiddenWallEvent((STRING)_flag, (INTEGER)_wallIndex) + THEN + PROC_OpenWall(_WallIndex); + + //END_REGION + + //REGION Action When Player Uses Item To Open Wall + + IF + CharacterUsedItem(_Player, _Item) + AND + _Player.DB_IsPlayer() + AND + DB_HiddenWallItem((ITEMGUID)_Item, (INTEGER)_WallIndex) + THEN + PROC_CommentHiddenEffect(_Player); + PROC_OpenWall(_WallIndex); + + //END_REGION + + //REGION Action When Player Uses Trigger To Open Wall + + IF + DB_HiddenWallTrigger(_trigger, _) + THEN + DB_HW_CharCountInTrigger(_trigger, 0); + + IF + CharacterEnteredTrigger(_, _trigger) + AND + DB_HiddenWallTrigger((TRIGGERGUID)_trigger, (INTEGER)_wallIndex) + AND + DB_HW_CharCountInTrigger(_trigger, _current) + AND + IntegerSum(_current, 1, _new) + THEN + NOT DB_HW_CharCountInTrigger(_trigger, _current); + DB_HW_CharCountInTrigger(_trigger, _new); + + IF + CharacterLeftTrigger(_, _trigger) + AND + DB_HiddenWallTrigger((TRIGGERGUID)_trigger, (INTEGER)_wallIndex) + AND + DB_HW_CharCountInTrigger(_trigger, _current) + AND + IntegerSubtract(_current, 1, _new) + THEN + NOT DB_HW_CharCountInTrigger(_trigger, _current); + DB_HW_CharCountInTrigger(_trigger, _new); + + IF + DB_HW_CharCountInTrigger(_trigger, _current) + AND + _current < 0 + THEN + NOT DB_HW_CharCountInTrigger(_trigger, _current); + DB_HW_CharCountInTrigger(_trigger, 0); + + IF + DB_HW_CharCountInTrigger(_trigger, _current) + AND + _current > 0 + AND + DB_HiddenWallTrigger(_trigger, _wallIndex) + THEN + PROC_OpenWall(_wallIndex); + + IF + DB_HW_CharCountInTrigger(_trigger, _current) + AND + _current == 0 + AND + DB_HiddenWallTrigger(_trigger, _wallIndex) + THEN + PROC_CloseWall(_wallIndex); + + + //END_REGION + + //REGION Action When Player Uses Skills To Open Wall + + IF + StoryEvent(_Wall, "Open") + AND + DB_HiddenWall(_WallIndex, (ITEMGUID)_Wall) + THEN + PROC_OpenWall(_WallIndex); + + //END_REGION + + //REGION Registering all the hidden walls + + PROC + PROC_Register_HiddenWall((ITEMGUID)_Wall) + AND + NOT DB_HiddenWall(_, (ITEMGUID)_Wall) + AND + DB_HiddenWallCount(_Current) + AND + IntegerSum(_Current, 1, _New) + THEN + NOT DB_HiddenWallCount(_Current); + DB_HiddenWallCount(_New); + DB_HiddenWall((INTEGER)_New, (ITEMGUID)_Wall); + DB_HW_ClosedWalls(_New); + + //END_REGION + + //REGION Open/Close Hidden walls + + PROC + PROC_OpenWall((INTEGER)_WallIndex) + AND + DB_HW_ClosedWalls(_WallIndex) + AND + DB_HiddenWall(_WallIndex, _Wall) + THEN + NOT DB_HW_ClosedWalls(_WallIndex); + PlayEffect(_Wall, "RS3_FX_GP_ScriptedEvent_Teleport_GenericSmoke_01"); + SetOnStage(_Wall, 0); + + PROC + PROC_CloseWall((INTEGER)_WallIndex) + AND + NOT DB_HW_ClosedWalls(_WallIndex) + AND + DB_HiddenWall(_WallIndex, _Wall) + THEN + DB_HW_ClosedWalls(_WallIndex); + PlayEffect(_Wall, "RS3_FX_GP_ScriptedEvent_Teleport_GenericSmoke_01"); + SetOnStage(_Wall, 1); + + //END_REGION + + } + EXIT + { + + } +} +Goal(6).Title("__GLOBAL_ItemInteraction"); +Goal(6) +{ + INIT + { + + } + KB + { + IF + CharacterUsedItemTemplate(_Player, _Template, _) + AND + _Player.DB_IsPlayer() + AND + DB_RecipeBook(_Template, (STRING)_ID) + THEN + UnlockJournalRecipe(_ID); + NOT DB_RecipeBook(_Template, _ID); + + PROC + Proc_ItemRotateYduration((ITEMGUID)_Item,(REAL)_Angle,(INTEGER)_DurationMS) + AND + RealProduct(_Angle,1000.0,_AngleProd) + AND + Real(_DurationMS,_DurationReal) + AND + RealDivide(_AngleProd,_DurationReal,_Speed) + THEN + ItemRotateY(_Item,_Angle,_Speed); + + PROC + Proc_ItemRotateToAngleYduration((ITEMGUID)_Item,(REAL)_Angle,(INTEGER)_DurationMS) + AND + RealProduct(_Angle,1000.0,_AngleProd) + AND + Real(_DurationMS,_DurationReal) + AND + RealDivide(_AngleProd,_DurationReal,_Speed) + THEN + ItemRotateY(_Item,_Angle,_Speed); + + } + EXIT + { + + } +} +Goal(7).Title("__GLOBAL_ItemRotationPuzzles"); +Goal(7) +{ + INIT + { + DB_IRP_Internal_IndexCount(0); + + } + KB + { + //REGION Initialization of databases + + PROC + PROC_Puzzle_RegisterRotatingItem((STRING)_puzzleName, (ITEMGUID)_item, (INTEGER)_solution) + AND + DB_IRP_Internal_IndexCount(_index) + THEN + DB_IRP_IncreaseInternalCount(); + DB_IRP_Internal_Items((INTEGER)_index, (ITEMGUID)_item, 0); + DB_IRP_Internal_Solutions((INTEGER)_index, (INTEGER)_solution); + DB_IRP_Internal_Puzzles((STRING)_puzzleName, (INTEGER)_index, 0); + PROC_IRP_Internal_IncreasePuzzleItemcount(_puzzleName); + + PROC + DB_IRP_IncreaseInternalCount() + AND + DB_IRP_Internal_IndexCount(_indexCount) + AND + IntegerSum(_indexCount, 1, _newIndex) + THEN + NOT DB_IRP_Internal_IndexCount(_indexCount); + DB_IRP_Internal_IndexCount(_newIndex); + + PROC + PROC_ItemRotatePuzzle_AddItemToHandle((ITEMGUID)_handle, (ITEMGUID)_item) + AND + DB_IRP_Internal_Items(_index, _item, _) + THEN + DB_IRP_Internal_Handles(_handle, _index); + + PROC + PROC_IRP_Internal_IncreasePuzzleItemcount((STRING)_puzzleName) + AND + NOT DB_IRP_Internal_PuzzlesTotalItemCount(_puzzleName, _) + THEN + DB_IRP_Internal_PuzzlesTotalItemCount(_puzzleName, 0); + DB_IRP_Internal_PuzzlesItemsSolved(_puzzleName, 0); + + PROC + PROC_IRP_Internal_IncreasePuzzleItemcount((STRING)_puzzleName) + AND + DB_IRP_Internal_PuzzlesTotalItemCount(_puzzleName, _count) + AND + IntegerSum(_count, 1, _newTotal) + THEN + NOT DB_IRP_Internal_PuzzlesTotalItemCount(_puzzleName, _count); + DB_IRP_Internal_PuzzlesTotalItemCount(_puzzleName, _newTotal); + + //END_REGION + + + //REGION Rotate items + + IF + CharacterUsedItem(_player, _handle) + AND + _player.DB_IsPlayer() + AND + DB_IRP_Internal_Handles(_handle, _index) + AND + DB_IRP_Internal_Items(_index, _item, _) + THEN + PROC_IRP_RotateItem(_item); + + IF + CharacterUsedItem(_player, _item) + AND + _player.DB_IsPlayer() + AND + DB_IRP_Internal_Items(_index, _item, _) + THEN + PROC_IRP_RotateItem(_item); + + PROC + PROC_IRP_RotateItem((ITEMGUID)_item) + AND + DB_IRP_Internal_Items(_index, _item, _turnCount) + AND + DB_IRP_Internal_Puzzles(_puzzleName, _index, _) + AND + IntegerSum(_turnCount, 1, _newTurnCount) + THEN + NOT DB_IRP_Internal_Items(_index, _item, _turnCount); + DB_IRP_Internal_Items(_index, _item, _newTurnCount); + ItemRotateY(_item, 90.0, 135.0); + PROC_IRP_CheckItemState(_item); + PROC_IRP_CheckPuzzleState(_puzzleName); + + IF + DB_IRP_Internal_Items(_index, _item, 4) + THEN + NOT DB_IRP_Internal_Items(_index, _item, 4); + DB_IRP_Internal_Items(_index, _item, 0); + + //END_REGION + + //REGION Check for item solution + + PROC + PROC_IRP_CheckItemState((ITEMGUID)_item) + AND + DB_IRP_Internal_Items(_index, _item, _turnCount) + AND + DB_IRP_Internal_Puzzles(_puzzleName, _index, 0) + AND + DB_IRP_Internal_Solutions(_index, _solution) + AND + _turnCount == _solution + THEN + NOT DB_IRP_Internal_Puzzles(_puzzleName, _index, 0); + DB_IRP_Internal_Puzzles(_puzzleName, _index, 1); + + PROC + PROC_IRP_CheckItemState((ITEMGUID)_item) + AND + DB_IRP_Internal_Items(_index, _item, _turnCount) + AND + DB_IRP_Internal_Puzzles(_puzzleName, _index, 1) + AND + DB_IRP_Internal_Solutions(_index, _solution) + AND + NOT _turnCount == _solution + THEN + NOT DB_IRP_Internal_Puzzles(_puzzleName, _index, 1); + DB_IRP_Internal_Puzzles(_puzzleName, _index, 0); + + //END_REGION + + //REGION Check for puzzle solution + + PROC + PROC_IRP_CheckPuzzleState((STRING)_puzzleName) + AND + DB_IRP_Internal_Puzzles(_puzzleName, _, 1) + AND + DB_IRP_Internal_PuzzlesItemsSolved(_puzzleName, _count) + AND + IntegerSum(_count, 1, _new) + THEN + NOT DB_IRP_Internal_PuzzlesItemsSolved(_puzzleName, _count); + DB_IRP_Internal_PuzzlesItemsSolved(_puzzleName, _new); + + PROC + PROC_IRP_CheckPuzzleState((STRING)_puzzleName) + AND + DB_IRP_Internal_PuzzlesItemsSolved(_puzzleName, _itemsSolved) + AND + NOT DB_IRP_Internal_PuzzlesTotalItemCount(_puzzleName, _itemsSolved) + THEN + NOT DB_IRP_Internal_PuzzlesItemsSolved(_puzzleName, _itemsSolved); + DB_IRP_Internal_PuzzlesItemsSolved(_puzzleName, 0); + GlobalClearFlag(_puzzleName); + + PROC + PROC_IRP_CheckPuzzleState((STRING)_puzzleName) + AND + DB_IRP_Internal_PuzzlesItemsSolved(_puzzleName, _itemsSolved) + AND + DB_IRP_Internal_PuzzlesTotalItemCount(_puzzleName, _itemsSolved) + THEN + NOT DB_IRP_Internal_PuzzlesItemsSolved(_puzzleName, _itemsSolved); + DB_IRP_Internal_PuzzlesItemsSolved(_puzzleName, 0); + GlobalSetFlag(_puzzleName); + + //END_REGION + + + } + EXIT + { + + } +} +Goal(8).Title("__OneshotDialogs"); +Goal(8) +{ + INIT + { + + } + KB + { + //REGION One Shot Normal Dialogs + IF + DB_OneShot_PlayerOnlyDialogTrigger((TRIGGERGUID)_Trigger,(STRING)_Dialog,(CHARACTERGUID)_NPC) + THEN + DB_OneShotPlayerOnlyTrigger(_Trigger); + DB_OneShot_DialogSpeakerCount(_Trigger,_Dialog,1); + DB_OneShot_DialogSpeakers(_Trigger,_Npc,1); + + IF + DB_OneShot_DialogTrigger((TRIGGERGUID)_Trigger,(STRING)_Dialog,(CHARACTERGUID)_NPC) + THEN + DB_OneShotPlayerTrigger(_Trigger); + DB_OneShot_DialogSpeakerCount(_Trigger,_Dialog,1); + DB_OneShot_DialogSpeakers(_Trigger,_Npc,1); + + IF + DB_OneShot_DialogTrigger((TRIGGERGUID)_Trigger,(STRING)_Dialog,(CHARACTERGUID)_NPC,(CHARACTERGUID)_NPC2) + THEN + DB_OneShotPlayerTrigger(_Trigger); + DB_OneShot_DialogSpeakerCount(_Trigger,_Dialog,2); + DB_OneShot_DialogSpeakers(_Trigger,_Npc,1); + DB_OneShot_DialogSpeakers(_Trigger,_Npc2,2); + + IF + DB_OneShot_DialogTrigger((TRIGGERGUID)_Trigger,(STRING)_Dialog,(CHARACTERGUID)_NPC,(CHARACTERGUID)_NPC2,(CHARACTERGUID)_NPC3) + THEN + DB_OneShotPlayerTrigger(_Trigger); + DB_OneShot_DialogSpeakerCount(_Trigger,_Dialog,3); + DB_OneShot_DialogSpeakers(_Trigger,_Npc,1); + DB_OneShot_DialogSpeakers(_Trigger,_Npc2,2); + DB_OneShot_DialogSpeakers(_Trigger,_Npc3,3); + + IF + DB_OneShot_DialogTrigger((TRIGGERGUID)_Trigger,(STRING)_Dialog,(CHARACTERGUID)_NPC,(CHARACTERGUID)_NPC2,(CHARACTERGUID)_NPC3,(CHARACTERGUID)_NPC4) + THEN + DB_OneShotPlayerTrigger(_Trigger); + DB_OneShot_DialogSpeakerCount(_Trigger,_Dialog,4); + DB_OneShot_DialogSpeakers(_Trigger,_Npc,1); + DB_OneShot_DialogSpeakers(_Trigger,_Npc2,2); + DB_OneShot_DialogSpeakers(_Trigger,_Npc3,3); + DB_OneShot_DialogSpeakers(_Trigger,_Npc4,4); + + PROC + ProcCheckIfNPCsBusy((TRIGGERGUID)_Trigger) + AND + DB_OneShot_DialogSpeakers(_Trigger,_Npc,_) + AND + NOT QRY_SpeakerIsAvailable(_Npc,1) + THEN + DB_OneShot_BusyNPC(_Trigger); + + PROC + ProcDoStartOneShotDialog((CHARACTERGUID)_Player,(TRIGGERGUID)_Trigger) + AND + DB_OneShot_DialogSpeakerCount(_Trigger,_Dialog,1) + AND + DB_OneShot_DialogSpeakers(_Trigger,_Npc,1) + THEN + Proc_StartDialog(0,_Dialog,_Npc,_Player); + + PROC + ProcDoStartOneShotDialog((CHARACTERGUID)_Player,(TRIGGERGUID)_Trigger) + AND + DB_OneShot_DialogSpeakerCount(_Trigger,_Dialog,2) + AND + DB_OneShot_DialogSpeakers(_Trigger,_Npc,1) + AND + DB_OneShot_DialogSpeakers(_Trigger,_Npc2,2) + THEN + Proc_StartDialog(0,_Dialog,_Npc,_Npc2,_Player); + + PROC + ProcDoStartOneShotDialog((CHARACTERGUID)_Player,(TRIGGERGUID)_Trigger) + AND + DB_OneShot_DialogSpeakerCount(_Trigger,_Dialog,3) + AND + DB_OneShot_DialogSpeakers(_Trigger,_Npc,1) + AND + DB_OneShot_DialogSpeakers(_Trigger,_Npc2,2) + AND + DB_OneShot_DialogSpeakers(_Trigger,_Npc3,3) + THEN + Proc_StartDialog(0,_Dialog,_Npc,_Npc2,_Npc3,_Player); + + PROC + ProcStartOneShotDialog((CHARACTERGUID)_Player,(TRIGGERGUID)_Trigger) + AND + NOT DB_OneShot_BusyNPC(_Trigger) + THEN + ProcDoStartOneShotDialog(_Player,_Trigger); + + PROC + ProcClearOneShotCount((TRIGGERGUID)_Trigger) + AND + DB_OneShot_DialogSpeakerCount(_Trigger,_Dialog,1) + AND + DB_OneShot_DialogTrigger((TRIGGERGUID)_Trigger,(STRING)_Dialog,(CHARACTERGUID)_NPC) + THEN + NOT DB_OneShot_DialogTrigger(_Trigger,_Dialog,_NPC); + + PROC + ProcClearOneShotCount((TRIGGERGUID)_Trigger) + AND + DB_OneShot_DialogSpeakerCount(_Trigger,_Dialog,2) + AND + DB_OneShot_DialogTrigger((TRIGGERGUID)_Trigger,(STRING)_Dialog,(CHARACTERGUID)_NPC,(CHARACTERGUID)_NPC2) + THEN + NOT DB_OneShot_DialogTrigger(_Trigger,_Dialog,_NPC,_NPC2); + + PROC + ProcClearOneShotCount((TRIGGERGUID)_Trigger) + AND + DB_OneShot_DialogSpeakerCount(_Trigger,_Dialog,3) + AND + DB_OneShot_DialogTrigger((TRIGGERGUID)_Trigger,(STRING)_Dialog,(CHARACTERGUID)_NPC,(CHARACTERGUID)_NPC2,(CHARACTERGUID)_NPC3) + THEN + NOT DB_OneShot_DialogTrigger(_Trigger,_Dialog,_NPC,_NPC2,_NPC3); + + + PROC + ProcClearOneShotCount((TRIGGERGUID)_Trigger) + AND + DB_OneShot_DialogSpeakerCount(_Trigger,_Dialog,4) + AND + DB_OneShot_DialogTrigger((TRIGGERGUID)_Trigger,(STRING)_Dialog,(CHARACTERGUID)_NPC,(CHARACTERGUID)_NPC2,(CHARACTERGUID)_NPC3,(CHARACTERGUID)_NPC4) + THEN + NOT DB_OneShot_DialogTrigger(_Trigger,_Dialog,_NPC,_NPC2,_NPC3,_NPC4); + + PROC + ProcClearOneShotCount((TRIGGERGUID)_Trigger) + AND + DB_OneShot_DialogSpeakerCount(_Trigger,_Dialog,_Count) + THEN + NOT DB_OneShot_DialogSpeakerCount(_Trigger,_Dialog,_Count); + + PROC + ProcClearOneShotSpeakers((TRIGGERGUID)_Trigger) + AND + DB_OneShot_DialogSpeakers(_Trigger,_Npc,_Count) + THEN + NOT DB_OneShot_DialogSpeakers(_Trigger,_Npc,_Count); + + PROC + ProcOneShotDialogCleanup((TRIGGERGUID)_Trigger) + THEN + ProcClearOneShotCount(_Trigger); + ProcClearOneShotSpeakers(_Trigger); + + PROC + RemoveOneShotDialog((TRIGGERGUID)_Trigger) + THEN + ProcOneShotDialogCleanup(_Trigger); + ProcTriggerUnregisterForPlayers(_Trigger); + NOT DB_OneShotPlayerTrigger(_Trigger); + + IF + DB_Dead(_Npc) + AND + DB_OneShot_DialogSpeakers(_Trigger,_Npc,_) + THEN + RemoveOneShotDialog(_Trigger); + + PROC + ProcOneShotTriggerEntered((CHARACTERGUID)_Player,(TRIGGERGUID)_Trigger) + AND + DB_OneShot_DialogSpeakerCount(_Trigger,_,_) + AND + QRY_SpeakerIsAvailable(_Player) + THEN + ProcCheckIfNPCsBusy(_Trigger); //TODO: do this busy check? Dialogs won't ever start if one of these NPCs is busy + ProcStartOneShotDialog(_Player,_Trigger); + + PROC + ProcStartOneShotDialog(_,(TRIGGERGUID)_Trigger) + THEN + NOT DB_OneShot_BusyNPC(_Trigger); + + PROC + ProcStartOneShotDialog(_,(TRIGGERGUID)_Trigger) + THEN + ProcOneShotDialogCleanup(_Trigger); + + //if the dialog is started (manually), cleanup so this doesn't get started again + IF + DialogStarted(_Dialog,_) + AND + DB_OneShot_DialogSpeakerCount(_Trigger,_Dialog,_Count) + THEN + ProcOneShotDialogCleanup(_Trigger); + ProcTriggerUnregisterForPlayers(_Trigger); + NOT DB_OneShotPlayerTrigger(_Trigger); //because this might not have triggerd yet and we don't want to leave these around + + //END_REGION + + //REGION One Shot Automated Dialogs + + IF + DB_OneShot_ADTrigger((TRIGGERGUID)_Trigger,(STRING)_Dialog,(CHARACTERGUID)_NPC) + THEN + DB_OneShotPlayerTrigger(_Trigger); + DB_OneShot_ADSpeakerCount(_Trigger,_Dialog,1); + DB_OneShot_ADSpeakers(_Trigger,_Npc,1); + + IF + DB_OneShot_ADTrigger((TRIGGERGUID)_Trigger,(STRING)_Dialog,(CHARACTERGUID)_NPC,(CHARACTERGUID)_NPC2) + THEN + DB_OneShotPlayerTrigger(_Trigger); + DB_OneShot_ADSpeakerCount(_Trigger,_Dialog,2); + DB_OneShot_ADSpeakers(_Trigger,_Npc,1); + DB_OneShot_ADSpeakers(_Trigger,_Npc2,2); + + PROC + ProcDoStartOneShotAD((CHARACTERGUID)_Player,(TRIGGERGUID)_Trigger) + AND + DB_OneShot_ADSpeakerCount(_Trigger,_Dialog,1) + AND + DB_OneShot_ADSpeakers(_Trigger,_Npc,1) + THEN + Proc_StartDialog(1,_Dialog,_Npc); + + PROC + ProcDoStartOneShotAD((CHARACTERGUID)_Player,(TRIGGERGUID)_Trigger) + AND + DB_OneShot_ADSpeakerCount(_Trigger,_Dialog,2) + AND + DB_OneShot_ADSpeakers(_Trigger,_Npc,1) + AND + DB_OneShot_ADSpeakers(_Trigger,_Npc2,2) + THEN + Proc_StartDialog(1,_Dialog,_Npc,_Npc2); + + PROC + ProcClearOneShotADCount((TRIGGERGUID)_Trigger) + AND + DB_OneShot_ADSpeakerCount(_Trigger,_Dialog,1) + AND + DB_OneShot_ADTrigger((TRIGGERGUID)_Trigger,(STRING)_Dialog,(CHARACTERGUID)_NPC) + THEN + NOT DB_OneShot_ADTrigger(_Trigger,_Dialog,_NPC); + + PROC + ProcClearOneShotADCount((TRIGGERGUID)_Trigger) + AND + DB_OneShot_ADSpeakerCount(_Trigger,_Dialog,2) + AND + DB_OneShot_ADTrigger((TRIGGERGUID)_Trigger,(STRING)_Dialog,(CHARACTERGUID)_NPC,(CHARACTERGUID)_NPC2) + THEN + NOT DB_OneShot_ADTrigger(_Trigger,_Dialog,_NPC,_NPC2); + + PROC + ProcClearOneShotADSpeakers((TRIGGERGUID)_Trigger) + AND + DB_OneShot_ADSpeakers(_Trigger,_Npc,_Count) + THEN + NOT DB_OneShot_ADSpeakers(_Trigger,_Npc,_Count); + + PROC + ProcOneShotADCleanup((TRIGGERGUID)_Trigger) + THEN + ProcClearOneShotADCount(_Trigger); + ProcClearOneShotADSpeakers(_Trigger); + + PROC + RemoveOneShotAD((TRIGGERGUID)_Trigger) + THEN + ProcOneShotADCleanup(_Trigger); + ProcTriggerUnregisterForPlayers(_Trigger); + NOT DB_OneShotPlayerTrigger(_Trigger); + + IF + DB_Dead(_Npc) + AND + DB_OneShot_ADSpeakers(_Trigger,_Npc,_) + THEN + RemoveOneShotAD(_Trigger); + + PROC + ProcOneShotTriggerEntered((CHARACTERGUID)_Player,(TRIGGERGUID)_Trigger) + AND + DB_OneShot_ADSpeakerCount(_Trigger,_,_) + AND + DB_OneShot_ADSpeakers(_Trigger,_Npc,1) + AND + QRY_SpeakerIsAvailable(_Player) + AND + QRY_SpeakerIsAvailable(_Npc) + THEN + ProcDoStartOneShotAD(_Player,_Trigger); + ProcOneShotADCleanup(_Trigger); + + //if the dialog is started (manually), cleanup so this doesn't get started again + IF + AutomatedDialogStarted(_Dialog,_) + AND + DB_OneShot_ADSpeakerCount(_Trigger,_Dialog,_Count) + THEN + ProcOneShotADCleanup(_Trigger); + ProcTriggerUnregisterForPlayers(_Trigger); + + //END_REGION + + //REGION One Shot Voice Bark + + IF + DB_OneShot_VoiceBarkTrigger((TRIGGERGUID)_Trigger,(STRING)_VoiceBark) + THEN + DB_OneShotPlayerTrigger(_Trigger); + + PROC + ProcOneShotTriggerEntered(_Player,_Trigger) + AND + DB_OneShot_VoiceBarkTrigger(_Trigger,_VoiceBark) + AND + QRY_SpeakerIsAvailable(_Player) + THEN + StartVoiceBark(_VoiceBark,_Player); + + IF + VoiceBarkStarted(_VoiceBark,_) + AND + DB_OneShot_VoiceBarkTrigger(_Trigger,_VoiceBark) + THEN + NOT DB_OneShot_VoiceBarkTrigger(_Trigger,_VoiceBark); + + IF + VoiceBarkFailed(_VoiceBark) + AND + DB_OneShot_VoiceBarkTrigger(_Trigger,_VoiceBark) + THEN + NOT DB_OneShot_VoiceBarkTrigger(_Trigger,_VoiceBark); + + //END_REGION + + } + EXIT + { + + } +} +Goal(9).Title("__PROC"); +Goal(9) +{ + INIT + { + DB_InternalGroup_Count(0); + + + } + KB + { + PROC + CharacterGiveReward((CHARACTERGUID)_Player,(STRING)_Reward) + THEN + CharacterGiveReward(_Player,_Reward,1); + + //REGION Defaults for item adding + PROC + ItemTemplateAddTo((STRING)_ItemTemplate, (GUIDSTRING)_Object, (INTEGER)_Count) + THEN + ItemTemplateAddTo(_ItemTemplate,_Object,_Count,1); + + PROC + ItemToInventory((ITEMGUID)_Item,(GUIDSTRING)_Container) + THEN + ItemToInventory(_Item,_Container,1,1,1); + + PROC + ItemToInventory((ITEMGUID)_Item, (GUIDSTRING)_TargetObject, (INTEGER)_Amount) + THEN + ItemToInventory(_Item, _TargetObject, _Amount, 1, 1); + + PROC + ItemToInventory((ITEMGUID)_Item, (GUIDSTRING)_TargetObject, (INTEGER)_Amount, (INTEGER)_ShowNotification) + THEN + ItemToInventory(_Item, _TargetObject, _Amount, _ShowNotification, 1); + + //END_REGION + + //REGION Follow logic + PROC + ProcCharacterFollowCharacter((CHARACTERGUID)_Char,(CHARACTERGUID)_Target) + THEN + ProcCharacterStopFollow(_Char); + DB_Following(_Char,_Target); + CharacterFollowCharacter(_Char,_Target); + + PROC + ProcCharacterStopFollow((CHARACTERGUID)_Char) + AND + CharacterIsDead(_Char,0) + THEN + CharacterStopFollow(_Char); + + PROC + ProcCharacterStopFollow((CHARACTERGUID)_Char) + AND + DB_Following(_Char,_Target) + THEN + NOT DB_Following(_Char,_Target); + + IF + CharacterDying(_Char) + THEN + ProcCharacterStopFollow(_Char); + + IF + ObjectEnteredCombat((CHARACTERGUID)_Obj,_) + AND + DB_Following(_Obj,_) + THEN + CharacterStopFollow(_Obj); + + IF + ObjectLeftCombat((CHARACTERGUID)_Obj,_) + AND + DB_Following(_Obj,_Target) + THEN + CharacterFollowCharacter(_Obj,_Target); + + //END_REGION + + IF + DB_DoNotFace((GUIDSTRING)_Char) + THEN + CharacterSetDoNotFaceFlag((CHARACTERGUID)_Char,1); + DB_CheckDoNotFace(_Char); + + IF + DB_CheckDoNotFace(_Char) + AND + NOT DB_DoNotFace(_Char) + THEN + CharacterSetDoNotFaceFlag((CHARACTERGUID)_Char,0); + NOT DB_CheckDoNotFace(_Char); + + PROC + ProcFaceCharacter((GUIDSTRING)_Char,(GUIDSTRING)_Target) + AND + NOT DB_DoNotFace(_Char) + AND + ObjectIsCharacter(_Char,1) + AND + CharacterIsIncapacitated((CHARACTERGUID)_Char,0) + THEN + CharacterLookAt((CHARACTERGUID)_Char,_Target,0); + + PROC + ProcFaceEachother((GUIDSTRING)_Char,(GUIDSTRING)_Target) + AND + NOT DB_DoNotFace(_Char) + AND + ObjectIsCharacter(_Char,1) + AND + CharacterIsIncapacitated((CHARACTERGUID)_Char,0) + THEN + CharacterLookAt((CHARACTERGUID)_Char,_Target,0); + + PROC + ProcFaceEachother((GUIDSTRING)_Char,(GUIDSTRING)_Target) + AND + NOT DB_DoNotFace(_Target) + AND + ObjectIsCharacter(_Target,1) + AND + CharacterIsIncapacitated((CHARACTERGUID)_Target,0) + THEN + CharacterLookAt((CHARACTERGUID)_Target,_Char,0); + + //REGION Internal Dialog Logic (Starting dialog by clicking on NPC) + PROC + ProcIncreaseInternalCount() + AND + DB_InternalGroup_Count(_Nr) + AND + IntegerSum(_Nr,1,_New) + THEN + NOT DB_InternalGroup_Count(_Nr); + DB_InternalGroup_Count(_New); + + IF + DB_Dialogs((GUIDSTRING)_Npc,(STRING)_Dialog) + THEN + SetHasDialog(_Npc,1); + ProcIncreaseInternalCount(); + ProcInteralCounterEntry(_Npc,_Dialog); + + PROC + ProcInteralCounterEntry((GUIDSTRING)_Npc,(STRING)_Dialog) + AND + DB_InternalGroup_Count(_New) + THEN + DB_InternalCounter(_New,_Dialog,1); + DB_Internal_Dialogs(_Npc,_Dialog,_New,1); + + IF + DB_Dialogs((GUIDSTRING)_Npc,(GUIDSTRING)_Npc2,(STRING)_Dialog) + THEN + SetHasDialog(_Npc,1); + SetHasDialog(_Npc2,1); + ProcIncreaseInternalCount(); + ProcInteralCounterEntry(_Npc,_Npc2,_Dialog); + + PROC + ProcInteralCounterEntry((GUIDSTRING)_Npc,(GUIDSTRING)_Npc2,(STRING)_Dialog) + AND + DB_InternalGroup_Count(_Group) + THEN + DB_InternalCounter(_Group,_Dialog,2); + DB_Internal_Dialogs(_Npc,_Dialog,_Group,1); + DB_Internal_Dialogs(_Npc2,_Dialog,_Group,2); + + IF + DB_Dialogs((GUIDSTRING)_Npc,(GUIDSTRING)_Npc2,(GUIDSTRING)_Npc3,(STRING)_Dialog) + THEN + SetHasDialog(_Npc,1); + SetHasDialog(_Npc2,1); + SetHasDialog(_Npc3,1); + ProcIncreaseInternalCount(); + ProcInteralCounterEntry(_Npc,_Npc2,_Npc3,_Dialog); + + PROC + ProcInteralCounterEntry((GUIDSTRING)_Npc,(GUIDSTRING)_Npc2,(GUIDSTRING)_Npc3,(STRING)_Dialog) + AND + DB_InternalGroup_Count(_Group) + THEN + DB_InternalCounter(_Group,_Dialog,3); + DB_Internal_Dialogs(_Npc,_Dialog,_Group,1); + DB_Internal_Dialogs(_Npc2,_Dialog,_Group,2); + DB_Internal_Dialogs(_Npc3,_Dialog,_Group,3); + + IF + DB_Dialogs((GUIDSTRING)_Npc,(GUIDSTRING)_Npc2,(GUIDSTRING)_Npc3,(GUIDSTRING)_Npc4,(STRING)_Dialog) + THEN + SetHasDialog(_Npc,1); + SetHasDialog(_Npc2,1); + SetHasDialog(_Npc3,1); + SetHasDialog(_Npc4,1); + ProcIncreaseInternalCount(); + ProcInteralCounterEntry(_Npc,_Npc2,_Npc3,_Npc4,_Dialog); + + PROC + ProcInteralCounterEntry((GUIDSTRING)_Npc,(GUIDSTRING)_Npc2,(GUIDSTRING)_Npc3,(GUIDSTRING)_Npc4,(STRING)_Dialog) + AND + DB_InternalGroup_Count(_Group) + THEN + DB_InternalCounter(_Group,_Dialog,4); + DB_Internal_Dialogs(_Npc,_Dialog,_Group,1); + DB_Internal_Dialogs(_Npc2,_Dialog,_Group,2); + DB_Internal_Dialogs(_Npc3,_Dialog,_Group,3); + DB_Internal_Dialogs(_Npc4,_Dialog,_Group,4); + + PROC + ProcStartNPCDialog((GUIDSTRING)_Player,(STRING)_Dialog,(INTEGER)_Group) + AND + DB_InternalCounter(_Group,_Dialog,1) + AND + DB_Internal_Dialogs(_Npc,_Dialog,_Group,1) + THEN + Proc_StartDialog(0,_Dialog,_Npc,_Player); + + PROC + ProcStartNPCDialog((GUIDSTRING)_Player,(STRING)_Dialog,(INTEGER)_Group) + AND + DB_InternalCounter(_Group,_Dialog,2) + AND + DB_Internal_Dialogs(_Npc,_Dialog,_Group,1) + AND + DB_Internal_Dialogs(_Npc2,_Dialog,_Group,2) + THEN + Proc_StartDialog(0,_Dialog,_Npc,_Npc2,_Player); + + PROC + ProcStartNPCDialog((GUIDSTRING)_Player,(STRING)_Dialog,(INTEGER)_Group) + AND + DB_InternalCounter(_Group,_Dialog,3) + AND + DB_Internal_Dialogs(_Npc,_Dialog,_Group,1) + AND + DB_Internal_Dialogs(_Npc2,_Dialog,_Group,2) + AND + DB_Internal_Dialogs(_Npc3,_Dialog,_Group,3) + THEN + Proc_StartDialog(0,_Dialog,_Npc,_Npc2,_Npc3,_Player); + + PROC + ProcStartNPCDialog((GUIDSTRING)_Player,(STRING)_Dialog,(INTEGER)_Group) + AND + DB_InternalCounter(_Group,_Dialog,4) + AND + DB_Internal_Dialogs(_Npc,_Dialog,_Group,1) + AND + DB_Internal_Dialogs(_Npc2,_Dialog,_Group,2) + AND + DB_Internal_Dialogs(_Npc3,_Dialog,_Group,3) + AND + DB_Internal_Dialogs(_Npc4,_Dialog,_Group,4) + THEN + Proc_StartDialog(0,_Dialog,_Npc,_Npc2,_Npc3,_Npc4,_Player); + + + PROC + NPCDialogStartRequested((GUIDSTRING)_Npc,(GUIDSTRING)_Player) + AND + DB_Internal_Dialogs(_Npc,_Dialog,_Group,1) + AND + NOT DB_TempRequested(_NPC,_,_) + THEN + DB_TempRequested(_NPC,_Dialog,_Group); + + PROC + NPCDialogStartRequested((GUIDSTRING)_Npc,(GUIDSTRING)_Player) + AND + DB_Internal_Dialogs(_Npc,_Dialog,_Group,_) + AND + NOT DB_TempRequested(_NPC,_,_) + THEN + DB_TempRequested(_NPC,_Dialog,_Group); + + PROC + NPCDialogStartRequested((GUIDSTRING)_Npc,(GUIDSTRING)_Player) + AND + NOT DB_TempRequested(_NPC,_,_) + AND + QRY_SpeakerIsAvailable(_Npc) + AND + QRY_SpeakerIsAvailable(_Player) + AND + ObjectIsCharacter(_Npc,1) + AND + HasDefaultDialog((CHARACTERGUID)_Npc,1) + THEN + DialogRequestStop(_Npc); + DialogRequestStop(_Player); + ProcTryStartDefaultDialog(_Npc,(CHARACTERGUID)_Player); + + PROC + NPCDialogStartRequested((GUIDSTRING)_Npc,(GUIDSTRING)_Player) + AND + DB_TempRequested(_NPC,_Dialog,_Group) + THEN + NOT DB_TempRequested(_NPC,_Dialog,_Group); + ProcStartNPCDialog(_Player,_Dialog,_Group); + + PROC + ProcTryStartDefaultDialog((CHARACTERGUID)_Npc,(CHARACTERGUID)_Player) + AND + StartDefaultDialog(_Npc,_Player,_Dialog,_Automated) + THEN + ProcHandleDefaultDialogSetting(_Npc,_Player,_Dialog,_Automated); + + PROC + ProcHandleDefaultDialogSetting((CHARACTERGUID)_Npc,(CHARACTERGUID)_Player,(STRING)_Dialog,1) + THEN + ProcFaceCharacter(_Npc,_Player); + Proc_DialogFlagSetup(_Dialog,_Npc,_Player); + + PROC + ProcHandleDefaultDialogSetting((CHARACTERGUID)_Npc,(CHARACTERGUID)_Player,(STRING)_Dialog,0) + THEN + Proc_DialogFlagSetup(_Dialog,_Npc,_Player); + ProcFaceCharacter(_Npc,_Player); + ProcFaceCharacter(_Player,_Npc); + ProcItemSetInvulnerableForDialog(_Npc); + ProcItemSetInvulnerableForDialog(_Player); + CharacterMakeStoryNpc(_Npc,1); + CharacterMakeStoryNpc(_Player,1); + DB_HasMetCharactersToCheck(_Npc,_Player); + + //END_REGION + + //REGION Internal Dialog Cleanup + PROC + ProcRemoveAllDialogEntriesForSpeaker((GUIDSTRING)_NPC) + AND + DB_Internal_Dialogs(_Npc,_Dialog,_Group,1) + THEN + ProcRemoveInternalDialogEntries(_Dialog,_Group); + ProcRemoveExternalDialogEntry(_Npc,_Dialog,_Group); + + PROC + ProcRemoveDialogEntryForSpeaker((GUIDSTRING)_NPC,(STRING)_Dialog) + AND + DB_Internal_Dialogs(_Npc,_Dialog,_Group,1) + THEN + ProcRemoveInternalDialogEntries(_Dialog,_Group); + ProcRemoveExternalDialogEntry(_Npc,_Dialog,_Group); + + //remove dialogs when an NPC dies. + IF + DB_Dead((CHARACTERGUID)_Npc) + AND + NOT DB_KeepDialogsOnDeath(_Npc) + AND + DB_Internal_Dialogs(_Npc,_Dialog,_Group,_Nr) + AND + DB_Internal_Dialogs(_FirstSpeaker,_Dialog,_Group,1) + THEN + ProcRemoveDialogEntryForSpeaker(_FirstSpeaker,_Dialog); + + PROC + ProcRemoveExternalDialogEntry((GUIDSTRING)_Npc,(STRING)_Dialog,(INTEGER)_Group) + AND + DB_InternalCounter(_Group,_Dialog,1) + AND + DB_Dialogs(_Npc,_Dialog) + THEN + NOT DB_Dialogs(_Npc,_Dialog); + NOT DB_InternalCounter(_Group,_Dialog,1); + + PROC + ProcRemoveExternalDialogEntry((GUIDSTRING)_Npc,(STRING)_Dialog,(INTEGER)_Group) + AND + DB_InternalCounter(_Group,_Dialog,2) + AND + DB_Dialogs(_Npc,_Npc2,_Dialog) + THEN + NOT DB_Dialogs(_Npc,_Npc2,_Dialog); + NOT DB_InternalCounter(_Group,_Dialog,2); + + PROC + ProcRemoveExternalDialogEntry((GUIDSTRING)_Npc,(STRING)_Dialog,(INTEGER)_Group) + AND + DB_InternalCounter(_Group,_Dialog,3) + AND + DB_Dialogs(_Npc,_Npc2,_Npc3,_Dialog) + THEN + NOT DB_Dialogs(_Npc,_Npc2,_Npc3,_Dialog); + NOT DB_InternalCounter(_Group,_Dialog,3); + + PROC + ProcRemoveExternalDialogEntry((GUIDSTRING)_Npc,(STRING)_Dialog,(INTEGER)_Group) + AND + DB_InternalCounter(_Group,_Dialog,4) + AND + DB_Dialogs(_Npc,_Npc2,_Npc3,_Npc4,_Dialog) + THEN + NOT DB_Dialogs(_Npc,_Npc2,_Npc3,_Npc4,_Dialog); + NOT DB_InternalCounter(_Group,_Dialog,4); + + PROC + ProcRemoveInternalDialogEntries((STRING)_Dialog,(INTEGER)_Group) + AND + DB_Internal_Dialogs(_Npc,_Dialog,_Group,_Nr) + THEN + NOT DB_Internal_Dialogs(_Npc,_Dialog,_Group,_Nr); + //END_REGION + + //REGION Start Dialog with 1 Item + IF + CharacterUsedItem(_Player,_Item) + AND + DB_Dialogs(_Item,_Dialog) + AND + DB_CombatCharacters(_Player, _) + AND + NOT DB_IgnoreCombatItems((ITEMGUID) _Item) + THEN + Proc_StartDialog(1,"GLO_AD_CannotUseNow", _Player); + + IF + CharacterUsedItem(_Player,_Item) + AND + DB_Dialogs(_Item,_Dialog) + AND + NOT DB_CombatCharacters(_Player, _) + THEN + Proc_StartDialog(0,_Dialog,_Item,_Player); + + IF + CharacterUsedItem(_Player,_Item) + AND + DB_Dialogs(_Item,_Dialog) + AND + DB_CombatCharacters(_Player, _) + AND + DB_IgnoreCombatItems((ITEMGUID) _Item) + THEN + Proc_StartDialog(0,_Dialog,_Item,_Player); + + // From item's GEN_ItemDialog behavior script we can start interactive dialogs (used for modders to start item dialogs without Osiris support) + IF + CharacterItemEvent(_Player,_Item,"GEN_StartItemDialog") + AND + NOT DB_Dialogs(_Item,_) + AND + DB_IsPlayer(_Player) + AND + NOT DB_CombatCharacters(_Player,_) + AND + GetVarString(_Item,"ItemDialog",_Dialog) + THEN + Proc_StartDialog(0,_Dialog,_Item,_Player); + + IF + CharacterItemEvent(_Player,_Item,"GEN_StartItemDialog") + AND + NOT DB_Dialogs(_Item,_) + AND + DB_IsPlayer(_Player) + AND + DB_CombatCharacters(_Player,_) + AND + DB_IgnoreCombatItems((ITEMGUID)_Item) + AND + GetVarString(_Item,"ItemDialog",_Dialog) + THEN + Proc_StartDialog(0,_Dialog,_Item,_Player); + //END_REGION + + + //REGION Start Automated Dialog with 1 Item + IF + CharacterUsedItem(_Char,_Item) + AND + DB_AD_Dialog(_Item,_Dialog) + THEN + Proc_StartDialog(1,_Dialog, _Item); + //END_REGION + + //REGION Track Object Invulnerable (mainly used by itemdialogs setting items temporarily invulnerable) + PROC + ProcSetInvulnerable((GUIDSTRING)_Object,1) + THEN + DB_ObjectStoryInvulnerable(_Object); + SetInvulnerable_UseProcSetInvulnerable(_Object,1); + + PROC + ProcSetInvulnerable((GUIDSTRING)_Object,0) + THEN + NOT DB_ObjectStoryInvulnerable(_Object); + SetInvulnerable_UseProcSetInvulnerable(_Object,0); + //END_REGION + + //REGION Clear Involved NPCs in Dialog + PROC + ProcClearDialogFlagsForPlayers((INTEGER)_Instance) + AND + DB_DialogPlayers(_Instance,_Player,_Index) + THEN + ProcClearPlayerIfNotInOtherDialog(_Instance,_Player); + + PROC + ProcClearPlayerIfNotInOtherDialog((INTEGER)_Inst,(GUIDSTRING)_Player) + AND + DB_DialogPlayers(_OtherInstance,_Player,_) + AND + _OtherInstance!=_Inst + AND + NOT DB_AutomatedDialog(_OtherInstance) + AND + NOT DB_MarkedForDelete(_OtherInstance) + THEN + DB_TempIsInOtherDialog(_Player,1); + + PROC + ProcClearPlayerIfNotInOtherDialog((INTEGER)_Inst,(GUIDSTRING)_Player) + AND + NOT DB_TempIsInOtherDialog(_Player,1) + THEN + SetStoryNpcStatus((CHARACTERGUID)_Player); + + PROC + ProcClearPlayerIfNotInOtherDialog((INTEGER)_Inst,(GUIDSTRING)_Player) + THEN + NOT DB_TempIsInOtherDialog(_Player,1); + + PROC + ProcClearDialogFlagsForNPCs((INTEGER)_Instance) + AND + DB_DialogNPCs(_Instance,_Npc,_Index) + THEN + ProcClearNPCIfNotInOtherDialog(_Instance,_Npc); + + PROC + ProcClearNPCIfNotInOtherDialog((INTEGER)_Inst,(GUIDSTRING)_Npc) + AND + DB_DialogNPCs(_OtherInstance,_Npc,_) + AND + _OtherInstance!=_Inst + AND + NOT DB_AutomatedDialog(_OtherInstance) + AND + NOT DB_MarkedForDelete(_OtherInstance) + THEN + DB_TempIsInOtherDialog(_Npc,1); + + PROC + ProcClearNPCIfNotInOtherDialog((INTEGER)_Inst,(GUIDSTRING)_Npc) + AND + NOT DB_TempIsInOtherDialog(_Npc,1) + THEN + SetStoryNpcStatus((CHARACTERGUID)_Npc); + + PROC + ProcClearNPCIfNotInOtherDialog((INTEGER)_Inst,(GUIDSTRING)_Npc) + AND + NOT DB_TempIsInOtherDialog(_Npc,1) + AND + NOT DB_ObjectStoryInvulnerable(_Npc) + AND + ObjectIsItem(_Npc,1) + THEN + SetInvulnerable_UseProcSetInvulnerable(_Npc,0); + + PROC + ProcClearNPCIfNotInOtherDialog((INTEGER)_Inst,(GUIDSTRING)_Npc) + THEN + NOT DB_TempIsInOtherDialog(_Npc,1); + + //END_REGION + + //REGION Set Relation to Players + PROC + SetRelationFactionToPlayers((STRING)_Faction,(INTEGER)_Relation) + THEN + CharacterSetRelationFactionToFaction(_Faction,"Hero",_Relation); + CharacterSetRelationFactionToFaction("Hero",_Faction,_Relation); + CharacterSetRelationFactionToFaction(_Faction,"Companion",_Relation); + CharacterSetRelationFactionToFaction("Companion",_Faction,_Relation); + + PROC + SetRelationIndivFactionToPlayers((CHARACTERGUID)_Char,(INTEGER)_Relation) + THEN + CharacterSetRelationIndivFactionToFaction(_Char,"Hero",_Relation); + CharacterSetRelationFactionToIndivFaction("Hero",_Char,_Relation); + CharacterSetRelationIndivFactionToFaction(_Char,"Companion",_Relation); + CharacterSetRelationFactionToIndivFaction("Companion",_Char,_Relation); + + PROC + ProcSetRelationToPlayers((CHARACTERGUID)_Character,(INTEGER)_Relation) + AND + _Relation == 0 + AND + GetFaction(_Character,_Faction) + THEN + SetFaction(_Character,"Evil NPC"); + DB_PreviousAlignment(_Character,_Faction); + + PROC + ProcSetRelationToPlayers((CHARACTERGUID)_Character,(INTEGER)_Relation) + AND + _Relation == 100 + AND + NOT DB_PreviousAlignment(_Character,_) + AND + GetFaction(_Character,_Faction) + THEN + CharacterSetRelationFactionToFaction(_Faction,"Hero",100); + CharacterSetRelationFactionToFaction("Hero",_Faction,100); + + PROC + ProcSetRelationToPlayers((CHARACTERGUID)_Character,(INTEGER)_Relation) + AND + DB_IsPlayer(_Player) + AND + _Relation == 100 + AND + DB_PreviousAlignment(_Character,_Faction) + THEN + SetFaction(_Character,_Faction); + CharacterSetRelationFactionToFaction(_Faction,"Hero",100); + CharacterSetRelationFactionToFaction("Hero",_Faction,100); + NOT DB_PreviousAlignment(_Character,_Faction); + + PROC + ProcSetHostileToIndivPlayer((CHARACTERGUID)_Character,(CHARACTERGUID)_Player) + THEN + CharacterSetRelationIndivFactionToIndivFaction(_Character,_Player,0); + CharacterSetRelationIndivFactionToIndivFaction(_Player,_Character,0); + + PROC + ProcSetFactionHostileToIndivPlayer((STRING)_Faction,(CHARACTERGUID)_Player) + THEN + CharacterSetRelationFactionToIndivFaction(_Faction,_Player,0); + CharacterSetRelationIndivFactionToFaction(_Player,_Faction,0); + //END_REGION + + //REGION Change Attitude + IF + ObjectFlagSet("ChangeAttitude",_Player,_Instance) + AND + GetVarInteger(_Player,"ChangeAttitude",_Value) + AND + DB_DialogNPCs(_Instance,_Npc,1) + THEN + ObjectClearFlag(_Player,"ChangeAttitude",_Instance); + CharacterAddAttitudeTowardsPlayer((CHARACTERGUID)_Npc,(CHARACTERGUID)_Player,_Value); + + PROC + ChangeAttitude((CHARACTERGUID)_NPC,(CHARACTERGUID)_Player,(INTEGER)_Value) + THEN + CharacterAddAttitudeTowardsPlayer(_Npc,_Player,_Value); + //END_REGION + + //REGION Peace Timer + PROC + PROC_PeaceTimerLaunch((STRING)_TimerName,(INTEGER)_TimerLength) + AND + DB_CombatCharacters(_Player,_) + AND + DB_IsPlayer(_Player) + AND + NOT DB_PeaceTimer(_TimerName) + THEN + DB_PeaceTimer(_TimerName); + DB_PeaceTimerStillToStart(_TimerName,_TimerLength); + + PROC + PROC_PeaceTimerLaunch((STRING)_TimerName,(INTEGER)_TimerLength) + AND + NOT DB_PeaceTimer(_TimerName) + THEN + DB_PeaceTimer(_TimerName); + TimerLaunch(_TimerName,_TimerLength); + + IF + ObjectEnteredCombat((CHARACTERGUID)_Player,_) + AND + _Player.DB_IsPlayer() + AND + DB_PeaceTimer(_TimerName) + THEN + TimerPause(_TimerName); + + IF + ObjectLeftCombat((CHARACTERGUID)_Player,_) + AND + _Player.DB_IsPlayer() + AND + DB_PeaceTimer(_TimerName) + THEN + ProcUnPausePeaceTimerIfNoPlayerInCombat(_TimerName); + + PROC + ProcUnPausePeaceTimerIfNoPlayerInCombat((STRING)_TimerName) + AND + _Player.DB_IsPlayer() + AND + DB_CombatCharacters(_Player,_) + THEN + DB_PlayerInCombat(1); + + PROC + ProcUnPausePeaceTimerIfNoPlayerInCombat((STRING)_TimerName) + AND + NOT DB_PlayerInCombat(1) + AND + NOT DB_PeaceTimerStillToStart(_TimerName,_) + THEN + TimerUnpause(_TimerName); + + PROC + ProcUnPausePeaceTimerIfNoPlayerInCombat((STRING)_TimerName) + AND + NOT DB_PlayerInCombat(1) + AND + DB_PeaceTimerStillToStart(_TimerName,_TimerLength) + THEN + TimerLaunch(_TimerName,_TimerLength); + NOT DB_PeaceTimerStillToStart(_TimerName,_TimerLength); + + PROC + ProcUnPausePeaceTimerIfNoPlayerInCombat((STRING)_TimerName) + THEN + NOT DB_PlayerInCombat(1); + IF + TimerFinished(_TimerName) + AND + DB_PeaceTimer(_TimerName) + THEN + NOT DB_PeaceTimer(_TimerName); + //END_REGION + + //REGION Doors + PROC + ItemCloseAndLock((ITEMGUID)_Item,(STRING)_Key) + THEN + ItemClose(_Item); + ItemLock(_Item,_Key); + + PROC + ItemUnlockAndOpen((ITEMGUID)_Item) + THEN + ItemUnLock(_Item); + ItemOpen(_Item); + //END_REGION + + //REGION Automated dialogs + IF + DB_AD_Dialog((GUIDSTRING)_Char,(STRING)_) + THEN + SetHasDialog(_Char,1); + + IF + DB_AD_Dialog((GUIDSTRING)_Char1,(GUIDSTRING)_Char2,(STRING)_) + THEN + SetHasDialog(_Char1,1); + SetHasDialog(_Char2,1); + + PROC + PROC_GLOBAL_DialogStartRequested((GUIDSTRING)_Char,(GUIDSTRING)_Player) + AND + DB_AD_Dialog(_Char,(STRING)_Dialog) + AND + NOT DB_ADRequested(_Char) + AND + QRY_StartDialog(1,_Dialog,_Char) + THEN + DB_ADRequested(_Char); + DB_FoundDialog(_Char,_Player); + + PROC + PROC_GLOBAL_DialogStartRequested((GUIDSTRING)_Char1,(GUIDSTRING)_Player) + AND + DB_AD_Dialog(_Char1,_Char2,(STRING)_Dialog) + AND + NOT DB_ADRequested(_Char1) + AND + NOT DB_ADRequested(_Char2) + AND + QRY_StartDialog(1,_Dialog,_Char1,_Char2) + THEN + DB_ADRequested(_Char1); + DB_ADRequested(_Char2); + DB_FoundDialog(_Char1,_Player); + + PROC + PROC_GLOBAL_DialogStartRequested((GUIDSTRING)_Char2,(GUIDSTRING)_Player) + AND + DB_AD_Dialog(_Char1,_Char2,(STRING)_Dialog) + AND + NOT DB_ADRequested(_Char1) + AND + NOT DB_ADRequested(_Char2) + AND + QRY_StartDialog(1,_Dialog,_Char1,_Char2) + THEN + DB_ADRequested(_Char1); + DB_ADRequested(_Char2); + DB_FoundDialog(_Char2,_Player); + + IF + AutomatedDialogEnded(_Dialog,_Inst) + THEN + ProcClearADRequests(_Dialog,_Inst); + + IF + AutomatedDialogRequestFailed(_Dialog,_Inst) + THEN + ProcClearADRequests(_Dialog,_Inst); + + PROC + ProcClearADRequests((STRING)_Dialog,(INTEGER)_Inst) + AND + DB_DialogNPCs(_Inst,_Npc,1) + AND + DB_AD_Dialog(_Npc,_Dialog) + THEN + NOT DB_ADRequested(_Npc); + + PROC + ProcClearADRequests((STRING)_Dialog,(INTEGER)_Inst) + AND + DB_DialogPlayers(_Inst,_Player,1) + AND + DB_AD_Dialog(_Player,_Dialog) + THEN + NOT DB_ADRequested(_Player); + + PROC + ProcClearADRequests((STRING)_Dialog,(INTEGER)_Inst) + AND + DB_DialogNPCs(_Inst,_Npc,_) + AND + DB_AD_Dialog(_,_,_Dialog) + THEN + NOT DB_ADRequested(_Npc); + + PROC + ProcClearADRequests((STRING)_Dialog,(INTEGER)_Inst) + AND + DB_DialogPlayers(_Inst,_Player,1) + AND + DB_AD_Dialog(_,_,_Dialog) + THEN + NOT DB_ADRequested(_Player); + + PROC + ProcRemoveNPCADs((GUIDSTRING)_Npc) + AND + DB_AD_Dialog(_Npc,_Dialog) + THEN + NOT DB_AD_Dialog(_Npc,_Dialog); + NOT DB_ADRequested(_Npc); + + PROC + ProcRemoveNPCADs((GUIDSTRING)_Npc) + AND + DB_AD_Dialog(_Npc,_Npc2,_Dialog) + THEN + NOT DB_AD_Dialog(_Npc,_Npc2,_Dialog); + NOT DB_ADRequested(_Npc); + NOT DB_ADRequested(_Npc2); + + PROC + ProcRemoveNPCADs((GUIDSTRING)_Npc2) + AND + DB_AD_Dialog(_Npc,_Npc2,_Dialog) + THEN + NOT DB_AD_Dialog(_Npc,_Npc2,_Dialog); + NOT DB_ADRequested(_Npc); + NOT DB_ADRequested(_Npc2); + + PROC + PROC_GLOBAL_DialogStartRequested((GUIDSTRING)_NPC,(GUIDSTRING)_Player) + AND + DB_AD_Dialog(_NPC,_) + THEN + ProcFaceEachother(_NPC,_Player); + + //END_REGION + + //REGION Object timer + PROC + ProcObjectTimer((GUIDSTRING)_Object,(STRING)_TimerName,(INTEGER)_Time) + AND + GetUUID(_Object,_UUID) + AND + StringConcatenate(_UUID,_TimerName,_ObjectTimerName) + THEN + DB_ObjectTimer(_Object,_ObjectTimerName,_TimerName); + TimerLaunch(_ObjectTimerName,_Time); + + PROC + ProcObjectTimerCancel((GUIDSTRING)_Object,(STRING)_TimerName) + AND + DB_ObjectTimer(_Object,_ObjectTimerName,_TimerName) + THEN + NOT DB_ObjectTimer(_Object,_ObjectTimerName,_TimerName); + TimerCancel(_ObjectTimerName); + + IF + TimerFinished(_ObjectTimerName) + AND + DB_ObjectTimer(_Object,_ObjectTimerName,_TimerName) + THEN + NOT DB_ObjectTimer(_Object,_ObjectTimerName,_TimerName); + ProcObjectTimerFinished(_Object,_TimerName); + + PROC + ProcObjectTimerFinished((GUIDSTRING)_Object,(STRING)_TimerName) + THEN + DB_NOOP(1); + //END_REGION + + //REGION Tutorial Messages + PROC + PROC_CheckPlayTut((STRING)_Message) + AND + _Player.DB_IsPlayer() + THEN + PROC_CheckPlayTut(_Player,_Message); + + PROC + PROC_CheckPlayTut((CHARACTERGUID)_Player,(STRING)_Message) + AND + DB_StartTutMessages(1) + THEN + ProcPlayTut(_Player,_Message); + + PROC + PROC_CheckPlayTutWithDelay((STRING)_Message,(INTEGER)_Delay) + AND + _Player.DB_IsPlayer() + THEN + PROC_CheckPlayTutWithDelay(_Player,_Message,_Delay); + + PROC + PROC_CheckPlayTutWithDelay((CHARACTERGUID)_Player,(STRING)_Message,(INTEGER)_Delay) + AND + NOT DB_TutorialMessage(_Player,_Message,_) + AND + GetUUID(_Player,_Timer) + AND + StringConcatenate(_Timer,"_Tut",_TimerMsg) + THEN + TimerLaunch(_TimerMsg,_Delay); + DB_TutorialMessage(_Player,_Message,_TimerMsg); + + IF + TimerFinished(_TimerMsg) + AND + DB_StartTutMessages(1) + AND + DB_TutorialMessage(_Player,_Message,_TimerMsg) + THEN + NOT DB_TutorialMessage(_Player,_Message,_TimerMsg); + ProcPlayTut(_Player,_Message); + //END_REGION + + //REGION Move to changes + PROC + ProcSaveGenericBehaviourState((CHARACTERGUID)_Char) + AND + NOT DB_StoryMoving(_Char,1) + AND + DB_Internal_Dialogs(_Char,_,_,_) + THEN + DB_NPCHadDialog(_Char,1); + + PROC + ProcSaveGenericBehaviourState((CHARACTERGUID)_Char) + AND + NOT DB_StoryMoving(_Char,1) + AND + DB_AD_Dialog(_Char,_) + THEN + DB_NPCHadDialog(_Char,1); + + PROC + ProcInternalMoveDisableGenericBehaviours((CHARACTERGUID)_Char) + AND + NOT DB_AD_Dialog(_Char,_) + THEN + SetHasDialog(_Char,0); + + PROC + ProcInternalMoveDisableGenericBehaviours((CHARACTERGUID)_Char) + THEN + CharacterDisableAllCrimes(_Char); + + PROC + ProcRestoreGenericBehaviour((CHARACTERGUID)_Char) + AND + NOT DB_CharacterAllCrimesDisabled(_Char) + THEN + CharacterEnableAllCrimes(_Char); + + PROC + ProcRestoreGenericBehaviour((CHARACTERGUID)_Char) + AND + DB_CharacterCrimeDisabled(_Char,_Crime) + THEN + CharacterDisableCrime(_Char,_Crime); + + PROC + ProcRestoreGenericBehaviour((CHARACTERGUID)_Char) + AND + DB_CharacterCrimeEnabled(_Char,_Crime) + THEN + CharacterEnableCrime(_Char,_Crime); + + PROC + ProcRestoreGenericBehaviour((CHARACTERGUID)_Char) + AND + DB_NPCHadDialog(_Char,1) + THEN + NOT DB_NPCHadDialog(_Char,1); + SetHasDialog(_Char,1); + + PROC + ProcSetMoveEvent((STRING)_Event) + AND + _Event!="" + THEN + DB_MoveEvent(_Event); + + PROC + ProcSetMoveEvent("") + THEN + DB_MoveEvent("_ResetGenericBehaviours_"); + + PROC + ProcExecuteMove((CHARACTERGUID)_Char,(GUIDSTRING)_Point,(INTEGER)_Running) + AND + DB_MoveEvent(_Event) + AND + DB_CharMovementCommandID(_Char,_ID) + THEN + NOT DB_MoveEvent(_Event); + CharacterMoveTo(_Char,_Point,_Running,_Event,0); + DB_CharacterMovement(_Char,_Event,_ID); + + PROC + ProcBumpOsirisMoveCommandID((CHARACTERGUID)_Char) + AND + NOT DB_CharMovementCommandID(_Char,_) + THEN + DB_CharMovementCommandID(_Char,0); + + PROC + ProcBumpOsirisMoveCommandID((CHARACTERGUID)_Char) + AND + DB_CharMovementCommandID(_Char,_ID) + AND + IntegerSum(1,_ID,_New) + THEN + NOT DB_CharMovementCommandID(_Char,_ID); + DB_CharMovementCommandID(_Char,_New); + + PROC + ProcCharacterMoveTo((CHARACTERGUID)_Char,_,_,_) + THEN + SetStoryEvent(_Char,"ClearCrimeReturnPos"); + ProcBumpOsirisMoveCommandID(_Char); + + PROC + ProcCharacterMoveTo((CHARACTERGUID)_Char,(GUIDSTRING)_Point,(INTEGER)_Running,(STRING)_Event) + AND + DB_CharMovementCommandID(_Char,_ID) + THEN + ProcSaveGenericBehaviourState(_Char); + ProcInternalMoveDisableGenericBehaviours(_Char); + ProcSetMoveEvent(_Event); + ProcExecuteMove(_Char,_Point,_Running); + DB_StoryMoving(_Char,1); + DB_MovingTo(_Char,_Point,_Running,_ID); + + PROC + ProcCharacterMoveTo((CHARACTERGUID)_Char,_,_,_) + THEN + ProcBumpOsirisMoveCommandID(_Char); + + PROC + ProcResumeStoryMoving((CHARACTERGUID)_Char) + AND + DB_CharacterMovement(_Char,_Event,_ID) + AND + NOT DB_SelectedMove(_Char) + THEN + ProcSelectMove(_Char,_ID); + + PROC + ProcSelectMove((CHARACTERGUID)_Char,(INTEGER)_ID) + AND + DB_MovingTo(_Char,_Point,_Running,_ID) + AND + DB_CharacterMovement(_Char,_Event,_ID) + THEN + DB_SelectedMove(_Char); + CharacterMoveTo(_Char,_Point,_Running,_Event,0); + + PROC + ProcResumeStoryMoving((CHARACTERGUID)_Char) + THEN + NOT DB_SelectedMove(_Char); + + PROC + ProcClearMovingFacts((CHARACTERGUID)_Char) + AND + DB_CharacterMovement(_Char,_Event,_ID) + THEN + NOT DB_CharacterMovement(_Char,_Event,_ID); + + PROC + ProcClearMovingFacts((CHARACTERGUID)_Char) + AND + DB_MovingTo(_Char,_Point,_Running,_ID) + THEN + NOT DB_MovingTo(_Char,_Point,_Running,_ID); + + PROC + ProcClearMovingFacts((CHARACTERGUID)_Char) + THEN + ProcRestoreGenericBehaviour(_Char); + + IF + StoryEvent((CHARACTERGUID)_Char,_Event) + AND + DB_CharacterMovement(_Char,_Event,_ID) + THEN + NOT DB_ClearedMoveEvent(_Char); + + IF + StoryEvent((CHARACTERGUID)_Char,_Event) + AND + DB_CharacterMovement(_Char,_Event,_ID) + AND + DB_MovingTo(_Char,_Point,_Running,_ID) + AND + NOT DB_ClearedMoveEvent(_Char) + THEN + DB_ClearedMoveEvent(_Char); + NOT DB_MovingTo(_Char,_Point,_Running,_ID); + + IF + StoryEvent((CHARACTERGUID)_Char,_Event) + AND + DB_CharacterMovement(_Char,_Event,_ID) + THEN + NOT DB_HandledMoveEvent(_Char); + + IF + StoryEvent((CHARACTERGUID)_Char,_Event) + AND + DB_CharacterMovement(_Char,_Event,_ID) + AND + NOT DB_HandledMoveEvent(_Char) + THEN + DB_HandledMoveEvent(_Char); + NOT DB_CharacterMovement(_Char,_Event,_ID); + SetStoryEvent(_Char,"ClearCrimeReturnPos"); + ProcCheckRestoreGenericBehaviours(_Char); + + + PROC + ProcCheckRestoreGenericBehaviours((CHARACTERGUID)_Char) + AND + NOT DB_CharacterMovement(_Char,_,_) + THEN + NOT DB_StoryMoving(_Char,1); + ProcRestoreGenericBehaviour(_Char); + + IF + AttackedByObject((CHARACTERGUID)_Char,(CHARACTERGUID)_Source,_,_,_DamageSource) + AND + DB_StoryMoving(_Char,1) + AND + NOT DB_CombatCharacters(_Char,_) + AND + NOT QryIgnoreDamageSource(_DamageSource) + AND + CharacterIsPlayer(_Source,1) + THEN + ProcMakeNPCHostile(_Source,_Char); + + IF + DB_Dead(_Char) + AND + DB_StoryMoving(_Char,1) + THEN + NOT DB_StoryMoving(_Char,1); + ProcRestoreGenericBehaviour(_Char); + ProcClearMovingFacts(_Char); + + IF + ObjectEnteredCombat((CHARACTERGUID)_Char,_) + AND + DB_StoryMoving(_Char,1) + THEN + CharacterPurgeQueue(_Char); + + IF + ObjectLeftCombat((CHARACTERGUID)_Char,_) + AND + NOT DB_Dead(_Char) + AND + DB_StoryMoving(_Char,1) + THEN + ProcResumeStoryMoving(_Char); + + PROC + ProcClearStoryMove((CHARACTERGUID)_Char) + THEN + NOT DB_StoryMoving(_Char,1); + ProcRestoreGenericBehaviour(_Char); + ProcClearMovingFacts(_Char); + //END_REGION + + //REGION Movement via State_Manager_GoTo + // The parameters are roughly the same as with ProcCharacterMoveTo. Additional parameters: + // - _MinDist: the minimal distance to which the character must have neared its destination before the event is triggered + // - _AfterArrivalState: the State_Manager state to go to once the character reaches its destination + PROC + ProcStateManagerCharacterMoveTo((CHARACTERGUID)_Char,(GUIDSTRING)_Destination,(INTEGER)_Running,(REAL)_MinDist,(STRING)_Event,(STRING)_AfterArrivalState) + AND + ObjectIsCharacter(_Destination,1) + THEN + DB_ProcStateManagerCharacterMoveTo_Handled(1); + SetVarObject(_Char,"DestinationCharacter",_Destination); + SetVarFixedString(_Char,"currentState","State_Manager_Go_To_Character"); + + PROC + ProcStateManagerCharacterMoveTo((CHARACTERGUID)_Char,(GUIDSTRING)_Destination,(INTEGER)_Running,(REAL)_MinDist,(STRING)_Event,(STRING)_AfterArrivalState) + AND + NOT DB_ProcStateManagerCharacterMoveTo_Handled(1) + AND + ObjectIsItem(_Destination,1) + THEN + DB_ProcStateManagerCharacterMoveTo_Handled(1); + SetVarObject(_Char,"DestinationItem",_Destination); + SetVarFixedString(_Char,"currentState","State_Manager_Go_To_Item"); + + PROC + ProcStateManagerCharacterMoveTo((CHARACTERGUID)_Char,(GUIDSTRING)_Destination,(INTEGER)_Running,(REAL)_MinDist,(STRING)_Event,(STRING)_AfterArrivalState) + AND + NOT DB_ProcStateManagerCharacterMoveTo_Handled(1) + THEN + SetVarObject(_Char,"Destination",_Destination); + SetVarFixedString(_Char,"currentState","State_Manager_Go_To_Trigger"); + + PROC + ProcStateManagerCharacterMoveTo((CHARACTERGUID)_Char,(GUIDSTRING)_Destination,(INTEGER)_Running,(REAL)_MinDist,(STRING)_Event,(STRING)_AfterArrivalState) + THEN + NOT DB_ProcStateManagerCharacterMoveTo_Handled(1); + SetVarInteger(_Char,"Running",_Running); + SetVarFloat(_Char,"Distance",_MinDist); + SetVarString(_Char,"ArriveEvent",_Event); + SetVarFixedString(_Char,"AfterArriveState",_AfterArrivalState); + //END_REGION + + PROC + ReactOnKillCounter((STRING)_Counter) + THEN + DB_NOOP(1); + + PROC + ProcStartMovie((STRING)_Movie) + AND + _Char.DB_IsPlayer() + AND + CharacterGetReservedUserID(_Char,_ID) + AND + GetUserProfileID(_ID,_UserProfile) + AND + NOT DB_MoviePlayed(_UserProfile,_Movie) + THEN + DB_MoviePlayed(_UserProfile,_Movie); + MoviePlay(_Char,_Movie); + + + //REGION check closest available character to object + + PROC + ProcGetClosestAvailableCharacterTo((CHARACTERGUID)_Obj,(INTEGER)_SightCheck) + THEN + ProcGetClosestAvailableCharacterTo((CHARACTERGUID)_Obj,(INTEGER)_SightCheck,0,NULL_00000000-0000-0000-0000-000000000000); + + + PROC + ProcGetClosestAvailableCharacterTo((CHARACTERGUID)_Obj,(INTEGER)_SightCheck,(INTEGER)_PartyCheck,(CHARACTERGUID)_Player) + THEN + ProcGetClosestAvailableCharacterTo((CHARACTERGUID)_Obj,(INTEGER)_SightCheck,(INTEGER)_PartyCheck,(CHARACTERGUID)_Player,(CHARACTERGUID)NULL_00000000-0000-0000-0000-000000000000); + + PROC + ProcGetClosestAvailableCharacterTo((CHARACTERGUID)_Obj,(INTEGER)_SightCheck,(INTEGER)_PartyCheck,(CHARACTERGUID)_Player,(CHARACTERGUID)_ExceptPlayer) + AND + DB_ProcGetClosestAvailableCharacterTo_Dist(_Char,_Obj,_Dist) + THEN + NOT DB_ProcGetClosestAvailableCharacterTo_Dist(_Char,_Obj,_Dist); + + PROC + ProcGetClosestAvailableCharacterTo((CHARACTERGUID)_Obj,(INTEGER)_SightCheck,(INTEGER)_PartyCheck,(CHARACTERGUID)_Player,(CHARACTERGUID)_ExceptPlayer) + AND + DB_ClosestAvailablePlayer(_Char,_Obj) + THEN + NOT DB_ClosestAvailablePlayer(_Char,_Obj); + + PROC + ProcGetClosestAvailableCharacterTo((CHARACTERGUID)_Obj,(INTEGER)_SightCheck,(INTEGER)_PartyCheck,(CHARACTERGUID)_Player,(CHARACTERGUID)_ExceptPlayer) + AND + DB_ClosestAvailablePlayer_NoAvailablePlayer(_Obj) + THEN + NOT DB_ClosestAvailablePlayer_NoAvailablePlayer(_Obj); + + PROC + ProcGetClosestAvailableCharacterTo((CHARACTERGUID)_Obj,1,1,(CHARACTERGUID)_Player,(CHARACTERGUID)_ExceptPlayer) + AND + DB_IsPlayer(_Char) + AND + _Char != _ExceptPlayer + AND + CharacterCanSee(_Obj,(CHARACTERGUID)_Char,1) + AND + QRY_SpeakerIsAvailable(_Char) + AND + CharacterIsInPartyWith(_Char,_Player,1) + AND + GetDistanceTo(_Char,_Obj,_Dist) + THEN + DB_ProcGetClosestAvailableCharacterTo_Dist(_Char,_Obj,_Dist); + + PROC + ProcGetClosestAvailableCharacterTo((CHARACTERGUID)_Obj,0,1,(CHARACTERGUID)_Player,(CHARACTERGUID)_ExceptPlayer) + AND + DB_IsPlayer(_Char) + AND + _Char != _ExceptPlayer + AND + QRY_SpeakerIsAvailable(_Char) + AND + CharacterIsInPartyWith(_Char,_Player,1) + AND + GetDistanceTo(_Char,_Obj,_Dist) + THEN + DB_ProcGetClosestAvailableCharacterTo_Dist(_Char,_Obj,_Dist); + + PROC + ProcGetClosestAvailableCharacterTo((CHARACTERGUID)_Obj,1,0,(CHARACTERGUID)_Player,(CHARACTERGUID)_ExceptPlayer) + AND + DB_IsPlayer(_Char) + AND + _Char != _ExceptPlayer + AND + CharacterCanSee(_Obj,(CHARACTERGUID)_Char,1) + AND + QRY_SpeakerIsAvailable(_Char) + AND + GetDistanceTo(_Char,_Obj,_Dist) + THEN + DB_ProcGetClosestAvailableCharacterTo_Dist(_Char,_Obj,_Dist); + + PROC + ProcGetClosestAvailableCharacterTo((CHARACTERGUID)_Obj,0,0,(CHARACTERGUID)_Player,(CHARACTERGUID)_ExceptPlayer) + AND + DB_IsPlayer(_Char) + AND + _Char != _ExceptPlayer + AND + QRY_SpeakerIsAvailable(_Char) + AND + GetDistanceTo(_Char,_Obj,_Dist) + THEN + DB_ProcGetClosestAvailableCharacterTo_Dist(_Char,_Obj,_Dist); + + PROC + ProcGetClosestAvailableCharacterTo((CHARACTERGUID)_Obj,(INTEGER)_SightCheck,(INTEGER)_PartyCheck,(CHARACTERGUID)_Player,(CHARACTERGUID)_ExceptPlayer) + AND + DB_ProcGetClosestAvailableCharacterTo_Dist(_Char1,_Obj,_Dist1) + AND + DB_ProcGetClosestAvailableCharacterTo_Dist(_Char2,_Obj,_Dist2) + AND + _Dist1 < _Dist2 + THEN + NOT DB_ProcGetClosestAvailableCharacterTo_Dist(_Char2,_Obj,_Dist2); + + PROC + ProcGetClosestAvailableCharacterTo((CHARACTERGUID)_Obj,(INTEGER)_SightCheck,(INTEGER)_PartyCheck,(CHARACTERGUID)_Player,(CHARACTERGUID)_ExceptPlayer) + AND + NOT DB_ProcGetClosestAvailableCharacterTo_Dist(_,_Obj,_) + THEN + DB_ClosestAvailablePlayer_NoAvailablePlayer(_Obj); + + PROC + ProcGetClosestAvailableCharacterTo((CHARACTERGUID)_Obj,(INTEGER)_SightCheck,(INTEGER)_PartyCheck,(CHARACTERGUID)_Player,(CHARACTERGUID)_ExceptPlayer) + AND + DB_ProcGetClosestAvailableCharacterTo_Dist(_Char,_Obj,_Dist) + THEN + DB_ClosestAvailablePlayer(_Char,_Obj); + NOT DB_ProcGetClosestAvailableCharacterTo_Dist(_Char,_Obj,_Dist); + + //END_REGION + + + //REGION Disappear out of sight + + PROC + ProcCharacterDisappearOutOfSight((CHARACTERGUID)_Character,(INTEGER)_Angle,(INTEGER)_Running,(STRING)_Event,(INTEGER)_IncreaseSpeed) + THEN + CharacterDisableAllCrimes(_Character); + DB_CharacterDisappearedOutOfSight(_Character,_Event,1); + CharacterDisappearOutOfSight(_Character,_Angle,_Running,_Event,_IncreaseSpeed); + + PROC + ProcCharacterDisappearOutOfSightToObject((CHARACTERGUID)_Character,(GUIDSTRING)_Object,(INTEGER)_Running,(STRING)_Event,(INTEGER)_IncreaseSpeed) + THEN + CharacterDisableAllCrimes(_Character); + DB_CharacterDisappearedOutOfSight(_Character,_Event,1); + CharacterDisappearOutOfSightToObject(_Character,_Object,_Running,_Event,_IncreaseSpeed); + + QRY + QryCanSeeAttackers((CHARACTERGUID)_Char,(CHARACTERGUID)_Src,(CHARACTERGUID)_SrcSummon) + AND + _SrcSummon != NULL_00000000-0000-0000-0000-000000000000 + AND + _SrcSummon != _Src + AND + CharacterCanSee(_Char,_SrcSummon,1) + THEN + DB_SawAttacker(_SrcSummon); + + QRY + QryCanSeeAttackers((CHARACTERGUID)_Char,(CHARACTERGUID)_Src,(CHARACTERGUID)_SrcSummon) + AND + NOT DB_SawAttacker(_) + AND + CharacterCanSee(_Char,_Src,1) + THEN + DB_SawAttacker(_Src); + + IF + AttackedByObject((CHARACTERGUID)_Char,(CHARACTERGUID)_Src,_SrcSummon,_,_) + AND + _Src != _Char + AND + ObjectIsOnStage(_Char,1) + AND + DB_CharacterDisappearedOutOfSight(_Char,_,1) + AND + IsTagged(_Char,"ANIMAL",0) + AND + CharacterCanFight(_Char,1) + AND + ObjectIsCharacter(_Src,1) + AND + QryCanSeeAttackers(_Char,_Src,_SrcSummon) + AND + DB_SawAttacker(_Target) + THEN + DB_StoppedOutOfSight(_Char); + CharacterPurgeQueue(_Char); + CharacterSetTemporaryHostileRelation(_Char,_Target); + NOT DB_SawAttacker(_Target); + + IF + CharacterSetTemporaryRelationsFailed(_Char,_Target) + AND + DB_StoppedOutOfSight(_Char) + THEN + ProcResumeDisappearOutOfSight(_Char); + + IF + ObjectLeftCombat((CHARACTERGUID)_Char,_) + AND + DB_StoppedOutOfSight(_Char) + THEN + ProcResumeDisappearOutOfSight(_Char); + + PROC + ProcResumeDisappearOutOfSight((CHARACTERGUID)_Char) + AND + NOT DB_Dead(_Char) + AND + DB_CharacterDisappearedOutOfSight(_Char,_Event,1) + THEN + NOT DB_StoppedOutOfSight(_Char); + CharacterDisappearOutOfSight(_Char,0,1,_Event,1); + + IF + CharacterWentOnStage(_Character,1) + AND + DB_CharacterDisappearedOutOfSight(_Character,_Event,_ReactToCombat) + THEN + NOT DB_CharacterDisappearedOutOfSight(_Character,_Event,_ReactToCombat); + ProcRestoreGenericBehaviour(_Character); + + IF + StoryEvent((CHARACTERGUID)_Character,"GEN_CharacterDisableAllCrimesBeforeDisappear") + THEN + CharacterDisableAllCrimes(_Character); + DB_CharacterDisappearedOutOfSight(_Character,"",0); + + PROC + ProcClearDisappearData((CHARACTERGUID)_Character) + AND + DB_CharacterDisappearedOutOfSight(_Character,_Event,_ReactToCombat) + THEN + NOT DB_CharacterDisappearedOutOfSight(_Character,_Event,_ReactToCombat); + + //END_REGION + + //REGION TemporaryHostileRelation + PROC + Proc_CharacterSetTemporaryHostileRelation((CHARACTERGUID)_Char1,(CHARACTERGUID)_Char2) + AND + _Char2!= NULL_00000000-0000-0000-0000-000000000000 + AND + _Char1!= NULL_00000000-0000-0000-0000-000000000000 + THEN + CharacterSetTemporaryHostileRelation(_Char1,_Char2); + //END_REGION + + IF + RegionEnded(_Region) + THEN + ProcRegionEnded(_Region); + + PROC + ProcRegionEnded((STRING)_Region) + THEN + DB_NOOP(1); + + //REGION Healing status query + QRY + QRY_IsHealingStatus((STRING)_Status) + AND + _Status != "HEAL" //DOSTWO-24618 The actual HEAL status is a tick that is set from different statusses (that possibly doesn't heal because it has its parameters overwritten). It's never set on its own. + AND + GetStatusType(_Status,"HEAL") + AND + GetHealStat(_Status,"Vitality") + THEN + DB_NOOP(1); + + QRY + QRY_IsHealingStatus((STRING)_Status) + AND + GetStatusType(_Status,"HEALING") + AND + GetHealStat(_Status,"Vitality") + THEN + DB_NOOP(1); + //END_REGION + + //REGION item moving defaults + PROC + ItemMoveToTrigger((ITEMGUID)_Item,(TRIGGERGUID)_Trigger, (REAL)_Speed, (REAL)_Acceleration, (INTEGER)_UseRotation, (STRING)_Event) + THEN + ItemMoveToTrigger(_Item,_Trigger,_Speed,_Acceleration,_UseRotation,_Event,1); + + PROC + ItemMoveToTrigger((ITEMGUID)_Item,(TRIGGERGUID)_Trigger, (REAL)_Speed, (REAL)_Acceleration, (INTEGER)_UseRotation) + THEN + ItemMoveToTrigger(_Item,_Trigger,_Speed,_Acceleration,_UseRotation,"",1); + + PROC + ItemMoveToPosition((ITEMGUID)_Item, (REAL)_X, (REAL)_Y, (REAL)_Z, (REAL)_Speed, (REAL)_Acceleration, (STRING)_Event) + THEN + ItemMoveToPosition(_Item, _X, _Y, _Z, _Speed, _Acceleration,_Event,1); + + PROC + ItemMoveToPosition((ITEMGUID)_Item, (REAL)_X, (REAL)_Y, (REAL)_Z, (REAL)_Speed, (REAL)_Acceleration) + THEN + ItemMoveToPosition(_Item, _X, _Y, _Z, _Speed, _Acceleration,"",1); + + //END_REGION + + //REGION transform overloads + PROC + Transform((ITEMGUID)_Object, (STRING)_Template) + THEN + Transform(_Object,_Template,0,0,0); + + PROC + Transform((GUIDSTRING)_Object, (STRING)_Template, (INTEGER)_ReplaceScripts) + THEN + Transform(_Object,_Template,_ReplaceScripts,0,0); + + PROC + Transform((GUIDSTRING)_Object, (STRING)_Template, (INTEGER)_ReplaceScripts, (INTEGER)_ReplaceScale) + THEN + Transform(_Object,_Template,_ReplaceScripts,_ReplaceScale,0); + + //END_REGION + + //REGION Perception + PROC + ProcSetPerceptionDifficulty((ITEMGUID)_Item,(STRING)_Difficulty) + THEN + SetVarFixedString(_Item,"Difficulty",_Difficulty); + SetStoryEvent(_Item,"GLO_OverrideWitsCheck"); + //END_REGION + + PROC + PlayEffect((GUIDSTRING)_Object,(STRING)_FxName) + THEN + PlayEffect(_Object,_FxName,""); + + PROC + ApplyDamage((GUIDSTRING)_Object, (INTEGER)_Damage, (STRING)_DamageType) + THEN + ApplyDamage(_Object, _Damage, _DamageType, NULL_00000000-0000-0000-0000-000000000000); + + PROC + ApplyStatus((GUIDSTRING)_Object, (STRING)_Status, (REAL)_Duration, (INTEGER)_Force) + THEN + ApplyStatus(_Object, _Status, _Duration, _Force, NULL_00000000-0000-0000-0000-000000000000); + + PROC + CharacterDie((CHARACTERGUID)_Character, (INTEGER)_GenerateTreasure, (STRING)_DeathType) + THEN + CharacterDie(_Character, _GenerateTreasure, _DeathType, NULL_00000000-0000-0000-0000-000000000000); + + PROC + CharacterDieImmediate((CHARACTERGUID)_Character, (INTEGER)_GenerateTreasure, (STRING)_DeathType) + THEN + CharacterDieImmediate(_Character, _GenerateTreasure, _DeathType, NULL_00000000-0000-0000-0000-000000000000); + + } + EXIT + { + + } +} +Goal(10).Title("__Start"); +Goal(10) +{ + INIT + { + Proc_ExclamationMarkCleanup(); + + + } + KB + { + IF + GameEventSet("GAMEEVENT_GameStarted") + THEN + InitStory(); + GoalCompleted; + + + PROC + Proc_ExclamationMarkCleanup() + AND + _Player.DB_IsPlayer() + THEN + CharacterStopAllEffectsWithName(_Player,"RS3_FX_UI_Exclamation_Mark_01"); + SetHasDialog(_Player,0); + + } + EXIT + { + + } +} +Goal(11).Title("_AAA_FirstGoal"); +Goal(11) +{ + INIT + { + // Time(_Day,_Hour,_TotalHours) + // Current time (updated last time event NewHour was generated) + // _TotalHours = (_Day - 1)*24 + _Hour + // _TotalHours is suitable for calculating time differences + //SetTime(ENGINE_GAME,10); TODO + DB_Time(1,10,10); + // CurrentHalfHour and total HalfHours + DB_HalfHour(20,20); + + //TODO: for now we use these timers to simulate time passing. This should come from the game engine if we're going to support different time of day settings + DB_GameHour(300000); + StartTimeOfDayTimerLoop(); + + // Money dialog variables + DB_FirstGoal_MoneyDialogVar(1,"GEN_CheckMagicPocketGold_6057ad05-9492-4630-9f0a-be548b134c54"); + DB_FirstGoal_MoneyDialogVar(2,"GEN_CheckMagicPocketGold_2_463b0f43-5410-412d-aba3-875cf81c38ca"); + DB_FirstGoal_MoneyDialogVar(3,"GEN_CheckMagicPocketGold_3_01f129ea-b4dc-44b6-8154-9a948f876a82"); + DB_FirstGoal_MoneyDialogVar(4,"GEN_CheckMagicPocketGold_4_5dac5eea-faeb-459c-b675-46c51519b784"); + DB_FirstGoal_MoneyDialogVar(5,"GEN_CheckMagicPocketGold_5_8860ed48-ba5f-4b7b-82bf-f1220f967d41"); + + DB_FirstGoal_CheckPocketGoldSpeakerVar(1,"GEN_CheckPocketGold_SpeakerIndex_8504b4e0-886e-4912-9525-fbe559c5f8ff"); + DB_FirstGoal_CheckPocketGoldSpeakerVar(2,"GEN_CheckPocketGold_2_SpeakerIndex_a9c4d456-3ef5-491d-a1eb-49f09b91e8b4"); + DB_FirstGoal_CheckPocketGoldSpeakerVar(3,"GEN_CheckPocketGold_3_SpeakerIndex_d7fa1537-77d4-4d7d-a955-7ffda90e6207"); + DB_FirstGoal_CheckPocketGoldSpeakerVar(4,"GEN_CheckPocketGold_4_SpeakerIndex_5c499066-448b-4d0c-8dc9-d1553d04c6d4"); + DB_FirstGoal_CheckPocketGoldSpeakerVar(5,"GEN_CheckPocketGold_5_SpeakerIndex_65757687-b815-43fe-be9c-1cefe82efb60"); + + } + KB + { + //REGION Game Mode handling + PROC + Proc_GameModeStarted((STRING)_Mode,(INTEGER)_) + THEN + ProcSetCurrentGameMode(_Mode); + + PROC + ProcSetCurrentGameMode((STRING)_Mode) + AND + DB_CurrentGameMode(_Old) + THEN + NOT DB_CurrentGameMode(_Old); + + PROC + ProcSetCurrentGameMode((STRING)_Mode) + THEN + DB_CurrentGameMode(_Mode); + + //END_REGION + + //REGION Dialog book keeping + IF + DialogRequestFailed(_,_Inst) + THEN + DB_MarkedForDelete(_Inst); + ProcGetInvolvedPlayers(_Inst); + ProcGetInvolvedNPCs(_Inst); + ProcSaveNumActors(_Inst); + ProcClearDialogFlagsForPlayers(_Inst); + ProcClearDialogFlagsForNPCs(_Inst); + + IF + AutomatedDialogRequestFailed(_,_Inst) + THEN + ProcGetInvolvedPlayers(_Inst); + ProcGetInvolvedNPCs(_Inst); + ProcSaveNumActors(_Inst); + DB_AutomatedDialog(_Inst); + + IF + DialogStarted(_Dialog,_Inst) + THEN + ProcGetInvolvedPlayers(_Inst); + ProcGetInvolvedNPCs(_Inst); + ProcSaveNumActors(_Inst); + ProcSetDialogGoldCheckAmount(_Dialog); + DB_DialogName(_Dialog,_Inst); + + IF + DialogActorJoined(_Dialog,_Inst,_Actor) + THEN + ProcAddActorToDialogList(_Inst,_Actor); + ProcSetStoryNPC(_Inst,_Actor); + ProcSetNumberOfInvolvedActors(_Inst); + + IF + VoiceBarkStarted(_,_Inst) + THEN + DB_AutomatedDialogIsVB(_Inst); + + IF + AutomatedDialogStarted(_Dialog,_Inst) + THEN + ProcGetInvolvedPlayers(_Inst); + ProcGetInvolvedNPCs(_Inst); + ProcSaveNumActors(_Inst); + DB_AutomatedDialog(_Inst); + DB_DialogName(_Dialog,_Inst); + + PROC + ProcSetStoryNPC((INTEGER)_Inst,(GUIDSTRING)_Actor) + AND + NOT DB_AutomatedDialog(_Inst) + AND + ObjectIsCharacter((CHARACTERGUID)_Actor,1) + AND + NOT DB_CombatCharacters(_Actor,_) + THEN + CharacterMakeStoryNpc(_Actor,1); + + PROC + ProcSetStoryNPC((INTEGER)_Inst,(GUIDSTRING)_Actor) + AND + NOT DB_AutomatedDialog(_Inst) + THEN + ProcItemSetInvulnerableForDialog(_Actor); + + //add these in the back so we don't disrupt the original flow + PROC + ProcAddActorToDialogList((INTEGER)_Inst,(GUIDSTRING)_Actor) + AND + DB_IsPlayer((CHARACTERGUID)_Actor) + AND + DB_DialogNumPlayers(_Inst,_NumPlayers) + AND + IntegerSum(_NumPlayers,1,_NewSlot) + THEN + DB_DialogPlayers(_Inst,(GUIDSTRING)_Actor,_NewSlot); + + PROC + ProcAddActorToDialogList((INTEGER)_Inst,(GUIDSTRING)_Actor) + AND + NOT DB_IsPlayer((CHARACTERGUID)_Actor) + AND + DB_DialogNumNPCs(_Inst,_NumNPCs) + AND + IntegerSum(_NumNPCs,1,_NewSlot) + THEN + DB_DialogNPCs(_Inst,(GUIDSTRING)_Actor,_NewSlot); + + + PROC + ProcSetNumberOfInvolvedActors((INTEGER)_Inst) + AND + DB_DialogNumPlayers(_Inst,_NumPlayers) + AND + DB_DialogNumNPCs(_Inst,_NumNPCs) + THEN + NOT DB_DialogNumPlayers(_Inst,_NumPlayers); + NOT DB_DialogNumNPCs(_Inst,_NumNPCs); + ProcSaveNumActors(_Inst); + + PROC + ProcSaveNumActors((INTEGER)_Inst) + AND + DialogGetNumberOfInvolvedNPCs(_Inst,_NumNPCs) + AND + DialogGetNumberOfInvolvedPlayers(_Inst,_NumPlayers) + THEN + DB_DialogNumPlayers(_Inst,_NumPlayers); + DB_DialogNumNPCs(_Inst,_NumNPCs); + + PROC + ProcGetInvolvedNPCs((INTEGER)_Inst) + THEN + ProcGetInvolveNPC(_Inst,1); + + PROC + ProcGetInvolveNPC((INTEGER)_Inst,(INTEGER)_Index) + AND + DialogGetInvolvedNPC(_Inst,_Index,_NPC) + AND + IntegerSum(_Index,1,_New) + THEN + DB_DialogNPCs(_Inst,_NPC,_Index); + ProcGetInvolveNPC(_Inst,_New); + + PROC + ProcGetInvolvedPlayers((INTEGER)_Inst) + THEN + ProcGetInvolvePlayer(_Inst,1); + + PROC + ProcGetInvolvePlayer((INTEGER)_Inst,(INTEGER)_Index) + AND + DialogGetInvolvedPlayer(_Inst,_Index,_Player) + AND + IntegerSum(_Index,1,_New) + THEN + DB_DialogPlayers(_Inst,_Player,_Index); + ProcGetInvolvePlayer(_Inst,_New); + + PROC + ProcSetDialogGoldCheckAmount((STRING)_Dialog) + AND + DB_DialogMoneyTransfer((INTEGER)_Index,(STRING)_Dialog,(INTEGER)_Amount,(INTEGER)_CheckSpeakerIndex,(INTEGER)_) + AND + DB_FirstGoal_MoneyDialogVar(_Index,_MoneyVarName) + AND + DB_FirstGoal_CheckPocketGoldSpeakerVar(_Index,_SpeakerVarName) + THEN + DialogSetVariableInt(_Dialog,_MoneyVarName,_Amount); + DialogSetVariableInt(_Dialog,_SpeakerVarName,_CheckSpeakerIndex); + + //END_REGION + + //REGION Player NPC seeing + + PROC + ProcCheckIfOtherPlayersCanSeeNPC((CHARACTERGUID)_,(CHARACTERGUID)_) + AND + DB_OtherPlayersSee(_Npc) + THEN + NOT DB_OtherPlayersSee(_Npc); + + PROC + ProcCheckIfOtherPlayersCanSeeNPC((CHARACTERGUID)_Player,(CHARACTERGUID)_Npc) + AND + _Other.DB_IsPlayer() + AND + _Other!=_Player + AND + DB_Sees(_OtherPlayer,_Npc) + THEN + DB_OtherPlayersSee(_Npc); + + + //END_REGION + + //REGION Player trigger checking + + QRY + QryCheckOtherPlayersInTrigger((CHARACTERGUID)_Player,(TRIGGERGUID)_CheckTrigger) + AND + _Other.DB_IsPlayer() + AND + _Other!=_Player + AND + DB_InRegion(_Other, _CheckTrigger) + THEN + DB_NOOP(1); + + //END_REGION + + //REGION Player triggers + PROC + ProcTriggerRegisterForPlayers((TRIGGERGUID)_Trig) + AND + GetRegion(_Trig,_Level) + THEN + DB_PlayerTriggers(_Level,_Trig); + ProcDoTriggerRegisterForPlayers(_Level,_Trig); + + PROC + ProcDoTriggerRegisterForPlayers((STRING)_Level,(TRIGGERGUID)_Trig) + AND + ObjectExists(_Trig,0) + THEN + DB_PlayerTriggerToRegister(_Level,_Trig); + + PROC + ProcDoTriggerRegisterForPlayers((STRING)_Level,(TRIGGERGUID)_Trig) + AND + NOT DB_PlayerTriggerToRegister(_Level,_Trig) + THEN + TriggerRegisterForPlayers(_Trig); + + PROC + ProcTriggerUnregisterForPlayers((TRIGGERGUID)_Trig) + AND + DB_PlayerTriggers(_Level,_Trig) + AND + ObjectExists(_Trig,0) + THEN + DB_PlayerTriggerToUnregister(_Level,_Trig); + + PROC + ProcTriggerUnregisterForPlayers((TRIGGERGUID)_Trig) + AND + DB_PlayerTriggers(_Level,_Trig) + AND + NOT DB_PlayerTriggerToUnregister(_Level,_Trig) + THEN + TriggerUnregisterForPlayers(_Trig); + + PROC + ProcTriggerUnregisterForPlayers((TRIGGERGUID)_Trig) + AND + DB_PlayerTriggers(_Level,_Trig) + THEN + NOT DB_PlayerTriggers(_Level,_Trig); + + PROC + ProcRegisterPlayerTriggers((CHARACTERGUID)_Char) + AND + DB_PlayerTriggers(_Level,_Trig) + AND + ObjectExists(_Trig,0) + THEN + DB_PlayerTriggerToRegisterForCharacter(_Char,_Level,_Trig); + + PROC + ProcRegisterPlayerTriggers((CHARACTERGUID)_Char) + AND + DB_PlayerTriggers(_Level,_Trig) + AND + NOT DB_PlayerTriggerToRegisterForCharacter(_Char,_Level,_Trig) + THEN + TriggerRegisterForCharacter(_Trig,_Char); + + PROC + ProcUnRegisterPlayerTriggers((CHARACTERGUID)_Char) + AND + DB_PlayerTriggers(_Level,_Trig) + AND + ObjectExists(_Trig,0) + THEN + DB_PlayerTriggerToUnregisterForCharacter(_Char,_Level,_Trig); + + PROC + ProcUnRegisterPlayerTriggers((CHARACTERGUID)_Char) + AND + DB_PlayerTriggers(_Level,_Trig) + AND + NOT DB_PlayerTriggerToUnregisterForCharacter(_Char,_Level,_Trig) + THEN + TriggerUnregisterForCharacter(_Trig,_Char); + + IF + DB_PlayerTriggerToUnregisterForCharacter(_Char,_Level,_Trig) + THEN + NOT DB_PlayerTriggerToRegisterForCharacter(_Char,_Level,_Trig); + + IF + DB_PlayerTriggerToRegisterForCharacter(_Char,_Level,_Trig) + THEN + NOT DB_PlayerTriggerToUnregisterForCharacter(_Char,_Level,_Trig); + + IF + DB_PlayerTriggerToUnregister(_Level,_Trig) + THEN + NOT DB_PlayerTriggerToRegister(_Level,_Trig); + + IF + DB_PlayerTriggerToRegister(_Level,_Trig) + THEN + NOT DB_PlayerTriggerToUnregister(_Level,_Trig); + + IF + RegionStarted(_Level) + THEN + ProcRegisterTriggersForRegion(_Level); + ProcUnregisterTriggersForRegion(_Level); + ProcRegisterTriggersForCharacters(_Level); + ProcUnregisterTriggersForCharacters(_Level); + + PROC + ProcRegisterTriggersForRegion((STRING)_Level) + AND + DB_PlayerTriggerToRegister(_Level,_Trig) + THEN + TriggerRegisterForPlayers(_Trig); + NOT DB_PlayerTriggerToRegister(_Level,_Trig); + + PROC + ProcUnregisterTriggersForRegion((STRING)_Level) + AND + DB_PlayerTriggerToUnregister(_Level,_Trig) + THEN + TriggerUnregisterForPlayers(_Trig); + NOT DB_PlayerTriggerToUnregister(_Level,_Trig); + + PROC + ProcRegisterTriggersForCharacters((STRING)_Level) + AND + DB_PlayerTriggerToRegisterForCharacter(_Char,_Level,_Trig) + THEN + TriggerRegisterForCharacter(_Trig,_Char); + NOT DB_PlayerTriggerToRegisterForCharacter(_Char,_Level,_Trig); + + PROC + ProcUnregisterTriggersForCharacters((STRING)_Level) + AND + DB_PlayerTriggerToUnregisterForCharacter(_Char,_Level,_Trig) + THEN + TriggerUnregisterForCharacter(_Trig,_Char); + NOT DB_PlayerTriggerToUnregisterForCharacter(_Char,_Level,_Trig); + + PROC + ProcSetupGenericTimers((CHARACTERGUID)_Char) + AND + GetUUID(_Char,_UUID) + AND + StringConcatenate(_UUID,"_genTim",_Timer) + THEN + DB_GenericDialogTimers(_Char,_Timer); + + PROC + ProcRemoveGenericTimer((CHARACTERGUID)_Char) + AND + DB_GenericDialogTimers(_Char,_Timer) + THEN + NOT DB_GenericDialogTimers(_Char,_Timer); + //END_REGION + + // Since storyeditor orders goals alphabetically, this is the only way to make sure this goal comes first in story.div + // This is important because Osiris uses rule order to execute alternatives. + + /////////////////////////////////////////////////////////////////////// + // buffer time + + PROC + StartTimeOfDayTimerLoop() + AND + DB_GameHour(_Msec) + THEN + TimerLaunch("TimeOfDay",_Msec); + + IF + TimerFinished("TimeOfDay") + AND + DB_GameHour(_Msec) + AND + DB_Time(_,_Hour,_) + AND + IntegerSum(_Hour,1,_NewHour) + AND + IntegerModulo(_NewHour,24,_Mod) + THEN + TimerLaunch("TimeOfDay",_Msec); + NewHour(_Mod); + + PROC + NewHour(0) + AND // Day shifts to tomorrow + DB_Time((INTEGER)_Day,(INTEGER)_,(INTEGER)_) + AND + IntegerSum(_Day,1,_DayP1) + THEN + UpdateTime(_DayP1,0); + + PROC + NewHour((INTEGER)_Hour) + AND + _Hour != 0 + AND // Day stays today + DB_Time(_Day,_,_) + THEN + UpdateTime(_Day,_Hour); + + // Asserts Time(_Day,_Hour,...) with ... = the corresponding total hours = 24*(_Day - 1) + _Hour + PROC + UpdateTime((INTEGER)_Day,(INTEGER)_Hour) + AND + DB_Time(_D,_H,_TH) + AND + // calc new total hours: + IntegerSubtract(_Day,1,_DM1) + AND + IntegerProduct(_DM1,24,_T1) + AND + IntegerSum(_T1,_Hour,_NewTH) + THEN + NOT DB_Time(_D,_H,_TH); + DB_Time(_Day,_Hour,_NewTH); + + //TODO: commented stuff + /* + // + IF NewHalfHour(ENGINE_GAME,0) + THEN SetNewHalfHour(24); + IF NewHalfHour(ENGINE_GAME,_HH) AND _HH != 0 + THEN SetNewHalfHour(_HH); + + PROC SetNewHalfHour((INTEGER)_HH) AND + HalfHour((INTEGER)_CurrentHH,(INTEGER)_TotalHH) AND + IntegerSubtract(FUNCTION_Math,_HH,_CurrentHH,_Delta) AND + // Normally, Delta == 1 + IntegerSum(FUNCTION_Math,_CurrentHH,_Delta,_CurrentHHPDelta) AND + IntegerSum(FUNCTION_Math,_TotalHH,_Delta,_TotalHHPDelta) + THEN NOT HalfHour(_CurrentHH,_TotalHH); + HalfHour(_CurrentHHPDelta,_TotalHHPDelta); + + + /////////////////////////////////////////////////////////////////////// + // Buffer player invisibility + IF PlayerTurnsInvisible(NPC_Hero,1) + THEN PlayerIsInvisible(1); + + IF PlayerTurnsInvisible(NPC_Hero,0) + THEN NOT PlayerIsInvisible(1); + */ + /////////////////////////////////////////////////////////////////////// + // Buffer seeing events + // Note: "Sees" is asserted as soon as the "NpcSees" is generated. So you can use + // "Sees" as event as well. Major difference: NpcSees ignores player invisibility. + + // Buffer effects of player movement: + // (NpcSees actually means: "player starts seeing", NpcDoesNotSee means "players ends seeing") + IF + CharacterSawCharacter(_Player,_Npc) + AND + _Player.DB_IsPlayer() + THEN + DB_Sees(_Player, _Npc); + + //DB_Dead NPCs are blind + IF + CharacterDying(_Npc) + AND + DB_Sees(_Npc,_Player) + THEN + NOT DB_Sees(_Npc,_Player); + + IF + CharacterLostSightOfCharacter(_Player,_Npc) + AND + _Player.DB_IsPlayer() + THEN + NOT DB_Sees(_Player, _Npc); + NOT DB_Sees(_Npc, _Player); + + // Bring player invisibility into account (neglect for story npcs): + IF + DB_Sees(_Npc, _Player) + AND + _Player.DB_IsPlayer() + AND + DB_PlayerIsInvisible(_Player,1) + AND + NOT DB_IsStoryNpc(_Npc) + THEN + NOT DB_Sees(_Npc, _Player); + + IF + DB_Sees(_Player, _Npc) + AND + _Player.DB_IsPlayer() + AND + NOT DB_PlayerIsInvisible(_Player,1) + THEN + DB_Sees(_Npc, _Player); + + IF + DB_Sees(_Player, _Npc) + AND + _Player.DB_IsPlayer() + AND + DB_IsStoryNpc(_Npc) + THEN + DB_Sees(_Npc, _Player); + + /* + /////////////////////////////////////////////////////////////////////// + // Buffer dialog distance (analogous to seeing events) + + // Buffer effects of player movement: + IF NpcEntersDialogDistance(NPC_Hero, _Npc) + THEN InDialogDistance(NPC_Hero, _Npc); + + IF NpcLeavesDialogDistance(NPC_Hero, _Npc) + THEN NOT InDialogDistance(NPC_Hero, _Npc); + NOT InDialogDistance(_Npc, NPC_Hero); + + // Bring player invisibility into account (neglect for story npcs): + IF InDialogDistance(_Npc, NPC_Hero) AND PlayerIsInvisible(1) AND NOT DB_IsStoryNpc(_Npc) + THEN NOT InDialogDistance(_Npc, NPC_Hero); + + IF InDialogDistance(NPC_Hero, _Npc) AND NOT PlayerIsInvisible(1) + THEN InDialogDistance(_Npc, NPC_Hero); + + IF InDialogDistance(NPC_Hero, _Npc) AND DB_IsStoryNpc(_Npc) + THEN InDialogDistance(_Npc, NPC_Hero); + */ + + /////////////////////////////////////////////////////////////////////// + // Buffer region events + IF + CharacterEnteredTrigger(_Npc,_Region) + THEN + DB_InRegion(_Npc, _Region); + + IF + CharacterLeftTrigger(_Npc,_Region) + THEN + NOT DB_InRegion(_Npc, _Region); + DB_WasInRegion(_Npc,_Region); + + /////////////////////////////////////////////////////////////////////// + // Buffer killed + IF + CharacterDying(_Npc) + THEN + DB_Dead(_Npc); + + IF + CharacterDied(_Npc) + AND + NOT DB_Dead(_Npc) + THEN + DB_Dead(_Npc); + + IF + CharacterResurrected(_Npc) + THEN + NOT DB_Dead(_Npc); + + //TODO: comments + /* + // Engine does not generate NpcDoesNotSee for dead npcs anymore, so we have to clean up ourself: + IF NpcKilled(_Npc) AND Sees(_Npc,_OtherNpc) + THEN NOT Sees(_Npc,_OtherNpc); + IF NpcKilled(_Npc) AND Sees(_OtherNpc,_Npc) + THEN NOT Sees(_OtherNpc,_Npc); + // Cleanup InDialogDistance + IF NpcKilled(_Npc) AND InDialogDistance(_Npc,_OtherNpc) + THEN NOT InDialogDistance(_Npc,_OtherNpc); + IF NpcKilled(_Npc) AND InDialogDistance(_OtherNpc,_Npc) + THEN NOT InDialogDistance(_OtherNpc,_Npc); + */ + + /* ///This Breaks a lot of things especially Arrest in Generics + IF + CharacterDied(_Npc) + AND + DB_InRegion(_Npc,_Region) + THEN + NOT DB_InRegion(_Npc,_Region); + */ + + //TODO: comments + /* + IF NpcResurrected((NPC)_Npc) + THEN NOT DB_Dead(_Npc); + + IF NpcKilledBy(_Npc,NPC_Hero) + THEN HeroKilled(_Npc); + */ + /////////////////////////////////////////////////////////////////////// + // Cleanup DB_InRegion of npc (not the hero) if they are killed + // test for _Npc != NPC_Hero explicitely added because debug cheat key "F1" does not + // resurrect NPC_Hero, hence he remains "dead" although he can play on, enter regions, etc... + IF + DB_Dead(_Npc) + AND + NOT _Npc.DB_IsPlayer() + AND + DB_InRegion(_Npc,_Region) + THEN + NOT DB_InRegion(_Npc,_Region); + + IF + DB_Dead(_Npc) + AND + DB_DeadEvent(_Npc,(STRING)_Event) + AND + GlobalGetFlag(_Event,0) + THEN + GlobalSetFlag(_Event); + + IF + RegionStarted((STRING)_NewRegion) + THEN + DB_CurrentLevel(_NewRegion); + + IF + RegionEnded((STRING)_Region) + THEN + NOT DB_CurrentLevel(_Region); + + + //REGION OneshotTriggers + IF + DB_OneShotPlayerTrigger(_Trigger) + THEN + ProcTriggerRegisterForPlayers(_Trigger); + + IF + DB_OneShotPlayerOnlyTrigger(_Trigger) + THEN + TriggerRegisterForPlayers(_Trigger); + + IF + CharacterEnteredTrigger(_Player,_Trigger) + AND + DB_OneShotPlayerOnlyTrigger(_Trigger) + THEN + TriggerUnregisterForPlayers(_Trigger); + NOT DB_OneShotPlayerOnlyTrigger(_Trigger); + ProcOneShotTriggerEntered(_Player,_Trigger); + + PROC + RemoveOneShotTrigger((TRIGGERGUID)_Trigger) + AND + DB_OneShotPlayerTrigger(_Trigger) + THEN + ProcTriggerUnregisterForPlayers(_Trigger); + NOT DB_OneShotPlayerTrigger(_Trigger); + + IF + CharacterEnteredTrigger(_Player,_Trigger) + AND + DB_OneShotPlayerTrigger(_Trigger) + AND + _Player.DB_IsPlayer() + THEN + RemoveOneShotTrigger(_Trigger); + ProcOneShotTriggerEntered(_Player,_Trigger); + + PROC + ProcOneShotTriggerEntered((CHARACTERGUID)_Player,(TRIGGERGUID)_Trigger) + THEN + DB_NOOP(1); + //END_REGION + + //REGION _Global_CharacterAnimations + // Flush player character animations that may be ongoing at the end of a dialog + IF + DialogEnded(_,_Dialog) + AND + DB_GLO_CharacterAnimationForDialog(_Character,_Dialog) + AND + DB_IsPlayer(_Character) + THEN + CharacterFlushQueue(_Character); + + IF + DialogEnded(_,_Dialog) + AND + DB_GLO_CharacterAnimationForDialog(_Character,_Dialog) + THEN + // The animation may be purged by other code and hence never finish -> this DB would never be unset + NOT DB_GLO_CharacterAnimationForDialog(_Character,_Dialog); + //END_REGION + + } + EXIT + { + + } +} +Goal(12).Title("_AdvancedSneakTriggerSpotter"); +Goal(12) +{ + INIT + { + //REGION AdvancedSneakTriggerSupport + DB_SneakTriggerVars(1,"SpottedTrigger"); + DB_SneakTriggerVars(2,"SpottedTrigger1"); + DB_SneakTriggerVars(3,"SpottedTrigger2"); + DB_SneakTriggerVars(4,"SpottedTrigger3"); + //END_REGION + + } + KB + { + //REGION AdvancedSneakTrigger + IF + DB_AdvancedSneakTriggerSpotter((TRIGGERGUID)_Trigger,(CHARACTERGUID)_Char) + THEN + ProcSetSneakyTriggerVar(_Char,_Trigger); + PROC + ProcSetSneakyTriggerVar((CHARACTERGUID)_Char,(TRIGGERGUID)_Trigger) + AND + NOT DB_SneakyTriggersCounter(_Char,_) + THEN + DB_SneakyTriggersCounter(_Char,0); + PROC + ProcSetSneakyTriggerVar((CHARACTERGUID)_Char,(TRIGGERGUID)_Trigger) + AND + DB_SneakyTriggersCounter(_Char,_Count) + AND + IntegerSum(_Count,1,_New) + AND + DB_SneakTriggerVars(_New,_Var) + THEN + DB_SneakyTriggersCounter(_Char,_New); + NOT DB_SneakyTriggersCounter(_Char,_Count); + SetVarObject(_Char,_Var,_Trigger); + ProcTriggerRegisterForPlayers(_Trigger); + + IF + StoryEvent((CHARACTERGUID)_Char, "GLO_AdvancedSpotterSneaker") + AND + GetVarObject(_Char, "SpottedDude", _Player) + THEN + ProcCharSpottedByChar((CHARACTERGUID)_Player,_Char); + ProcReceiveActivatedTriggers(_Char,_Player); + ProcLockSpotting(_Char); + + PROC + ProcReceiveActivatedTriggers((CHARACTERGUID)_Char,(CHARACTERGUID)_Player) + AND + DB_AdvancedSneakTriggerSpotter((TRIGGERGUID)_Trigger, (CHARACTERGUID)_Char) + AND + DB_InRegion(_Player,_Trigger) + THEN + ProcCharInTriggerSpotted(_Player,_Trigger); + ProcCharInTriggerSpottedByChar(_Player,_Trigger,_Char); + + PROC + ProcCharSpottedByChar((CHARACTERGUID)_Player,(CHARACTERGUID)_Spotter) + THEN + DB_NOOP(1); + + PROC + ProcLockSpotting((CHARACTERGUID)_Char) + THEN + SetVarInteger(_Char, "SpottedCounter", 1); + + PROC + ProcCleanUpAdvancedSneakTriggers((CHARACTERGUID)_Char) + AND + DB_AdvancedSneakTriggerSpotter((TRIGGERGUID)_Trigger, (CHARACTERGUID)_Char) + THEN + NOT DB_AdvancedSneakTriggerSpotter(_Trigger, _Char); + SetVarInteger(_Char, "SpottedCounter", 1); + + PROC + ProcTriggerUnregisterForPlayers((TRIGGERGUID)_Trigger) + AND + DB_AdvancedSneakTriggerSpotter((TRIGGERGUID)_Trigger, (CHARACTERGUID)_Char) + THEN + NOT DB_AdvancedSneakTriggerSpotter((TRIGGERGUID)_Trigger, (CHARACTERGUID)_Char); + IF + DB_SpotterGroup((CHARACTERGUID)_Char,(STRING)_Name) + AND + _Player.DB_isPlayer() + THEN + SetVarInteger(_Player,_Name,0); + SetVarFixedString(_Char,"SpotterGroup",_Name); + //END_REGION + + } + EXIT + { + + } +} +Goal(13).Title("_Attitude"); +Goal(13) +{ + INIT + { + + } + KB + { + PROC + ProcMakeNPCHostile((CHARACTERGUID)_Player,(CHARACTERGUID)_Npc) + AND + CharacterIsDead(_Npc,0) + AND + CharacterIsPlayer(_Player,1) + AND + CharacterIsPlayer(_Npc,0) + THEN + CharacterSetTemporaryHostileRelation(_Npc,_Player); + + PROC + ProcMakeNPCHostile((CHARACTERGUID)_Npc,(CHARACTERGUID)_Player) + AND + CharacterIsDead(_Npc,0) + AND + CharacterIsPlayer(_Player,1) + THEN + CharacterSetTemporaryHostileRelation(_Npc,_Player); + + PROC + ProcMakeNPCHostile((CHARACTERGUID)_Npc,(CHARACTERGUID)_OtherNpc) + AND + CharacterIsDead(_Npc,0) + AND + CharacterIsDead(_OtherNpc,0) + AND + CharacterIsPlayer(_Npc,0) + AND + CharacterIsPlayer(_OtherNpc,0) + THEN + CharacterSetTemporaryHostileRelation(_Npc,_OtherNpc); + + } + EXIT + { + + } +} +Goal(14).Title("_CRIME_CrimeBribes"); +Goal(14) +{ + INIT + { + // small (0) or big (1) bribe, max player level, min amount, max-min amount + DB_CrimeBribes_BaseAmountUntilLevel(0,50,10,1); + DB_CrimeBribes_BaseAmountUntilLevel(0,70,20,5); + DB_CrimeBribes_BaseAmountUntilLevel(0,150,50,9); + DB_CrimeBribes_BaseAmountUntilLevel(0,245,65,13); + DB_CrimeBribes_BaseAmountUntilLevel(0,410,110,16); + DB_CrimeBribes_BaseAmountUntilLevel(0,600,200,18); + DB_CrimeBribes_BaseAmountUntilLevel(0,800,250,20); + + DB_CrimeBribes_BaseAmountUntilLevel(1,125,25,1); + DB_CrimeBribes_BaseAmountUntilLevel(1,175,30,5); + DB_CrimeBribes_BaseAmountUntilLevel(1,400,60,9); + DB_CrimeBribes_BaseAmountUntilLevel(1,625,100,13); + DB_CrimeBribes_BaseAmountUntilLevel(1,1100,200,16); + DB_CrimeBribes_BaseAmountUntilLevel(1,1600,400,18); + DB_CrimeBribes_BaseAmountUntilLevel(1,2200,500,20); + + DB_CrimeBribes_SevereCrimes("Murder"); + DB_CrimeBribes_SevereCrimes("SneakMurder"); + DB_CrimeBribes_SevereCrimes("Assault"); + + } + KB + { + //REGION Polymorph statuses are handled in the DivinityOrigins mod, so add a dummy proxy and a fallback + // Actual implemetation in GLOBAL_ShapeShifting in DivinityOrigins + QRY + QRY_GLOBAL_Shapeshifting_GetPolymorphStatus((CHARACTERGUID)_Player) + THEN + DB_NOOP(1); + + // If we're not using DivinityOrigins, ensure we still define a + // generic DB_GLOBAL_Shapeshifting_PolymorphStatus() as fallback + QRY + QRY_GLOBAL_Shapeshifting_GetPolymorphStatus_Fallback((CHARACTERGUID)_Player) + AND + NOT DB_GLOBAL_Shapeshifting_PolymorphStatus(_) + THEN + DB_GLOBAL_Shapeshifting_PolymorphStatus(""); + + // Ensure fallback query always succeeds + QRY + QRY_GLOBAL_Shapeshifting_GetPolymorphStatus_Fallback((CHARACTERGUID)_Player) + THEN + DB_NOOP(1); + + //END_REGION + + //REGION Determine whether we select from the high or low bribes + // Possible selectors: + // * Tension + // * Severity of the crime + // * Number of times this person has been bribed + + // Determine severity + // Return value: + // Severe crime: DB_CrimeBribes_CrimeSeverity(1) + // Non-sever crime: DB_CrimeBribes_CrimeSeverity(0) + QRY + QRY_CrimeBribes_CrimeGetSeverity((STRING)_CrimeType) + AND + DB_CrimeBribes_CrimeSeverity(_Severity) + THEN + NOT DB_CrimeBribes_CrimeSeverity(_Severity); + + QRY + QRY_CrimeBribes_CrimeGetSeverity((STRING)_CrimeType) + AND + DB_CrimeBribes_SevereCrimes(_CrimeType) + THEN + DB_CrimeBribes_CrimeSeverity(1); + + QRY + QRY_CrimeBribes_CrimeGetSeverity((STRING)_CrimeType) + AND + NOT DB_CrimeBribes_CrimeSeverity(1) + THEN + DB_CrimeBribes_CrimeSeverity(0); + + // Keep track of number of times this criminal has bribed this interrogator + IF + ObjectFlagSet(_Flag,_Speaker,_ID) + AND + DB_ItemEvents_TransferFlagToMoneyVarIndex(_Flag,_MoneyVarIndex) + AND + DB_DialogName(_Dialog,_ID) + AND + DB_CrimeTriggers_GeneralBribeDialog(_MoneyVarIndex,_Dialog) + AND + DB_DialogMoneyTransfer(_MoneyVarIndex,_Dialog,_,_,_TargetDBIndex) + AND + DB_DialogPlayers(_ID,_Player,1) + AND + DB_DialogNPCs(_ID,_NPC,_TargetDBIndex) + AND + QRY_GLOBAL_Shapeshifting_GetPolymorphStatus(_Player) + AND + QRY_GLOBAL_Shapeshifting_GetPolymorphStatus_Fallback(_Player) + AND + DB_GLOBAL_Shapeshifting_PolymorphStatus(_Race) + THEN + PROC_CrimeBribes_RecordBribe((CHARACTERGUID)_NPC,(CHARACTERGUID)_Player,_Race); + + PROC + PROC_CrimeBribes_RecordBribe((CHARACTERGUID)_NPC,(CHARACTERGUID)_Player,(STRING)_Race) + AND + DB_CrimeBribes_PairBribes(_NPC,_Player,_Race,_Bribes) + AND + IntegerSum(_Bribes,1,_NewBribes) + THEN + NOT DB_CrimeBribes_PairBribes(_NPC,_Player,_Race,_Bribes); + DB_CrimeBribes_PairBribes(_NPC,_Player,_Race,_NewBribes); + + PROC + PROC_CrimeBribes_RecordBribe((CHARACTERGUID)_NPC,(CHARACTERGUID)_Player,(STRING)_Race) + AND + NOT DB_CrimeBribes_PairBribes(_NPC,_Player,_Race,_) + THEN + DB_CrimeBribes_PairBribes(_NPC,_Player,_Race,1); + + // Return value: + // Bribed too many times: DB_CrimeBribes_BribedTooManyTimes(1) + // Not yet bribed or a few times: DB_CrimeBribes_BribedTooManyTimes(0) + QRY + QRY_CrimeBribes_BribedTooManyTimes((CHARACTERGUID)_Interrogator,(CHARACTERGUID)_Criminal,(STRING)_Race) + AND + DB_CrimeBribes_BribedTooManyTimes(_Result) + THEN + NOT DB_CrimeBribes_BribedTooManyTimes(_Result); + + QRY + QRY_CrimeBribes_BribedTooManyTimes((CHARACTERGUID)_Interrogator,(CHARACTERGUID)_Criminal,(STRING)_Race) + AND + DB_CrimeBribes_PairBribes(_Interrogator,_Criminal,_Race,_Bribes) + AND + // This is the number of times the NPC has already been bribed before. So starting at the second time, + // it's too much + _Bribes >= 1 + THEN + DB_CrimeBribes_BribedTooManyTimes(1); + + QRY + QRY_CrimeBribes_BribedTooManyTimes((CHARACTERGUID)_Interrogator,(CHARACTERGUID)_Criminal,(STRING)_Race) + AND + NOT DB_CrimeBribes_BribedTooManyTimes(1) + THEN + DB_CrimeBribes_BribedTooManyTimes(0); + + // Get bribe base & variation from table + PROC + PROC_CrimeBribes_GetBribeBounds((INTEGER)_HighBase,(INTEGER)_InterrogatorLevel) + AND + DB_CrimeBribes_GetBribeBounds(_Base,_Rand) + THEN + NOT DB_CrimeBribes_GetBribeBounds(_Base,_Rand); + + PROC + PROC_CrimeBribes_GetBribeBounds((INTEGER)_HighBase,(INTEGER)_InterrogatorLevel) + AND + DB_CrimeBribes_BaseAmountUntilLevel(_HighBase,_Base,_Rand,1) + THEN + DB_CrimeBribes_GetBribeBounds(_Base,_Rand); + + PROC + PROC_CrimeBribes_GetBribeBounds((INTEGER)_HighBase,(INTEGER)_InterrogatorLevel) + AND + DB_CrimeBribes_BaseAmountUntilLevel(_HighBase,_Base,_Rand,_Level) + AND + DB_CrimeBribes_GetBribeBounds(_OldBase,_OldRand) + AND + _Level <= _InterrogatorLevel + AND + _Base > _OldBase + THEN + NOT DB_CrimeBribes_GetBribeBounds(_OldBase,_OldRand); + DB_CrimeBribes_GetBribeBounds(_Base,_Rand); + + // Get the bribe amount + // Once a bribe amount has been set for an NPC, it stays that way until it switches from low to high + // base, or if the level of the NPC changes (it doesn't make sense if the bribe changes randomly for + // the same NPC with every new encounter). + PROC + PROC_CrimeBribes_GetBribe((CHARACTERGUID)_NPC,(INTEGER)_NPCLevel,(INTEGER)_HighBase) + AND + NOT DB_CrimeBribes_GetBribe_NPC(_NPC,_NPCLevel,_HighBase,_) + THEN + PROC_CrimeBribes_GetBribeBounds((INTEGER)_HighBase,(INTEGER)_NPCLevel); + + PROC + PROC_CrimeBribes_GetBribe((CHARACTERGUID)_NPC,(INTEGER)_NPCLevel,(INTEGER)_HighBase) + AND + NOT DB_CrimeBribes_GetBribe_NPC(_NPC,_NPCLevel,_HighBase,_) + AND + DB_CrimeBribes_GetBribeBounds(_Base,_Rand) + AND + Random(_Rand,_Variation) + AND + IntegerSum(_Base,_Variation,_Bribe) + THEN + DB_CrimeBribes_GetBribe_NPC(_NPC,_NPCLevel,_HighBase,_Bribe); + + // return value + PROC + PROC_CrimeBribes_GetBribe((CHARACTERGUID)_NPC,(INTEGER)_NPCLevel,(INTEGER)_HighBase) + AND + DB_CrimeBribes_GetBribe(_Bribe) + THEN + NOT DB_CrimeBribes_GetBribe(_Bribe); + + PROC + PROC_CrimeBribes_GetBribe((CHARACTERGUID)_NPC,(INTEGER)_NPCLevel,(INTEGER)_HighBase) + AND + DB_CrimeBribes_GetBribe_NPC(_NPC,_NPCLevel,_HighBase,_Bribe) + THEN + DB_CrimeBribes_GetBribe(_Bribe); + + // Set the bribe amount + IF + CrimeInterrogationRequest(_RegionID,_ID,_Interrogator,_Criminal1,_Criminal2,_Criminal3,_Criminal4,_Dialog) + THEN + PROC_CrimeBribes_SetCrimeBribe(_ID,_Interrogator,_Criminal1,_Dialog); + + PROC + ProcStartCrimeDialog((INTEGER)_CrimeID,(STRING)_CrimeDialog,1,(CHARACTERGUID)_NPC,(CHARACTERGUID)_Criminal,(CHARACTERGUID)_Criminal2,(CHARACTERGUID)_Criminal3,(CHARACTERGUID)_Criminal4) + THEN + PROC_CrimeBribes_SetCrimeBribe(_CrimeID,_NPC,_Criminal,_CrimeDialog); + + //REGION Generic callback to disable bribing by certain (classes of) characters + PROC + PROC_CrimeBribes_SetCrimeBribe((INTEGER)_CrimeID,(CHARACTERGUID)_Interrogator,(CHARACTERGUID)_Criminal,(STRING)_Dialog) + AND + QRY_CrimeBribes_DisableBribe(_CrimeID,_Interrogator,_Criminal,_Dialog) + THEN + DB_CrimeBribesDisabledBribe(_Interrogator,_Criminal,_Dialog); + ObjectSetFlag(_Interrogator,"GEB_BribeDisabled"); + //END_REGION + + //REGION Only allow bribing a few times + QRY + QRY_CrimeBribes_DisableBribe((INTEGER)_CrimeID,(CHARACTERGUID)_Interrogator,(CHARACTERGUID)_Criminal,(STRING)_Dialog) + AND + QRY_GLOBAL_Shapeshifting_GetPolymorphStatus(_Criminal) + AND + DB_GLOBAL_Shapeshifting_PolymorphStatus(_Race) + AND + QRY_CrimeBribes_BribedTooManyTimes(_Interrogator,_Criminal,_Race) + AND + DB_CrimeBribes_BribedTooManyTimes(1) + THEN + DB_NOOP(1); + + IF + DialogEnded(_Dialog,_ID) + AND + DB_CrimeBribesDisabledBribe(_Interrogator,_Criminal,_Dialog) + AND + DB_DialogNPCs(_ID,_Interrogator,_) + AND + DB_DialogPlayers(_ID,_Criminal,_) + THEN + NOT DB_CrimeBribesDisabledBribe(_Interrogator,_Criminal,_Dialog); + ObjectClearFlag(_Interrogator,"GEB_BribeDisabled"); + //END_REGION + + // Low bribe if this NPC has not yet been bribed too many time by this player, + // AND it's not a severe crime, AND tension is below warning threshold. + // Otherwise higher bribe. + PROC + PROC_CrimeBribes_SetCrimeBribe((INTEGER)_CrimeID,(CHARACTERGUID)_Interrogator,(CHARACTERGUID)_Criminal,(STRING)_Dialog) + AND + DB_CrimeTriggers_GeneralBribeDialog(_MoneyVarIndex,_Dialog) + AND + CrimeGetType(_CrimeID,_CrimeType) + AND + QRY_CrimeBribes_CrimeGetSeverity(_CrimeType) + AND + QRY_GLOBAL_Shapeshifting_GetPolymorphStatus(_Criminal) + AND + DB_GLOBAL_Shapeshifting_PolymorphStatus(_Race) + AND + QRY_CrimeBribes_BribedTooManyTimes(_Interrogator,_Criminal,_Race) + AND + DB_CrimeBribes_CrimeSeverity(_Severity) + AND + DB_CrimeBribes_BribedTooManyTimes(_TooManyTimes) + AND + IntegerMax(_Severity,_TooManyTimes,_HighBase1) + AND + CrimeIsTensionOverWarningTreshold(_Criminal,_TensionOverWarning) + AND + IntegerMax(_HighBase1,_TensionOverWarning,_HighBaseFinal) + AND + CharacterGetLevel(_Interrogator,_InterrogatorLevel) + THEN + PROC_CrimeBribes_GetBribe(_Interrogator,_InterrogatorLevel,_HighBaseFinal); + + PROC + PROC_CrimeBribes_SetCrimeBribe((INTEGER)_CrimeID,(CHARACTERGUID)_Interrogator,(CHARACTERGUID)_Criminal,(STRING)_Dialog) + AND + DB_CrimeTriggers_GeneralBribeDialog(_MoneyVarIndex,_Dialog) + AND + DB_CrimeBribes_GetBribe(_Bribe) + THEN + // Will clear previous entries, if any + DB_DialogMoneyTransfer(_MoneyVarIndex,_Dialog,_Bribe); + + } + EXIT + { + + } +} +Goal(15).Title("_CRIME_CrimeTriggers"); +Goal(15) +{ + INIT + { + //Warning As AD + // TODO: what if a custom warning is an AD? Does it get added in there? + DB_CrimeWarningIsAD("GEB_AD_Noticed_EscapedPrisonerHelpful"); + DB_CrimeWarningIsAD("GEB_AD_Noticed_Vandalise"); + DB_CrimeWarningIsAD("GEB_AD_Noticed_Sneaking"); + DB_CrimeWarningIsAD("GEB_AD_Noticed_Smelly"); + DB_CrimeWarningIsAD("GEB_AD_Noticed_Weapons"); + DB_CrimeWarningIsAD("GEB_AD_Noticed_AttackedAnimal"); + DB_CrimeWarningIsAD("GEB_AD_Noticed_KilledAnimal"); + DB_CrimeWarningIsAD("GEB_AD_Noticed_AnimalAttackedAnimal"); + DB_CrimeWarningIsAD("GEB_AD_Help_Thief"); + DB_CrimeWarningIsAD("GEB_AD_FTJ_Noticed_NoCollar"); + DB_CrimeWarningIsAD("GEB_AD_Noticed_SummonAssault"); + DB_CrimeWarningIsAD("GEB_AD_Noticed_SummonVandalise"); + DB_CrimeWarningIsAD("GEB_AD_Noticed_Summon"); + DB_CrimeWarningIsAD("GEB_AD_Noticed_Polymorphed"); + DB_CrimeWarningIsAD("GEB_Lohse_AD_Warning_SummonAssault"); + DB_CrimeWarningIsAD("GEB_Ifan_AD_Warning_SummonAssault"); + DB_CrimeWarningIsAD("GEB_Sebille_AD_Warning_SummonAssault"); + DB_CrimeWarningIsAD("GEB_RedPrince_AD_Warning_SummonAssault"); + DB_CrimeWarningIsAD("GEB_AD_NoticedDeadSilentMonk"); + + // Trespassing + DB_TrespassingCrimes("Trespassing"); + + // Murder + + // Killing -> Murder type mapping : Sneak or not, Killed animal or not, killer is Summon or not + DB_CRIME_MurderType(0,0,0,"Murder"); + DB_CRIME_MurderType(1,0,0,"SneakMurder"); + DB_CRIME_MurderType(0,1,0,"KilledAnimal"); + DB_CRIME_MurderType(1,1,0,"SneakKilledAnimal"); + DB_CRIME_MurderType(0,0,1,"SummonMurder"); + DB_CRIME_MurderType(1,0,1,"SummonMurder"); + DB_CRIME_MurderType(0,1,1,"SummonKilledAnimal"); + DB_CRIME_MurderType(1,1,1,"SummonKilledAnimal"); + + DB_CRIME_ForbiddenStatus("LYING"); + + //For Status Reactions + DB_StatusReaction("SMELLY","Smelly"); + DB_StatusReaction("UNSHEATHED","WeaponsDrawn"); + + //Ingnore Vandalise Tags + DB_IgnoreVandaliseTag("JUNK"); + DB_IgnoreVandaliseTag("IGNOREVANDALISE"); + + //Bribe + DB_CrimeTriggers_GeneralBribeDialog(1,"GEB_Arrest"); + DB_CrimeTriggers_GeneralBribeDialog(1,"GEB_Arrest_EscapedPrison"); + DB_CrimeTriggers_GeneralBribeDialog(1,"GEB_Arrest_Theft"); + DB_CrimeTriggers_GeneralBribeDialog(1,"GEB_Arrest_Trespassing"); + DB_CrimeTriggers_GeneralBribeDialog(1,"GEB_Arrest_UseForbiddenItem"); + DB_CrimeTriggers_GeneralBribeDialog(1,"GEB_Arrest_Vandalise"); + DB_CrimeTriggers_GeneralBribeDialog(1,"GEB_Interrogation"); + DB_CrimeTriggers_GeneralBribeDialog(1,"GEB_Interrogation_Assault"); + DB_CrimeTriggers_GeneralBribeDialog(1,"GEB_Interrogation_Murder"); + DB_CrimeTriggers_GeneralBribeDialog(1,"GEB_Interrogation_PickPocketNoticed"); + DB_CrimeTriggers_GeneralBribeDialog(1,"GEB_Interrogation_Steal"); + DB_CrimeTriggers_GeneralBribeDialog(1,"GEB_Interrogation_UsedSource"); + DB_CrimeTriggers_GeneralBribeDialog(1,"GEB_Interrogation_Vandalise"); + DB_CrimeTriggers_GeneralBribeDialog(1,"GEB_Warning_PickPocketFailed_EvidenceCheck"); + + //Pickpocket + DB_CannotPickpocketTags("ANIMAL"); + DB_CannotPickpocketTags("ELEMENTAL"); + DB_CannotPickpocketTags("GHOST"); + DB_CannotPickpocketTags("VOIDLING"); + DB_CannotPickpocketTags("VOIDSECT"); + DB_CannotPickpocketTags("VOID_SOLDIER"); + DB_CannotPickpocketTags("ALANBIRD"); + DB_CannotPickpocketTags("MOLESPITTER"); + DB_CannotPickpocketTags("NIGHTFANG"); + DB_CannotPickpocketTags("VOIDWOLF"); + DB_CannotPickpocketTags("FLESH_GOLEM"); + DB_CannotPickpocketTags("BLOCK_PICKPOCKET"); + + //Attitude Change + DB_CrimeAttitudeChange("SourceMagic",-5); + DB_CrimeAttitudeChange("Murder",-30); + DB_CrimeAttitudeChange("SpiritTalk",-1); + DB_CrimeAttitudeChange("Assault",-20); + DB_CrimeAttitudeChange("Steal",-5); + DB_CrimeAttitudeChange("PickPocketFailed",-5); + DB_CrimeAttitudeChange("PickPocket",-10); + DB_CrimeAttitudeChange("ItemDestroy",-15); + DB_CrimeAttitudeChange("Vandalise",-10); + DB_CrimeAttitudeChange("VandaliseNoOwner",-5); + DB_CrimeAttitudeChange("Trespassing",-5); + DB_CrimeAttitudeChange("UseForbiddenItem",-5); + DB_CrimeAttitudeChange("MoveForbiddenItem",-5); + DB_CrimeAttitudeChange("AttackAnimal",-5); + + //Evidence check + DB_EvidenceCheckInWarning("GEB_Warning_PickPocketFailed_EvidenceCheck"); + DB_EvidenceCheckInWarning("RC_WH_DijinnItem_Interrogation"); + + DB_NeutralTagIgnore("GHOST"); + + DB_Negate(0,1); + DB_Negate(1,0); + + DB_IgnoreDamageSources("SurfaceMove"); + DB_IgnoreDamageSources("StatusTick"); + DB_IgnoreDamageSources("SurfaceStatus"); + DB_IgnoreDamageSources("Offhand"); + + //crime tpyes that should be kept in sync when the parent crime is enabled/disabled + DB_LinkedCrimes("Assault","SummonAssault"); + DB_LinkedCrimes("Assault","IncapacitatedAssault"); + + DB_LinkedCrimes("Murder","KilledAnimal"); + DB_LinkedCrimes("Murder","SneakKilledAnimal"); + DB_LinkedCrimes("Murder","SneakMurder"); + DB_LinkedCrimes("Murder","SummonMurder"); + DB_LinkedCrimes("Murder","SummonKilledAnimal"); + + //crime warning to hostile dialogs + DB_CrimeWarningHostileADs("GEB_Warning_Theft","GEB_AD_Help_Thief"); + DB_CrimeWarningHostileADs("GEB_Warning_UseForbiddenItem","GEB_AD_Help_UseForbiddenItem"); + + // Apply item effect to user + DB_ItemStatusAffectCharacterOnUse("BURNING"); + DB_ItemStatusAffectCharacterOnUse("SHOCKED"); + + // Score increase based on invisible/sneaking. At 9 or more, NPC will realise where attack + // comes from and go to location of that attacker. Script will actually increase by one more + // for implementation reasons (taken into account in values of table below) + // sneaking invisible base increase random increase (mod, 1 -> 0) + DB_CRIME_CrimeLocationScoreIncrease(0, 0, 3, 1); + DB_CRIME_CrimeLocationScoreIncrease(1, 0, 2, 1); + DB_CRIME_CrimeLocationScoreIncrease(0, 1, 1, 1); + DB_CRIME_CrimeLocationScoreIncrease(1, 1, 2, 1); + + // score >= threshold -> perform action + DB_CRIME_CrimeLocationScore_GoToAttackerThreshold(9); + DB_CRIME_CrimeLocationScore_StartAttackingThreshold(13); + // health < threshold -> perform action + DB_CRIME_Assault_HealthStartAttackingThreshold(80); + + // Flee crime reactions + DB_CRIME_FleeReaction("CRIME_Flee"); + DB_CRIME_FleeReaction("CRIME_FleeWithAD"); + + } + KB + { + PROC + Proc_GameModeStarted("GameMaster",_) + THEN + GoalCompleted; + + //REGION Register crime wrapper to handle ignoring crimes + QRY + QRY_CRIME_CrimeTriggers_IsCrimeFamilyMember((STRING)_Crime,(STRING)_CrimeOrFamily) + AND + _Crime == _CrimeOrFamily + THEN + DB_NOOP(1); + + QRY + QRY_CRIME_CrimeTriggers_IsCrimeFamilyMember((STRING)_Crime,(STRING)_CrimeOrFamily) + AND + DB_LinkedCrimes(_CrimeOrFamily,_Crime) + THEN + DB_NOOP(1); + + // Built-in: + // * DB_AssaultIgnoreFor(_Witness, _Victim): _Witness ignores all Assault crimes against victim + // * DB_AssaultFamilyIgnoreFor((CHARACTERGUID)_AnyWitness,(CHARACTERGUID)_Victim): _Witness ignores all Assault-like crimes against victim + PROC + PROC_CRIME_CrimeTriggers_GetSilentWitnessesForCrime((INTEGER)_CrimeID,(CHARACTERGUID)_Perpetrator,"Assault",(GUIDSTRING)_Evidence,(CHARACTERGUID)_Victim) + AND + DB_AssaultIgnoreFor((CHARACTERGUID)_AnyWitness,(CHARACTERGUID)_Victim) + THEN + DB_CRIME_CrimeTriggers_SilentWitnessesForCrime(_AnyWitness); + + PROC + PROC_CRIME_CrimeTriggers_GetSilentWitnessesForCrime((INTEGER)_CrimeID,(CHARACTERGUID)_Perpetrator,(STRING)_CrimeType,(GUIDSTRING)_Evidence,(CHARACTERGUID)_Victim) + AND + DB_AssaultFamilyIgnoreFor((CHARACTERGUID)_AnyWitness,(CHARACTERGUID)_Victim) + AND + QRY_CRIME_CrimeTriggers_IsCrimeFamilyMember(_CrimeType,"Assault") + THEN + DB_CRIME_CrimeTriggers_SilentWitnessesForCrime(_AnyWitness); + + PROC + ProcCharacterRegisterCrime((CHARACTERGUID)_Char,(STRING)_CrimeType,(GUIDSTRING)_Evidence,(CHARACTERGUID)_CrimeWitness,(INTEGER)_CrimeID,(CHARACTERGUID)_Victim) + THEN + ProcCharacterRegisterCrimeHandleIgnoresBefore(_Char,_CrimeType,_Evidence,_CrimeWitness,_CrimeID,_Victim); + + PROC + ProcCharacterRegisterCrimeWithPosition((CHARACTERGUID)_Char,(STRING)_CrimeType,(GUIDSTRING)_Evidence,(CHARACTERGUID)_CrimeWitness,(REAL)_X,(REAL)_Y,(REAL)_Z,(INTEGER)_CrimeID,(CHARACTERGUID)_Victim) + THEN + ProcCharacterRegisterCrimeHandleIgnoresBefore(_Char,_CrimeType,_Evidence,_CrimeWitness,_CrimeID,_Victim); + + PROC + ProcCharacterRegisterCrimeHandleIgnoresBefore((CHARACTERGUID)_Char,(STRING)_CrimeType,(GUIDSTRING)_Evidence,(CHARACTERGUID)_CrimeWitness,(INTEGER)_CrimeID,(CHARACTERGUID)_Victim) + THEN + PROC_CRIME_CrimeTriggers_GetSilentWitnessesForCrime(_CrimeID,_Char,_CrimeType,_Evidence,_Victim); + + // If this crime is specifically already disabled for the witness, don't do anything special + // except for ignoring the crime + PROC + ProcCharacterRegisterCrimeHandleIgnoresBefore((CHARACTERGUID)_Char,(STRING)_CrimeType,(GUIDSTRING)_Evidence,(CHARACTERGUID)_CrimeWitness,(INTEGER)_CrimeID,(CHARACTERGUID)_Victim) + AND + DB_CRIME_CrimeTriggers_SilentWitnessesForCrime(_AnyWitness) + AND + DB_CharacterCrimeDisabled(_AnyWitness,_CrimeType) + THEN + // Undefine here instead of directly checking in QRY_CRIME_CrimeTriggers_GetSilentWitnessesForCrime(), as + // QRY_CRIME_CrimeTriggers_GetSilentWitnessesForCrime() may be overloaded in user goals and may miss such checks + NOT DB_CRIME_CrimeTriggers_SilentWitnessesForCrime(_AnyWitness); + DB_CRIME_CrimeTriggers_SilentWitnessIgnoreOnly(_AnyWitness); + + // If all crimes are disabled for the witness and this one is not enabled, don't do anything special either + // except for ignoring the crime + PROC + ProcCharacterRegisterCrimeHandleIgnoresBefore((CHARACTERGUID)_Char,(STRING)_CrimeType,(GUIDSTRING)_Evidence,(CHARACTERGUID)_CrimeWitness,(INTEGER)_CrimeID,(CHARACTERGUID)_Victim) + AND + DB_CRIME_CrimeTriggers_SilentWitnessesForCrime(_AnyWitness) + AND + DB_CharacterAllCrimesDisabled(_AnyWitness) + AND + NOT DB_CharacterCrimeEnabled(_AnyWitness,_CrimeType) + THEN + NOT DB_CRIME_CrimeTriggers_SilentWitnessesForCrime(_AnyWitness); + DB_CRIME_CrimeTriggers_SilentWitnessIgnoreOnly(_AnyWitness); + + // The remaining witnesses that need to temporarily ignore this crime + PROC + ProcCharacterRegisterCrimeHandleIgnoresBefore((CHARACTERGUID)_Char,(STRING)_CrimeType,(GUIDSTRING)_Evidence,(CHARACTERGUID)_CrimeWitness,(INTEGER)_CrimeID,(CHARACTERGUID)_Victim) + AND + DB_CRIME_CrimeTriggers_SilentWitnessesForCrime(_AnyWitness) + THEN + CharacterDisableCrime(_AnyWitness,_CrimeType); + + // Register the crime + PROC + ProcCharacterRegisterCrime((CHARACTERGUID)_Char,(STRING)_CrimeType,(GUIDSTRING)_Evidence,(CHARACTERGUID)_CrimeWitness,(INTEGER)_CrimeID,(CHARACTERGUID)_Victim) + THEN + CharacterRegisterCrime(_Char,_CrimeType,_Evidence,_CrimeWitness,_CrimeID); + + PROC + ProcCharacterRegisterCrimeWithPosition((CHARACTERGUID)_Char,(STRING)_CrimeType,(GUIDSTRING)_Evidence,(CHARACTERGUID)_CrimeWitness,(REAL)_X,(REAL)_Y,(REAL)_Z,(INTEGER)_CrimeID,(CHARACTERGUID)_Victim) + THEN + CharacterRegisterCrimeWithPosition(_Char,_CrimeType,_Evidence,_CrimeWitness,_X,_Y,_Z,_CrimeID); + + PROC + ProcCharacterRegisterCrime((CHARACTERGUID)_Char,(STRING)_CrimeType,(GUIDSTRING)_Evidence,(CHARACTERGUID)_CrimeWitness,(INTEGER)_CrimeID,(CHARACTERGUID)_Victim) + THEN + ProcCharacterRegisterCrimeHandleIgnoresAfter(_CrimeType,_CrimeID); + + PROC + ProcCharacterRegisterCrimeWithPosition((CHARACTERGUID)_Char,(STRING)_CrimeType,(GUIDSTRING)_Evidence,(CHARACTERGUID)_CrimeWitness,(REAL)_X,(REAL)_Y,(REAL)_Z,(INTEGER)_CrimeID,(CHARACTERGUID)_Victim) + THEN + ProcCharacterRegisterCrimeHandleIgnoresAfter(_CrimeType,_CrimeID); + + // Ignore the crime for the silent witnesses (they will also ignore it in case they were not a witness + // and discover the crime scene later on) and then re-enable the crime for them + PROC + ProcCharacterRegisterCrimeHandleIgnoresAfter((STRING)_CrimeType,(INTEGER)_CrimeID) + AND + DB_CRIME_CrimeTriggers_SilentWitnessesForCrime(_AnyWitness) + THEN + CrimeIgnoreCrime(_CrimeID,_AnyWitness); + CharacterEnableCrime(_AnyWitness,_CrimeType); + NOT DB_CRIME_CrimeTriggers_SilentWitnessesForCrime(_AnyWitness); + + PROC + ProcCharacterRegisterCrimeHandleIgnoresAfter((STRING)_CrimeType,(INTEGER)_CrimeID) + AND + DB_CRIME_CrimeTriggers_SilentWitnessIgnoreOnly(_AnyWitness) + THEN + NOT DB_CRIME_CrimeTriggers_SilentWitnessIgnoreOnly(_AnyWitness); + CrimeIgnoreCrime(_CrimeID,_AnyWitness); + //END_REGION + + //REGION Stopping Dialog + IF + CharacterOnCrimeSensibleActionNotification(_NPC,_Region,_CrimeID,_PriorityName,_Dialog,_Criminal,_Criminal2,_Criminal3,_Criminal4,_) + THEN + ProcForceStopDialog(_NPC); //This will only stop ADs Code stops interactive ones and SensibleAction will fail if he can't react to it + //END_REGION + + //REGION Behaviour scripts disabling + IF + CrimeDisabled(_Char,"") + THEN + ProcCharacterDisableAllCrimes((CHARACTERGUID)_Char); + + IF + CrimeDisabled(_Char,_Crime) + AND + _Crime != "" + THEN + ProcCharacterDisableCrime(_Char,_Crime); + + IF + CrimeEnabled(_Char,"") + THEN + ProcCharacterEnableAllCrimes((CHARACTERGUID)_Char); + + IF + CrimeEnabled(_Char,_Crime) + AND + _Crime != "" + THEN + ProcCharacterEnableCrime(_Char,_Crime); + //END_REGION + + //REGION Crime Enabling/Disabling + PROC + ProcCharacterDisableAllCrimes((CHARACTERGUID)_Char) + THEN + DB_CharacterAllCrimesDisabled(_Char); + CharacterDisableAllCrimes(_Char); + + PROC + ProcCharacterEnableAllCrimes((CHARACTERGUID)_Char) + THEN + NOT DB_CharacterAllCrimesDisabled(_Char); + CharacterEnableAllCrimes(_Char); + + PROC + ProcCharacterEnableCrime((CHARACTERGUID)_Char,(STRING)_Crime) + THEN + DB_CharacterCrimeEnabled(_Char,_Crime); + CharacterEnableCrime(_Char,_Crime); + + PROC + ProcCharacterDisableCrime((CHARACTERGUID)_Char,(STRING)_Crime) + THEN + DB_CharacterCrimeDisabled(_Char,_Crime); + CharacterDisableCrime(_Char,_Crime); + + IF + DB_CharacterCrimeEnabled(_Char,_Crime) + THEN + NOT DB_CharacterCrimeDisabled(_Char,_Crime); + + IF + DB_CharacterCrimeDisabled(_Char,_Crime) + THEN + NOT DB_CharacterCrimeEnabled(_Char,_Crime); + + //END_REGION + + //REGION Warning + IF + DB_CrimeReaction_DoNotWarn((CHARACTERGUID)_Char) + THEN + DB_CrimeReaction_DoNotWarn2(_Char); + CharacterEnableCrimeWarnings(_Char,0); + + IF + DB_CrimeReaction_DoNotWarn2((CHARACTERGUID)_Char) + AND + NOT DB_CrimeReaction_DoNotWarn(_Char) + THEN + NOT DB_CrimeReaction_DoNotWarn2(_Char); + CharacterEnableCrimeWarnings(_Char,1); + + + // Crime Warning Request events + //--- Generic Warning: + IF + CharacterOnCrimeSensibleActionNotification(_Warner,_CrimeRegion,_CrimeID,"CRIME_OsirisReaction",_WarningDialog,_Criminal1,_Criminal2,_Criminal3,_Criminal4,_) + AND + NOT DB_CrimeReaction_DoNotWarn(_Warner) + AND + CrimeGetType(_CrimeID,_CrimeName) + AND + NOT DB_CrimeReaction_CustomWarning((CHARACTERGUID)_Warner,_CrimeName,(STRING)_) + THEN + ProcCrimeCheckCrimeTypeForFlags(_CrimeID,_Warner,_Criminal1,_Criminal2,_Criminal3,_Criminal4,_CrimeName,_WarningDialog); + ProcCrimeSetupWarning(_CrimeRegion,_CrimeID,_Warner,_Criminal1,_Criminal2,_Criminal3,_Criminal4,_WarningDialog); + ProcCrimeWarningTryStopDialogs(_CrimeID,_Warner,_Criminal1,_Criminal2,_Criminal3,_Criminal4,_WarningDialog); + ProcCrimeCheckStartedWarning(_CrimeID,_Warner,_Criminal1,_Criminal2,_Criminal3,_Criminal4,_WarningDialog); + + + //--- Custom Warning: + IF + CharacterOnCrimeSensibleActionNotification(_Warner,_CrimeRegion,_CrimeID,"CRIME_OsirisReaction",_WarningDialog,_Criminal1,_Criminal2,_Criminal3,_Criminal4,_) + AND + NOT DB_CrimeReaction_DoNotWarn(_Warner) + //AND + //NOT QryCrimeOnlyHasSummonCriminal(_CrimeID,_Criminal1,_Criminal2,_Criminal3,_Criminal4) + AND + CrimeGetType(_CrimeID,_CrimeName) + AND + DB_CrimeReaction_CustomWarning(_Warner,_CrimeName,_CustomDialog) + THEN + ProcCrimeCheckCrimeTypeForFlags(_CrimeID,_Warner,_Criminal1,_Criminal2,_Criminal3,_Criminal4,_CrimeName,_CustomDialog); + ProcCrimeSetupWarning(_CrimeRegion,_CrimeID,_Warner,_Criminal1,_Criminal2,_Criminal3,_Criminal4,_CustomDialog); + ProcCrimeWarningTryStopDialogs(_CrimeID,_Warner,_Criminal1,_Criminal2,_Criminal3,_Criminal4,_CustomDialog); + ProcCrimeCheckStartedWarning(_CrimeID,_Warner,_Criminal1,_Criminal2,_Criminal3,_Criminal4,_CustomDialog); + + //--- No Warning + IF + CharacterOnCrimeSensibleActionNotification(_Warner,_CrimeRegion,_CrimeID,"CRIME_OsirisReaction",_Dialog,_Criminal1,_Criminal2,_Criminal3,_Criminal4,_) + AND + DB_CrimeReaction_DoNotWarn(_Warner) + THEN + CrimeConfrontationDone(_CrimeID,_Warner); + + + //--- 1) Prepare a DB and set flag + PROC + ProcCrimeCheckCrimeTypeForFlags((INTEGER)_CrimeID,(CHARACTERGUID)_Warner,(CHARACTERGUID)_Criminal1,(CHARACTERGUID)_Criminal2,(CHARACTERGUID)_Criminal3,(CHARACTERGUID)_Criminal4,(STRING)_CrimeType,(STRING)_WarningDialog) + AND + QryCrimeIsVandalismWithTension(_CrimeType) + THEN + ProcCrimeSetupCountFlag(_CrimeID,_Warner,_Criminal1,_Criminal2,_Criminal3,_Criminal4,"Vandalise",_WarningDialog); + + PROC + ProcCrimeCheckCrimeTypeForFlags((INTEGER)_CrimeID,(CHARACTERGUID)_Warner,(CHARACTERGUID)_Criminal1,(CHARACTERGUID)_Criminal2,(CHARACTERGUID)_Criminal3,(CHARACTERGUID)_Criminal4,(STRING)_CrimeType,(STRING)_WarningDialog) + AND + NOT QryCrimeIsVandalismWithTension(_CrimeType) + THEN + ProcCrimeSetupCountFlag(_CrimeID,_Warner,_Criminal1,_Criminal2,_Criminal3,_Criminal4,_CrimeType,_WarningDialog); + + QRY + QryCrimeIsVandalismWithTension((STRING)_CrimeType) + AND + _CrimeType == "Vandalise" + THEN + DB_NOOP(1); + + QRY + QryCrimeIsVandalismWithTension((STRING)_CrimeType) + AND + _CrimeType == "ItemDestroy" + THEN + DB_NOOP(1); + + PROC + ProcCrimeSetupCountFlag((INTEGER)_CrimeID,(CHARACTERGUID)_Warner,(CHARACTERGUID)_Criminal1,(CHARACTERGUID)_Criminal2,(CHARACTERGUID)_Criminal3,(CHARACTERGUID)_Criminal4,(STRING)_CrimeType,(STRING)_WarningDialog) + AND + CrimeGetTension(_CrimeID,_Tension) + AND + _Tension > 0 + THEN + DB_Crime_RequestedDialogWithTension(_CrimeType,_WarningDialog,_Warner,_Criminal1,_Criminal2,_Criminal3,_Criminal4); + + PROC + ProcCrimeSetupCountFlag((INTEGER)_CrimeID,(CHARACTERGUID)_Warner,(CHARACTERGUID)_Criminal1,(CHARACTERGUID)_Criminal2,(CHARACTERGUID)_Criminal3,(CHARACTERGUID)_Criminal4,(STRING)_CrimeType,(STRING)_WarningDialog) + AND + NOT DB_Crime_WarningCount(_,_CrimeType,_Criminal1,_Warner) // DB doesn't exist yet, meaning the player has never been warned by that NPC with that dialog (i.e. for that crime) + THEN + DB_Crime_WarningCount(1,_CrimeType,_Criminal1,_Warner); + + PROC + ProcCrimeSetupCountFlag((INTEGER)_CrimeID,(CHARACTERGUID)_Warner,(CHARACTERGUID)_Criminal1,(CHARACTERGUID)_Criminal2,(CHARACTERGUID)_Criminal3,(CHARACTERGUID)_Criminal4,(STRING)_CrimeType,(STRING)_WarningDialog) + AND + DB_Crime_RequestedDialogWithTension(_CrimeType,_WarningDialog,_Warner,_,_,_,_) + AND + DB_Crime_WarningCount(_Pos,_CrimeType,_Player,_Warner) + AND + IntegertoString(_Pos,_StrPos) + AND + StringConcatenate("GEB_CrimeWarning_",_StrPos,_Flag) + THEN + ObjectSetFlag(_Player,_Flag); + + IF + DialogEnded(_Dialog,_ID) + AND + DB_TemporaryHostilityAfterDialog(_,_ID) + AND + DB_CrimeWarningHostileADs(_Dialog,_AD) + AND + DB_DialogNPCs(_ID,_NPC,1) + THEN + Proc_StartDialog(1,_AD,_NPC); + + //--- 2) Set variables in the Warner's script + PROC + ProcCrimeSetupWarning((STRING)_CrimeRegion,(INTEGER)_CrimeID,(CHARACTERGUID)_Warner,(CHARACTERGUID)_Criminal1,(CHARACTERGUID)_Criminal2,(CHARACTERGUID)_Criminal3,(CHARACTERGUID)_Criminal4,(STRING)_WarningDialog) + THEN + ObjectClearFlag(_Criminal1,"GEB_CrimeHasEvidenceInInventory"); + + PROC + ProcCrimeSetupWarning((STRING)_CrimeRegion,(INTEGER)_CrimeID,(CHARACTERGUID)_Warner,(CHARACTERGUID)_Criminal1,(CHARACTERGUID)_Criminal2,(CHARACTERGUID)_Criminal3,(CHARACTERGUID)_Criminal4,(STRING)_WarningDialog) + AND + CrimeGetType(_CrimeID,"Steal") + AND + CrimeFindEvidence(_CrimeID,_Warner,_Criminal1,_Criminal2,_Criminal3,_Criminal4,1,_,_) + THEN + DB_Interrogation(_Warner,_CrimeID); + ObjectSetFlag(_Criminal1,"GEB_CrimeHasEvidenceInInventory"); + + PROC + ProcCrimeSetupWarning((STRING)_CrimeRegion,(INTEGER)_CrimeID,(CHARACTERGUID)_Warner,(CHARACTERGUID)_Criminal1,(CHARACTERGUID)_Criminal2,(CHARACTERGUID)_Criminal3,(CHARACTERGUID)_Criminal4,(STRING)_WarningDialog) + THEN + SetVarInteger(_Warner,"CrimeID",_CrimeID); + SetVarFixedString(_Warner,"RegionID",_CrimeRegion); + DB_Crime_SetupCriminalIndex(_CrimeID,1); + ProcCrimeSetupValidWarningCriminals(_CrimeID,_Warner,_Criminal1); + ProcCrimeSetupValidWarningCriminals(_CrimeID,_Warner,_Criminal2); + ProcCrimeSetupValidWarningCriminals(_CrimeID,_Warner,_Criminal3); + ProcCrimeSetupValidWarningCriminals(_CrimeID,_Warner,_Criminal4); + ProcCrimeSetupNullWarningCriminals(_CrimeID,_Warner); + + PROC + ProcCrimeSetupValidWarningCriminals((INTEGER)_CrimeID,(CHARACTERGUID)_Warner,(CHARACTERGUID)_Criminal) + AND + NOT QryCrimeCharacterIsSummonOrNull(_Criminal) + AND + DB_Crime_SetupCriminalIndex(_CrimeID,_Index) + AND + IntegertoString(_Index,_StrIndex) + AND + StringConcatenate("Criminal",_StrIndex,_Param) + AND + IntegerSum(_Index,1,_NewIndex) + THEN + SetVarObject(_Warner,_Param,_Criminal); + NOT DB_Crime_SetupCriminalIndex(_CrimeID,_Index); + DB_Crime_SetupCriminalIndex(_CrimeID,_NewIndex); + + PROC + ProcCrimeSetupNullWarningCriminals((INTEGER)_CrimeID,(CHARACTERGUID)_Warner) + AND + DB_Crime_SetupCriminalIndex(_CrimeID,_Index) + AND + _Index < 5 //goes from 1 to 4 + AND + IntegertoString(_Index,_StrIndex) + AND + StringConcatenate("Criminal",_StrIndex,_Param) + AND + IntegerSum(_Index,1,_NewIndex) + THEN + ClearVarObject(_Warner,_Param); + NOT DB_Crime_SetupCriminalIndex(_CrimeID,_Index); + DB_Crime_SetupCriminalIndex(_CrimeID,_NewIndex); + ProcCrimeSetupNullWarningCriminals(_CrimeID,_Warner); + + PROC + ProcCrimeSetupWarning((STRING)_CrimeRegion,(INTEGER)_CrimeID,(CHARACTERGUID)_Warner,(CHARACTERGUID)_Criminal1,(CHARACTERGUID)_Criminal2,(CHARACTERGUID)_Criminal3,(CHARACTERGUID)_Criminal4,(STRING)_WarningDialog) + AND + DB_Crime_SetupCriminalIndex(_CrimeID,_Index) + THEN + NOT DB_Crime_SetupCriminalIndex(_CrimeID,_Index); + + PROC + ProcCrimeSetupWarning((STRING)_CrimeRegion,(INTEGER)_CrimeID,(CHARACTERGUID)_Warner,(CHARACTERGUID)_Criminal1,(CHARACTERGUID)_Criminal2,(CHARACTERGUID)_Criminal3,(CHARACTERGUID)_Criminal4,(STRING)_WarningDialog) + AND + GetPosition(_Warner,_x,_y,_z) + THEN + SetVarFloat3(_Warner,"NPCOrgPos",_x,_y,_z); + + PROC + ProcCrimeSetupWarning((STRING)_CrimeRegion,(INTEGER)_CrimeID,(CHARACTERGUID)_Warner,(CHARACTERGUID)_Criminal1,(CHARACTERGUID)_Criminal2,(CHARACTERGUID)_Criminal3,(CHARACTERGUID)_Criminal4,(STRING)_WarningDialog) + AND + DB_EvidenceCheckInWarning(_WarningDialog) //Sets up Interrogation DB for evidence check in warning Dialog + THEN + DB_Crime_Interrogation(_CrimeID,_Warner,_Criminal1,_Criminal2,_Criminal3,_Criminal4,_WarningDialog); + + + //--- 3) Try to stop the warner's dialog + PROC + ProcCrimeWarningTryStopDialogs((INTEGER)_CrimeID,(CHARACTERGUID)_Warner,(CHARACTERGUID)_Criminal1,(CHARACTERGUID)_Criminal2,(CHARACTERGUID)_Criminal3,(CHARACTERGUID)_Criminal4,(STRING)_WarningDialog) + AND + DB_CrimeWarningIsAD(_WarningDialog) + THEN + ProcCrimeInterruptStoryDialogs(_CrimeID,_WarningDialog,0,_Warner,_Criminal1,_Criminal2,_Criminal3,_Criminal4); + + PROC + ProcCrimeWarningTryStopDialogs((INTEGER)_CrimeID,(CHARACTERGUID)_Warner,(CHARACTERGUID)_Criminal1,(CHARACTERGUID)_Criminal2,(CHARACTERGUID)_Criminal3,(CHARACTERGUID)_Criminal4,(STRING)_WarningDialog) + AND + NOT DB_CrimeWarningIsAD(_WarningDialog) + THEN + ProcCrimeInterruptStoryDialogs(_CrimeID,_WarningDialog,1,_Warner,_Criminal1,_Criminal2,_Criminal3,_Criminal4); + + + //--- 4) Verify if the crime dialog succeeded to start and clean up + PROC + ProcCrimeCheckStartedWarning((INTEGER)_CrimeID,(CHARACTERGUID)_Warner,(CHARACTERGUID)_Criminal1,(CHARACTERGUID)_Criminal2,(CHARACTERGUID)_Criminal3,(CHARACTERGUID)_Criminal4,(STRING)_WarningDialog) + AND + DB_Crime_FailedToInterruptStoryDialogs(_CrimeID,_WarningDialog,_Warner,_Criminal1,_Criminal2,_Criminal3,_Criminal4) + AND + DB_Crime_RequestedDialogWithTension(_CrimeType,_WarningDialog,_Warner,_Criminal1,_Criminal2,_Criminal3,_Criminal4) + AND + DB_Crime_WarningCount(_Pos,_Dialog,_Player,_Warner) + AND + IntegertoString(_Pos,_StrPos) + AND + StringConcatenate("GEB_CrimeWarning_",_StrPos,_Flag) + THEN + NOT DB_Crime_RequestedDialogWithTension(_CrimeType,_WarningDialog,_Warner,_Criminal1,_Criminal2,_Criminal3,_Criminal4); + ObjectClearFlag(_Player,_Flag,0); + + PROC + ProcCrimeCheckStartedWarning((INTEGER)_CrimeID,(CHARACTERGUID)_Warner,(CHARACTERGUID)_Criminal1,(CHARACTERGUID)_Criminal2,(CHARACTERGUID)_Criminal3,(CHARACTERGUID)_Criminal4,(STRING)_WarningDialog) + AND + DB_Crime_FailedToInterruptStoryDialogs(_CrimeID,_WarningDialog,_Warner,_Criminal1,_Criminal2,_Criminal3,_Criminal4) + THEN + ProcCrimeFailedToInterruptStoryDialog(_CrimeID,_Warner,_Criminal1,_Criminal2,_Criminal3,_Criminal4,_WarningDialog); + NOT DB_Crime_FailedToInterruptStoryDialogs(_CrimeID,_WarningDialog,_Warner,_Criminal1,_Criminal2,_Criminal3,_Criminal4); + CrimeConfrontationDone(_CrimeID,_Warner); + + PROC + ProcCrimeCheckStartedWarning((INTEGER)_CrimeID,(CHARACTERGUID)_Warner,(CHARACTERGUID)_Criminal1,(CHARACTERGUID)_Criminal2,(CHARACTERGUID)_Criminal3,(CHARACTERGUID)_Criminal4,(STRING)_WarningDialog) + AND + DB_Crime_FailedToInterruptStoryDialogs(_CrimeID,_WarningDialog,_Warner,_Criminal1,_Criminal2,_Criminal3,_Criminal4) + AND + CrimeIsContinuous(_CrimeID,1) + THEN + ProcCrimeFailedToInterruptStoryDialog(_CrimeID,_Warner,_Criminal1,_Criminal2,_Criminal3,_Criminal4,_WarningDialog); + NOT DB_Crime_FailedToInterruptStoryDialogs(_CrimeID,_WarningDialog,_Warner,_Criminal1,_Criminal2,_Criminal3,_Criminal4); + + PROC // Useful for custom scripting + ProcCrimeFailedToInterruptStoryDialog((INTEGER)_CrimeID,(CHARACTERGUID)_Warner,(CHARACTERGUID)_Criminal1,(CHARACTERGUID)_Criminal2,(CHARACTERGUID)_Criminal3,(CHARACTERGUID)_Criminal4,(STRING)_WarningDialog) + THEN + DB_NOOP(1); + + PROC + ProcCrimeCheckStartedWarning((INTEGER)_CrimeID,(CHARACTERGUID)_Warner,(CHARACTERGUID)_Criminal1,(CHARACTERGUID)_Criminal2,(CHARACTERGUID)_Criminal3,(CHARACTERGUID)_Criminal4,(STRING)_WarningDialog) + AND + DB_Crime_FailedToInterruptStoryDialogs(_AnyCrimeID,_AnyWarningDialog,_AnyWarner,_AnyCriminal1,_AnyCriminal2,_AnyCriminal3,_AnyCriminal4) // Clean up anything that could have failed previously + THEN + NOT DB_Crime_FailedToInterruptStoryDialogs(_AnyCrimeID,_AnyWarningDialog,_AnyWarner,_AnyCriminal1,_AnyCriminal2,_AnyCriminal3,_AnyCriminal4); + + + //--- Count the warnings done: + IF + DialogEnded(_Dialog,_Inst) + AND + DB_Crime_RequestedDialogWithTension(_CrimeType,_Dialog,_Warner,_Criminal1,_Criminal2,_Criminal3,_Criminal4) + AND + DB_DialogNPCs(_Inst,_Warner,_) + AND + DB_DialogPlayers(_Inst,_Player,_) + AND + DB_Crime_WarningCount(_Pos,_CrimeType,(CHARACTERGUID)_Player,_Warner) + AND + IntegertoString(_Pos,_StrPos) + AND + StringConcatenate("GEB_CrimeWarning_",_StrPos,_Flag) + AND + ObjectGetFlag(_Player,_Flag,1) + AND + IntegerSum(_Pos,1,_NewPos) + THEN + NOT DB_Crime_RequestedDialogWithTension(_CrimeType,_Dialog,_Warner,_Criminal1,_Criminal2,_Criminal3,_Criminal4); + ObjectClearFlag(_Player,_Flag,0); + NOT DB_Crime_WarningCount(_Pos,_CrimeType,_Player,_Warner); + DB_Crime_WarningCount(_NewPos,_CrimeType,_Player,_Warner); + + IF + DB_Crime_WarningCount(_NewPos,_CrimeType,_Player,_Warner) + AND + _NewPos > 3 + THEN + NOT DB_Crime_WarningCount(_NewPos,_CrimeType,_Player,_Warner); + DB_Crime_WarningCount(3,_CrimeType,_Player,_Warner); + + IF + DialogEnded(_Dialog,_Inst) + AND + DB_CrimeWarner(_CrimeID,_Warner,_Dialog) + AND + DB_DialogNPCs(_Inst,_Warner,_) + THEN + CrimeConfrontationDone(_CrimeID,_Warner); + NOT DB_CrimeWarner(_CrimeID,_Warner,_Dialog); + + IF + AutomatedDialogEnded(_Dialog,_Inst) + AND + DB_Crime_RequestedDialogWithTension(_CrimeType,_Dialog,_Warner,_Criminal1,_Criminal2,_Criminal3,_Criminal4) + AND + DB_DialogNPCs(_Inst,_Warner,_) + AND + DB_Crime_WarningAD_Target(_CrimeType,_Warner,_Criminal1) + AND + DB_Crime_WarningCount(_Pos,_CrimeType,(CHARACTERGUID)_Player,_Warner) + AND + IntegertoString(_Pos,_StrPos) + AND + StringConcatenate("GEB_CrimeWarning_",_StrPos,_Flag) + AND + ObjectGetFlag(_Player,_Flag,1) + AND + IntegerSum(_Pos,1,_NewPos) + THEN + NOT DB_Crime_RequestedDialogWithTension(_CrimeType,_Dialog,_Warner,_Criminal1,_Criminal2,_Criminal3,_Criminal4); + ObjectClearFlag(_Player,_Flag,0); + NOT DB_Crime_WarningCount(_Pos,_CrimeType,_Player,_Warner); + DB_Crime_WarningCount(_NewPos,_CrimeType,_Player,_Warner); + + IF + AutomatedDialogEnded(_Dialog,_Inst) + AND + DB_DialogNPCs(_Inst,_Warner,_) + AND + DB_Crime_WarningAD_Target(_CrimeType,(CHARACTERGUID)_Warner,(CHARACTERGUID)_TargetPlayer) + THEN + NOT DB_Crime_WarningAD_Target(_CrimeType,_Warner,_TargetPlayer); + + PROC + ProcHandleCrimeDialog((CHARACTERGUID)_Warner,(CHARACTERGUID)_Criminal1,(CHARACTERGUID)_Criminal2,(CHARACTERGUID)_Criminal3,(CHARACTERGUID)_Criminal4,(STRING)_Dialog,0,(INTEGER)_MarkForInteractive) + THEN + DB_CrimeDialogStartFailed(_Warner,_Criminal1,_Criminal2,_Criminal3,_Criminal4,_Dialog); + + IF + OnCrimeResolved(_CrimeID,_Victim,_Criminal1,_Criminal2,_Criminal3,_Criminal4) + AND + DB_CrimeDialogStartFailed(_Warner,_Criminal1,_Criminal2,_Criminal3,_Criminal4,_Dialog) + AND + DB_Crime_RequestedDialogWithTension(_CrimeType,_Dialog,_Warner,_Criminal1,_Criminal2,_Criminal3,_Criminal4) + AND + DB_Crime_WarningCount(_Pos,_CrimeType,(CHARACTERGUID)_Player,_Warner) + AND + IntegertoString(_Pos,_StrPos) + AND + StringConcatenate("GEB_CrimeWarning_",_StrPos,_Flag) + AND + ObjectGetFlag(_Player,_Flag,1) + AND + IntegerSum(_Pos,1,_NewPos) + THEN + NOT DB_CrimeDialogStartFailed(_Warner,_Criminal1,_Criminal2,_Criminal3,_Criminal4,_Dialog); + NOT DB_Crime_RequestedDialogWithTension(_CrimeType,_Dialog,_Warner,_Criminal1,_Criminal2,_Criminal3,_Criminal4); + ObjectClearFlag(_Player,_Flag,0); + NOT DB_Crime_WarningAD_Target(_CrimeType,_Warner,_Player); + NOT DB_Crime_WarningCount(_Pos,_CrimeType,_Player,_Warner); + DB_Crime_WarningCount(_NewPos,_CrimeType,_Player,_Warner); + + + //REGION Prepare the characters for the dialog + PROC + ProcHandleCrimeDialog((CHARACTERGUID)_Warner,(CHARACTERGUID)_Criminal1,(CHARACTERGUID)_Criminal2,(CHARACTERGUID)_Criminal3,(CHARACTERGUID)_Criminal4,(STRING)_WarningDialog,1,(INTEGER)_MarkForInteractive) + AND + GetPosition(_Warner,_x,_y,_z) + THEN + SetVarFloat3(_Warner,"NPCOrgPos",_x,_y,_z); + + PROC + ProcHandleCrimeDialog((CHARACTERGUID)_Warner,(CHARACTERGUID)_Criminal1,(CHARACTERGUID)_Criminal2,(CHARACTERGUID)_Criminal3,(CHARACTERGUID)_Criminal4,(STRING)_WarningDialog,1,1) + THEN + CharacterLookAt(_Warner,_Criminal1,0); + CharacterLookAt(_Criminal1,_Warner,0); + CharacterMakeStoryNpc(_Warner,1); + CharacterMakeStoryNpc(_Criminal1,1); + CharacterMakeStoryNpc(_Criminal2,1); + CharacterMakeStoryNpc(_Criminal3,1); + CharacterMakeStoryNpc(_Criminal4,1); + + PROC + ProcHandleCrimeDialog((CHARACTERGUID)_Warner,(CHARACTERGUID)_Criminal1,(CHARACTERGUID)_Criminal2,(CHARACTERGUID)_Criminal3,(CHARACTERGUID)_Criminal4,(STRING)_WarningDialog,1,0) + THEN + CharacterLookAt(_Warner,_Criminal1,0); + + PROC + ProcHandleCrimeDialog((CHARACTERGUID)_Warner,(CHARACTERGUID)_Criminal1,(CHARACTERGUID)_Criminal2,(CHARACTERGUID)_Criminal3,(CHARACTERGUID)_Criminal4,(STRING)_WarningDialog,1,_) + AND + DB_CRIME_ForbiddenStatus(_Status) + AND + HasActiveStatus(_Warner,_Status,1) + THEN + RemoveStatus(_Warner,_Status); + + IF + CharacterLeftParty(_Summon) + AND + ObjectExists(_Summon,1) + AND + IsTagged(_Summon,"SUMMON",1) + AND + DB_Crime_WarningCount(_Pos,_CrimeType,_Summon,_Warner) + THEN + NOT DB_Crime_WarningCount(_Pos,_CrimeType,_Summon,_Warner); + + //END_REGION + + //REGION Check Prison Cell in Warning + + IF + CharacterOnCrimeSensibleActionNotification(_Warner,_CrimeRegion,_CrimeID,"CRIME_OsirisReaction",_Dialog,_Criminal1,_Criminal2,_Criminal3,_Criminal4,_) + THEN + ObjectClearFlag(_Warner,"GEB_Arrest_HavePrison",0); + + IF + CharacterOnCrimeSensibleActionNotification(_Warner,_CrimeRegion,_CrimeID,"CRIME_OsirisReaction",_Dialog,_Criminal1,_Criminal2,_Criminal3,_Criminal4,_) + AND + DB_RegionPrison(_CrimeRegion,_) + THEN + ObjectSetFlag(_Warner,"GEB_Arrest_HavePrison"); + + //END_REGION + //END_REGION + + //REGION Summon check + + //--- Only summon (or null) in criminals + QRY + QryCrimeOnlyHasSummonCriminal((CHARACTERGUID)_Criminal1,(CHARACTERGUID)_Criminal2,(CHARACTERGUID)_Criminal3,(CHARACTERGUID)_Criminal4) + AND + QryCrimeCharacterIsSummonOrNull(_Criminal1) + AND + QryCrimeCharacterIsSummonOrNull(_Criminal2) + AND + QryCrimeCharacterIsSummonOrNull(_Criminal3) + AND + QryCrimeCharacterIsSummonOrNull(_Criminal4) + THEN + DB_NOOP(1); + + QRY + QryCrimeCharacterIsSummonOrNull((CHARACTERGUID)_Char) + AND + QRY_CharacterIsNull(_Char) + THEN + DB_NOOP(1); + + QRY + QryCrimeCharacterIsSummonOrNull((CHARACTERGUID)_Char) + AND + NOT QRY_CharacterIsNull(_Char) + AND + IsTagged(_Char,"SUMMON",1) + THEN + DB_NOOP(1); + + + QRY + QryCrimeAssailantIsNotSummon((CHARACTERGUID)_Char,(CHARACTERGUID)_Summon) + AND + _Char == _Summon + THEN + DB_NOOP(1); + + QRY + QryCrimeAssailantIsNotSummon((CHARACTERGUID)_Char,(CHARACTERGUID)_Summon) + AND + QRY_CharacterIsNull(_Summon) + THEN + DB_NOOP(1); + + + //--- Continuous summon crime + IF + CharacterJoinedParty(_Char) + AND + IsTagged(_Char,"SUMMON",1) + THEN + CharacterRegisterCrime(_Char,"ActiveSummon",NULL_00000000-0000-0000-0000-000000000000,NULL_00000000-0000-0000-0000-000000000000,0); + + IF + CharacterLeftParty(_Char) + AND + IsTagged(_Char,"SUMMON",1) + THEN + CharacterStopCrime(_Char,"ActiveSummon",NULL_00000000-0000-0000-0000-000000000000); + + //END_REGION + + //REGION Investigation + // Override crime scene location (needs to be exact to ensure that e.g. in case of assault, + // we don't have a crime scene too far for the victim to see or hear) with a separate investigation + // location if appropriate + IF + StoryEvent((CHARACTERGUID)_Investigator,"CRIME_SetCrimeInvestigationPos") + AND + GetVarInteger(_Investigator,"CrimeID",_CrimeID) + AND + DB_CRIME_CrimeInvestigationPos(_CrimeID,_X,_Y,_Z) + THEN + // In case it's an alternate position, run rather than walk, because it means + // that we probably cannot find anything in the default location + SetVarInteger(_Investigator,"bool_RunToInvestigationScene",1); + SetVarFloat3(_Investigator,"CrimePos",_X,_Y,_Z); + + IF + StoryEvent((CHARACTERGUID)_Investigator,"CRIME_SetCrimeInvestigationPos") + THEN + CharacterSetReactionPriority(_Investigator,"WalkToCrimeScene",30000); + //END_REGION + + //REGION Interrogation + + IF + CrimeInterrogationRequest(_RegionID,_ID,_Interrogator,_Criminal1,_Criminal2,_Criminal3,_Criminal4,_Dialog) + THEN + ObjectClearFlag(_Interrogator,"GEB_Arrest_HavePrison",0); + + IF + CrimeInterrogationRequest(_RegionID,_ID,_Interrogator,_Criminal1,_Criminal2,_Criminal3,_Criminal4,_Dialog) + AND + DB_RegionPrison((STRING)_RegionID,(TRIGGERGUID)_) + THEN + ObjectSetFlag(_Interrogator,"GEB_Arrest_HavePrison"); + + PROC + ProcCrimeCheckInterrogationDialog((STRING)_RegionID,(INTEGER)_ID,(CHARACTERGUID)_Interrogator,(CHARACTERGUID)_Criminal1,(CHARACTERGUID)_Criminal2,(CHARACTERGUID)_Criminal3,(CHARACTERGUID)_Criminal4,(STRING)_Dialog) + AND + IsTagged(_Interrogator,"NOT_MESSING_AROUND",0) + AND + IsTagged(_Interrogator,"ANIMAL",0) + AND + IsTagged(_Interrogator,"KID",0) + THEN + DB_Crime_InterrogationStarted(_ID,_Interrogator); + ProcStartInterrogationDialog(_ID,_Interrogator,_Criminal1,_Criminal2,_Criminal3,_Criminal4,_Dialog); + + PROC + ProcCrimeCheckInterrogationDialog((STRING)_RegionID,(INTEGER)_ID,(CHARACTERGUID)_Interrogator,(CHARACTERGUID)_Criminal1,(CHARACTERGUID)_Criminal2,(CHARACTERGUID)_Criminal3,(CHARACTERGUID)_Criminal4,(STRING)_Dialog) + AND + NOT DB_Crime_InterrogationStarted(_ID,_Interrogator) + AND + IsTagged(_Interrogator,"NOT_MESSING_AROUND",1) + THEN + DB_Crime_InterrogationStarted(_ID,_Interrogator); + //TODO: should not just interrupt the dialog here + DialogRequestStop(_Criminal1); + ProcMakeNPCHostile(_Criminal1,_Interrogator); + CrimeInterrogationDone(_ID,_Interrogator,1,_Criminal1,_Criminal2,_Criminal3,_Criminal4); + ProcTryStartAttackAD(_Interrogator); + + PROC + ProcCrimeCheckInterrogationDialog((STRING)_RegionID,(INTEGER)_ID,(CHARACTERGUID)_Interrogator,(CHARACTERGUID)_Criminal1,(CHARACTERGUID)_Criminal2,(CHARACTERGUID)_Criminal3,(CHARACTERGUID)_Criminal4,(STRING)_Dialog) + AND + NOT DB_Crime_InterrogationStarted(_ID,_Interrogator) + AND + IsTagged(_Interrogator,"ANIMAL",1) + THEN + DB_Crime_InterrogationStarted(_ID,_Interrogator); + CrimeInterrogationDone(_ID,_Interrogator,1,_Criminal1,_Criminal2,_Criminal3,_Criminal4); + + PROC + ProcCrimeCheckInterrogationDialog((STRING)_RegionID,(INTEGER)_ID,(CHARACTERGUID)_Interrogator,(CHARACTERGUID)_Criminal1,(CHARACTERGUID)_Criminal2,(CHARACTERGUID)_Criminal3,(CHARACTERGUID)_Criminal4,(STRING)_Dialog) + AND + NOT DB_Crime_InterrogationStarted(_ID,_Interrogator) + AND + IsTagged(_Interrogator,"AGGRESSIVEANIMAL",1) + THEN + DB_Crime_InterrogationStarted(_ID,_Interrogator); + Proc_StartDialog(1,"GEB_AD_Noticed_AnimalAttackedAnimal",_Interrogator); + ProcMakeNPCHostile(_Interrogator,_Criminal1); + ProcMakeNPCHostile(_Interrogator,_Criminal2); + ProcMakeNPCHostile(_Interrogator,_Criminal3); + ProcMakeNPCHostile(_Interrogator,_Criminal4); + + PROC + ProcCrimeCheckInterrogationDialog((STRING)_RegionID,(INTEGER)_ID,(CHARACTERGUID)_Interrogator,(CHARACTERGUID)_Criminal1,(CHARACTERGUID)_Criminal2,(CHARACTERGUID)_Criminal3,(CHARACTERGUID)_Criminal4,(STRING)_Dialog) + AND + NOT DB_Crime_InterrogationStarted(_ID,_Interrogator) + AND + IsTagged(_Interrogator,"KID",1) + THEN + DB_Crime_InterrogationStarted(_ID,_Interrogator); + CrimeInterrogationDone(_ID,_Interrogator,1,_Criminal1,_Criminal2,_Criminal3,_Criminal4); + + PROC + ProcCrimeCheckInterrogationDialog((STRING)_RegionID,(INTEGER)_ID,(CHARACTERGUID)_Interrogator,(CHARACTERGUID)_Criminal1,(CHARACTERGUID)_Criminal2,(CHARACTERGUID)_Criminal3,(CHARACTERGUID)_Criminal4,(STRING)_Dialog) + THEN + NOT DB_Crime_InterrogationStarted(_ID,_Interrogator); + + + //--- Interrogation requested: + IF + CrimeInterrogationRequest(_RegionID,_ID,_Interrogator,_Criminal1,_Criminal2,_Criminal3,_Criminal4,_Dialog) + THEN + DB_InterrogationRequested(_RegionID,_ID,_Interrogator,_Criminal1,_Criminal2,_Criminal3,_Criminal4,_Dialog); + CharacterDisableAllCrimes(_Interrogator); + SetHasDialog(_Interrogator,0); //TODO: this is not great, we assume these guys have dialogs + CharacterMoveToAndTalk(_Interrogator,_Criminal1,"",0,"GEB_InterrogationMove",1,6.0); + + IF + CharacterMoveToAndTalkRequestDialog(_Arrester,(CHARACTERGUID)_Criminal,_,_,"GEB_InterrogationMove") + AND + DB_InterrogationRequested(_RegionID,_ID,_Arrester,_Criminal,_Criminal2,_Criminal3,_Criminal4,_Dialog) + THEN + ProcCrimeCheckInterrogationDialog(_RegionID,_ID,_Arrester,_Criminal,_Criminal2,_Criminal3,_Criminal4,_Dialog); + + IF + CharacterMoveToAndTalkFailed(_Arrester,_,"GEB_InterrogationMove") + AND + DB_InterrogationRequested(_RegionID,_ID,_Arrester,_Criminal,_Criminal2,_Criminal3,_Criminal4,_Dialog) + THEN + CrimeInterrogationDone(_ID,_Arrester,0,_Criminal,_Criminal2,_Criminal3,_Criminal4); + ProcCleanUpInterrogation(_Arrester); + NOT DB_IgnoreInterrogation(_Arrester,NULL_00000000-0000-0000-0000-000000000000); + ProcObjectTimer(_Arrester,"GEB_RestoreInterrogation",4500); + CrimeEnableInterrogation(_Arrester,0); + //allow others to interrogate them + CrimeResetInterrogationForCriminals(_ID,_Criminal,_Criminal2,_Criminal3,_Criminal4); + + PROC + ProcObjectTimerFinished((CHARACTERGUID)_Arrester,"GEB_RestoreInterrogation") + AND + NOT DB_CrimeReaction_DoNotInterrogate(_Arrester) + THEN + CrimeEnableInterrogation(_Arrester,1); + + PROC + ProcCleanUpInterrogation((CHARACTERGUID)_Arrester) + AND + DB_InterrogationRequested(_RegionID,_ID,_Arrester,_Criminal,_Criminal2,_Criminal3,_Criminal4,_Dialog) + THEN + ProcRestoreGenericBehaviour(_Arrester); + SetHasDialog(_Arrester,1); + NOT DB_InterrogationRequested(_RegionID,_ID,_Arrester,_Criminal,_Criminal2,_Criminal3,_Criminal4,_Dialog); + + IF + CharacterMoveToAndTalkRequestDialog(_Arrester,(CHARACTERGUID)_Criminal,_,_,"GEB_InterrogationMove") + AND + NOT DB_InterrogationRequested(_,_,_Arrester,_Criminal,_,_,_,_) + THEN + CharacterMoveToAndTalkRequestDialogFailed(_Arrester,_Criminal,"GEB_InterrogationMove"); + + IF + DB_Crime_FailedToInterruptStoryDialogs(_CrimeID,_ArrestDialog,_Arrester,_Criminal,_Criminal2,_Criminal3,_Criminal4) + AND + DB_InterrogationRequested(_RegionID,_CrimeID,_Arrester,_Criminal,_Criminal2,_Criminal3,_Criminal4,_ArrestDialog) + THEN + CharacterMoveToAndTalkRequestDialogFailed(_Arrester,_Criminal,"GEB_InterrogationMove"); + + IF + DB_Crime_InterrogationStarted(_CrimeID,_Arrester) + AND + DB_InterrogationRequested(_RegionID,_CrimeID,_Arrester,_Criminal,_Criminal2,_Criminal3,_Criminal4,_ArrestDialog) + THEN + ProcCleanUpInterrogation(_Arrester); + + IF + OnCrimeResolved(_CrimeID,_,_,_,_,_) + AND + DB_InterrogationRequested(_,_CrimeID,_Arrester,_,_,_,_,_) + THEN + ProcCleanUpInterrogation(_Arrester); + + //--- Start dialog: + PROC + ProcStartInterrogationDialog((INTEGER)_CrimeID,(CHARACTERGUID)_Interrogator,(CHARACTERGUID)_Criminal1,(CHARACTERGUID)_Criminal2,(CHARACTERGUID)_Criminal3,(CHARACTERGUID)_Criminal4,(STRING)_Dialog) + AND + _Dialog == "" + THEN + ProcDoStartInterrogationDialog(_CrimeID,_Interrogator,_Criminal1,_Criminal2,_Criminal3,_Criminal4,"GEB_Interrogation"); + + PROC + ProcStartInterrogationDialog((INTEGER)_CrimeID,(CHARACTERGUID)_Interrogator,(CHARACTERGUID)_Criminal1,(CHARACTERGUID)_Criminal2,(CHARACTERGUID)_Criminal3,(CHARACTERGUID)_Criminal4,(STRING)_Dialog) + AND + _Dialog != "" + THEN + ProcDoStartInterrogationDialog(_CrimeID,_Interrogator,_Criminal1,_Criminal2,_Criminal3,_Criminal4,_Dialog); + + PROC + ProcDoStartInterrogationDialog((INTEGER)_,(CHARACTERGUID)_Interrogator,(CHARACTERGUID)_Criminal1,(CHARACTERGUID)_Criminal2,(CHARACTERGUID)_Criminal3,(CHARACTERGUID)_Criminal4,(STRING)_Dialog) + AND + DB_Interrogation(_Interrogator,_CrimeID) + THEN + NOT DB_Interrogation(_Interrogator,_CrimeID); // This Database is set on CRIME_Allow_Search + + PROC + ProcDoStartInterrogationDialog((INTEGER)_CrimeID,(CHARACTERGUID)_Interrogator,(CHARACTERGUID)_Criminal1,(CHARACTERGUID)_Criminal2,(CHARACTERGUID)_Criminal3,(CHARACTERGUID)_Criminal4,(STRING)_Dialog) + THEN + ProcCrimeInterruptStoryDialogs(_CrimeID,_Dialog,1,_Interrogator,_Criminal1,_Criminal2,_Criminal3,_Criminal4); + ProcCrimeCheckInterrogationDialogSucceeded(_CrimeID,_Interrogator,_Criminal1,_Criminal2,_Criminal3,_Criminal4,_Dialog); + + PROC + ProcCrimeCheckInterrogationDialogSucceeded((INTEGER)_CrimeID,(CHARACTERGUID)_Interrogator,(CHARACTERGUID)_Criminal1,(CHARACTERGUID)_Criminal2,(CHARACTERGUID)_Criminal3,(CHARACTERGUID)_Criminal4,(STRING)_Dialog) + AND + NOT DB_Crime_FailedToInterruptStoryDialogs(_CrimeID,_Dialog,_Interrogator,_Criminal1,_Criminal2,_Criminal3,_Criminal4) + THEN + DB_Crime_Interrogation(_CrimeID,_Interrogator,_Criminal1,_Criminal2,_Criminal3,_Criminal4,_Dialog); + DB_Interrogation(_Interrogator,_CrimeID); + + PROC + ProcCrimeCheckInterrogationDialogSucceeded((INTEGER)_CrimeID,(CHARACTERGUID)_Interrogator,(CHARACTERGUID)_Criminal1,(CHARACTERGUID)_Criminal2,(CHARACTERGUID)_Criminal3,(CHARACTERGUID)_Criminal4,(STRING)_Dialog) + AND + DB_Crime_FailedToInterruptStoryDialogs(_CrimeID,_Dialog,_Interrogator,_Criminal1,_Criminal2,_Criminal3,_Criminal4) + THEN + CrimeInterrogationDone(_CrimeID,_Interrogator,1,_Criminal1,_Criminal2,_Criminal3,_Criminal4); + + IF + ObjectFlagSet("CRIME_Allow_Search",(CHARACTERGUID)_Interrogator,_ID) + THEN + ObjectClearFlag(_Interrogator,"CRIME_Allow_Search",_ID); + ObjectClearFlag(_Interrogator,"CRIME_EvidenceFound",_ID); + ObjectClearFlag(_Interrogator,"CRIME_GuiltFound",_ID); + ObjectClearFlag(_Interrogator,"CRIME_FoundEvidenceCurrentCrime",_ID); + ObjectClearFlag(_Interrogator,"CRIME_FoundGuiltyPlayer",_ID); + + IF + ObjectFlagSet("CRIME_Allow_Search",(CHARACTERGUID)_Interrogator,_ID) + AND + DB_Crime_Interrogation(_CrimeID,_Interrogator,_Criminal1,_Criminal2,_Criminal3,_Criminal4,_) + AND + CrimeFindEvidence(_CrimeID,_Interrogator,_Criminal1,_Criminal2,_Criminal3,_Criminal4,_FoundEvidenceCurrentCrime,_FoundEvidence,_FoundGuilty) + AND + QRY_GetCriminalGuilty(_CrimeID,_Interrogator,_FoundEvidenceCurrentCrime,_FoundEvidence,_FoundGuilty) + THEN + DB_Interrogation(_Interrogator,_CrimeID); + + IF + ObjectFlagSet("CRIME_Resist_Search",(CHARACTERGUID)_Character,_ID) + THEN + ObjectClearFlag(_Character,"CRIME_Resist_Search",_ID); + DB_Crime_CombatAfterDialog(_ID); + + QRY + QRY_GetCriminalGuilty((INTEGER)_CrimeID,(CHARACTERGUID)_Interrogator,(INTEGER)_FoundEvidenceCurrentCrime,(INTEGER)_FoundEvidence,(INTEGER)_FoundGuilty) + AND + _FoundEvidence == 1 + THEN + ObjectSetFlag(_Interrogator,"CRIME_EvidenceFound",0); + + QRY + QRY_GetCriminalGuilty((INTEGER)_CrimeID,(CHARACTERGUID)_Interrogator,(INTEGER)_FoundEvidenceCurrentCrime,(INTEGER)_FoundEvidence,(INTEGER)_FoundGuilty) + AND + _FoundGuilty == 1 + THEN + ObjectSetFlag(_Interrogator,"CRIME_GuiltFound",0); + + QRY + QRY_GetCriminalGuilty((INTEGER)_CrimeID,(CHARACTERGUID)_Interrogator,(INTEGER)_FoundEvidenceCurrentCrime,(INTEGER)_FoundEvidence,(INTEGER)_FoundGuilty) + AND + _FoundEvidenceCurrentCrime == 1 + THEN + ObjectSetFlag(_Interrogator,"CRIME_FoundEvidenceCurrentCrime",0); + + //Resolve Crime + IF + ObjectFlagSet("CRIME_FoundGuiltyPlayer",(CHARACTERGUID)_Interrogator,_ID) + AND + DB_Interrogation(_Interrogator,_CrimeID) + THEN + DB_EvidenceFound(_CrimeID,_Interrogator); + //DebugText(_Interrogator,"Evidence Found"); + + //Give Evidance Back to investigator + IF + ObjectFlagSet("CRIME_ReturnGoodsToOnwer",(CHARACTERGUID)_Interrogator,_ID) + AND + DB_Interrogation(_Interrogator,_CrimeID) + THEN + ObjectClearFlag(_Interrogator,"CRIME_ReturnGoodsToOnwer",_ID); + CrimeTransferEvidenceTo(_CrimeID,_Interrogator); + + IF + ObjectFlagSet("CRIME_CallGuardsFromDialog",(CHARACTERGUID)_Interrogator,_ID) // When A Civilian finds a Thief in Interrogation + AND + DB_DialogPlayers(_ID,_Criminal,1) + THEN + ObjectClearFlag(_Interrogator,"CRIME_CallGuardsFromDialog"); + DB_Crime_CallingGuards(_Interrogator,_Criminal,_ID); + + IF + DialogEnded(_Dialog,_ID) + AND + DB_DialogNPCs(_ID,_Interrogator,1) + AND + DB_Crime_Interrogation(_CrimeID,(CHARACTERGUID)_Interrogator,_Criminal1,_Criminal2,_Criminal3,_Criminal4,_Dialog) + AND + NOT DB_Crime_CallingGuards(_Interrogator,_,_ID) + THEN + ProcCheckInterrogationDone(_Interrogator,_Dialog); + + PROC + ProcCheckInterrogationDone((CHARACTERGUID)_Interrogator,(STRING)_Dialog) + AND + NOT DB_EvidenceCheckInWarning(_Dialog) + AND + DB_Crime_Interrogation(_CrimeID,_Interrogator,_Criminal1,_Criminal2,_Criminal3,_Criminal4,_Dialog) + THEN + NOT DB_Crime_Interrogation(_CrimeID,_Interrogator,_Criminal1,_Criminal2,_Criminal3,_Criminal4,_Dialog); + ProcStopInterrogation(_CrimeID,_Interrogator,_Criminal1,_Criminal2,_Criminal3,_Criminal4); + + + IF + DialogEnded(_Dialog,_ID) + AND + DB_Crime_CallingGuards(_Interrogator,_Criminal,_ID) + AND + DB_Interrogation(_Interrogator,_CrimeID) + THEN + SetVarObject(_Interrogator,"Criminal1",(CHARACTERGUID)_Criminal); // TODO REMOVE THIS HACK! + SetVarString(_Interrogator,"ArrestDialog","GEB_Arrest"); + SetVarInteger(_Interrogator,"CrimeID",_CrimeID); + SetStoryEvent(_Interrogator,"CRIME_CallGuardsFromDialog"); + NOT DB_Crime_CallingGuards(_Interrogator,_Criminal,_ID); + + IF + ObjectFlagSet("CRIME_FleeHelpFromDialog",(CHARACTERGUID)_Interrogator,_ID) // When A Kid finds a Criminal in Interrogation + AND + DB_DialogPlayers(_ID,_Criminal,1) + THEN + DB_Crime_FleeCallforHelp(_Interrogator,_Criminal,_ID); + + IF + DialogEnded(_Dialog,_ID) + AND + DB_Crime_FleeCallforHelp(_Interrogator,_Criminal,_ID) + AND + DB_Interrogation(_Interrogator,_CrimeID) + THEN + SetVarObject(_Interrogator,"Criminal1",(CHARACTERGUID)_Criminal); // TODO REMOVE THIS HACK! + SetVarString(_Interrogator,"ArrestDialog","GEB_Arrest"); + SetVarInteger(_Interrogator,"CrimeID",_CrimeID); + SetStoryEvent(_Interrogator,"CRIME_FleeHelpFromDialog"); + //CrimeInterrogationDone(_CrimeID,_Interrogator,1); + NOT DB_Crime_FleeCallforHelp(_Interrogator,_Criminal,_ID); + + IF + DialogEnded(_Dialog,_ID) + AND + DB_DialogPlayers(_ID,_Player,1) + AND + ObjectGetFlag(_Player,"CRIME_PersuasionFailed",1) + THEN + ObjectClearFlag(_Player,"CRIME_PersuasionFailed",0); + + // Stop Investigation when join combat + + + //REGION Dialog Ended + IF + DialogEnded(_Dialog,_ID) + AND + DB_Crime_CombatAfterDialog(_ID) + AND + DB_DialogNPCs(_ID,_Interrogator,1) + AND + DB_DialogPlayers(_ID,_Player,_) + THEN + ProcMakeNPCHostile((CHARACTERGUID)_Player,(CHARACTERGUID)_Interrogator); + NOT DB_Crime_CombatAfterDialog(_ID); + + IF + DialogEnded(_Dialog,_ID) + AND + DB_DialogNPCs(_ID,_Interrogator,1) + AND + DB_Crime_Interrogation(_CrimeID,(CHARACTERGUID)_Interrogator,_Criminal1,_Criminal2,_Criminal3,_Criminal4,_Dialog) + THEN + NOT DB_Crime_Interrogation(_CrimeID,(CHARACTERGUID)_Interrogator,_Criminal1,_Criminal2,_Criminal3,_Criminal4,_Dialog); + + PROC + ProcStopInterrogation((INTEGER)_CrimeID,(CHARACTERGUID)_Interrogator,(CHARACTERGUID)_Criminal1,(CHARACTERGUID)_Criminal2,(CHARACTERGUID)_Criminal3,(CHARACTERGUID)_Criminal4) + AND + DB_EvidenceFound(_CrimeID,_Interrogator) + THEN + CrimeInterrogationDone(_CrimeID,_Interrogator,1,_Criminal1,_Criminal2,_Criminal3,_Criminal4); + + PROC + ProcStopInterrogation((INTEGER)_CrimeID,(CHARACTERGUID)_Interrogator,(CHARACTERGUID)_Criminal1,(CHARACTERGUID)_Criminal2,(CHARACTERGUID)_Criminal3,(CHARACTERGUID)_Criminal4) + AND + NOT DB_EvidenceFound(_CrimeID,_Interrogator) + THEN + CrimeInterrogationDone(_CrimeID,_Interrogator,0,_Criminal1,_Criminal2,_Criminal3,_Criminal4); + + PROC + ProcStopInterrogation((INTEGER)_CrimeID,(CHARACTERGUID)_Interrogator,(CHARACTERGUID)_Criminal1,(CHARACTERGUID)_Criminal2,(CHARACTERGUID)_Criminal3,(CHARACTERGUID)_Criminal4) + THEN + NOT DB_EvidenceFound(_CrimeID,_Interrogator); + //END_REGION + + //REGION Saw Criminal In Combat While Invesigating + IF + OnCrimeSawCriminalInCombat(_CrimeID,_Witness,_Criminal) + AND + IsTagged(_Witness,"CIVILIAN",0) + AND + IsTagged(_Witness,"ANIMAL",0) //Animals should not investigate should be disabled in excel + THEN + ProcMakeNPCHostile(_Witness,_Criminal); + //END_REGION + //END_REGION + + //REGION Trespassing + + //--- Registering DB + IF + DB_TrespassTrigger((TRIGGERGUID)_Trigger,(TRIGGERGUID)_OutTrigger) + THEN + DB_TrespassTrigger((TRIGGERGUID)_Trigger,(TRIGGERGUID)_OutTrigger,"Trespassing",(CHARACTERGUID)NULL_00000000-0000-0000-0000-000000000000); + + IF + DB_TrespassTrigger((TRIGGERGUID)_Trigger,(TRIGGERGUID)_OutTrigger,(STRING)_CrimeName) + THEN + DB_TrespassingCrimes((STRING)_CrimeName); + DB_TrespassTrigger((TRIGGERGUID)_Trigger,(TRIGGERGUID)_OutTrigger,(STRING)_CrimeName,(CHARACTERGUID)NULL_00000000-0000-0000-0000-000000000000); + + IF + DB_TrespassTrigger((TRIGGERGUID)_Trigger,(TRIGGERGUID)_OutTrigger,(STRING)_CrimeName,(CHARACTERGUID)_Victim) + THEN + DB_TrespassingCrimes((STRING)_CrimeName); + DB_TrespassTrigger((TRIGGERGUID)_Trigger,(TRIGGERGUID)_OutTrigger,(STRING)_CrimeName,(CHARACTERGUID)_Victim); + + IF + DB_TrespassTrigger((TRIGGERGUID)_Trigger,(TRIGGERGUID)_,_,_) + THEN + ProcTriggerRegisterForPlayers(_Trigger); + + + //--- Removing: + PROC + ProcRemoveDBTrespassTrigger((TRIGGERGUID)_Trigger,(TRIGGERGUID)_OutTrigger) + THEN + ProcRemoveDBTrespassTrigger((TRIGGERGUID)_Trigger,(TRIGGERGUID)_OutTrigger,(CHARACTERGUID)NULL_00000000-0000-0000-0000-000000000000); + + PROC + ProcRemoveDBTrespassTrigger((TRIGGERGUID)_Trigger,(TRIGGERGUID)_OutTrigger,(CHARACTERGUID)_Victim) + AND + DB_TrespassTrigger(_Trigger,_OutTrigger,_CrimeName,_Victim) + AND + DB_IsPlayer(_Player) + THEN + CharacterStopCrime(_Player, _CrimeName, _Trigger); + + PROC + ProcRemoveDBTrespassTrigger((TRIGGERGUID)_Trigger,(TRIGGERGUID)_OutTrigger,(CHARACTERGUID)_Victim) + AND + DB_TrespassTrigger(_Trigger,_OutTrigger,_CrimeName) + AND + DB_IsPlayer(_Player) + THEN + CharacterStopCrime(_Player, _CrimeName, _Trigger); + + PROC + ProcRemoveDBTrespassTrigger((TRIGGERGUID)_Trigger,(TRIGGERGUID)_OutTrigger,(CHARACTERGUID)_Victim) + AND + DB_TrespassTrigger(_Trigger,_OutTrigger) + AND + DB_IsPlayer(_Player) + THEN + CharacterStopCrime(_Player, "Trespassing", _Trigger); + + PROC + ProcRemoveDBTrespassTrigger((TRIGGERGUID)_Trigger,(TRIGGERGUID)_OutTrigger,(CHARACTERGUID)_Victim) + AND + DB_TrespassTrigger(_Trigger,_OutTrigger,_CrimeName,_Victim) + THEN + NOT DB_TrespassTrigger(_Trigger,_OutTrigger,_CrimeName,_Victim); + + PROC + ProcRemoveDBTrespassTrigger((TRIGGERGUID)_Trigger,(TRIGGERGUID)_OutTrigger,(CHARACTERGUID)_Victim) + AND + DB_TrespassTrigger(_Trigger,_OutTrigger) + THEN + NOT DB_TrespassTrigger(_Trigger,_OutTrigger); + + PROC + ProcRemoveDBTrespassTrigger((TRIGGERGUID)_Trigger,(TRIGGERGUID)_OutTrigger,(CHARACTERGUID)_Victim) + AND + DB_TrespassTrigger(_Trigger,_OutTrigger,_CrimeName) + THEN + NOT DB_TrespassTrigger(_Trigger,_OutTrigger,_CrimeName); + + //--- Register/stop crime + IF + CharacterEnteredTrigger(_Player,_Trigger) + AND + DB_TrespassTrigger(_Trigger,_,_CrimeName,_Victim) + AND + DB_IsPlayer(_Player) + THEN + DB_PlayerTrespassing(_Player,_Trigger); + CharacterRegisterCrime(_Player,_CrimeName,_Trigger,_Victim,0); + + IF + CharacterLeftTrigger(_Player,_Trigger) + AND + DB_TrespassTrigger(_Trigger,_,_CrimeName,_Victim) + THEN + CharacterStopCrime(_Player,_CrimeName,_Trigger); + NOT DB_PlayerTrespassing(_Player,_Trigger); + + IF + ObjectFlagSet("TeleportOutOfTrespass",_Player,_Inst) + THEN + DB_CrimeTeleportOutOfTrespass(_Player,_Inst); + + IF + DialogEnded(_,_ID) + AND + DB_DialogPlayers(_ID,_FirstPlayer,1) + AND + DB_PlayerTrespassing((CHARACTERGUID)_FirstPlayer,_Trigger) + AND + DB_CrimeTeleportOutOfTrespass(_FirstPlayer,_ID) + AND + DB_TrespassTrigger(_Trigger,_Outside,_,_) + AND + DB_DialogPlayers(_ID,_Player,_) + THEN + TeleportTo(_Player,_Outside,"",1); + NOT DB_PlayerTrespassing(_FirstPlayer,_Trigger); + NOT DB_CrimeTeleportOutOfTrespass(_Player,_ID); + ObjectClearFlag(_Player,"TeleportOutOfTrespass",0); + CharacterFlushQueue((CHARACTERGUID)_Player); + FireOsirisEvents(); + + IF + DialogEnded(_,_ID) + AND + DB_CrimeTeleportOutOfTrespass(_Player,_ID) + THEN + NOT DB_CrimeTeleportOutOfTrespass(_Player,_ID); + ObjectClearFlag(_Player,"TeleportOutOfTrespass",0); + + ////////////////////////////// + // Unavailable Fallback lead + IF + CharacterSelectedAsBestUnavailableFallbackLead(_NPC,_RegionID,_CrimeID,_BusyCrimeID,_Criminal1,_Criminal2,_Criminal3,_Criminal4) + AND + NOT QryCrimeOnlyHasSummonCriminal(_Criminal1,_Criminal2,_Criminal3,_Criminal4) + AND + CrimeGetType(_CrimeID,_CrimeName) + AND + DB_TrespassingCrimes(_CrimeName) + THEN + ProcCrimeTrespassingCheckDetection(_CrimeID,_NPC,_Criminal1); + ProcCrimeTrespassingCheckDetection(_CrimeID,_NPC,_Criminal2); + ProcCrimeTrespassingCheckDetection(_CrimeID,_NPC,_Criminal3); + ProcCrimeTrespassingCheckDetection(_CrimeID,_NPC,_Criminal4); + ProcCrimeTrespassingCheckValidLead(_CrimeID,_NPC); + ProcCrimeTrespassingStopNPCsDialogAndMakeHostileTo(_CrimeID,_BusyCrimeID,_NPC); + + PROC + ProcCrimeTrespassingCheckDetection((INTEGER)_CrimeID,(CHARACTERGUID)_NPC,(CHARACTERGUID)_Player) + AND + _Player != NULL_00000000-0000-0000-0000-000000000000 + AND + GetDistanceTo(_NPC,_Player,_Dist) + AND + CrimeGetDetectionRange(_CrimeID,_Range) + AND + _Dist <= _Range + THEN + DB_Crime_TrespassUnavailableLeadDetected((CHARACTERGUID)_NPC,(CHARACTERGUID)_Player); + + PROC + ProcCrimeTrespassingCheckValidLead((INTEGER)_CrimeID,(CHARACTERGUID)_NPC) + AND + QryCrimeTrespassUnavailableLeadDetected(_NPC) + THEN + ProcCrimeTrespassingBlockHostileFallback(_CrimeID,_NPC); + + QRY + QryCrimeTrespassUnavailableLeadDetected((CHARACTERGUID)_NPC) + AND + DB_Crime_TrespassUnavailableLeadDetected((CHARACTERGUID)_NPC,_) + THEN + DB_NOOP(1); + + PROC + ProcCrimeTrespassingBlockHostileFallback((INTEGER)_CrimeID,(CHARACTERGUID)_NPC) + THEN + DB_NOOP(1); + + PROC + ProcCrimeTrespassingStopNPCsDialogAndMakeHostileTo((INTEGER)_CrimeID,(INTEGER)_BusyCrimeID,(CHARACTERGUID)_NPC) + AND + DB_Crime_TrespassUnavailableLeadDetected(_NPC,_Player) + AND + NOT DB_CrimeTrespassingBlockHostile(_CrimeID,_NPC) + THEN + ProcTryMergingCrimes(_CrimeID,_BusyCrimeID,_NPC,_Player); + + PROC + ProcTryMergingCrimes((INTEGER)_NewCrime,(INTEGER)_OldCrime,(CHARACTERGUID)_NPC,(CHARACTERGUID)_Player) + THEN + NOT DB_CanMergeCrimes(1); + + PROC + ProcTryMergingCrimes((INTEGER)_NewCrime,(INTEGER)_OldCrime,(CHARACTERGUID)_NPC,(CHARACTERGUID)_Player) + AND + QryCanMergeCrimes(_NewCrime,_OldCrime,_Player) + THEN + DB_CanMergeCrimes(1); + + IF + OnCriminalMergedWithCrime(_Crime,_Criminal) + AND + CrimeGetLeadInvestigator(_Crime,_Lead) + AND + CrimeGetType(_Crime,_Type) + AND + DB_CrimeAttitudeChange(_Type,_Adjust) + THEN + ProcCrimeCheckIfAttitudeCauseCombat(_Lead,_Criminal,_Adjust); + + PROC + ProcTryMergingCrimes((INTEGER)_NewCrime,(INTEGER)_OldCrime,(CHARACTERGUID)_NPC,(CHARACTERGUID)_Player) + AND + NOT DB_CanMergeCrimes(1) + AND + QueryOnlyOnce("CRIME_Trespassing_NPCForceStopDialog") + THEN + ProcForceStopDialog(_NPC); + + PROC + ProcTryMergingCrimes((INTEGER)_NewCrime,(INTEGER)_OldCrime,(CHARACTERGUID)_NPC,(CHARACTERGUID)_Player) + AND + NOT DB_CanMergeCrimes(1) + THEN + //ignore these 2 crimes, since we're dealing with them in our own way + CrimeIgnoreCrime(_OldCrime,_NPC); + CrimeIgnoreCrime(_NewCrime,_NPC); + DB_CrimeTresPassCombatFallback(_NPC); + ProcTryStartAttackAD(_NPC); + CharacterSetTemporaryHostileRelation(_NPC,_Player); + + PROC + ProcTryMergingCrimes((INTEGER)_NewCrime,(INTEGER)_OldCrime,(CHARACTERGUID)_NPC,(CHARACTERGUID)_Player) + AND + DB_CanMergeCrimes(1) + AND + DB_DialogNPCs(_ID,_NPC,_) + THEN + CharacterStopCrimeWithID(_Player,_NewCrime); + DialogAddCharacter(_ID,_Player); + + QRY + QryCanMergeCrimes((INTEGER)_NewCrime,(INTEGER)_OldCrime,(CHARACTERGUID)_Player) + AND + CrimeGetType(_NewCrime,_Type) + AND + CrimeGetType(_OldCrime,_Type) + AND + CrimeAddCriminal(_OldCrime,_Player,1) + THEN + DB_Noop(1); + + PROC + ProcTryStartAttackAD((CHARACTERGUID)_NPC) + AND + IsTagged(_NPC,"ANIMAL",0) + AND + HasAppliedStatus(_NPC, "UNCONSCIOUS", 0) + AND + HasAppliedStatus(_NPC, "LIE_DYING", 0) + AND + HasAppliedStatus(_NPC, "MUTED", 0) + THEN + Proc_StartDialog(1,"GEB_AD_AttackHelp",_NPC); + + //Clearing + PROC + ProcCrimeTrespassingStopNPCsDialogAndMakeHostileTo((INTEGER)_CrimeID,(INTEGER)_BusyCrimeID,(CHARACTERGUID)_NPC) + AND + DB_Crime_TrespassUnavailableLeadDetected(_NPC,_Player) + THEN + NOT DB_OnlyOnce("CRIME_Trespassing_NPCForceStopDialog"); + NOT DB_Crime_TrespassUnavailableLeadDetected(_NPC,_Player); + + PROC + ProcCrimeTrespassingStopNPCsDialogAndMakeHostileTo((INTEGER)_CrimeID,(INTEGER)_BusyCrimeID,(CHARACTERGUID)_NPC) + THEN + DB_CrimeTrespassingBlockHostile(_CrimeID,_NPC); + + IF + ObjectEnteredCombat((CHARACTERGUID)_NPC,_) + AND + DB_CrimeTresPassCombatFallback(_NPC) + AND + DB_CrimeTrespassingBlockHostile(_CrimeID,_NPC) + THEN + NOT DB_CrimeTrespassingBlockHostile(_CrimeID,_NPC); + + IF + OnCrimeResolved(_CrimeID,_,_,_,_,_) + AND + DB_CrimeTrespassingBlockHostile(_CrimeID,_NPC) + THEN + NOT DB_CrimeTrespassingBlockHostile(_CrimeID,_NPC); + + //END_REGION + + //REGION Crimes against a character that can't react because it's in a dialog + IF + CharacterSelectedAsBestUnavailableFallbackLead(_Char,_Region,_NewCrime,_OldCrime,_NewCriminal1,_NewCriminal2,_NewCriminal3,_NewCriminal4) + AND + CrimeGetTension(_NewCrime,_Tension) + AND + _Tension > 0 + AND + CrimeGetType(_NewCrime,_CrimeName) + AND + NOT DB_TrespassingCrimes(_CrimeName) + AND + DB_DialogNPCs(_OldCrimeDialog,_Char,_) + THEN + CrimeConfrontationDone(_NewCrime,_Char); + ProcForceStopDialog(_Char); + Proc_CharacterSetTemporaryHostileRelation(_Char,_NewCriminal1); + Proc_CharacterSetTemporaryHostileRelation(_Char,_NewCriminal2); + Proc_CharacterSetTemporaryHostileRelation(_Char,_NewCriminal3); + Proc_CharacterSetTemporaryHostileRelation(_Char,_NewCriminal4); + //END_REGION + //----------------------------------- ASSAULT ----------------------------------- + //REGION Chicken touch + + IF + CharacterStatusAttempt((CHARACTERGUID)_NPC,"CHICKEN",(CHARACTERGUID)_Player) + AND + DB_IsPlayer(_Player) + AND + NOT DB_IsPlayer(_NPC) + AND + NOT DB_IgnoreAssault(_NPC) + AND + NOT DB_CombatCharacters(_NPC,_) + AND + NOT DB_IgnoreAssaultFor(_Player,_NPC) + AND + ObjectIsCharacter(_NPC,1) + AND + IsTagged(_NPC,"GHOST",0) + AND + IsTagged(_NPC,"SUMMON",0) + AND + HasAppliedStatus(_NPC,"CHICKEN",1) + AND + CharacterIsEnemy(_NPC,_Player,0) + THEN + DB_Crime_PolymorphedIgnoreAssault(_NPC,_Player); //keep this first, the register forces a flush! + + //END_REGION + + //REGION Assault + + QRY + QryHasNeutralBlockTag((CHARACTERGUID)_NPC) + AND + DB_NeutralTagIgnore((STRING)_Tag) + AND + IsTagged(_NPC,_Tag,1) + THEN + DB_NOOP(1); + + //--- Checks if the NPC and/or the player/summon are in combat while being neutral to each other, then makes them hostile + IF + AttackedByObject((CHARACTERGUID)_NPC,(CHARACTERGUID)_Player,(CHARACTERGUID)_Summon,_,_DamageSource) + AND + NOT DB_Crime_Assault(_,_,_NPC) + AND + NOT DB_Crime_Assault(_,_Player,_) + AND + NOT DB_Crime_PolymorphedIgnoreAssault(_NPC,_Player) + AND + NOT QryIgnoreDamageSource(_DamageSource) + AND + ObjectIsCharacter(_NPC,1) + AND + CharacterIsPlayer(_Player,1) + AND + CharacterIsDeadOrFeign(_Player,0) + AND + CharacterIsPlayer(_NPC,0) + AND + Qry_AreInCombat(_Player,_NPC) + AND + CharacterCanFight(_NPC,1) + AND + CharacterIsDead(_NPC,0) + AND + CharacterIsNeutral(_NPC,_Player,1) + AND + NOT QryHasNeutralBlockTag(_NPC) + AND + IsTagged(_NPC,"ANIMAL",0) + AND + DB_CombatCharacters(_NPC,_ID) + THEN + ProcWarnPlayerOfAttack(_NPC,_Player,_ID); + + //REGION Combat warnings + IF + ObjectLeftCombat((CHARACTERGUID)_NPC,_) + AND + DB_CombatWarnings(_NPC,_Cnt) + THEN + NOT DB_CombatWarnings(_NPC,_Cnt); + + PROC + ProcWarnPlayerOfAttack((CHARACTERGUID)_NPC,(CHARACTERGUID)_Player,(INTEGER)_ID) + AND + NOT DB_CombatWarnings(_NPC,_) + THEN + DB_CombatWarnings(_NPC,0); + + PROC + ProcWarnPlayerOfAttack((CHARACTERGUID)_NPC,(CHARACTERGUID)_Player,(INTEGER)_ID) + AND + NOT DB_CombatCharacters(_Player,_) + AND + DB_CombatWarnings(_NPC,_Count) + THEN + NOT DB_CombatWarnings(_NPC,_Count); + DB_CombatWarnings(_NPC,3); + + PROC + ProcWarnPlayerOfAttack((CHARACTERGUID)_NPC,_,_) + THEN + ObjectClearFlag(_NPC,"GEB_Crime_LastNeutralWarning"); + ObjectClearFlag(_NPC,"GEB_Crime_AttackNeutralWarning"); + + PROC + ProcWarnPlayerOfAttack((CHARACTERGUID)_NPC,(CHARACTERGUID)_Player,(INTEGER)_ID) + AND + DB_CombatWarnings(_NPC,_Old) + AND + IntegerSum(_Old,1,_Count) + THEN + NOT DB_CombatWarnings(_NPC,_Old); + DB_CombatWarnings(_NPC,_Count); + + PROC + ProcWarnPlayerOfAttack((CHARACTERGUID)_NPC,(CHARACTERGUID)_Player,(INTEGER)_ID) + AND + DB_CombatWarnings(_NPC,_Count) + AND + _Count > 3 + THEN + ObjectSetFlag(_NPC,"GEB_Crime_AttackNeutralWarning"); + ProcMakeNPCHostile(_NPC,_Player); + + PROC + ProcWarnPlayerOfAttack((CHARACTERGUID)_NPC,(CHARACTERGUID)_Player,(INTEGER)_ID) + AND + DB_CombatWarnings(_NPC,3) + THEN + ObjectSetFlag(_NPC,"GEB_Crime_LastNeutralWarning"); + + PROC + ProcWarnPlayerOfAttack((CHARACTERGUID)_NPC,(CHARACTERGUID)_Player,(INTEGER)_ID) + AND + DB_CombatWarnings(_NPC,_Count) + AND + QryDoNeutralAttackWarning(_Count) + THEN + Proc_StartDialog(1,"GEB_AD_WarnFriendlyFire",_NPC); + + QRY + QryDoNeutralAttackWarning((INTEGER)_Count) + AND + _Count < 3 + AND + Random(100,_Rnd) + AND + _Rnd <= 40 + THEN + DB_Noop(1); + + QRY + QryDoNeutralAttackWarning(3) + THEN + DB_Noop(1); + + //END_REGION + QRY + Qry_AreInCombat((CHARACTERGUID)_Player,(CHARACTERGUID)_NPC) + AND + DB_CombatCharacters(_NPC,_) + THEN + DB_NOOP(1); + + QRY + Qry_AreInCombat((CHARACTERGUID)_Player,(CHARACTERGUID)_NPC) + AND + DB_CombatCharacters(_Player,_) + THEN + DB_NOOP(1); + + PROC + ProcClearAssaultDBForCrime((INTEGER)_CrimeID) + AND + DB_Crime_Assault(_CrimeID,_Criminal,_Victim) + THEN + NOT DB_Crime_Assault(_CrimeID,_Criminal,_Victim); + + IF + OnCrimeRemoved(_CrimeID,_Victim,_Criminal1,_Criminal2,_Criminal3,_Criminal4) + THEN + ProcClearAssaultDBForCrime(_CrimeID); + + IF + OnCrimeResolved(_CrimeID,_Victim,_Criminal1,_Criminal2,_Criminal3,_Criminal4) + AND + _Victim != NULL_00000000-0000-0000-0000-000000000000 + AND + DB_Crime_Assault(_CrimeID,_,_) + AND + IntegertoString(_CrimeID,_CrimeSuffix) + AND + StringConcatenate("Timer_ClearAttackDB",_CrimeSuffix,_TimerName) + THEN + DB_Crime_PlayerAttacked(_CrimeID,_Criminal1,_Victim); // This is For Murder crime + DB_Crime_PlayerAttacked(_CrimeID,_Criminal2,_Victim); + DB_Crime_PlayerAttacked(_CrimeID,_Criminal3,_Victim); + DB_Crime_PlayerAttacked(_CrimeID,_Criminal4,_Victim); + DB_AttackTimer(_CrimeID,_TimerName); + ProcObjectTimer(_Victim,_TimerName,1500); + NOT DB_Crime_PlayerAttacked(_CrimeID,NULL_00000000-0000-0000-0000-000000000000,_Victim); //Future Feature if needed Generate Murder for every on in combat with the OG DB_Crime_PlayerAttacked _Victim + + IF + OnCrimeResolved(_CrimeID,_Victim,_Criminal1,_Criminal2,_Criminal3,_Criminal4) + THEN + ProcClearAssaultDBForCrime(_CrimeID); + + PROC + ProcObjectTimerFinished((CHARACTERGUID)_Victim,_TimerName) + AND + DB_AttackTimer(_CrimeID,_TimerName) + AND + DB_Crime_PlayerAttacked(_CrimeID,_Player,_Victim) + AND + NOT DB_CombatCharacters(_Victim, _) + THEN + NOT DB_Crime_PlayerAttacked(_CrimeID,_Player,_Victim); + + PROC + ProcObjectTimerFinished(_Victim,_TimerName) + AND + DB_AttackTimer(_CrimeID,_TimerName) + THEN + NOT DB_AttackTimer(_CrimeID,_TimerName); + + IF + CrimeInterrogationRequest(_RegionID,_CrimeID,_Interrogator,_Criminal1,_Criminal2,_Criminal3,_Criminal4,_Dialog) + THEN + ProcClearAssaultDBForCrime(_CrimeID); + + IF + OnCrimeMergedWith(_Old,_New) + AND + DB_Crime_Assault(_Old,_Criminal1,_Victim) + THEN + NOT DB_Crime_Assault(_Old,_Criminal1,_Victim); + DB_Crime_Assault(_New,_Criminal1,_Victim); + + //if our vicitim is not selected as lead (incapacitated), don't track this DB because that will mean he won't be able to react anymore + //update it with the new lead + IF + CharacterOnCrimeSensibleActionNotification(_NPC,_,_CrimeID,_,_,_Criminal1,_,_,_,1) + AND + DB_Crime_Assault(_CrimeID,_Criminal1,_Victim) + THEN + NOT DB_Crime_Assault(_CrimeID,_Criminal1,_Victim); + DB_Crime_Assault(_CrimeID,_Criminal1,_NPC); + + //END_REGION + + //REGION General Assault crime -- verifies if the assailant is a summon or not and registers the Assault or AttackAnimal crime + + QRY + QryIgnoreDamageSource((STRING)_Dmg) + AND + DB_IgnoreDamageSources(_Dmg) + THEN + DB_NOOP(1); + + IF + AttackedByObject((CHARACTERGUID)_NPC,(CHARACTERGUID)_Player,(CHARACTERGUID)_Summon,_,_DamageSource) + AND + NOT DB_IgnoreAssault(_NPC) + AND + NOT DB_Crime_PolymorphedIgnoreAssault(_NPC,_Player) + AND + NOT DB_CombatCharacters(_NPC,_) + AND + NOT DB_Crime_Assault(_,_,_NPC) + AND + CharacterIsPlayer(_Player,1) + AND + NOT QryIgnoreDamageSource(_DamageSource) + AND + CharacterIsDeadOrFeign(_Player,0) + AND + ObjectIsCharacter(_NPC,1) + AND + CharacterIsPlayer(_NPC,0) + AND + CharacterIsDead(_NPC,0) + AND + IsTagged(_NPC,"GHOST",0) + AND + IsTagged(_NPC,"SUMMON",0) + THEN + ProcCrimeCheckAssailant(_NPC,_Player,_Summon); + + + //--- Assailant is not a summon + PROC + ProcCrimeCheckAssailant((CHARACTERGUID)_NPC,(CHARACTERGUID)_Player,(CHARACTERGUID)_Summon) + AND + NOT QRY_CharacterIsNull(_Summon) + AND + IsTagged(_Summon,"SUMMON",1) + AND + NOT DB_IgnoreAssaultFor(_Summon,_NPC) + AND + NOT DB_Crime_Assault(_,_Summon,_) + AND + CharacterIsEnemy(_NPC,_Summon,0) + THEN + ProcCrimeRegisterAssault(_Summon,_NPC,1); + + //--- Assailant is a summon + PROC + ProcCrimeCheckAssailant((CHARACTERGUID)_NPC,(CHARACTERGUID)_Player,(CHARACTERGUID)_Summon) + AND + QryCrimeAssailantIsNotSummon(_Player,_Summon) + AND + NOT DB_IgnoreAssaultFor(_Player,_NPC) + AND + NOT DB_Crime_Assault(_,_Player,_) + AND + CharacterIsEnemy(_NPC,_Player,0) + THEN + ProcCrimeRegisterAssault(_Player,_NPC,0); + + //--- Split the type of assault + // Normal + PROC + ProcCrimeRegisterAssault((CHARACTERGUID)_Assailant,(CHARACTERGUID)_NPC,0) //_AssailantIsSummon + AND + IsTagged(_NPC,"ANIMAL",0) + THEN + ProcCrimeRegisterAssaultType(_Assailant,_NPC,"Assault"); + + PROC + ProcCrimeRegisterAssault((CHARACTERGUID)_Assailant,(CHARACTERGUID)_NPC,0) + AND + IsTagged(_NPC,"ANIMAL",1) + THEN + ProcCrimeRegisterAssaultType(_Assailant,_NPC,"AttackAnimal"); + + // Summon + PROC + ProcCrimeRegisterAssault((CHARACTERGUID)_Assailant,(CHARACTERGUID)_NPC,1) + AND + IsTagged(_NPC,"ANIMAL",0) + THEN + ProcCrimeRegisterAssaultType(_Assailant,_NPC,"SummonAssault"); + + PROC + ProcCrimeRegisterAssault((CHARACTERGUID)_Assailant,(CHARACTERGUID)_NPC,1) + AND + IsTagged(_NPC,"ANIMAL",1) + THEN + ProcCrimeRegisterAssaultType(_Assailant,_NPC,"SummonAttackAnimal"); + + //REGION Assault investigation location determination + // Default: victim location + // Problem: if attacked from out of sight range, they won't be able to find the attacker + // Solution: after a while, report the assailant's rather than the victim's location as investigation location + // If assaulted even more: force combat + // + // Reasoning: the more an NPC has been attacked, the more they'll pay attention + // regarding where the attack came from. Factors influencing them homing in on + // the source: sneaking, invisibility, wits difference between victim and assailant + + QRY + QRY_CRIMES_AssaultCrimeGetCrimeLocationScoreIncrease((CHARACTERGUID)_Assailant,(CHARACTERGUID)_Victim) + AND + DB_CRIME_AssaultCrimeGetCrimeLocationScoreIncrease(_Total) + THEN + NOT DB_CRIME_AssaultCrimeGetCrimeLocationScoreIncrease(_Total); + + QRY + QRY_CRIMES_AssaultCrimeGetCrimeLocationScoreIncrease((CHARACTERGUID)_Assailant,(CHARACTERGUID)_Victim) + AND + HasActiveStatus(_Assailant,"SNEAKING",_IsSneaking) + AND + HasActiveStatus(_Assailant,"INVISIBLE",_IsInvisible) + AND + CharacterGetAttribute(_Assailant,"WITS",_AssailantWits) + AND + CharacterGetAttribute(_Assailant,"WITS",_VictimWits) + AND + IntegerSubtract(_VictimWits,_AssailantWits,_WitsRes) + AND + QRY_IntegerSign(_WitsRes) + AND + DB_IntegerSign(_WitsBonus) + AND + DB_CRIME_CrimeLocationScoreIncrease(_IsSneaking,_IsInvisible,_Base,_Rand) + AND + Random(_Rand,_Extra) + AND + IntegerSum(_Base,_Extra,_BaseTotal) + AND + IntegerSum(_BaseTotal,_WitsBonus,_Total) + THEN + DB_CRIME_AssaultCrimeGetCrimeLocationScoreIncrease(_Total); + + QRY + QRY_CRIME_AssaultCrimeGetInvestigationPos((CHARACTERGUID)_Assailant,(CHARACTERGUID)_Victim) + AND + DB_CRIME_AssaultCrimeInvestigationPos(_x,_yUp,_z) + THEN + NOT DB_CRIME_AssaultCrimeInvestigationPos(_x,_yUp,_z); + + QRY + QRY_CRIME_AssaultCrimeGetInvestigationPos((CHARACTERGUID)_Assailant,(CHARACTERGUID)_Victim) + THEN + Proc_CharCountHelper(_Victim,"CRIME_CrimeTriggers_AssaultCrimeLocation"); + + // Update assault score + QRY + QRY_CRIME_AssaultCrimeGetInvestigationPos((CHARACTERGUID)_Assailant,(CHARACTERGUID)_Victim) + AND + QRY_CRIMES_AssaultCrimeGetCrimeLocationScoreIncrease(_Assailant,_Victim) + AND + DB_CRIME_AssaultCrimeGetCrimeLocationScoreIncrease(_Increase) + THEN + Proc_CharCountHelper(_Victim,"CRIME_CrimeTriggers_AssaultCrimeLocation",_Increase); + + // Assaulted many times -> investigate location of assailant + QRY + QRY_CRIME_AssaultCrimeGetInvestigationPos((CHARACTERGUID)_Assailant,(CHARACTERGUID)_Victim) + AND + DB_CharCountHelper(_Victim,"CRIME_CrimeTriggers_AssaultCrimeLocation",_Count) + AND + DB_CRIME_CrimeLocationScore_GoToAttackerThreshold(_Threshold) + AND + _Count >= _Threshold + AND + GetPosition(_Assailant,_x,_y,_z) + AND + RealSum(_y,1.0,_yUp) + THEN + DB_CRIME_AssaultCrimeInvestigationPos(_x,_yUp,_z); + + // Fallback if not yet assaulted many times + QRY + QRY_CRIME_AssaultCrimeGetInvestigationPos((CHARACTERGUID)_Assailant,(CHARACTERGUID)_Victim) + AND + NOT DB_CRIME_AssaultCrimeInvestigationPos(_,_,_) + AND + GetPosition(_Victim,_x,_y,_z) + AND + RealSum(_y,1.0,_yUp) + THEN + DB_CRIME_AssaultCrimeInvestigationPos(_x,_yUp,_z); + + // Clean up when dying + IF + CharacterDied(_Victim) + AND + DB_CharCountHelper(_Victim,"CRIME_CrimeTriggers_AssaultCrimeLocation",_Count) + THEN + NOT DB_CharCountHelper(_Victim,"CRIME_CrimeTriggers_AssaultCrimeLocation",_Count); + + //END_REGION + + //REGION Assaulted really a lot or down on health -> start combat + // Allows for at least three assaults -> don't cut off warning system + PROC + ProcCrimeRegisterAssaultType((CHARACTERGUID)_Assailant,(CHARACTERGUID)_Victim,(STRING)_AssaultType) + AND + CharacterIsCrimeEnabled(_Victim,_AssaultType,1) + AND + QRY_CRIME_AssaultCrimeGetInvestigationPos(_Assailant,_Victim) + AND + DB_CharCountHelper(_Victim,"CRIME_CrimeTriggers_AssaultCrimeLocation",_Count) + AND + DB_CRIME_CrimeLocationScore_StartAttackingThreshold(_Threshold) + AND + _Count >= _Threshold + AND + CharacterCanFight(_Victim,1) + THEN + DB_CrimeRegisterAssaultType_Handled(1); + ProcTryStartAttackAD(_Victim); + Proc_CharacterSetTemporaryHostileRelation(_Victim,_Assailant); + EnterCombat(_Victim,_Assailant); + + PROC + ProcCrimeRegisterAssaultType((CHARACTERGUID)_Assailant,(CHARACTERGUID)_Victim,(STRING)_AssaultType) + AND + CharacterIsCrimeEnabled(_Victim,_AssaultType,1) + AND + CharacterGetHitpointsPercentage(_Victim,_Percentage) + AND + DB_CRIME_Assault_HealthStartAttackingThreshold(_Threshold) + AND + _Percentage < _Threshold + AND + CharacterCanFight(_Victim,1) + AND + CharacterIsDead(_Victim,0) + THEN + DB_CrimeRegisterAssaultType_Handled(1); + ProcTryStartAttackAD(_Victim); + Proc_CharacterSetTemporaryHostileRelation(_Victim,_Assailant); + EnterCombat(_Victim,_Assailant); + //END_REGION + + PROC + ProcCrimeRegisterAssaultType((CHARACTERGUID)_Assailant,(CHARACTERGUID)_Victim,(STRING)_AssaultType) + AND + NOT DB_CrimeRegisterAssaultType_Handled(1) + AND + GetPosition(_Victim,_x,_y,_z) + AND + RealSum(_y,1.0,_yUp) + AND + CrimeIsAnyNPCGoingToReact(_Assailant,_AssaultType,_x,_yUp,_z,_WillReact) + AND + CrimeGetNewID(_CrimeID) + AND + DB_CRIME_AssaultCrimeInvestigationPos(_XInv,_YInv,_ZInv) + THEN + NOT DB_CRIME_AssaultCrimeInvestigationPos(_XInv,_YInv,_ZInv); + DB_CRIME_CrimeInvestigationPos(_CrimeID,_XInv,_YInv,_ZInv); + DB_Crime_Assault(_CrimeID,_Assailant,_Victim); + ProcCheckRegisterAssault(_Assailant,_Victim,_AssaultType,_x,_yUp,_z,_CrimeID,_WillReact); + + PROC + ProcCrimeRegisterAssaultType((CHARACTERGUID)_Assailant,(CHARACTERGUID)_Victim,(STRING)_AssaultType) + AND + DB_CrimeRegisterAssaultType_Handled(1) + THEN + NOT DB_CrimeRegisterAssaultType_Handled(1); + + PROC + ProcClearAssaultDBForCrime((INTEGER)_CrimeID) + AND + DB_CRIME_CrimeInvestigationPos(_CrimeID,_XInv,_YInv,_ZInv) + THEN + NOT DB_CRIME_CrimeInvestigationPos(_CrimeID,_XInv,_YInv,_ZInv); + + PROC + ProcCheckRegisterAssault((CHARACTERGUID)_Assailant,(CHARACTERGUID)_Victim,(STRING)_AssaultType,(REAL)_X,(REAL)_Y,(REAL)_Z,(INTEGER)_CrimeID,1) + THEN + ProcCharacterRegisterCrimeWithPosition(_Assailant,_AssaultType,NULL_00000000-0000-0000-0000-000000000000,_Victim,_X,_Y,_Z,_CrimeID,_Victim); + + //noone reacted to this one, so assume the victim was incapacitated + PROC + ProcCheckRegisterAssault((CHARACTERGUID)_Assailant,(CHARACTERGUID)_Victim,(STRING)_AssaultType,(REAL)_X,(REAL)_Y,(REAL)_Z,(INTEGER)_CrimeID,0) + THEN + ProcCharacterRegisterCrimeWithPosition(_Assailant,"IncapacitatedAssault",NULL_00000000-0000-0000-0000-000000000000,_Victim,_X,_Y,_Z,_CrimeID,_Victim); + + //END_REGION + + //REGION Clear PolymorphedIgnoreAssault fact + + IF + AttackedByObject((CHARACTERGUID)_NPC,(CHARACTERGUID)_Player,(CHARACTERGUID)_Summon,_,_) + AND + DB_Crime_PolymorphedIgnoreAssault(_NPC,_Player) + AND + GetPosition(_NPC,_x,_y,_z) + AND + RealSum(_y,1.0,_yUp) + AND + CrimeGetNewID(_CrimeID) + THEN + NOT DB_Crime_PolymorphedIgnoreAssault(_NPC,_Player); + CharacterRegisterCrimeWithPosition(_Player,"Polymorphed",NULL_00000000-0000-0000-0000-000000000000,_NPC,_x,_yUp,_z,_CrimeID); + + //END_REGION + + //REGION Teleportation Netherswap + IF + CharacterUsedSkillOnTarget(_Player,(CHARACTERGUID)_Npc,"Teleportation_Netherswap",_) + AND + NOT Qry_AreInCombat(_Player,_NPC) + AND + CharacterIsPlayer(_Player,1) + AND + ObjectIsCharacter(_Npc,1) + AND + CharacterIsDeadOrFeign(_Player,0) + AND + CharacterIsPlayer(_NPC,0) + AND + CharacterIsDead(_NPC,0) + THEN + ProcDoCrimeCheckAssailant(_Player,_NPC); + + PROC + ProcDoCrimeCheckAssailant((CHARACTERGUID)_Summon,(CHARACTERGUID)_NPC) + AND + CharacterIsSummon(_Summon,1) + AND + CharacterGetOwner(_Summon,_Player) + THEN + ProcCrimeCheckAssailant(_NPC,_Player,_Summon); + + PROC + ProcDoCrimeCheckAssailant((CHARACTERGUID)_Player,(CHARACTERGUID)_NPC) + AND + CharacterIsSummon(_Player,0) + THEN + ProcCrimeCheckAssailant(_NPC,_Player,_Player); + + //END_REGION + + //----------------------------------- /ASSAULT ----------------------------------- + + //REGION GEB_FleeOutOfSight + IF + AutomatedDialogEnded("GEB_AD_CallForHelp",_ID) // For kids looking for help + AND + DB_DialogNPCs(_ID,_NPC,1) + AND + IsTagged(_NPC,"KID",1) + AND + ObjectGetFlag(_NPC,"GEB_FleeOutOfSight",0) + THEN + ObjectSetFlag(_NPC,"GEB_FleeOutOfSight"); + + IF + ObjectFlagSet("GEB_FleeOutOfSight",(CHARACTERGUID)_NPC,_) + THEN + SetHasDialog(_NPC,0); + ProcCharacterDisappearOutOfSight((CHARACTERGUID)_NPC,0,1,"GEB_NPCFledOutOfSight",1); + Proc_StartDialog(1,"GEB_AD_CallForHelp",_NPC); + ProcForceStopDialog(_NPC); + + IF + ObjectFlagSet("GEB_FleeOutOfSight",_NPC,_) + AND + NOT DB_GEB_FledOutOfSight(_NPC,_,_,_) + AND + GetPosition(_NPC,_X,_Y,_Z) + THEN + DB_GEB_FledOutOfSight(_NPC,_X,_Y,_Z); + + IF + ObjectFlagSet("GEB_FleeOutOfSight",(CHARACTERGUID)_NPC,_) + AND + DB_Crime_Assault(_CrimeID,_Player,_NPC) + THEN + NOT DB_Crime_Assault(_CrimeID,_Player,_NPC); + + IF + StoryEvent(_NPC,"GEB_NPCFledOutOfSight") + AND + ObjectGetFlag(_NPC,"GEB_DontAppearAfter",0) + THEN + ProcObjectTimer(_NPC,"GEB_AppearNPCOutOfSight",15000); + + IF + ObjectFlagSet("GEB_DontAppearAfter",_Char,_) + THEN + ProcObjectTimerCancel(_Char,"GEB_AppearNPCOutOfSight"); + + IF + ObjectFlagSet("GEB_DontAppearAfter",(CHARACTERGUID)_Char,_) + AND + DB_GEB_NPCAppearAfterCombat(_Char,_CombatID) + THEN + NOT DB_GEB_NPCAppearAfterCombat(_Char,_CombatID); + + //--- Start trying to reappear + PROC + ProcObjectTimerCancel((CHARACTERGUID)_NPC,"GEB_AppearNPCOutOfSight") + AND + DB_GEB_FledOutOfSight(_NPC,_X,_Y,_Z) + THEN + NOT DB_GEB_FledOutOfSight(_NPC,_X,_Y,_Z); + + PROC + ProcObjectTimerFinished((CHARACTERGUID)_NPC,"GEB_AppearNPCOutOfSight") + AND + DB_GEB_FledOutOfSight(_NPC,_X,_Y,_Z) + AND + GetClosestPlayerToPosition(_X,_Y,_Z,_Player,_Dist) + THEN + ProcCrimeAppearOutOfSightChecks(_NPC,_Player,_Dist); + + //--- Check the distance and if the player is dead, in dialog or in combat + PROC + ProcCrimeAppearOutOfSightChecks((CHARACTERGUID)_NPC,(CHARACTERGUID)_Player,(REAL)_Dist) + AND + _Dist > 20.0 + THEN + ProcAppearOutOfSightAfterFleeing(_NPC); + + PROC + ProcCrimeAppearOutOfSightChecks((CHARACTERGUID)_NPC,(CHARACTERGUID)_Player,(REAL)_Dist) + AND + _Dist <= 20.0 + AND + Query_CharacterIsAliveAndNotInCombat(_Player) + AND + IsSpeakerReserved(_Player,0) + THEN + ProcAppearOutOfSightAfterFleeing(_NPC); + + PROC + ProcCrimeAppearOutOfSightChecks((CHARACTERGUID)_NPC,(CHARACTERGUID)_Player,(REAL)_Dist) + AND + _Dist <= 20.0 + AND + DB_CombatCharacters(_Player,_CombatID) + THEN + DB_GEB_NPCAppearAfterCombat(_NPC,_CombatID); + + PROC + ProcCrimeAppearOutOfSightChecks((CHARACTERGUID)_NPC,(CHARACTERGUID)_Player,(REAL)_Dist) + AND + _Dist <= 20.0 + AND + CharacterIsDeadOrFeign(_Player,1) + THEN + ProcAppearOutOfSight_DeadClosestPlayer(_NPC,_Player); + + PROC + ProcCrimeAppearOutOfSightChecks((CHARACTERGUID)_NPC,(CHARACTERGUID)_Player,(REAL)_Dist) + AND + NOT DB_GEB_NPCAppearAfterCombat(_NPC,_) + AND + _Dist <= 20.0 + AND + IsSpeakerReserved(_Player,1) + AND + DB_DialogPlayers(_Inst,_Player,1) + THEN + DB_GEB_NPCAppearAfterDialog(_NPC,_Inst); + + // Try to find a live player + PROC + ProcAppearOutOfSight_DeadClosestPlayer((CHARACTERGUID)_NPC,(CHARACTERGUID)_Player) + AND + GetClosestAlivePlayer(_Player,_AlivePlayer,_) + THEN + DB_GEB_AppearOutOfSight_AlivePlayerFound(_NPC,_AlivePlayer); + + PROC + ProcAppearOutOfSight_DeadClosestPlayer((CHARACTERGUID)_NPC,(CHARACTERGUID)_Player) + AND + NOT DB_GEB_AppearOutOfSight_AlivePlayerFound(_NPC,_) + THEN + ProcAppearOutOfSightAfterFleeing(_NPC); + + PROC + ProcAppearOutOfSight_DeadClosestPlayer((CHARACTERGUID)_NPC,(CHARACTERGUID)_) + AND + DB_GEB_AppearOutOfSight_AlivePlayerFound(_NPC,_AlivePlayer) + AND + DB_GEB_FledOutOfSight(_NPC,_X,_Y,_Z) + AND + GetDistanceToPosition(_AlivePlayer,_X,_Y,_Z,_Dist) + THEN + NOT DB_GEB_AppearOutOfSight_AlivePlayerFound(_NPC,_AlivePlayer); + ProcCrimeAppearOutOfSightChecks(_NPC,(CHARACTERGUID)_AlivePlayer,_Dist); + + //--- Recheck after dialog & combat + IF + DialogEnded(_,_Inst) + AND + DB_GEB_NPCAppearAfterDialog(_NPC,_Inst) + THEN + NOT DB_GEB_NPCAppearAfterDialog(_NPC,_Inst); + ProcObjectTimer(_NPC,"GEB_AppearNPCOutOfSight",3000); // restart from the beginning (delay in case combat starts after the dialog) + + IF + CombatEnded(_CombatID) + AND + DB_GEB_NPCAppearAfterCombat(_NPC,_CombatID) + THEN + NOT DB_GEB_NPCAppearAfterCombat(_NPC,_CombatID); + ProcObjectTimer(_NPC,"GEB_AppearNPCOutOfSight",3000); // restart the checks from the beginning + + //--- Appear + PROC + ProcAppearOutOfSightAfterFleeing((CHARACTERGUID)_NPC) + AND + DB_GEB_FledOutOfSight(_NPC,_X,_Y,_Z) + THEN + NOT DB_GEB_FledOutOfSight(_NPC,_X,_Y,_Z); + SetHasDialog(_NPC,1); + CharacterAppearAtPositionOutOfSightTo((CHARACTERGUID)_NPC,_X,_Y,_Z,0,0,"GEB_NPCAppeared"); + ObjectClearFlag(_NPC,"GEB_FleeOutOfSight",0); + CharacterSetHitpointsPercentage(_NPC,100); + + //END_REGION + + //REGION Source Magic + + IF + CharacterUsedSkill(_Character,_Skill,_) + AND + DB_IsPlayer(_Character) + AND + IsSourceSkill(_Skill,1) + THEN + CharacterRegisterCrime(_Character,"SourceMagic",NULL_00000000-0000-0000-0000-000000000000,NULL_00000000-0000-0000-0000-000000000000,0); + + //END_REGION + + //REGION Stealing + IF + CharacterStoleItem(_Character,_Item,_X,_Y,_Z,_Victim,_SrcContainer,_) + AND + CharacterIsPlayer(_Character,1) + AND + _SrcContainer==NULL_00000000-0000-0000-0000-000000000000 + THEN + //TODO: do not register Steal for stelaing from a container? IE: the problem with doing this is, that NPCs will detect missign items from a barrel without looking into it + CharacterRegisterCrimeWithPosition(_Character,"Steal",_Item,_Victim,_X,_Y,_Z,0); + + //END_REGION + + //REGION Murder (animals and non-animals) + + //REGION Helpers + // A killed character may get removed from a combat before the CharacterKilledByCharacter event gets triggered + // -> also check DB_WasInCombat for the _Victim + QRY + QryCrimeKillerVictimWereInSameCombat((CHARACTERGUID)_Killer,(CHARACTERGUID)_Victim) + AND + DB_CombatCharacters(_Killer,_ID) + AND + DB_WasInCombat(_Victim,_ID) + THEN + DB_NOOP(1); + + QRY + QryCrimeKillerVictimWereInSameCombat((CHARACTERGUID)_Killer,(CHARACTERGUID)_Victim) + AND + DB_CombatCharacters(_Killer,_ID) + AND + DB_CombatCharacters(_Victim,_ID) + THEN + DB_NOOP(1); + + // Killing a totem or a summon is not murder + QRY + QryCrimeCharacterCanCreateMurder((CHARACTERGUID)_Char) + AND + IsTagged(_Char,"SUMMON",0) + AND + IsTagged(_Char,"TOTEM",0) + THEN + DB_NOOP(1); + //END_REGION + + //REGION Determine crime type + // By default, murdering an animal results in "KilledAnimal" and murdering anyone + // else in "Murder". Can override QryCrimeMurderGetCrimeTypeCustom() to return custom results. + // + // Note: QryCrimeMurderGetCrimeTypeCustom() can be called multiple times for the same murder + // (-> should not keep state, e.g. don't assume second call is for second murder) + + // Define signature for custom query + QRY + QryCrimeMurderGetCrimeTypeCustom((CHARACTERGUID)_Killer,(CHARACTERGUID)_Victim) + AND + DB_CrimeNeverEverSet__INVALID(1) + THEN + DB_NOOP(1); + + // Reset previous result, if any + QRY + QryCrimeMurderGetCrimeType((CHARACTERGUID)_Killer,(CHARACTERGUID)_Victim) + AND + DB_CrimeMurderCrimeType(_Type) + THEN + NOT DB_CrimeMurderCrimeType(_Type); + + // Query custom overrides + QRY + QryCrimeMurderGetCrimeType((CHARACTERGUID)_Killer,(CHARACTERGUID)_Victim) + AND + QryCrimeMurderGetCrimeTypeCustom(_Killer,_Victim) + THEN + DB_NOOP(1); + + // Default fallbacks + QRY + QryCrimeMurderGetCrimeType((CHARACTERGUID)_Killer,(CHARACTERGUID)_Victim) + AND + NOT DB_CrimeMurderCrimeType(_) + AND + IsTagged(_Victim,"ANIMAL",_Animal) + AND + HasActiveStatus(_Killer,"SNEAKING",_Sneaking) + AND + CharacterIsSummon(_Killer,_Summon) + AND + DB_CRIME_MurderType(_Sneaking,_Animal,_Summon,_MurderType) + THEN + DB_CrimeMurderCrimeType(_MurderType); + //END_REGION + + //REGION Witnesses that should ignore this murder crime + // Only have to do something before registering the crime if + // the witness does not ignore crimes like this in general + PROC + ProcCrimeMurderDefineSilentWitness((INTEGER)_CrimeID,(CHARACTERGUID)_Witness,(CHARACTERGUID)_Killer,(CHARACTERGUID)_Victim) + AND + QryCrimeMurderGetCrimeType(_Killer,_Victim) + AND + DB_CrimeMurderCrimeType(_CrimeType) + AND + NOT DB_CharacterCrimeDisabled(_Witness,_CrimeType) + THEN + DB_CrimeSilentWitness(_CrimeID,_CrimeType,_Witness); + // Don't set it to ignore this crime yet, as another crime (murder) + // could occur between now and when this murder is registered due to + // the use of an iterator) -- that will be done by ProcCrimeCreateMurder) + + // Convert DB_CrimeSilentWitness() to DB_CRIME_CrimeTriggers_SilentWitnessesForCrime(), as used by ProcCharacterRegisterCrimeWithPosition() + PROC + PROC_CRIME_CrimeTriggers_GetSilentWitnessesForCrime((INTEGER)_CrimeID,(CHARACTERGUID)_Perpetrator,(STRING)_CrimeType,(GUIDSTRING)_Evidence,(CHARACTERGUID)_Victim) + AND + DB_CrimeSilentWitness(_CrimeID,_CrimeType,_Witness) + THEN + NOT DB_CrimeSilentWitness(_CrimeID,_CrimeType,_Witness); + DB_CRIME_CrimeTriggers_SilentWitnessesForCrime(_Witness); + + // NPCs that should ignore the murder of this victim should be treated as silent witnesses + PROC + PROC_CRIME_CrimeTriggers_GetSilentWitnessesForCrime((INTEGER)_CrimeID,(CHARACTERGUID)_Perpetrator,(STRING)_CrimeType,(GUIDSTRING)_Evidence,(CHARACTERGUID)_Victim) + AND + DB_MurderIgnoreFor((CHARACTERGUID)_Witness,(CHARACTERGUID)_Victim) + THEN + DB_CRIME_CrimeTriggers_SilentWitnessesForCrime(_Witness); + //END_REGION + + //REGION Register the murder crime + PROC + ProcCrimeCreateMurder((CHARACTERGUID)_Killer,(CHARACTERGUID)_Victim,(INTEGER)_CrimeID) + AND + QryCrimeMurderGetCrimeType(_Killer,_Victim) + AND + DB_CrimeMurderCrimeType(_CrimeType) + AND + GetPosition(_Victim,_x,_y,_z) + AND + RealSum(_y,1.0,_yUp) + THEN + ProcCharacterRegisterCrimeWithPosition(_Killer,_CrimeType,NULL_00000000-0000-0000-0000-000000000000,NULL_00000000-0000-0000-0000-000000000000,_x,_yUp,_z,_CrimeID,_Victim); + //END_REGION + + //REGION 1) Killed a non-enemy NPC outside of combat (one-shot, so combat did not have a chance to start) + IF + CharacterKilledBy(_Defender,_AttackerOwner,_Attacker) + AND + NOT DB_IsPlayer(_Defender) + AND + CharacterIsPlayer(_Attacker,1) + AND + CharacterIsPartyFollower(_Attacker,0) + AND + CharacterIsEnemy(_Defender,_Attacker,0) + AND + NOT DB_DontCreateMurder(_Defender) + AND + NOT QryCrimeKillerVictimWereInSameCombat(_Attacker,_Defender) + AND + QryCrimeCharacterCanCreateMurder(_Defender) + AND + CrimeGetNewID(_CrimeID) + THEN + DebugText(_Defender,"OneShot Murder Crime Scene"); + DB_CrimeMurderCreated(_Defender,_Attacker); + ProcCrimeCreateMurder(_Attacker,_Defender,_CrimeID); + //END_REGION + + //REGION 2) Killed an NPC while it was investigating + IF + CharacterKilledBy(_Defender,_AttackerOwner,_Attacker) + AND + NOT DB_CrimeMurderCreated(_Defender,_Attacker) + AND + NOT DB_IsPlayer(_Defender) + AND + NOT DB_DontCreateMurder(_Defender) + AND + DB_Crime_Assault(_,_,_Defender) // This DB Will be filled in if the character is still investigating + AND + QryCrimeCharacterCanCreateMurder(_Defender) + AND + CharacterIsPlayer(_Attacker,1) + AND + CharacterIsPartyFollower(_Attacker,0) + AND + CrimeGetNewID(_CrimeID) + THEN + DebugText(_Defender,"Mid Investigate Murder Crime Scene"); + ProcCrimeCreateMurder(_Attacker,_Defender,_CrimeID); + //END_REGION + + //REGION 3) Killed an NPC in a combat following an assault crime + // Current logic: + // - Create a murder for any NPC that you kill in this combat + // - This murder is, however, ignored by anyone within a 25 metre range that was not in combat + // Reasoning: the characters that joined in the combat due to the assault, presumably would + // have done the same for murder. The ones that did not, might not join for murder either. They also + // might, but we don't have a good way to determine this. Creating a murder in this case can easily + // lead to an avalache where a whole city starts fighting a player, which is not good -> be very + // restrictive about who will care about such a murder + // + // Don't check for CharacterIsEnemy(_NPC,0), because when someone attacks you after you assaulted them, + // they will obviously be an enemty to you. + IF + CharacterKilledBy(_Defender,_AttackerOwner,_Attacker) + AND + NOT DB_CrimeMurderCreated(_Defender,_Attacker) + AND + // This DB is set after an assault crime, and also for other + // characters that join a combat in which an assault victim is involved + DB_Crime_PlayerAttacked(_,_,_Defender) + AND + DB_CombatCharacters(_Defender,_ID) + AND + NOT DB_IsPlayer(_Defender) + AND + NOT DB_DontCreateMurder(_Defender) + AND + CharacterIsPlayer(_Attacker,1) + AND + CharacterIsPartyFollower(_Attacker,0) + AND + QryCrimeCharacterCanCreateMurder(_Defender) + AND + CrimeGetNewID(_CrimeID) + AND + IntegertoString(_CrimeID,_Iterator) + AND + StringConcatenate(_Iterator,"GEB_IgnoreCrime",_IteratorID) + THEN + DebugText(_Defender,"Assault murder scene"); + DB_CrimeMurderSilentWitnessCollectionContext(_CrimeID,_Defender,_IteratorID,_Attacker); + CharacterLaunchIteratorAroundCharacter(_Defender,25.0,_IteratorID); + + PROC + ProcCrimeMurderMaybeMakeSilentWitness((INTEGER)_CrimeID,(CHARACTERGUID)_Witness,(CHARACTERGUID)_Killer,(CHARACTERGUID)_Victim) + AND + NOT DB_CombatCharacters(_Witness,_) + THEN + ProcCrimeMurderDefineSilentWitness(_CrimeID,_Witness,_Killer,_Victim); + + // Allies let allies commit murders! That's what real friendship is all about. + PROC + ProcCrimeMurderMaybeMakeSilentWitness((INTEGER)_CrimeID,(CHARACTERGUID)_Witness,(CHARACTERGUID)_Killer,(CHARACTERGUID)_Victim) + AND + DB_CombatCharacters(_Witness,_) + AND + CharacterIsAlly(_Witness,_Killer,1) + THEN + ProcCrimeMurderDefineSilentWitness(_CrimeID,_Witness,_Killer,_Victim); + + // An ally of my ally is my friend (e.g. Butter in Fort Joy is only allied to the player that charmed her). + PROC + ProcCrimeMurderMaybeMakeSilentWitness((INTEGER)_CrimeID,(CHARACTERGUID)_Witness,(CHARACTERGUID)_Killer,(CHARACTERGUID)_Victim) + AND + DB_CombatCharacters(_Witness,_) + AND + DB_IsPlayer(_Player) + AND + CharacterIsAlly(_Witness,_Player,1) + AND + CharacterIsAlly(_Player,_Killer,1) + THEN + ProcCrimeMurderDefineSilentWitness(_CrimeID,_Witness,_Killer,_Victim); + + // Ignore murder seen in combat + IF + StoryEvent((CHARACTERGUID)_NPC,_IteratorID) + AND + DB_CrimeMurderSilentWitnessCollectionContext(_CrimeID,_DeadMan,_IteratorID,_Killer) + AND + _NPC != NULL_00000000-0000-0000-0000-000000000000 + AND + NOT DB_dead((CHARACTERGUID)_NPC) + THEN + ProcCrimeMurderMaybeMakeSilentWitness(_CrimeID,_NPC,_Killer,_DeadMan); + ProcCrimeMurderMaybeMakeSilentWitness(_CrimeID,_NPC,_Killer,_DeadMan); + + // Create Murder (Last NPC in the Iterator is NULL_00000000-0000-0000-0000-000000000000) + IF + StoryEvent(_NPC,_IteratorID) + AND + DB_CrimeMurderSilentWitnessCollectionContext(_CrimeID,_DeadMan,_IteratorID,_Killer) + AND + _NPC == NULL_00000000-0000-0000-0000-000000000000 + THEN + NOT DB_CrimeMurderSilentWitnessCollectionContext(_CrimeID,_DeadMan,_IteratorID,_Killer); + DebugText(_DeadMan,"Normal Murder Crime Scene"); + ProcCrimeCreateMurder(_Killer,_DeadMan,_CrimeID); + + QRY + Qry_HasDontCreateMurderInCombat((INTEGER)_ID) + AND + DB_CombatCharacters(_NPCS,_ID) + AND + DB_DontCreateMurder((CHARACTERGUID)_NPCS) + THEN + DB_NOOP(1); + + // One DB_DontCreateMurder() NPC in a combat -> killing others that join the combat should not create murders either + // (TODO: check that they're allies of one of the DB_DontCreateMurder() NPCs -- especially now that neutrals don't + // join random combats anymore) + IF + ObjectEnteredCombat(_Obj,_ID) + AND + ObjectIsCharacter(_Obj,1) + AND + Qry_HasDontCreateMurderInCombat(_ID) + AND + DB_CombatCharacters(_NPC,_ID) + AND + NOT DB_IsPlayer(_NPC) + AND + NOT DB_DontCreateMurder(_NPC) + THEN + DB_DontCreateMurder(_NPC); + DB_CrimeCreateMurderAfterCombat(_NPC,_ID); + DebugText(_NPC," Killing me won't create a murder in this combat, because I joined a combat with others whose murder gets ignored"); + + IF + ObjectSwitchedCombat((CHARACTERGUID)_NPC,_OldCombatID,_NewCombatID) + AND + DB_CrimeCreateMurderAfterCombat(_NPC,_OldCombatID) + THEN + NOT DB_CrimeCreateMurderAfterCombat(_NPC,_OldCombatID); + DB_CrimeCreateMurderAfterCombat(_NPC,_NewCombatID); + + IF + CombatEnded(_CombatID) + AND + DB_CrimeCreateMurderAfterCombat(_NPC,_CombatID) + THEN + NOT DB_CrimeCreateMurderAfterCombat(_NPC,_CombatID); + NOT DB_DontCreateMurder(_NPC); + + IF + ObjectEnteredCombat(_NPC,_) + AND + ObjectIsCharacter(_NPC,1) + THEN + ProcCrimeAddToAttackedDBIfAssaultCombat((CHARACTERGUID)_NPC); + + IF + ObjectSwitchedCombat(_NPC,_,_) + AND + ObjectIsCharacter(_NPC,1) + THEN + ProcCrimeAddToAttackedDBIfAssaultCombat((CHARACTERGUID)_NPC); + + IF + DB_Crime_PlayerAttacked(_CrimeID,_Player,_NPC) + THEN + ProcCrimeAddToAttackedDBIfAssaultCombat((CHARACTERGUID)_NPC); + + PROC + ProcCrimeAddToAttackedDBIfAssaultCombat((CHARACTERGUID)_NPC) + AND + DB_CombatCharacters(_NPC,_ID) + AND + DB_Crime_PlayerAttacked(_CrimeID,_Player,_OrigVic) + AND + DB_CombatCharacters(_Player,_ID) + AND + DB_CombatCharacters(_NPC2,_ID) + AND + NOT DB_Crime_PlayerAttacked(_CrimeID,_Player,_NPC2) + AND + CharacterIsPlayer(_NPC2,0) + AND + CharacterIsAlly(_OrigVic,_NPC2,1) + THEN + DB_Crime_PlayerAttacked(_CrimeID,_Player,_NPC2); + DebugText(_NPC2," Killing me will create a murder scene (probably joined combat to help assaulted NPC), although it will be ignored by anyone within a 25m radius of this combat"); + + IF + ObjectLeftCombat((CHARACTERGUID)_NPC,_) + AND + DB_Crime_PlayerAttacked(_CrimeID,_Player,_NPC) + THEN + NOT DB_Crime_PlayerAttacked(_CrimeID,_Player,_NPC); + + IF + OnCrimeMergedWith(_Old,_New) + AND + DB_CrimeMurderSilentWitnessCollectionContext(_Old,_DeadMan,_IteratorID,_Killer) + THEN + NOT DB_CrimeMurderSilentWitnessCollectionContext(_Old,_DeadMan,_IteratorID,_Killer); + DB_CrimeMurderSilentWitnessCollectionContext(_New,_DeadMan,_IteratorID,_Killer); + + IF + OnCrimeMergedWith(_Old,_New) + AND + DB_CrimeSilentWitness(_Old,_CrimeType,_NPC) + THEN + NOT DB_CrimeSilentWitness(_Old,_CrimeType,_NPC); + DB_CrimeSilentWitness(_New,_CrimeType,_NPC); + //END_REGION + + //REGION Reset DB_CrimeMurderCreated + IF + CharacterKilledBy(_Defender,_AttackerOwner,_Attacker) + AND + DB_CrimeMurderCreated(_Defender,_Attacker) + THEN + NOT DB_CrimeMurderCreated(_Defender,_Attacker); + //END_REGION + + //END_REGION + + //REGION Pickpocket + + IF + RequestPickpocket(_Player,_Npc) + AND + DB_CannotPickpocketTags(_Tag) + AND + IsTagged(_Npc, _Tag, 1) + AND + CharacterIsPartyMember(_Npc, 0) + THEN + DB_PickpocketingBlocked(1); + Proc_StartDialog(1,"GEB_AD_CannotPickpocket",_Player); + + IF + RequestPickpocket(_Player,_Npc) + AND + NOT DB_PickpocketingBlocked(1) + THEN + RemoveStatus(_Player,"INVISIBLE"); + + IF + CharacterPickpocketFailed(_Player,_) + THEN + RemoveStatus(_Player,"INVISIBLE"); + + + IF + RequestPickpocket(_Player,_Npc) + AND + DB_PickpocketingBlocked(1) + THEN + StartPickpocket(_Player,_Npc,0); + + IF + RequestPickpocket(_Player,_Npc) + AND + NOT DB_PickpocketingBlocked(1) + AND + CharacterIsPartyMember(_Npc, 0) + THEN + GenTradeItems(_Player,_Npc); + StartPickpocket(_Player,_Npc,1); + + IF + RequestPickpocket(_Player,_Npc) + THEN + NOT DB_PickpocketingBlocked(1); + + IF + RequestPickpocket(_Player,_OtherPlayer) + AND + CharacterIsPartyMember(_OtherPlayer, 1) + THEN + StartPickpocket(_Player,_OtherPlayer,1); + + + //--- PICKPOCKET SUCCESS + IF + CharacterPickpocketSuccess(_Thief,_Victim,_Item,_Amount) //TODO: Only trigger on HighValueItems + AND + CharacterIsCrimeEnabled(_Victim,"EmptyPocketNoticed",1) + AND + CharacterCanSpotCrimes(_Victim,1) + AND + Random(10000,_Rand) + AND + IntegerSum(_Rand,5000,_Time) + THEN + DB_Pickpocketed(_Thief,_Victim,_Item); + ProcObjectTimerCancel(_Victim,"CRIME_Pickpocket_CheckPockets"); + ProcObjectTimer(_Victim,"CRIME_Pickpocket_CheckPockets",_Time); + + + PROC + ProcObjectTimerFinished((CHARACTERGUID)_Victim,"CRIME_Pickpocket_CheckPockets") + AND + CharacterIsDead(_Victim,0) + AND + IsSpeakerReserved(_Victim,1) // Victim is in dialog, restart timer to delay reaction + AND + Random(10000,_Rand) + AND + IntegerSum(_Rand,5000,_Time) + THEN + ProcObjectTimerCancel(_Victim,"CRIME_Pickpocket_CheckPockets"); + ProcObjectTimer(_Victim,"CRIME_Pickpocket_CheckPockets",_Time); + + PROC + ProcObjectTimerFinished((CHARACTERGUID)_Victim,"CRIME_Pickpocket_CheckPockets") + AND + QRY_SpeakerIsAvailable(_Victim) + AND + DB_Pickpocketed(_Thief,_Victim,_) + AND + QueryOnlyOnce("CRIME_Pickpocket_PlayAnimation") + THEN + RemoveStatus(_Victim,"SITTING"); + RemoveStatus(_Victim,"LYING"); + PlayAnimation(_Victim,"PickPocket_01"); + + PROC + ProcObjectTimerFinished((CHARACTERGUID)_Victim,"CRIME_Pickpocket_CheckPockets") + AND + QRY_SpeakerIsAvailable(_Victim) + AND + DB_Pickpocketed(_Thief,_Victim,_Item) + AND + GetPosition(_Victim,_x,_y,_z) + THEN + NOT DB_OnlyOnce("CRIME_Pickpocket_PlayAnimation"); + NOT DB_Pickpocketed(_Thief,_Victim,_Item); + CharacterRegisterCrimeWithPosition(_Thief,"EmptyPocketNoticed",_Item,_Victim,_x,_y,_z,0); + + + //--- PICKPOCKET FAIL + //--- Don't create a crime if it failed because of a tag + IF + CharacterPickpocketFailed(_Player,_Npc) + AND + CharacterIsPartyMember(_Npc, 0) + AND + DB_CannotPickpocketTags(_Tag) + AND + IsTagged(_Npc, _Tag, 1) + THEN + DB_PickpocketingBlocked(1); + + //--- Check if player already stole items on that NPC + //--- Pass the item as evidence if it's the case. + IF + CharacterPickpocketFailed(_Player,_Npc) + AND + NOT DB_PickpocketingBlocked(1) + AND + CharacterIsPartyMember(_Npc, 0) + THEN + ProcCrimePickpocketFailedFlow(_Player,_Npc); + + PROC + ProcCrimePickpocketFailedFlow((CHARACTERGUID)_Player,(CHARACTERGUID)_Npc) + THEN + ProcObjectTimerCancel(_Npc,"CRIME_Pickpocket_CheckPockets"); + + PROC + ProcCrimePickpocketFailedFlow((CHARACTERGUID)_Player,(CHARACTERGUID)_Npc) + AND + CharacterIsCrimeEnabled(_Npc,"PickPocketFailed",1) + THEN + RemoveStatus(_Player,"SNEAKING"); + RemoveStatus(_Npc,"SLEEPING"); + RemoveStatus(_Npc,"LYING"); + + PROC + ProcCrimePickpocketFailedFlow((CHARACTERGUID)_Player,(CHARACTERGUID)_Npc) + AND + NOT DB_Pickpocketed(_Player,_Npc,_) + THEN + ProcRegisterPickPocketFailed(_Player,_Npc,NULL_00000000-0000-0000-0000-000000000000); + + PROC + ProcCrimePickpocketFailedFlow((CHARACTERGUID)_Player,(CHARACTERGUID)_Npc) + AND + DB_Pickpocketed(_Player,_Npc,_) + AND + QryCrimeWarningPickPocketFailed_NpcDoesNOTCheckEvidence(_Npc) + THEN + ProcRegisterPickPocketFailed(_Player,_Npc,NULL_00000000-0000-0000-0000-000000000000); + + // pass item as evidence if NPC must perform a search + PROC + ProcCrimePickpocketFailedFlow((CHARACTERGUID)_Player,(CHARACTERGUID)_Npc) + AND + DB_Pickpocketed(_Player,_Npc,_Item) + AND + NOT QryCrimeWarningPickPocketFailed_NpcDoesNOTCheckEvidence(_Npc) + THEN + ProcRegisterPickPocketFailed(_Player,_Npc,_Item); + NOT DB_Pickpocketed(_Player,_Npc,_Item); + + PROC + ProcRegisterPickPocketFailed((CHARACTERGUID)_Player,(CHARACTERGUID)_Npc,(GUIDSTRING)_Evidence) + AND + GetPosition(_Npc,_X,_Y,_Z) + AND + RealSum(_Y,1.0,_YUp) + THEN + CharacterRegisterCrimeWithPosition(_Player,"PickPocketFailed",_Evidence,_Npc,_X,_YUp,_Z,0); + + QRY + QryCrimeWarningPickPocketFailed_NpcDoesNOTCheckEvidence((CHARACTERGUID)_Npc) + AND + CharacterIsPartyMember(_Npc, 0) + AND + NOT QryNPCIsGuard(_Npc) + THEN + DB_NOOP(1); + + PROC + ProcCrimePickpocketFailedFlow((CHARACTERGUID)_Player,(CHARACTERGUID)_Npc) + THEN + NOT DB_PickpocketingBlocked(1); + + + //END_REGION + + //REGION NPC Draw Weapons + // -------------------------- CRIME_Draw Reaction -----------------------------> + IF + CharacterOnCrimeSensibleActionNotification(_NPC,_,_CrimeID,"CRIME_Draw",_,_Criminal,_Criminal2,_Criminal3,_Criminal4,_) + AND + NOT DB_CombatCharacters(_Criminal,_) + AND + NOT DB_CombatCharacters(_NPC,_) + AND + NOT DB_NPCDrawForCrimial(_NPC,_CrimeID,_Criminal) + AND + CharacterCanSee(_NPC,_Criminal,_) + THEN + DB_NPCDrawForCrimial(_NPC,_CrimeID,_Criminal); + + IF + CharacterLostSightOfCharacter(_NPC,_Criminal) + AND + NOT DB_CombatCharacters(_NPC,_) + AND + DB_NPCDrawForCrimial(_NPC,_CrimeID,_Criminal) + THEN + CharacterSetFightMode(_NPC,0,0); + NOT DB_NPCDrawForCrimial(_NPC,_CrimeID,_Criminal); + + // -------------------------- WeaponsDrawn Crime -----------------------------> + PROC + ProcStoreFightMode((CHARACTERGUID)_Npc) + AND + NOT DB_FightModeState(_Npc,_) + AND + CharacterIsInFightMode(_Npc,_FightMode) + THEN + DB_FightModeState(_Npc,_FightMode); + + PROC + ProcRestoreFightMode((CHARACTERGUID)_Npc) + AND + DB_FightModeState(_Npc,_State) + AND + NOT DB_CombatCharacters(_NPC,_) + THEN + CharacterSetFightMode(_Npc,_State,0); + NOT DB_FightModeState(_Npc,_State); + + IF + StoryEvent(_Npc,"CRIME_RestoreUnsheath") + AND + ObjectExists(_Npc,1) + THEN + ProcRestoreFightMode((CHARACTERGUID)_Npc); + + IF + CharacterOnCrimeSensibleActionNotification(_NPC,_,_CrimeID,_,_,_Criminal,_Criminal2,_Criminal3,_Criminal4,_) + AND + NOT DB_CombatCharacters(_NPC,_) + AND + CrimeGetType(_CrimeID,"WeaponsDrawn") + AND + NOT DB_NPCSheathWeapon(_NPC,_Criminal) + THEN + DB_NPCSheathWeapon(_NPC,_Criminal); + DB_NPCSheathWeapon(_NPC,_Criminal2); + DB_NPCSheathWeapon(_NPC,_Criminal3); + DB_NPCSheathWeapon(_NPC,_Criminal4); + NOT DB_NPCSheathWeapon(_NPC,NULL_00000000-0000-0000-0000-000000000000); + ProcStoreFightMode(_Npc); + CharacterSetFightMode(_NPC,1,0); + + IF + CharacterStatusRemoved(_Player,"UNSHEATHED",_) + AND + DB_IsPlayer(_Player) + AND + DB_NPCSheathWeapon(_NPC,_Player) + AND + NOT DB_CombatCharacters(_NPC,_) + THEN + NOT DB_NPCSheathWeapon(_NPC,_Player); + ProcCrimeCheckIfPlayersWeaponsOut((CHARACTERGUID)_NPC); + + PROC + ProcCrimeCheckIfPlayersWeaponsOut((CHARACTERGUID)_NPC) + AND + NOT DB_NPCSheathWeapon(_NPC,_) + AND + NOT DB_CombatCharacters(_NPC,_) + THEN + ProcRestoreFightMode(_NPC); + SetStoryEvent(_NPC,"NPCReturnToOrginalPos"); + + IF + ObjectLeftCombat((CHARACTERGUID)_NPC,_) + AND + DB_NPCSheathWeapon(_NPC,_) + AND + ObjectExists(_NPC,1) + AND + CharacterIsDead(_NPC,0) + THEN + ProcRestoreFightMode(_NPC); + + IF + CharacterLostSightOfCharacter(_NPC,_Player) + AND + DB_NPCSheathWeapon(_NPC,_Player) + THEN + NOT DB_NPCSheathWeapon(_NPC,_Player); + ProcCrimeCheckIfPlayersWeaponsOut(_NPC); + + IF + DB_BlockThreatenedDialog((CHARACTERGUID)_Char) + THEN + CharacterDisableCrime(_Char,"WeaponsDrawn"); + DB_CheckWeaponsDrawnCrime(_Char); + + IF + DB_CheckWeaponsDrawnCrime((CHARACTERGUID)_Char) + AND + NOT DB_BlockThreatenedDialog(_Char) + AND + NOT DB_CharacterCrimeDisabled(_Char,"WeaponsDrawn") + THEN + CharacterEnableCrime(_Char,"WeaponsDrawn"); + + //END_REGION + + //REGION Sneaking Spotted + IF + CharacterSawSneakingCharacter(_NPC,_Player) + AND + CharacterIsPlayer(_NPC,0) + THEN + CharacterRegisterCrime(_Player,"Sneaking",NULL_00000000-0000-0000-0000-000000000000,_NPC,0); + //END_REGION + + //REGION Vandalise - AttackItem + + //--- 1) Item attacked + IF + AttackedByObject((ITEMGUID)_Item,(CHARACTERGUID)_Player,(CHARACTERGUID)_Summon,_,_DamageSource) + AND + NOT QryIgnoreDamageSource(_DamageSource) + AND + ObjectIsItem(_Item,1) + AND + CharacterIsPlayer(_Player,1) + AND + ItemGetHealthPoints(_Item,_INT) + AND + _INT > 0 + THEN + ProcCrimeVandaliseCheckOwner(_Item,_Player,_Summon); + + + //--- 2) Check if the item has an owner + PROC + ProcCrimeVandaliseCheckOwner((ITEMGUID)_Item,(CHARACTERGUID)_Player,(CHARACTERGUID)_Summon) + AND + ItemGetOwner(_Item,_Owner) + AND + _Owner != NULL_00000000-0000-0000-0000-000000000000 + AND + CharacterIsPlayer(_Owner,0) + THEN + ProcCrimeVandaliseCheckVandal(_Item,_Owner,_Player,_Summon,""); + + PROC + ProcCrimeVandaliseCheckOwner((ITEMGUID)_Item,(CHARACTERGUID)_Player,(CHARACTERGUID)_Summon) + AND + ItemGetOwner(_Item,_Owner) + AND + _Owner == NULL_00000000-0000-0000-0000-000000000000 + AND + NOT QryCheckforVandaliseTags(_Item) + THEN + ProcCrimeVandaliseCheckVandal(_Item,NULL_00000000-0000-0000-0000-000000000000,_Player,_Summon,"NoOwner"); + + + //--- 3) Check if the vandal is a summon + PROC + ProcCrimeVandaliseCheckVandal((ITEMGUID)_Item,(CHARACTERGUID)_Owner,(CHARACTERGUID)_Player,(CHARACTERGUID)_Summon,(STRING)_Postfix) + AND + NOT QRY_CharacterIsNull(_Summon) + AND + CharacterIsSummon(_Summon,1) + AND + QryShouldTriggerVandaliseCrime(_Item,_Summon) + AND + NOT DB_Crime_Vandalise((CHARACTERGUID)_Summon,(ITEMGUID)_Item,(INTEGER)_) + THEN + ProcCrimeRegisterVandalise(_Item,_Owner,_Summon,"Summon",_Postfix); + + PROC + ProcCrimeVandaliseCheckVandal((ITEMGUID)_Item,(CHARACTERGUID)_Owner,(CHARACTERGUID)_Player,(CHARACTERGUID)_Summon,(STRING)_Postfix) + AND + _Player == _Summon + AND + QryShouldTriggerVandaliseCrime(_Item,_Player) + AND + NOT DB_Crime_Vandalise(_Player,_Item,_) + THEN + ProcCrimeRegisterVandalise(_Item,_Owner,_Player,"",_Postfix); + + + //--- 4) Concatenate the crime's name & register it + PROC + ProcCrimeRegisterVandalise((ITEMGUID)_Item,(CHARACTERGUID)_Owner,(CHARACTERGUID)_Vandal) + THEN + ProcCrimeRegisterVandalise(_Item,_Owner,_Vandal,"",""); + + PROC + ProcCrimeRegisterVandalise((ITEMGUID)_Item,(CHARACTERGUID)_Owner,(CHARACTERGUID)_Vandal,(STRING)_Prefix,(STRING)_Postfix) + AND + StringConcatenate(_Prefix,"Vandalise",_Part1) + AND + StringConcatenate(_Part1,_Postfix,_CrimeName) + AND + GetPosition(_Item,_x,_y,_z) + AND + RealSum(_y,1.0,_yUp) + AND + CrimeGetNewID(_CrimeID) + THEN + DB_Crime_Vandalise(_Vandal,_Item,_CrimeID); + CharacterRegisterCrimeWithPosition(_Vandal,_CrimeName,_Item,_Owner,_x,_yUp,_z,_CrimeID); + + + QRY + QryCheckforVandaliseTags((ITEMGUID)_Item) + AND + DB_IgnoreVandaliseTag((STRING)_Tag) + AND + IsTagged(_Item,_Tag,1) + THEN + DB_NOOP(1); + + QRY + QryShouldTriggerVandaliseCrime((ITEMGUID)_Item,(CHARACTERGUID)_Player) + AND + NOT DB_CombatCharacters(_Player,_) + THEN + DB_NOOP(1); + + QRY + QryShouldTriggerVandaliseCrime((ITEMGUID)_Item,(CHARACTERGUID)_Player) + AND + DB_TriggerVandaliseInCombat(_Item) + THEN + DB_NOOP(1); + + IF + OnCrimeMergedWith(_Old,_New) + AND + DB_Crime_Vandalise(_Player,_Item,_Old) + THEN + NOT DB_Crime_Vandalise(_Player,_Item,_Old); + DB_Crime_Vandalise(_Player,_Item,_New); + + QRY + QryCheckforVandaliseTags((ITEMGUID)_Item) + AND + DB_IgnoreVandaliseTag((STRING)_Tag) + AND + IsTagged(_Item,_Tag,1) + THEN + DB_NOOP(1); + + //REGION Resolve Vandalise + IF + OnCrimeRemoved(_CrimeID,_Victim,_Criminal1,_Criminal2,_Criminal3,_Criminal4) + THEN + ProcCrimeClearVandaliseDB(_Criminal1,_CrimeID); + ProcCrimeClearVandaliseDB(_Criminal2,_CrimeID); + ProcCrimeClearVandaliseDB(_Criminal3,_CrimeID); + ProcCrimeClearVandaliseDB(_Criminal4,_CrimeID); + + IF + OnCrimeResolved(_CrimeID,_Victim,_Criminal1,_Criminal2,_Criminal3,_Criminal4) + AND + CrimeGetType(_CrimeID,"Vandalise") + THEN + ProcCrimeClearVandaliseDB(_Criminal1,_CrimeID); + ProcCrimeClearVandaliseDB(_Criminal2,_CrimeID); + ProcCrimeClearVandaliseDB(_Criminal3,_CrimeID); + ProcCrimeClearVandaliseDB(_Criminal4,_CrimeID); + + IF + OnCrimeResolved(_CrimeID,_Victim,_Criminal1,_Criminal2,_Criminal3,_Criminal4) + AND + CrimeGetType(_CrimeID,"VandaliseNoOwner") + THEN + ProcCrimeClearVandaliseDB(_Criminal1,_CrimeID); + ProcCrimeClearVandaliseDB(_Criminal2,_CrimeID); + ProcCrimeClearVandaliseDB(_Criminal3,_CrimeID); + ProcCrimeClearVandaliseDB(_Criminal4,_CrimeID); + + PROC + ProcCrimeClearVandaliseDB((CHARACTERGUID)_Player,(INTEGER)_CrimeID) + AND + DB_Crime_Vandalise(_Player,_Item,_CrimeID) + THEN + NOT DB_Crime_Vandalise(_Player,_Item,_CrimeID); + //END_REGION + //END_REGION + + //REGION Vandalise - Destroy Item + + IF + AttackedByObject((ITEMGUID)_Item,(CHARACTERGUID)_Player,(CHARACTERGUID)_Summon,_,_DamageSource) + AND + NOT QryIgnoreDamageSource(_DamageSource) + AND + ObjectIsItem(_Item,1) + AND + CharacterIsPlayer(_Player,1) + AND + NOT DB_CombatCharacters(_Player,_) + AND + ItemGetHealthPoints(_Item,_INT) + AND + _INT < 1 + AND + NOT DB_Crime_ItemDestroyed(_Item,_Player) + AND + ItemGetOwner(_Item,_Owner) + AND + _Owner != NULL_00000000-0000-0000-0000-000000000000 + AND + CharacterIsPlayer(_Owner,0) + THEN + ProcCrimeItemDestroyCheckVandal(_Item,_Owner,_Player,_Summon); + + //2. check the criminal + PROC + ProcCrimeItemDestroyCheckVandal((ITEMGUID)_Item,(CHARACTERGUID)_Owner,(CHARACTERGUID)_Player,(CHARACTERGUID)_Summon) + AND + NOT QRY_CharacterIsNull(_Summon) + AND + CharacterIsSummon(_Summon,1) + AND + QryShouldTriggerVandaliseCrime(_Item,_Summon) + AND + NOT DB_Crime_Vandalise((CHARACTERGUID)_Summon,(ITEMGUID)_Item,(INTEGER)_) + THEN + ProcCrimeRegisterItemDestroy(_Item,_Owner,_Summon,"Summon"); + + PROC + ProcCrimeItemDestroyCheckVandal((ITEMGUID)_Item,(CHARACTERGUID)_Owner,(CHARACTERGUID)_Player,(CHARACTERGUID)_Summon) + AND + _Player == _Summon + AND + QryShouldTriggerVandaliseCrime(_Item,_Player) + AND + NOT DB_Crime_Vandalise(_Player,_Item,_) + THEN + ProcCrimeRegisterItemDestroy(_Item,_Owner,_Player,""); + + //3. register the crime + PROC + ProcCrimeRegisterItemDestroy((ITEMGUID)_Item,(CHARACTERGUID)_Owner,(CHARACTERGUID)_Vandal,(STRING)_Prefix) + AND + StringConcatenate(_Prefix,"ItemDestroy",_CrimeName) + AND + GetPosition(_Item,_x,_y,_z) + AND + RealSum(_y,1.0,_yUp) + THEN + DB_Crime_ItemDestroyed(_Item,_Vandal); + CharacterRegisterCrimeWithPosition(_Vandal,_CrimeName,_Item,_Owner,_x,_yUp,_z,0); + + //END_REGION + + //REGION Status Reaction + IF + CharacterStatusApplied(_Player,_Status,_) + AND + _Status != "UNSHEATHED" + AND + DB_IsPlayer(_Player) + AND + DB_StatusReaction(_Status,_Crime) + AND + HasActiveStatus(_Player,_Status,1) + THEN + DB_StatusReactingToPlayer(_Player,_Status); + CharacterRegisterCrime(_Player,_Crime,NULL_00000000-0000-0000-0000-000000000000,NULL_00000000-0000-0000-0000-000000000000,0); + + IF + CharacterStatusApplied(_Player,_Status,_) + AND + _Status == "UNSHEATHED" + AND + DB_IsPlayer(_Player) + AND + NOT DB_CombatCharacters(_Player,_) + AND + DB_StatusReaction(_Status,_Crime) + AND + HasActiveStatus(_Player,_Status,1) + THEN + DB_StatusReactingToPlayer(_Player,_Status); + CharacterRegisterCrime(_Player,_Crime,NULL_00000000-0000-0000-0000-000000000000,NULL_00000000-0000-0000-0000-000000000000,0); + + + IF + CharacterStatusRemoved(_Player, _Status, _) + AND + DB_IsPlayer(_Player) + AND + DB_StatusReaction(_Status,_Crime) + THEN + CharacterStopCrime(_Player,_Crime,NULL_00000000-0000-0000-0000-000000000000); + NOT DB_StatusReactingToPlayer(_Player,_Status); + + //END_REGION + + //REGION Is Not Messing Around + IF + DB_IsNotMessingAround((CHARACTERGUID)_Char) + THEN + DB_AttackOnAssault(_Char); + SetTag(_Char,"NOT_MESSING_AROUND"); + ProcCharacterEnableCrime(_Char,"Assault"); + + IF + DB_RemoveIsNotMessingAround((CHARACTERGUID)_Char) + THEN + NOT DB_IsNotMessingAround(_Char); + NOT DB_RemoveIsNotMessingAround(_Char); + ClearTag(_Char,"NOT_MESSING_AROUND"); + + IF + DB_AttackOnAssault(_Char) + AND + NOT DB_IsNotMessingAround(_Char) + THEN + NOT DB_AttackOnAssault(_Char); + ClearTag(_Char,"NOT_MESSING_AROUND"); + //END_REGION + + //REGION Attitude Change + QRY + QryNPCCaresAboutVictim((CHARACTERGUID)_NPC,(CHARACTERGUID)_Victim,(INTEGER)_) + AND + IsTagged(_NPC,"ANIMAL",1) + AND + IsTagged(_Victim,"ANIMAL",1) + THEN + DB_NOOP(1); + + QRY + QryNPCCaresAboutVictim((CHARACTERGUID)_NPC,(CHARACTERGUID)_Victim,_Primary) + AND + IsTagged(_NPC,"ANIMAL",0) + AND + QryNonAnimalNPCCaresAboutVictim(_NPC,_Victim,_Primary) + THEN + DB_NOOP(1); + + + QRY + QryNonAnimalNPCCaresAboutVictim((CHARACTERGUID)_NPC,(CHARACTERGUID)_Victim,0) + AND + ObjectIsCharacter(_Victim,0) + THEN + DB_NOOP(1); + + QRY + QryNonAnimalNPCCaresAboutVictim((CHARACTERGUID)_NPC,(CHARACTERGUID)_Victim,0) + AND + ObjectIsCharacter(_Victim,1) + AND + CharacterIsAlly(_NPC,_Victim,1) + THEN + DB_NOOP(1); + + QRY + QryNonAnimalNPCCaresAboutVictim((CHARACTERGUID)_NPC,(CHARACTERGUID)_Victim,1) + THEN + DB_NOOP(1); + + IF + CharacterOnCrimeSensibleActionNotification(_NPC,_Region,_CrimeID,_PriorityName,_Dialog,_Criminal,_Criminal2,_Criminal3,_Criminal4,_Primary) + AND + CrimeGetType(_CrimeID,_CrimeName) + AND + NOT DB_NPC_CrimeAttitude_DoNotChange(_NPC,_CrimeName) + AND + DB_CrimeAttitudeChange(_CrimeName,_AttitudeAmountToSub) + AND + CrimeGetVictim(_CrimeID,_Victim) + THEN + ProcCheckAdjustAttitude(_NPC,_Victim,_Criminal,_Criminal2,_Criminal3,_Criminal4,_AttitudeAmountToSub,_Primary); + + PROC + ProcCheckAdjustAttitude((CHARACTERGUID)_NPC,(CHARACTERGUID)_Victim,(CHARACTERGUID)_Criminal,(CHARACTERGUID)_Criminal2,(CHARACTERGUID)_Criminal3,(CHARACTERGUID)_Criminal4,(INTEGER)_AttitudeAmountToSub, (INTEGER)_Primary) + AND + QryNPCCaresAboutVictim(_NPC,_Victim,_Primary) + THEN + ProcCrimeCheckIfAttitudeCauseCombat(_NPC,_Criminal,_AttitudeAmountToSub); + ProcCrimeCheckIfAttitudeCauseCombat(_NPC,_Criminal2,_AttitudeAmountToSub); + ProcCrimeCheckIfAttitudeCauseCombat(_NPC,_Criminal3,_AttitudeAmountToSub); + ProcCrimeCheckIfAttitudeCauseCombat(_NPC,_Criminal4,_AttitudeAmountToSub); + + PROC + ProcCrimeCheckIfAttitudeCauseCombat((CHARACTERGUID)_NPC,(CHARACTERGUID)_Criminal,(INTEGER)_AttitudeAmountToSub) + AND + CharacterGetAttitudeTowardsPlayer(_NPC,_Criminal,_NPCCurrentAttitude) + AND + IntegerSum(_NPCCurrentAttitude,_AttitudeAmountToSub,_NewNPCAttitude) + AND + _NewNPCAttitude > -75 //We Doin't want Lowering Attitude To Start Combat + THEN + CharacterAddAttitudeTowardsPlayer(_NPC,_Criminal,_AttitudeAmountToSub); + + PROC + ProcCrimeCheckIfAttitudeCauseCombat((CHARACTERGUID)_NPC,(CHARACTERGUID)_Criminal,(INTEGER)_AttitudeAmountToSub) + AND + CharacterGetAttitudeTowardsPlayer(_NPC,_Criminal,_NPCCurrentAttitude) + AND + IntegerSum(_NPCCurrentAttitude,_AttitudeAmountToSub,_NewNPCAttitude) + AND + _NewNPCAttitude <= -75 //We Doin't want Lowering Attitude To Start Combat + AND + IntegerSubtract(-74,_NPCCurrentAttitude,_NewAttitudeToAdd) + THEN + CharacterAddAttitudeTowardsPlayer(_NPC,_Criminal,_NewAttitudeToAdd); + + //END_REGION + + //REGION Dialog interrupting + + QRY + QRY_CrimeNPCUnAvailableForDialog((CHARACTERGUID)_NPC,(CHARACTERGUID)_Criminal,(CHARACTERGUID)_Criminal2,(CHARACTERGUID)_Criminal3,(CHARACTERGUID)_Criminal4) + AND + IsSpeakerReserved(_NPC,1) + THEN + DB_NOOP(1); + + QRY + QRY_CrimeNPCUnAvailableForDialog((CHARACTERGUID)_NPC,(CHARACTERGUID)_Criminal,(CHARACTERGUID)_Criminal2,(CHARACTERGUID)_Criminal3,(CHARACTERGUID)_Criminal4) + AND + _Criminal != NULL_00000000-0000-0000-0000-000000000000 + AND + IsSpeakerReserved(_Criminal,1) + THEN + DB_NOOP(1); + + QRY + QRY_CrimeNPCUnAvailableForDialog((CHARACTERGUID)_NPC,(CHARACTERGUID)_Criminal,(CHARACTERGUID)_Criminal2,(CHARACTERGUID)_Criminal3,(CHARACTERGUID)_Criminal4) + AND + _Criminal2 != NULL_00000000-0000-0000-0000-000000000000 + AND + IsSpeakerReserved(_Criminal2,1) + THEN + DB_NOOP(1); + + QRY + QRY_CrimeNPCUnAvailableForDialog((CHARACTERGUID)_NPC,(CHARACTERGUID)_Criminal,(CHARACTERGUID)_Criminal2,(CHARACTERGUID)_Criminal3,(CHARACTERGUID)_Criminal4) + AND + _Criminal3 != NULL_00000000-0000-0000-0000-000000000000 + AND + IsSpeakerReserved(_Criminal3,1) + THEN + DB_NOOP(1); + + QRY + QRY_CrimeNPCUnAvailableForDialog((CHARACTERGUID)_NPC,(CHARACTERGUID)_Criminal,(CHARACTERGUID)_Criminal2,(CHARACTERGUID)_Criminal3,(CHARACTERGUID)_Criminal4) + AND + _Criminal4 != NULL_00000000-0000-0000-0000-000000000000 + AND + IsSpeakerReserved(_Criminal4,1) + THEN + DB_NOOP(1); + + QRY + QRY_CrimeNPCUnAvailableForDialog((CHARACTERGUID)_NPC,(CHARACTERGUID)_Criminal,(CHARACTERGUID)_Criminal2,(CHARACTERGUID)_Criminal3,(CHARACTERGUID)_Criminal4) + AND + CharacterIsDead(_NPC,1) + THEN + DB_NOOP(1); + + QRY + QRY_CrimeNPCUnAvailableForDialog((CHARACTERGUID)_NPC,(CHARACTERGUID)_Criminal,(CHARACTERGUID)_Criminal2,(CHARACTERGUID)_Criminal3,(CHARACTERGUID)_Criminal4) + AND + GetRegion(_NPC,_Region) + AND + NOT DB_CurrentLevel(_Region) + THEN + DB_NOOP(1); + + QRY + QRY_CrimeNPCUnAvailableForDialog((CHARACTERGUID)_NPC,(CHARACTERGUID)_Criminal,(CHARACTERGUID)_Criminal2,(CHARACTERGUID)_Criminal3,(CHARACTERGUID)_Criminal4) + AND + CombatGetIDForCharacter(_NPC,_ID) + AND + _ID!=0 + THEN + DB_NOOP(1); + + QRY + QRY_CrimeNPCUnAvailableForDialog((CHARACTERGUID)_NPC,(CHARACTERGUID)_Criminal,(CHARACTERGUID)_Criminal2,(CHARACTERGUID)_Criminal3,(CHARACTERGUID)_Criminal4) + AND + CharacterIsEnemy(_NPC,_Criminal,1) + THEN + DB_NOOP(1); + + QRY + QRY_CrimeNPCUnAvailableForDialog((CHARACTERGUID)_NPC,(CHARACTERGUID)_Criminal,(CHARACTERGUID)_Criminal2,(CHARACTERGUID)_Criminal3,(CHARACTERGUID)_Criminal4) + AND + _Criminal2 != NULL_00000000-0000-0000-0000-000000000000 + AND + CharacterIsEnemy(_NPC,_Criminal2,1) + THEN + DB_NOOP(1); + + QRY + QRY_CrimeNPCUnAvailableForDialog((CHARACTERGUID)_NPC,(CHARACTERGUID)_Criminal,(CHARACTERGUID)_Criminal2,(CHARACTERGUID)_Criminal3,(CHARACTERGUID)_Criminal4) + AND + _Criminal3 != NULL_00000000-0000-0000-0000-000000000000 + AND + CharacterIsEnemy(_NPC,_Criminal3,1) + THEN + DB_NOOP(1); + + QRY + QRY_CrimeNPCUnAvailableForDialog((CHARACTERGUID)_NPC,(CHARACTERGUID)_Criminal,(CHARACTERGUID)_Criminal2,(CHARACTERGUID)_Criminal3,(CHARACTERGUID)_Criminal4) + AND + _Criminal4 != NULL_00000000-0000-0000-0000-000000000000 + AND + CharacterIsEnemy(_NPC,_Criminal4,1) + THEN + DB_NOOP(1); + + PROC + ProcTryStopDialogFor((CHARACTERGUID)_Char) + AND + _Char != NULL_00000000-0000-0000-0000-000000000000 + AND + CharacterIsPlayer(_Char,1) + AND + DB_DialogPlayers(_Inst,_Char,_) + AND + DB_DialogName(_CurrentDialog,_Inst) + AND + NOT DB_Crime_DoNotStopDialog((STRING)_CurrentDialog) + THEN + DialogRequestStop(_Char); + DB_StoppedDialog(1); + + PROC + ProcTryStopDialogFor((CHARACTERGUID)_Char) + AND + _Char != NULL_00000000-0000-0000-0000-000000000000 + AND + CharacterIsPlayer(_Char,0) + AND + DB_DialogNPCs(_Inst,_Char,_) + AND + DB_DialogName(_CurrentDialog,_Inst) + AND + NOT DB_Crime_DoNotStopDialog(_CurrentDialog) + THEN + DialogRequestStop(_Char); + DB_StoppedDialog(1); + + PROC + ProcCrimeInterruptStoryDialogs((INTEGER)_CrimeID,(STRING)_CrimeDialog,1,(CHARACTERGUID)_NPC,(CHARACTERGUID)_Criminal,(CHARACTERGUID)_Criminal2,(CHARACTERGUID)_Criminal3,(CHARACTERGUID)_Criminal4) + THEN + ProcTryStopDialogFor(_NPC); + ProcTryStopDialogFor(_Criminal); + ProcTryStopDialogFor(_Criminal2); + ProcTryStopDialogFor(_Criminal3); + ProcTryStopDialogFor(_Criminal4); + + PROC + ProcCrimeInterruptStoryDialogs(_,_,1,_,_,_,_,_) + AND + DB_StoppedDialog(1) + THEN + NOT DB_StoppedDialog(1); + FireOsirisEvents(); + + PROC + ProcCrimeInterruptStoryDialogs((INTEGER)_CrimeID,(STRING)_CrimeDialog,0,(CHARACTERGUID)_NPC,(CHARACTERGUID)_Criminal,(CHARACTERGUID)_Criminal2,(CHARACTERGUID)_Criminal3,(CHARACTERGUID)_Criminal4) + AND + DB_DialogNPCs(_Inst,_NPC,_) + AND + DB_DialogName(_CurrentDialog,_Inst) + AND + NOT DB_Crime_DoNotStopDialog(_CurrentDialog) + THEN + DialogRequestStop(_NPC); + FireOsirisEvents(); + + PROC + ProcCrimeInterruptStoryDialogs((INTEGER)_CrimeID,(STRING)_CrimeDialog,0,(CHARACTERGUID)_NPC,(CHARACTERGUID)_Criminal,(CHARACTERGUID)_Criminal2,(CHARACTERGUID)_Criminal3,(CHARACTERGUID)_Criminal4) + AND + NOT QRY_CrimeNPCUnAvailableForDialog(_NPC,NULL_00000000-0000-0000-0000-000000000000,NULL_00000000-0000-0000-0000-000000000000,NULL_00000000-0000-0000-0000-000000000000,NULL_00000000-0000-0000-0000-000000000000) + THEN + ProcStartCrimeDialog(_CrimeID,_CrimeDialog,0,_NPC,_Criminal,_Criminal2,_Criminal3,_Criminal4); + + PROC + ProcCrimeInterruptStoryDialogs((INTEGER)_CrimeID,(STRING)_CrimeDialog,1,(CHARACTERGUID)_NPC,(CHARACTERGUID)_Criminal,(CHARACTERGUID)_Criminal2,(CHARACTERGUID)_Criminal3,(CHARACTERGUID)_Criminal4) + AND + NOT QRY_CrimeNPCUnAvailableForDialog(_NPC,_Criminal,_Criminal2,_Criminal3,_Criminal4) + THEN + ProcStartCrimeDialog(_CrimeID,_CrimeDialog,1,_NPC,_Criminal,_Criminal2,_Criminal3,_Criminal4); + + PROC + ProcHandleCrimeDialog((CHARACTERGUID)_Warner,(CHARACTERGUID)_Criminal1,(CHARACTERGUID)_Criminal2,(CHARACTERGUID)_Criminal3,(CHARACTERGUID)_Criminal4,(STRING)_CrimeDialog,1,_) + AND + DB_CheckInterrupt(_CrimeID,_Warner) + THEN + DB_Interrupted(_CrimeID,_Warner,1); + + PROC + ProcHandleCrimeDialog((CHARACTERGUID)_Warner,(CHARACTERGUID)_Criminal1,(CHARACTERGUID)_Criminal2,(CHARACTERGUID)_Criminal3,(CHARACTERGUID)_Criminal4,(STRING)_CrimeDialog,1,_) + AND + DB_CheckInterrupt(_CrimeID,_Warner) + THEN + NOT DB_CheckInterrupt(_CrimeID,_Warner); + + PROC + ProcStartCrimeDialog((INTEGER)_CrimeID,(STRING)_CrimeDialog,(INTEGER)_IsInteractive,(CHARACTERGUID)_NPC,(CHARACTERGUID)_Criminal,(CHARACTERGUID)_Criminal2,(CHARACTERGUID)_Criminal3,(CHARACTERGUID)_Criminal4) + THEN + DB_NOOP(1); + + PROC + ProcStartCrimeDialog((INTEGER)_CrimeID,(STRING)_CrimeDialog,1,(CHARACTERGUID)_NPC,(CHARACTERGUID)_Criminal,(CHARACTERGUID)_Criminal2,(CHARACTERGUID)_Criminal3,(CHARACTERGUID)_Criminal4) + AND + DialogStartCrimeDialog(_CrimeID,_CrimeDialog,1,_NPC,_Criminal,_Criminal2,_Criminal3,_Criminal4,_Success) + THEN + DB_CheckInterrupt(_CrimeID,_NPC); + DB_CrimeWarner(_CrimeID,_NPC,_CrimeDialog); + ProcHandleCrimeDialog(_NPC,_Criminal,_Criminal2,_Criminal3,_Criminal4,_CrimeDialog,_Success,1); + + PROC + ProcStartCrimeDialog((INTEGER)_CrimeID,(STRING)_CrimeDialog,0,(CHARACTERGUID)_NPC,(CHARACTERGUID)_Criminal,(CHARACTERGUID)_Criminal2,(CHARACTERGUID)_Criminal3,(CHARACTERGUID)_Criminal4) + AND + DialogStartCrimeDialog(_CrimeID,_CrimeDialog,0,_NPC,NULL_00000000-0000-0000-0000-000000000000,NULL_00000000-0000-0000-0000-000000000000,NULL_00000000-0000-0000-0000-000000000000,NULL_00000000-0000-0000-0000-000000000000,_Success) + AND + CrimeGetType(_CrimeID,_CrimeType) + THEN + DB_Crime_WarningAD_Target(_CrimeType,_NPC,_Criminal); // in AD we don't have player, so to clear Warning flags, store target player in this DB + DB_CheckInterrupt(_CrimeID,_NPC); + CrimeConfrontationDone(_CrimeID,_NPC); // confrontation done for ADs + ProcHandleCrimeDialog(_NPC,_Criminal,_Criminal2,_Criminal3,_Criminal4,_CrimeDialog,_Success,0); + DB_Interrupted(_CrimeID,_NPC,1); + + PROC + ProcCrimeInterruptStoryDialogs((INTEGER)_CrimeID,(STRING)_CrimeDialog,(INTEGER)_MarkForInteractive,(CHARACTERGUID)_NPC,(CHARACTERGUID)_Criminal,(CHARACTERGUID)_Criminal2,(CHARACTERGUID)_Criminal3,(CHARACTERGUID)_Criminal4) + AND + NOT DB_Interrupted(_CrimeID,_NPC,1) + THEN + //NPC not available for dialog anymore + DB_Crime_FailedToInterruptStoryDialogs((INTEGER)_CrimeId,(STRING)_CrimeDialog,(CHARACTERGUID)_NPC,(CHARACTERGUID)_Criminal,(CHARACTERGUID)_Criminal2,(CHARACTERGUID)_Criminal3,(CHARACTERGUID)_Criminal4); + + PROC + ProcCrimeInterruptStoryDialogs((INTEGER)_CrimeID,(STRING)_CrimeDialog,(INTEGER)_MarkForInteractive,(CHARACTERGUID)_NPC,(CHARACTERGUID)_Criminal,(CHARACTERGUID)_Criminal2,(CHARACTERGUID)_Criminal3,(CHARACTERGUID)_Criminal4) + THEN + NOT DB_Interrupted(_CrimeID,_NPC,1); + //END_REGION + + //REGION -ForbiddenItem crime. Action Requests handling. + + /***************************************/ + //--- Crime reactions & Move crime + /***************************************/ + IF + DialogStarted("GEB_Warning_UseForbiddenItem",_Inst) + AND + DB_DialogPlayers(_Inst,_Player,1) + AND + DB_CRIME_ForbiddenStatus(_Status) + AND + HasActiveStatus(_Player,_Status,1) + THEN + RemoveStatus(_Player,_Status); + + IF + CharacterUsedSkillOnTarget(_Player,_Item,_,"teleportation") + AND + CharacterIsPlayer(_Player,1) + AND + ObjectIsItem(_Item,1) + AND + ItemCanSitOn((ITEMGUID)_Item,0) + AND + ItemGetOwner(_Item,_Owner) + AND + NOT QRY_CharacterIsNull(_Owner) + AND + CharacterIsPlayer(_Owner,0) + THEN + CharacterRegisterCrime(_Player,"MoveForbiddenItem",_Item,(CHARACTERGUID)_Owner,0); + + PROC + ProcCrimeTryRegisteringMoveForbiddenItem((CHARACTERGUID)_Player,(ITEMGUID)_Item,(INTEGER)_RequestID) + AND + CharacterIsPlayer(_Player,1) + AND + ItemCanSitOn(_Item,0) + AND + ItemGetOwner(_Item,_Owner) + AND + NOT QRY_CharacterIsNull(_Owner) + AND + CharacterIsPlayer(_Owner,0) + THEN + ProcSendMoveRequestResult(_Player,_Item,_RequestID); + CharacterRegisterCrime(_Player,"MoveForbiddenItem",_Item,(CHARACTERGUID)_Owner,0); + DB_MoveCrimeRegistered(1); + + PROC + ProcSendMoveRequestResult((CHARACTERGUID)_Player,(ITEMGUID)_Item,(INTEGER)_RequestID) + AND + GetPosition(_Item,_X,_Y,_Z) + AND + CrimeIsAnyNPCGoingToReact(_Player,"MoveForbiddenItem",_X,_Y,_Z,_Result) + AND + DB_Negate(_Result,_Inverse) + THEN + RequestProcessed(_Player,_RequestID,_Inverse); + + PROC + ProcCrimeTryRegisteringMoveForbiddenItem((CHARACTERGUID)_Player,(ITEMGUID)_Item,(INTEGER)_RequestID) + AND + NOT DB_MoveCrimeRegistered(1) + THEN + RequestProcessed(_Player,_RequestID,1); + + PROC + ProcCrimeTryRegisteringMoveForbiddenItem((CHARACTERGUID)_Player,(ITEMGUID)_Item,(INTEGER)_RequestID) + THEN + NOT DB_MoveCrimeRegistered(1); + + /***************************************/ + //--- REQUESTS + /***************************************/ + IF + CanUseItem(_Char,_Item,_RequestID) + THEN + ProcBlockUseOfItem(_Char,_Item); + ProcProcessUseOfItemWithStatus(_Char,_Item); + ProcProcessUseOfItem(_Char,_Item,_RequestID); + + IF + CanMoveItem(_Char,_Item,_RequestID) + THEN + ProcBlockMoveOfItem(_Char,_Item); + ProcProcessMoveOfItem(_Char,_Item,_RequestID); + + IF + CanPickupItem(_Char,_Item,_RequestID) + THEN + ProcBlockPickupOfItem(_Char,_Item); + ProcProcessPickupOfItem(_Char,_Item,_RequestID); + + IF + CanLockpickItem(_Char,_Item,_RequestID) + THEN + ProcBlockLockpickItem(_Char,_Item); + ProcProcessLockpickItem(_Char,_Item,_RequestID); + + IF + CanCombineItem(_Char,_ItemA,_ItemB,_ItemC,_ItemD,_ItemE,_RequestID) + THEN + ProcBlockCombineItem(_Char,_ItemA); + ProcBlockCombineItem(_Char,_ItemB); + ProcBlockCombineItem(_Char,_ItemC); + ProcBlockCombineItem(_Char,_ItemD); + ProcBlockCombineItem(_Char,_ItemE); + ProcProcessCombineItem(_Char,_ItemA,_ItemB,_ItemC,_ItemD,_ItemE,_RequestID); + + + /***************************************/ + //--- BLOCK ACTIONS + /***************************************/ + /* Catch one of the following PROCs to set the appropriate DB: + DB_CustomUseItemResponse((CHARACTERGUID)_Char,(ITEMGUID)_Item,(INTEGER)_Result) + DB_CustomMoveItemResponse((CHARACTERGUID)_Char,(ITEMGUID)_Item,(INTEGER)_Result) + DB_CustomPickupItemResponse((CHARACTERGUID)_Char,(ITEMGUID)_Item,(INTEGER)_Result) + DB_CustomLockpickItemResponse((CHARACTERGUID)_Char,(ITEMGUID)_Item,(INTEGER)_Result) + DB_CustomCombineItemResponse((CHARACTERGUID)_Char,(ITEMGUID)_Item,(INTEGER)_Result) + */ + + PROC + ProcBlockUseOfItem((CHARACTERGUID)_Char,(ITEMGUID)_Item) + THEN + DB_NOOP(1); + + PROC + ProcBlockMoveOfItem((CHARACTERGUID)_Char,(ITEMGUID)_Item) + THEN + DB_NOOP(1); + + PROC + ProcBlockPickupOfItem((CHARACTERGUID)_Char,(ITEMGUID)_Item) + THEN + DB_NOOP(1); + + PROC + ProcBlockLockpickItem((CHARACTERGUID)_Char,(ITEMGUID)_Item) + THEN + DB_NOOP(1); + + PROC + ProcBlockCombineItem((CHARACTERGUID)_Char,(ITEMGUID)_Item) + THEN + DB_NOOP(1); + + + /***************************************/ + //--- BURNING AND ELECTRIFIED items apply status + /***************************************/ + PROC + ProcProcessUseOfItemWithStatus((CHARACTERGUID)_Char,(ITEMGUID)_Item) + AND + DB_IsPlayer(_Char) // apply only on player not to break story events with NPCs using items + AND + NOT DB_CustomUseItemResponse(_Char,_Item,0) // if handled in story, don't apply effect + AND + DB_ItemStatusAffectCharacterOnUse(_Status) + AND + HasActiveStatus(_Item,_Status,1) + AND + NOT QRYItemStatusIsNotAppliedToUser(_Item,_Status) + THEN + ApplyStatus(_Char,_Status,12.0); + + QRY + QRYItemStatusIsNotAppliedToUser((ITEMGUID)_Item,(STRING)_Status) + AND + _Status == "BURNING" + AND + ItemIsTorch(_Item,1) + THEN + DB_NOOP(1); + + /***************************************/ + //--- PROCESS + /***************************************/ + // Use + PROC + ProcProcessUseOfItem((CHARACTERGUID)_Char,(ITEMGUID)_Item,(INTEGER)_RequestID) + AND + DB_CustomUseItemResponse(_Char,_Item,(INTEGER)_Result) + THEN + RequestProcessed(_Char,_RequestID,_Result); + + PROC + ProcProcessUseOfItem((CHARACTERGUID)_Char,(ITEMGUID)_Item,(INTEGER)_RequestID) + AND + NOT DB_CustomUseItemResponse(_Char,_Item,_) + AND + ItemCanSitOn(_Item,0) + AND + ItemIsLadder(_Item,0) + AND + ItemIsPublicDomain(_Item,0) + THEN + DB_HandledRequest(_Char,_Item,_RequestID); + ProcCrimeTryRegisteringUseForbiddenItem(_Char,_Item,_RequestID); + + PROC + ProcProcessUseOfItem((CHARACTERGUID)_Char,(ITEMGUID)_Item,(INTEGER)_RequestID) + AND + NOT DB_CustomUseItemResponse(_Char,_Item,_) + AND + NOT DB_HandledRequest(_Char,_Item,_RequestID) + THEN + RequestProcessed(_Char,_RequestID,1); + + PROC + ProcProcessUseOfItem((CHARACTERGUID)_Char,(ITEMGUID)_Item,(INTEGER)_RequestID) + AND + DB_HandledRequest(_Char,_Item,_RequestID) + THEN + NOT DB_HandledRequest(_Char,_Item,_RequestID); + + // Move (registers MoveForbiddenItem instead of UseForbiddenItem) + PROC + ProcProcessMoveOfItem((CHARACTERGUID)_Char,(ITEMGUID)_Item,(INTEGER)_RequestID) + AND + DB_CustomMoveItemResponse(_Char,_Item,(INTEGER)_Result) + THEN + RequestProcessed(_Char,_RequestID,_Result); + + PROC + ProcProcessMoveOfItem((CHARACTERGUID)_Char,(ITEMGUID)_Item,(INTEGER)_RequestID) + AND + NOT DB_CustomMoveItemResponse(_Char,_Item,_) + THEN + ProcCrimeTryRegisteringMoveForbiddenItem(_Char,_Item,_RequestID); + + // Pickup + PROC + ProcProcessPickupOfItem((CHARACTERGUID)_Char,(ITEMGUID)_Item,(INTEGER)_RequestID) + AND + DB_CustomPickupItemResponse(_Char,_Item,(INTEGER)_Result) + THEN + RequestProcessed(_Char,_RequestID,_Result); + + PROC + ProcProcessPickupOfItem((CHARACTERGUID)_Char,(ITEMGUID)_Item,(INTEGER)_RequestID) + AND + NOT DB_CustomPickupItemResponse(_Char,_Item,_) + THEN + ProcCrimeTryRegisteringStealItem(_Char,_Item,_RequestID); + + // Lockpick + PROC + ProcProcessLockpickItem((CHARACTERGUID)_Char,(ITEMGUID)_Item,(INTEGER)_RequestID) + AND + DB_CustomLockpickItemResponse(_Char,_Item,(INTEGER)_Result) + THEN + RequestProcessed(_Char,_RequestID,_Result); + + PROC + ProcProcessLockpickItem((CHARACTERGUID)_Char,(ITEMGUID)_Item,(INTEGER)_RequestID) + AND + NOT DB_CustomLockpickItemResponse(_Char,_Item,_) + THEN + ProcCrimeTryRegisteringUseForbiddenItem(_Char,_Item,_RequestID); + + // Combine + PROC + ProcProcessCombineItem(_Player,_ItemA,_ItemB,_ItemC,_ItemD,_ItemE,_RequestID) + AND + NOT QryCombineItemHasCustomResult(_Player,_ItemA,_ItemB,_ItemC,_ItemD,_ItemE) + AND + CharacterIsPlayer(_Player,1) + THEN + ProcCrimeCheckRegisterUseForbiddenItem(_Player,_ItemA,_RequestID); + ProcCrimeCheckRegisterUseForbiddenItem(_Player,_ItemB,_RequestID); + ProcCrimeCheckRegisterUseForbiddenItem(_Player,_ItemC,_RequestID); + ProcCrimeCheckRegisterUseForbiddenItem(_Player,_ItemD,_RequestID); + ProcCrimeCheckRegisterUseForbiddenItem(_Player,_ItemE,_RequestID); + + PROC + ProcProcessCombineItem((CHARACTERGUID)_Char,(ITEMGUID)_ItemA,(ITEMGUID)_ItemB,(ITEMGUID)_ItemC,(ITEMGUID)_ItemD,(ITEMGUID)_ItemE,(INTEGER)_RequestID) + AND + QryCombineItemHasCustomResult(_Char,_ItemA,_ItemB,_ItemC,_ItemD,_ItemE) + THEN + ProcProcessCustomCombineResponse(_Char,_ItemA,_ItemB,_ItemC,_ItemD,_ItemE,_RequestID); + + PROC + ProcProcessCustomCombineResponse((CHARACTERGUID)_Char,(ITEMGUID)_ItemA,(ITEMGUID)_ItemB,(ITEMGUID)_ItemC,(ITEMGUID)_ItemD,(ITEMGUID)_ItemE,(INTEGER)_RequestID) + AND + QryCombineItemIsBlocked(_Char,_ItemA,_ItemB,_ItemC,_ItemD,_ItemE) + THEN + RequestProcessed(_Char,_RequestID,0); + + PROC + ProcProcessCustomCombineResponse((CHARACTERGUID)_Char,(ITEMGUID)_ItemA,(ITEMGUID)_ItemB,(ITEMGUID)_ItemC,(ITEMGUID)_ItemD,(ITEMGUID)_ItemE,(INTEGER)_RequestID) + AND + NOT QryCombineItemIsBlocked(_Char,_ItemA,_ItemB,_ItemC,_ItemD,_ItemE) + THEN + RequestProcessed(_Char,_RequestID,1); + + + //--- General proc: + PROC + ProcCrimeTryRegisteringUseForbiddenItem((CHARACTERGUID)_Char,(ITEMGUID)_Item,(INTEGER)_RequestID) + AND + CharacterIsPlayer(_Char,1) + THEN + ProcCrimeCheckRegisterUseForbiddenItem(_Char,_Item,_RequestID); + + PROC + ProcCrimeTryRegisteringUseForbiddenItem((CHARACTERGUID)_Char,(ITEMGUID)_Item,(INTEGER)_RequestID) + AND + CharacterIsPlayer(_Char,0) + THEN + RequestProcessed(_Char,_RequestID,1); + + PROC + ProcCrimeTryRegisteringStealItem((CHARACTERGUID)_Char,(ITEMGUID)_Item,(INTEGER)_RequestID) + AND + CharacterIsPlayer(_Char,0) + THEN + DB_StealRequestHandled(1); + RequestProcessed(_Char,_RequestID,1); + + PROC + ProcCrimeTryRegisteringStealItem((CHARACTERGUID)_Char,(ITEMGUID)_Item,(INTEGER)_RequestID) + AND + NOT DB_StealRequestHandled(1) + AND + NOT QryCrimeItemHasNPCOwner(_Item) + THEN + DB_StealRequestHandled(1); + RequestProcessed(_Char,_RequestID,1); + + //we only register a crime here if they're going ot react, sicne we then block the pickup + //in the case they won't the reaction to the steal event will register the crime + PROC + ProcCrimeTryRegisteringStealItem((CHARACTERGUID)_Char,(ITEMGUID)_Item,(INTEGER)_RequestID) + AND + NOT DB_StealRequestHandled(1) + AND + ItemGetOwner(_Item,_Victim) + AND + CharacterIsPlayer(_Victim,0) + AND + GetPosition(_Item,_X,_Y,_Z) + AND + CrimeIsAnyNPCGoingToReact(_Char,"Steal",_X,_Y,_Z,1) + THEN + DB_StealRequestHandled(1); + RequestProcessed(_Char,_RequestID,0); + CharacterRegisterCrimeWithPosition(_Char,"Steal",_Item,_Victim,_X,_Y,_Z,0); + + PROC + ProcCrimeTryRegisteringStealItem((CHARACTERGUID)_Char,(ITEMGUID)_Item,(INTEGER)_RequestID) + AND + NOT DB_StealRequestHandled(1) + THEN + RequestProcessed(_Char,_RequestID,1); + + PROC + ProcCrimeTryRegisteringStealItem(_,_,_) + THEN + NOT DB_StealRequestHandled(1); + + /***************************************/ + //--- CLEAR custom response facts + /***************************************/ + PROC + ProcProcessUseOfItem((CHARACTERGUID)_Char,(ITEMGUID)_Item,(INTEGER)_RequestID) + AND + DB_CustomUseItemResponse(_Char,_Item,_Result) + THEN + NOT DB_CustomUseItemResponse(_Char,_Item,_Result); + + PROC + ProcProcessMoveOfItem((CHARACTERGUID)_Char,(ITEMGUID)_Item,(INTEGER)_RequestID) + AND + DB_CustomMoveItemResponse(_Char,_Item,_Result) + THEN + NOT DB_CustomMoveItemResponse(_Char,_Item,_Result); + + PROC + ProcProcessPickupOfItem((CHARACTERGUID)_Char,(ITEMGUID)_Item,(INTEGER)_RequestID) + AND + DB_CustomPickupItemResponse(_Char,_Item,_Result) + THEN + NOT DB_CustomPickupItemResponse(_Char,_Item,_Result); + + PROC + ProcProcessLockpickItem((CHARACTERGUID)_Char,(ITEMGUID)_Item,(INTEGER)_RequestID) + AND + DB_CustomLockpickItemResponse(_Char,_Item,_Result) + THEN + NOT DB_CustomLockpickItemResponse(_Char,_Item,_Result); + + PROC + ProcProcessCombineItem((CHARACTERGUID)_Char,(ITEMGUID)_Item,(INTEGER)_RequestID) + AND + DB_CustomCombineItemResponse(_Char,_Item,_Result) + THEN + NOT DB_CustomCombineItemResponse(_Char,_Item,_Result); + + + /***************************************/ + //--- REGISTERING CRIMES and responding to the requests + /***************************************/ + PROC + ProcCrimeCheckRegisterUseForbiddenItem((CHARACTERGUID)_Player,(ITEMGUID)_Item,(INTEGER)_RequestID) + AND + NOT QryCrimeItemHasNPCOwner(_Item) + AND + NOT DB_DontCreateUseForbiddenItem(_Item) + THEN + RequestProcessed(_Player,_RequestID,1); + + PROC + ProcCrimeCheckRegisterUseForbiddenItem((CHARACTERGUID)_Player,(ITEMGUID)_Item,(INTEGER)_RequestID) + AND + DB_DontCreateUseForbiddenItem(_Item) + THEN + RequestProcessed(_Player,_RequestID,1); + + PROC + ProcCrimeCheckRegisterUseForbiddenItem((CHARACTERGUID)_Player,(ITEMGUID)_Item,(INTEGER)_RequestID) + AND + NOT DB_DontCreateUseForbiddenItem(_Item) + AND + QryCrimeItemHasNPCOwner(_Item) + AND + HasActiveStatus(_Player,"SNEAKING",1) + THEN + DB_Crime_UseForbiddenItem(_Player,"SneakUseForbiddenItem",_Item); + RequestProcessed(_Player,_RequestID,1); + CharacterRegisterCrime(_Player,"SneakUseForbiddenItem",_Item,NULL_00000000-0000-0000-0000-000000000000,0); + + PROC + ProcCrimeCheckRegisterUseForbiddenItem((CHARACTERGUID)_Player,(ITEMGUID)_Item,(INTEGER)_RequestID) + AND + NOT DB_DontCreateUseForbiddenItem(_Item) + AND + QryCrimeItemHasNPCOwner(_Item) + AND + HasActiveStatus(_Player,"SNEAKING",0) + AND + GetPosition(_Item,_X,_Y,_Z) + AND + CrimeIsAnyNPCGoingToReact(_Player,"UseForbiddenItem",_X,_Y,_Z,_NPCWillReact) + THEN + ProcCrimeRegisterUseForbiddenItem(_Player,_Item,_RequestID,_NPCWillReact); + + PROC + ProcCrimeRegisterUseForbiddenItem((CHARACTERGUID)_Player,(ITEMGUID)_Item,(INTEGER)_RequestID,(INTEGER)_NPCWillReact) + AND + DB_Negate(_NPCWillReact,_Result) + THEN + DB_Crime_UseForbiddenItem(_Player,"UseForbiddenItem",_Item); + RequestProcessed(_Player,_RequestID,_Result); + CharacterRegisterCrime(_Player,"UseForbiddenItem",_Item,NULL_00000000-0000-0000-0000-000000000000,0); + + /***************************************/ + //--- CHECK CRIME REACTIONS + /***************************************/ + /* + IF + CharacterOnCrimeSensibleActionNotification(_,_,_CrimeID,_,_,_,_Criminal1,_Criminal2,_Criminal3,_Criminal4) + AND + CrimeGetType(_CrimeID,"UseForbiddenItem") + THEN + ProcCrimeForbiddenItemGetEvidence(_CrimeID,_Criminal1,"UseForbiddenItem"); + + IF + CharacterOnCrimeSensibleActionNotification(_,_,_CrimeID,_,_,_,_Criminal1,_Criminal2,_Criminal3,_Criminal4) + AND + CrimeGetType(_CrimeID,"SneakUseForbiddenItem") + THEN + ProcCrimeForbiddenItemGetEvidence(_CrimeID,_Criminal1,"SneakUseForbiddenItem"); + */ + + IF + OnCrimeConfrontationDone(_CrimeID,_Investigator,1,_Criminal1,_Criminal2,_Criminal3,_Criminal4) + AND + CrimeGetType(_CrimeID,"UseForbiddenItem") + THEN + ProcCrimeForbiddenItemGetEvidence(_CrimeID,_Criminal1,"UseForbiddenItem"); + + IF + OnCrimeConfrontationDone(_CrimeID,_Investigator,1,_Criminal1,_Criminal2,_Criminal3,_Criminal4) + AND + CrimeGetType(_CrimeID,"SneakUseForbiddenItem") + THEN + ProcCrimeForbiddenItemGetEvidence(_CrimeID,_Criminal1,"SneakUseForbiddenItem"); + + PROC + ProcCrimeForbiddenItemGetEvidence((INTEGER)_CrimeID,(CHARACTERGUID)_Criminal,(STRING)_CrimeType) + AND + NOT QRY_CharacterIsNull(_Criminal) + AND + CrimeGetNumberOfEvidence(_CrimeID,_NumEvidence) + AND + _NumEvidence > 0 + AND + CrimeGetEvidence(_CrimeID,1,(ITEMGUID)_Item) + THEN + ProcCrimeStopForbiddenItem(_Criminal,_CrimeType,_Item); + + PROC + ProcCrimeForbiddenItemGetEvidence((INTEGER)_CrimeID,(CHARACTERGUID)_Criminal,(STRING)_CrimeType) + AND + NOT QRY_CharacterIsNull(_Criminal) + AND + CrimeGetNumberOfEvidence(_CrimeID,_NumEvidence) + AND + _NumEvidence < 1 + THEN + ProcCrimeStopForbiddenItem(_Criminal,_CrimeType,NULL_00000000-0000-0000-0000-000000000000); + + PROC + ProcCrimeStopForbiddenItem((CHARACTERGUID)_Criminal,(STRING)_CrimeType,(ITEMGUID)_Evidence) + THEN + CharacterStopCrime(_Criminal,_CrimeType,_Evidence); + + PROC + ProcCrimeStopForbiddenItem((CHARACTERGUID)_Criminal,(STRING)_CrimeType,(ITEMGUID)_Evidence) + AND + DB_Crime_UseForbiddenItem(_Player,_CrimeType,_Evidence) + THEN + NOT DB_Crime_UseForbiddenItem(_Player,_CrimeType,_Evidence); + + + /***************************************/ + //--- STOPPING CRIMES + /***************************************/ + IF + CharacterStoppedUsingItem(_Player,_Item) + AND + DB_Crime_UseForbiddenItem(_Player,_CrimeType,_Item) + THEN + NOT DB_Crime_UseForbiddenItem(_Player,_CrimeType,_Item); + CharacterStopCrime(_Player,_CrimeType,_Item); + + /* + IF + CharacterMovedItem(_Player,_Item) + CharacterStoppedMovingItem(_Player,_Item) + AND + DB_Crime_UseForbiddenItem(_Player,_CrimeType,_Item) + THEN + NOT DB_Crime_UseForbiddenItem(_Player,_CrimeType,_Item); + CharacterStopCrime(_Player,_CrimeType,_Item); + + IF + CharacterStoppedPickingUpItem(_Player,_Item) + AND + DB_Crime_UseForbiddenItem(_Player,_CrimeType,_Item) + THEN + NOT DB_Crime_UseForbiddenItem(_Player,_CrimeType,_Item); + CharacterStopCrime(_Player,_CrimeType,_Item); + + IF + CharacterStoppedLockpickingItem(_Player,_Item) + AND + DB_Crime_UseForbiddenItem(_Player,_CrimeType,_Item) + THEN + NOT DB_Crime_UseForbiddenItem(_Player,_CrimeType,_Item); + CharacterStopCrime(_Player,_CrimeType,_Item); + */ + + IF + CharacterStoppedCombiningItems(_Player,_ItemA,_ItemB,_ItemC,_ItemD,_ItemE) + THEN + ProcCrimeStopCombineItemCrime(_Player,_ItemA); + ProcCrimeStopCombineItemCrime(_Player,_ItemB); + ProcCrimeStopCombineItemCrime(_Player,_ItemC); + ProcCrimeStopCombineItemCrime(_Player,_ItemD); + ProcCrimeStopCombineItemCrime(_Player,_ItemE); + + PROC + ProcCrimeStopCombineItemCrime((CHARACTERGUID)_Player,(ITEMGUID)_Item) + AND + DB_Crime_UseForbiddenItem(_Player,_CrimeType,_Item) + THEN + CharacterStopCrime(_Player,_CrimeType,_Item); + + QRY + QryCrimeClearCombineItemBD((CHARACTERGUID)_Player,(STRING)_CrimeType) + AND + DB_Crime_UseForbiddenItem(_Player,_CrimeType,_Item) + THEN + NOT DB_Crime_UseForbiddenItem(_Player,_CrimeType,_Item); + + + /***************************************/ + //--- QUERIES + /***************************************/ + + QRY + QryCrimeIsValidItemRequest((CHARACTERGUID)_Char,(ITEMGUID)_Item) + AND + CharacterIsPlayer(_Char,0) + THEN + DB_NOOP(1); + + QRY + QryCrimeIsValidItemRequest((CHARACTERGUID)_Char,(ITEMGUID)_Item) + AND + CharacterIsPlayer(_Char,1) + AND + NOT QryCrimeItemHasNPCOwner((ITEMGUID)_Item) + THEN + DB_NOOP(1); + + QRY + QryCrimeCombinedItemsAreValid((ITEMGUID)_ItemA,(ITEMGUID)_ItemB,(ITEMGUID)_ItemC,(ITEMGUID)_ItemD,(ITEMGUID)_ItemE) + AND + NOT QryCrimeItemHasNPCOwner(_ItemA) + AND + NOT QryCrimeItemHasNPCOwner(_ItemB) + AND + NOT QryCrimeItemHasNPCOwner(_ItemC) + AND + NOT QryCrimeItemHasNPCOwner(_ItemD) + AND + NOT QryCrimeItemHasNPCOwner(_ItemE) + THEN + DB_NOOP(1); + + QRY + QryCrimeItemHasNPCOwner((ITEMGUID)_Item) + AND + ItemGetOwner(_Item,_Char) + AND + NOT QRY_CharacterIsNull(_Char) + AND + CharacterIsPlayer(_Char,0) + THEN + DB_NOOP(1); + + /**** Combine item custom result ****/ + QRY + QryCombineItemHasCustomResult((CHARACTERGUID)_Player,(ITEMGUID)_ItemA,(ITEMGUID)_ItemB,(ITEMGUID)_ItemC,(ITEMGUID)_ItemD,(ITEMGUID)_ItemE) + AND + DB_CustomCombineItemResponse(_Player,_ItemA,_) + THEN + DB_NOOP(1); + + QRY + QryCombineItemHasCustomResult((CHARACTERGUID)_Player,(ITEMGUID)_ItemA,(ITEMGUID)_ItemB,(ITEMGUID)_ItemC,(ITEMGUID)_ItemD,(ITEMGUID)_ItemE) + AND + DB_CustomCombineItemResponse(_Player,_ItemB,_) + THEN + DB_NOOP(1); + + QRY + QryCombineItemHasCustomResult((CHARACTERGUID)_Player,(ITEMGUID)_ItemA,(ITEMGUID)_ItemB,(ITEMGUID)_ItemC,(ITEMGUID)_ItemD,(ITEMGUID)_ItemE) + AND + DB_CustomCombineItemResponse(_Player,_ItemC,_) + THEN + DB_NOOP(1); + + QRY + QryCombineItemHasCustomResult((CHARACTERGUID)_Player,(ITEMGUID)_ItemA,(ITEMGUID)_ItemB,(ITEMGUID)_ItemC,(ITEMGUID)_ItemD,(ITEMGUID)_ItemE) + AND + DB_CustomCombineItemResponse(_Player,_ItemD,_) + THEN + DB_NOOP(1); + + QRY + QryCombineItemHasCustomResult((CHARACTERGUID)_Player,(ITEMGUID)_ItemA,(ITEMGUID)_ItemB,(ITEMGUID)_ItemC,(ITEMGUID)_ItemD,(ITEMGUID)_ItemE) + AND + DB_CustomCombineItemResponse(_Player,_ItemE,_) + THEN + DB_NOOP(1); + + + /**** Combine item is blocked ****/ + QRY + QryCombineItemIsBlocked((CHARACTERGUID)_Player,(ITEMGUID)_ItemA,(ITEMGUID)_ItemB,(ITEMGUID)_ItemC,(ITEMGUID)_ItemD,(ITEMGUID)_ItemE) + AND + DB_CustomCombineItemResponse(_Player,_ItemA,1) + THEN + DB_NOOP(1); + + QRY + QryCombineItemIsBlocked((CHARACTERGUID)_Player,(ITEMGUID)_ItemA,(ITEMGUID)_ItemB,(ITEMGUID)_ItemC,(ITEMGUID)_ItemD,(ITEMGUID)_ItemE) + AND + DB_CustomCombineItemResponse(_Player,_ItemB,1) + THEN + DB_NOOP(1); + + QRY + QryCombineItemIsBlocked((CHARACTERGUID)_Player,(ITEMGUID)_ItemA,(ITEMGUID)_ItemB,(ITEMGUID)_ItemC,(ITEMGUID)_ItemD,(ITEMGUID)_ItemE) + AND + DB_CustomCombineItemResponse(_Player,_ItemC,1) + THEN + DB_NOOP(1); + + QRY + QryCombineItemIsBlocked((CHARACTERGUID)_Player,(ITEMGUID)_ItemA,(ITEMGUID)_ItemB,(ITEMGUID)_ItemC,(ITEMGUID)_ItemD,(ITEMGUID)_ItemE) + AND + DB_CustomCombineItemResponse(_Player,_ItemD,1) + THEN + DB_NOOP(1); + + QRY + QryCombineItemIsBlocked((CHARACTERGUID)_Player,(ITEMGUID)_ItemA,(ITEMGUID)_ItemB,(ITEMGUID)_ItemC,(ITEMGUID)_ItemD,(ITEMGUID)_ItemE) + AND + DB_CustomCombineItemResponse(_Player,_ItemE,1) + THEN + DB_NOOP(1); + + //END_REGION + + //----------------------------------- CUSTOM ----------------------------------- + //REGION Override Sensible Action + + // ------ Setting + PROC + ProcCrimeSetAllCustomSensibleAction((CHARACTERGUID)_Character,(STRING)_CrimeName,(STRING)_CustomReactionName) + THEN + ProcCrimeSetCustomPrimarySensibleAction(_Character,_CrimeName,_CustomReactionName); + ProcCrimeSetCustomSecondarySensibleAction(_Character,_CrimeName,_CustomReactionName); + + + // Primary + PROC + ProcCrimeSetCustomPrimarySensibleAction((CHARACTERGUID)_Character,(STRING)_CrimeName,(STRING)_CustomReactionName) + AND + DB_CrimeReaction_CustomSensibleAction(_Character,_CrimeName,1) + AND + StringConcatenate("Setting a custom primary sensible action [",_CustomReactionName,_Part1) + AND + StringConcatenate(_Part1,"] on a character that already have one for crime: ",_Part2) + AND + StringConcatenate(_Part2,_CrimeName,_Message) + THEN + DebugBreak(_Message); + + PROC + ProcCrimeSetCustomPrimarySensibleAction((CHARACTERGUID)_Character,(STRING)_CrimeName,(STRING)_) + THEN + DB_CrimeReaction_CustomSensibleAction(_Character,_CrimeName,1); + + PROC + ProcCrimeSetCustomPrimarySensibleAction((CHARACTERGUID)_Character,(STRING)_CrimeName,(STRING)_CustomReactionName) + AND + _CustomReactionName == "" + THEN + ProcCrimeSetCustomSensibleActionVariable(_Character,_CrimeName,"CRIME_UndefinedSensibleAction","Primary_"); + + PROC + ProcCrimeSetCustomPrimarySensibleAction((CHARACTERGUID)_Character,(STRING)_CrimeName,(STRING)_CustomReactionName) + AND + _CustomReactionName != "" + THEN + ProcCrimeSetCustomSensibleActionVariable(_Character,_CrimeName,_CustomReactionName,"Primary_"); + + + // Secondary + PROC + ProcCrimeSetCustomSecondarySensibleAction((CHARACTERGUID)_Character,(STRING)_CrimeName,(STRING)_CustomReactionName) + AND + DB_CrimeReaction_CustomSensibleAction(_Character,_CrimeName,0) + AND + StringConcatenate("Setting a custom secondary sensible action [",_CustomReactionName,_Part1) + AND + StringConcatenate(_Part1,"] on a character that already have one for crime: ",_Part2) + AND + StringConcatenate(_Part2,_CrimeName,_Message) + THEN + DebugBreak(_Message); + + PROC + ProcCrimeSetCustomSecondarySensibleAction((CHARACTERGUID)_Character,(STRING)_CrimeName,(STRING)_) + THEN + DB_CrimeReaction_CustomSensibleAction(_Character,_CrimeName,0); + + PROC + ProcCrimeSetCustomSecondarySensibleAction((CHARACTERGUID)_Character,(STRING)_CrimeName,(STRING)_CustomReactionName) + AND + _CustomReactionName == "" + THEN + ProcCrimeSetCustomSensibleActionVariable(_Character,_CrimeName,"CRIME_UndefinedSensibleAction","Secondary_"); + + PROC + ProcCrimeSetCustomSecondarySensibleAction((CHARACTERGUID)_Character,(STRING)_CrimeName,(STRING)_CustomReactionName) + AND + _CustomReactionName != "" + THEN + ProcCrimeSetCustomSensibleActionVariable(_Character,_CrimeName,_CustomReactionName,"Secondary_"); + + PROC + ProcCrimeSetCustomSensibleActionVariable((CHARACTERGUID)_Character,(STRING)_CrimeName,(STRING)_CustomReactionName,(STRING)_Type) + AND + StringConcatenate("Custom",_Type,_Prefix) + AND + StringConcatenate(_Prefix,_CrimeName,_VarName) + THEN + SetVarFixedString(_Character,_VarName,_CustomReactionName); + + + // --- Clearing + PROC + ProcCrimeClearCustomPrimarySensibleAction((CHARACTERGUID)_Character,(STRING)_CrimeName) + AND + StringConcatenate("CustomPrimary_",_CrimeName,_VarName) + THEN + SetVarFixedString(_Character,_VarName,""); + + PROC + ProcCrimeClearCustomPrimarySensibleAction((CHARACTERGUID)_Character,(STRING)_CrimeName) + AND + DB_CrimeReaction_CustomSensibleAction(_Character,_CrimeName,1) + THEN + NOT DB_CrimeReaction_CustomSensibleAction(_Character,_CrimeName,1); + + PROC + ProcCrimeClearCustomSecondarySensibleAction((CHARACTERGUID)_Character,(STRING)_CrimeName) + AND + StringConcatenate("CustomSecondary_",_CrimeName,_VarName) + THEN + SetVarFixedString(_Character,_VarName,""); + + PROC + ProcCrimeClearCustomSecondarySensibleAction((CHARACTERGUID)_Character,(STRING)_CrimeName) + AND + DB_CrimeReaction_CustomSensibleAction(_Character,_CrimeName,0) + THEN + NOT DB_CrimeReaction_CustomSensibleAction(_Character,_CrimeName,0); + + PROC + ProcCrimeClearAllCustomSensibleAction((CHARACTERGUID)_Character,(STRING)_CrimeName) + AND + StringConcatenate("CustomPrimary_",_CrimeName,_PrimaryVarName) + AND + StringConcatenate("CustomSecondary_",_CrimeName,_SecondaryVarName) + THEN + SetVarFixedString(_Character,_PrimaryVarName,""); + SetVarFixedString(_Character,_SecondaryVarName,""); + + PROC + ProcCrimeClearAllCustomSensibleAction((CHARACTERGUID)_Character,(STRING)_CrimeName) + AND + DB_CrimeReaction_CustomSensibleAction(_Character,_CrimeName,_Type) + THEN + NOT DB_CrimeReaction_CustomSensibleAction(_Character,_CrimeName,_Type); + + + // --- Throwing a proc to allow reacting in story + IF + CharacterOnCrimeSensibleActionNotification(_NPC,_Region,_CrimeID,_PriorityName,_Dialog,_Criminal,_Criminal2,_Criminal3,_Criminal4,_IsPrimary) + AND + CrimeGetType(_CrimeID,_CrimeName) + AND + DB_CrimeReaction_CustomSensibleAction(_NPC,_CrimeName,_IsPrimary) + THEN + ProcCrimeOnCustomSensibleAction(_NPC,_Region,_CrimeID,_PriorityName,_Dialog,_Criminal,_Criminal2,_Criminal3,_Criminal4,_IsPrimary); + + PROC + ProcCrimeOnCustomSensibleAction((CHARACTERGUID)_NPC, (STRING)_RegionID, (INTEGER)_CrimeID, (STRING)_PriorityName, (STRING)_PrimaryDialog, (CHARACTERGUID)_Criminal1, (CHARACTERGUID)_Criminal2, (CHARACTERGUID)_Criminal3, (CHARACTERGUID)_Criminal4, (INTEGER)_IsPrimary) + THEN + DB_NOOP(1); + + /** + * IMPORTANT! Call this event on the NPC who's reacting to the crime: + + SetStoryEvent(_NPC,"Crime_CustomSensibleActionDone"); + + */ + //END_REGION + + //REGION Override investigation + + /* Put your character in this fact to disable the interractive interrogation dialog: + DB_CrimeReaction_DoNotInterrogate(_Interrogator) + */ + + IF + DB_CrimeReaction_DoNotInterrogate((CHARACTERGUID)_Char) + THEN + DB_Crime_CheckInterrogate(_Char); + CrimeEnableInterrogation(_Char,0); + + IF + DB_Crime_CheckInterrogate(_Char) + AND + NOT DB_CrimeReaction_DoNotInterrogate(_Char) + THEN + NOT DB_Crime_CheckInterrogate(_Char); + CrimeEnableInterrogation(_Char,1); + + PROC + ProcCrimeSetCustomInvestigationAD((CHARACTERGUID)_Character,(STRING)_Dialog) + THEN + SetVarString(_Character,"CRIME_InvestigateAD",_Dialog); + + + //END_REGION + + //REGION Linked crimes + PROC + ProcCharacterDisableCrime((CHARACTERGUID)_Char,_Crime) + AND + DB_LinkedCrimes(_Crime,_LinkedCrime) + THEN + DB_CharacterCrimeDisabled(_Char,_LinkedCrime); + CharacterDisableCrime(_Char,_LinkedCrime); + + PROC + ProcCharacterEnableCrime((CHARACTERGUID)_Char,_Crime) + AND + DB_LinkedCrimes(_Crime,_LinkedCrime) + THEN + DB_CharacterCrimeEnabled(_Char,_LinkedCrime); + CharacterEnableCrime(_Char,_LinkedCrime); + + IF + DB_CrimeAttitudeChange("Assault",_Amount) + THEN + DB_CrimeAttitudeChange("IncapacitatedAssault",_Amount); + + //END_REGION + + + //REGION Zero-tension crimes without fleeing + // Zero-tension crimes that don't cause people to flee explicitly should not cause people to + // return to their starting position at the end (DOSTWO-24283) + IF + CharacterOnCrimeSensibleActionNotification(_Char,_,_ID,_Reaction,_,_,_,_,_,_) + AND + DB_CRIME_FleeReaction(_Rection) + AND + CrimeGetTension(_ID,0) + THEN + DB_CRIME_ZeroTensionFlee(_ID,_Char); + + IF + OnCrimeConfrontationDone(_ID,_Investigator,_,_,_,_,_) + AND + CrimeGetTension(_ID,0) + AND + NOT DB_CRIME_ZeroTensionFlee(_ID,_Investigator) + THEN + SetStoryEvent(_Investigator,"ClearCrimeReturnPos"); + + IF + OnCrimeConfrontationDone(_ID,_Investigator,_,_,_,_,_) + THEN + NOT DB_CRIME_ZeroTensionFlee(_ID,_Investigator); + //END_REGION + + } + EXIT + { + + + } +} +Goal(16).Title("_CRIME_ItemOwnership"); +Goal(16) +{ + INIT + { + DB_ItemOwnerShipIgnoreTemplates("LTS_Campfire_A_6fee7bbb-4adc-4222-a5b1-82fcfb8d1230"); + DB_ItemOwnerShipIgnoreTemplates("LTS_Campfire_B_130692f2-a065-4168-be66-27cf4df2fff0"); + DB_ItemOwnerShipIgnoreTemplates("LTS_Campfire_C_1086cce2-9164-4746-b3c3-2f8c943cb8fc"); + DB_ItemOwnerShipIgnoreTemplates("FUR_Humans_Camping_Sleepingbag_A_d76d0118-23cd-42af-b693-449c96c71d6d"); + DB_ItemOwnerShipIgnoreTemplates("FUR_Humans_Citz_Mattress_A_DarkBrown_eae64571-4e99-4036-b3e2-1dcc6da78c89"); + DB_ItemOwnerShipIgnoreTemplates("LTS_Candle_A_f21393ff-31bc-46ff-a024-985e72cd83f5"); + DB_ItemOwnerShipIgnoreTemplates("LTS_Candle_B_d5e83188-69f5-428f-8bca-64f00b441aef"); + DB_ItemOwnerShipIgnoreTemplates("LTS_Candle_C_b98b4f53-5cf3-46b1-8ecd-278f178a9816"); + DB_ItemOwnerShipIgnoreTemplates("LTS_Candle_D_511c5c17-2ed0-4b2e-a4b5-425621cb61ac"); + DB_ItemOwnerShipIgnoreTemplates("LTS_Candle_E_f497d3af-8837-4183-9bf4-aef2ef1145d1"); + DB_ItemOwnerShipIgnoreTemplates("LTS_Candle_F_0121d78b-8831-4607-9c2f-db63edcdfbb6"); + DB_ItemOwnerShipIgnoreTemplates("LTS_Candle_G_83e373a2-ca7b-4c35-8761-d04605aaabca"); + DB_ItemOwnerShipIgnoreTemplates("LTS_Candle_H_49acf8f4-946e-4f62-a12e-e85d332f3d96"); + DB_ItemOwnerShipIgnoreTemplates("LTS_Candle_I_728b84a6-3929-48eb-9368-631a0614e38d"); + DB_ItemOwnerShipIgnoreTemplates("LTS_Candle_J_b8b63ed1-7c28-4c68-90b2-e83db433467b"); + DB_ItemOwnerShipIgnoreTemplates("LTS_Candle_K_ef4d1dd9-8d2a-4677-98c3-bb9dd52c0865"); + DB_ItemOwnerShipIgnoreTemplates("BOOK_ARX_Letter_B_ShadyTraderAd_cb5bd6ad-ae01-4ba9-bd95-752f9d659316"); + + } + KB + { + PROC + PROC_Init_SetItemOwners((STRING)_Region) + AND + DB_ItemOwnerShipTriggers(_Region,_Trigger,_Owner) + THEN + TriggerSetItemOwner(_Trigger,_Owner); + ProcTriggerRegisterForPlayers(_Trigger); + NOT DB_ItemOwnerShipTriggers(_Region,_Trigger,_Owner); + DB_TempItemOwnerShipTrigger(_Region,_Trigger); + DB_OwnershipTrigger(_Trigger); + + PROC + PROC_Init_ClearItemOwners((STRING)_Region) + AND + DB_ItemOwnerShipClearItem(_Region,_Item) + THEN + ItemClearOwner(_Item); + NOT DB_ItemOwnerShipClearItem(_Region,_Item); + + PROC + PROC_Init_ClearItemOwners((STRING)_Region) + AND + DB_TempItemOwnerShipTrigger((STRING)_Region,(TRIGGERGUID)_Trigger) + AND + DB_ItemOwnerShipIgnoreTemplates(_Temp) + THEN + NOT DB_TempItemOwnerShipTrigger(_Region,_Trigger); + TriggerClearItemTemplateOwners(_Trigger,_Temp); + + IF + DB_ItemOwnerShipTriggers(_Region,_Trigger,_Owner) + AND + DB_CurrentLevel(_Region) + THEN + PROC_Init_SetItemOwners(_Region); + PROC_Init_ClearItemOwners(_Region); + + IF + RegionStarted(_Region) + THEN + PROC_Init_SetItemOwners(_Region); + PROC_Init_ClearItemOwners(_Region); + + } + EXIT + { + + } +} +Goal(17).Title("_CRIME_Prison"); +Goal(17) +{ + INIT + { + //Setting up Flags for the Arrest Dialogs + DB_ArrestDialogFlags("Assault","GEB_Arrest_Assault"); + DB_ArrestDialogFlags("SpiritTalk","GEB_Arrest_Source"); + DB_ArrestDialogFlags("SourceMagic","GEB_Arrest_Source"); + DB_ArrestDialogFlags("Steal","GEB_Arrest_Theft"); + DB_ArrestDialogFlags("PickPocketFailed","GEB_Arrest_Theft"); + DB_ArrestDialogFlags("PickPocket","GEB_Arrest_Theft"); + DB_ArrestDialogFlags("ItemDestroy","GEB_Arrest_Vandalise"); + DB_ArrestDialogFlags("Vandalise","GEB_Arrest_Vandalise"); + DB_ArrestDialogFlags("VandaliseNoOwner","GEB_Arrest_Vandalise"); + DB_ArrestDialogFlags("Trespassing","GEB_Arrest_Trespassing"); + DB_ArrestDialogFlags("UseForbiddenItem","GEB_Arrest_UseForbiddenItem"); + DB_ArrestDialogFlags("SneakUseForbiddenItem","GEB_Arrest_UseForbiddenItem"); + + DB_ReactOutOfRegion("RED PRINCE"); + DB_ReactOutOfRegion("IFAN"); + DB_ReactOutOfRegion("LOHSE"); + DB_ReactOutOfRegion("SEBILLE"); + DB_ReactOutOfRegion("BEAST"); + DB_ReactOutOfRegion("FANE"); + DB_ReactOutOfRegion("AGGRESSIVEANIMAL"); + + } + KB + { + IF + DB_RegionPrison(_RegionName,(TRIGGERGUID)_PrisonTrigger) + THEN + ProcTriggerRegisterForPlayers(_PrisonTrigger); + + QRY + QryNPCIsGuard((GUIDSTRING)_NPC) + AND + IsTagged(_Npc,"GUARD",1) + THEN + DB_Noop(1); + + QRY + QryNPCIsGuard((GUIDSTRING)_NPC) + AND + IsTagged(_Npc,"PALADIN",1) + THEN + DB_Noop(1); + + QRY + QryNPCIsGuard((GUIDSTRING)_NPC) + AND + IsTagged(_Npc,"MAGISTER",1) + THEN + DB_Noop(1); + + //REGION Start Arrest + //Wait for dialog to end to arrest Player // Needs to be removed + IF + ObjectFlagSet("Start_Arrest_AfterDialog",(CHARACTERGUID)_Character,_ID) + THEN + ObjectClearFlag(_Character,"Start_Arrest_AfterDialog",0); + DB_ArrestAfterDialog(_Character,_ID); + + + ///////// Handle Evidence //////////// + IF + ObjectFlagSet("Allow_Arrest",(CHARACTERGUID)_Player,_ID) + THEN + DB_ArrestDialog(_Player,_ID); + + IF + DialogEnded(_,_ID) + AND + DB_ArrestDialog(_Player,_ID) + AND + DB_DialogNPCs(_ID,_NPC,1) + AND + ObjectGetFlag((CHARACTERGUID)_NPC,"CRIME_FoundEvidenceCurrentCrime",1) + AND + DB_Interrogation(_NPC,_CrimeID) + THEN + ProcCrimeHandleEvidence(_Player,_NPC,_CrimeID); + + PROC + ProcCrimeHandleEvidence((CHARACTERGUID)_Player,(CHARACTERGUID)_Arrester,(INTEGER)_CrimeID) + AND + CharacterGetCrimeRegion(_Player,_CrimeRegionID) + AND + DB_RegionPrison(_CrimeRegionID,_PrisonTrigger) + AND + DB_PrisonEvidenceChest((TRIGGERGUID)_PrisonTrigger,(ITEMGUID)_EvidenceChest) + THEN + CrimeTransferEvidenceTo(_CrimeID,_EvidenceChest); + + PROC + ProcCrimeHandleEvidence((CHARACTERGUID)_Player,(CHARACTERGUID)_Arrester,(INTEGER)_CrimeID) + AND + CharacterGetCrimeRegion(_Player,_CrimeRegionID) + AND + DB_RegionPrison(_CrimeRegionID,_PrisonTrigger) + AND + NOT DB_PrisonEvidenceChest((TRIGGERGUID)_PrisonTrigger,_) + THEN + CrimeTransferEvidenceTo(_CrimeID,_Arrester); + + + ///////// Start Arrest //////////// + IF + DialogEnded(_,_ID) + AND + DB_ArrestAfterDialog(_Character,_ID) + THEN + NOT DB_ArrestAfterDialog(_Character,_ID); + ProcCrimeStartArrest((CHARACTERGUID)_Character,_ID); + + PROC + ProcCrimeStartArrest((CHARACTERGUID)_Character,(INTEGER)_ID) + AND + CharacterGetCrimeRegion(_Character,_CrimeRegion) + THEN + ProcCrimeDoPerformArrest(_ID,_CrimeRegion); + + PROC + ProcCrimeDoPerformArrest((INTEGER)_Inst,(STRING)_Region) + AND + DB_DialogNPCs(_Inst,_Player,_) + AND + CharacterIsPlayer((CHARACTERGUID)_Player,1) + THEN + ProcCrimePerformArrest(_Player,_Region); + + PROC + ProcCrimeDoPerformArrest((INTEGER)_Inst,(STRING)_Region) + AND + DB_DialogPlayers(_Inst,_Player,_) + AND + CharacterIsPlayer((CHARACTERGUID)_Player,1) + THEN + ProcCrimePerformArrest(_Player,_Region); + + PROC + ProcCrimePerformArrest((CHARACTERGUID)_Character,(STRING)_CrimeRegionID) + AND + DB_RegionPrison(_CrimeRegionID,_PrisonTrigger) + THEN + ProcCrimeTeleportCharacterToPrison(_Character,_PrisonTrigger); + + PROC //--- can be used outside of the Crime System + ProcCrimeTeleportCharacterToPrison((CHARACTERGUID)_Character,(TRIGGERGUID)_PrisonTrigger) + THEN + ObjectSetFlag(_Character,"IsInPrison"); + RemoveStatus(_Character,"FUGITIVE"); + CharacterDetachFromGroup(_Character); + Proc_TeleportSmoke(_Character); + TeleportTo(_Character,_PrisonTrigger,"",0); + Proc_TeleportSmoke(_Character); + DB_IsArrested(_Character); + CharacterFlushQueue(_Character); + LeaveCombat(_Character); + CharacterRemoveTension(_Character); + + PROC + ProcCrimeTeleportCharacterToPrison((CHARACTERGUID)_Character,(TRIGGERGUID)_PrisonTrigger) + AND + DB_PrisonChest((TRIGGERGUID)_PrisonTrigger,(ITEMGUID)_PrisonChest) + THEN + MoveAllItemsTo(_Character,_PrisonChest,0,1,1); + + //END_REGION + + //REGION Arrest dialog starting + + IF + CharacterOnCrimeSensibleActionNotification(_Arrester,_,_CrimeID,"CRIME_Arrest",_,_,_,_,_,_) + AND + CrimeGetType(_CrimeID,_CrimeName) + AND + NOT DB_CrimeReaction_CustomSensibleAction(_Arrester,_CrimeName) + THEN + Proc_StartArrestDialog(_Arrester); + + IF + StoryEvent(_Arrester,"ArrestOnRequest") + AND + GetVarInteger(_Arrester,"CrimeID",_CrimeID) + THEN + SetVarString(_Arrester,"ArrestDialog","GEB_Arrest"); + ProcStartArrest((CHARACTERGUID)_Arrester,(INTEGER)_CrimeID); + + IF + StoryEvent((CHARACTERGUID)_Arrester,"ArrestOnRequest") + AND + DB_Arresting(_Arrester,_CrimeID,(CHARACTERGUID)_Criminal1,(CHARACTERGUID)_Criminal2,(CHARACTERGUID)_Criminal3,(CHARACTERGUID)_Criminal4,_ArrestDialog) + THEN + CharacterMoveToAndTalk(_Arrester,_Criminal1,"",0,"GEB_ArrestMove",1,10.0); + + IF + StoryEvent((CHARACTERGUID)_Arrester, "CRIME_Perform_Arrest") + AND + GetVarInteger(_Arrester,"CrimeID",_CrimeID) + THEN + ProcStartArrest(_Arrester,_CrimeID); + + PROC + ProcStartArrest((CHARACTERGUID)_Arrester,(INTEGER)_CrimeID) + AND + GetVarObject(_Arrester,"Criminal1",_Criminal1) + AND + GetVarObject(_Arrester,"Criminal2",_Criminal2) + AND + GetVarObject(_Arrester,"Criminal3",_Criminal3) + AND + GetVarObject(_Arrester,"Criminal4",_Criminal4) + AND + GetVarString(_Arrester,"ArrestDialog",_ArrestDialog) + THEN + DB_Arresting(_Arrester,_CrimeID,(CHARACTERGUID)_Criminal1,(CHARACTERGUID)_Criminal2,(CHARACTERGUID)_Criminal3,(CHARACTERGUID)_Criminal4,_ArrestDialog); + SetHasDialog(_Arrester,0); + CharacterDisableAllCrimes(_Arrester); + DialogRequestStop(_Arrester); + + IF + DialogEnded(_,_Instance) + AND + DB_DialogPlayers(_Instance,_Criminal,1) + AND + DB_Arresting(_Arrester,_CrimeID,(CHARACTERGUID)_Criminal,_,_,_,_ArrestDialog) + THEN + CharacterMoveToAndTalk(_Arrester,_Criminal,"",0,"GEB_ArrestMove",1,10.0); + + PROC + ProcStartArrest((CHARACTERGUID)_Arrester,(INTEGER)_CrimeID) + AND + NOT DB_Arresting(_Arrester,_CrimeID,_,_,_,_,_) + THEN + CrimeConfrontationDone(_CrimeID,_Arrester); + + PROC + ProcCleanupArrest((CHARACTERGUID)_Arrester) + AND + DB_Arresting(_Arrester,_CrimeID,_Criminal1,_Criminal2,_Criminal3,_Criminal4,_ArrestDialog) + THEN + ProcRestoreFightMode(_Arrester); + ProcRestoreGenericBehaviour(_Arrester); + SetHasDialog(_Arrester,1); + NOT DB_Arresting(_Arrester,_CrimeID,_Criminal1,_Criminal2,_Criminal3,_Criminal4,_ArrestDialog); + SetStoryEvent(_Arrester,"CRIME_StartArrest"); + + IF + CharacterMoveToAndTalkRequestDialog(_Arrester,(CHARACTERGUID)_Criminal,_,_,"GEB_ArrestMove") + AND + DB_Arresting(_Arrester,_CrimeID,_Criminal1,_Criminal2,_Criminal3,_Criminal4,_ArrestDialog) + THEN + SetStoryEvent(_Arrester,"CRIME_StartArrest"); + Proc_StartArrestDialog(_Arrester,_CrimeID,_Criminal1,_Criminal2,_Criminal3,_Criminal4,_ArrestDialog); + + IF + CharacterMoveToAndTalkFailed(_Arrester,_,"GEB_ArrestMove") + AND + DB_Arresting(_Arrester,_CrimeID,_Criminal1,_Criminal2,_Criminal3,_Criminal4,_ArrestDialog) + THEN + CrimeConfrontationDone(_CrimeID,_Arrester); + ProcCleanupArrest(_Arrester); + + IF + DB_Crime_FailedToInterruptStoryDialogs(_CrimeID,_ArrestDialog,_Arrester,_Criminal,_Criminal2,_Criminal3,_Criminal4) + AND + DB_Arresting(_Arrester,_CrimeID,_Criminal1,_Criminal2,_Criminal3,_Criminal4,_ArrestDialog) + THEN + CharacterMoveToAndTalkRequestDialogFailed(_Arrester,_Criminal,"GEB_ArrestMove"); + + IF + ObjectFlagSet(_ActiveFlag,(CHARACTERGUID)_Arrester,_) + AND + DB_ArrestDialogFlags(_CrimeName,_ActiveFlag) + AND + DB_Arresting(_Arrester,_CrimeID,_Criminal1,_Criminal2,_Criminal3,_Criminal4,_ArrestDialog) + THEN + ProcCleanupArrest(_Arrester); + + IF + ObjectEnteredCombat((CHARACTERGUID)_Arrester,_) + AND + DB_Arresting(_Arrester,_,_,_,_,_,_) + THEN + ProcCleanupArrest(_Arrester); + + PROC + Proc_StartArrestDialog((CHARACTERGUID)_Arrester) + AND + GetVarObject(_Arrester,"Criminal1",(CHARACTERGUID)_Criminal1) + AND + GetVarObject(_Arrester,"Criminal2",(CHARACTERGUID)_Criminal2) + AND + GetVarObject(_Arrester,"Criminal3",(CHARACTERGUID)_Criminal3) + AND + GetVarObject(_Arrester,"Criminal4",(CHARACTERGUID)_Criminal4) + AND + GetVarInteger(_Arrester,"CrimeID",_CrimeID) + AND + GetVarString(_Arrester,"ArrestDialog",_ArrestDialog) + THEN + Proc_StartArrestDialog(_Arrester,_CrimeID,_Criminal1,_Criminal2,_Criminal3,_Criminal4,_ArrestDialog); + + PROC + Proc_StartArrestDialog((CHARACTERGUID)_Arrester,(INTEGER)_CrimeID,(CHARACTERGUID)_Criminal1,(CHARACTERGUID)_Criminal2,(CHARACTERGUID)_Criminal3,(CHARACTERGUID)_Criminal4,(STRING)_ArrestDialog) + THEN + ObjectClearFlag(_Arrester,"GEB_Arrest_HavePrison",0); + + PROC + Proc_StartArrestDialog((CHARACTERGUID)_Arrester,(INTEGER)_CrimeID,(CHARACTERGUID)_Criminal1,(CHARACTERGUID)_Criminal2,(CHARACTERGUID)_Criminal3,(CHARACTERGUID)_Criminal4,(STRING)_ArrestDialog) + AND + DB_ArrestDialogFlags(_CrimeName,_ActiveFlag) + AND + ObjectGetFlag(_Arrester,_ActiveFlag,1) + THEN + ObjectClearFlag(_Arrester,_ActiveFlag,0); + + PROC + Proc_StartArrestDialog((CHARACTERGUID)_Arrester,(INTEGER)_CrimeID,(CHARACTERGUID)_Criminal1,(CHARACTERGUID)_Criminal2,(CHARACTERGUID)_Criminal3,(CHARACTERGUID)_Criminal4,(STRING)_ArrestDialog) + AND + CrimeGetType(_CrimeID,_CrimeName) + AND + DB_ArrestDialogFlags(_CrimeName,_ActiveFlag) + THEN + ObjectSetFlag(_Arrester,_ActiveFlag); + + + //--- Setup the Arrester's script + PROC + Proc_StartArrestDialog((CHARACTERGUID)_Arrester,(INTEGER)_CrimeID,(CHARACTERGUID)_Criminal1,(CHARACTERGUID)_Criminal2,(CHARACTERGUID)_Criminal3,(CHARACTERGUID)_Criminal4,(STRING)_ArrestDialog) + AND + GetVarFixedString(_Arrester,"RegionID",_RegionID) + AND + CrimeGetType(_CrimeID,_CrimeType) + THEN + ProcCrimeSetupCountFlag(_CrimeID,_Arrester,_Criminal1,_Criminal2,_Criminal3,_Criminal4,_CrimeType,_ArrestDialog); + ProcCrimeStartRegionArrestInterrogation(_RegionID,_CrimeID,_ArrestDialog,_Arrester,_Criminal1,_Criminal2,_Criminal3,_Criminal4); + + PROC + ProcCheckStartArrestInterrogation((INTEGER)_CrimeID,(STRING)_ArrestDialog,(CHARACTERGUID)_Arrester,(CHARACTERGUID)_Criminal,(CHARACTERGUID)_Criminal2,(CHARACTERGUID)_Criminal3,(CHARACTERGUID)_Criminal4) + AND + NOT DB_Crime_FailedToInterruptStoryDialogs(_CrimeID,_ArrestDialog,_Arrester,_Criminal,_Criminal2,_Criminal3,_Criminal4) + THEN + ProcCrimeStartArrestInterrogation(_CrimeID,_Arrester,_Criminal,_Criminal2,_Criminal3,_Criminal4,1); + + PROC + ProcCheckStartArrestInterrogation((INTEGER)_CrimeID,(STRING)_ArrestDialog,(CHARACTERGUID)_Arrester,(CHARACTERGUID)_Criminal,(CHARACTERGUID)_Criminal2,(CHARACTERGUID)_Criminal3,(CHARACTERGUID)_Criminal4) + AND + DB_Crime_FailedToInterruptStoryDialogs(_CrimeID,_ArrestDialog,_Arrester,_Criminal,_Criminal2,_Criminal3,_Criminal4) + THEN + ProcCrimeStartArrestInterrogation(_CrimeID,_Arrester,_Criminal,_Criminal2,_Criminal3,_Criminal4,0); + NOT DB_Crime_FailedToInterruptStoryDialogs(_CrimeID,_ArrestDialog,_Arrester,_Criminal,_Criminal2,_Criminal3,_Criminal4); + + PROC + ProcCrimeStartRegionArrestInterrogation((STRING)_RegionID,(INTEGER)_CrimeID,(STRING)_ArrestDialog,(CHARACTERGUID)_Arrester,(CHARACTERGUID)_Criminal,(CHARACTERGUID)_Criminal2,(CHARACTERGUID)_Criminal3,(CHARACTERGUID)_Criminal4) + AND + DB_RegionPrison(_RegionID,_) + THEN + ObjectSetFlag(_Arrester,"GEB_Arrest_HavePrison"); + + PROC + ProcCrimeStartRegionArrestInterrogation((STRING)_RegionID,(INTEGER)_CrimeID,(STRING)_ArrestDialog,(CHARACTERGUID)_Arrester,(CHARACTERGUID)_Criminal,(CHARACTERGUID)_Criminal2,(CHARACTERGUID)_Criminal3,(CHARACTERGUID)_Criminal4) + AND + _RegionID != "" + THEN + ProcCrimeInterruptStoryDialogs(_CrimeID,_ArrestDialog,1,_Arrester,_Criminal,_Criminal2,_Criminal3,_Criminal4); + ProcCheckStartArrestInterrogation(_CrimeID,_ArrestDialog,_Arrester,_Criminal,_Criminal2,_Criminal3,_Criminal4); + + PROC + ProcCrimeStartRegionArrestInterrogation((STRING)_RegionID,(INTEGER)_CrimeID,(STRING)_ArrestDialog,(CHARACTERGUID)_Arrester,(CHARACTERGUID)_Criminal,(CHARACTERGUID)_Criminal2,(CHARACTERGUID)_Criminal3,(CHARACTERGUID)_Criminal4) + AND + _RegionID == "" + AND + DB_ReactOutOfRegion(_Tag) + AND + IsTagged(_Arrester,_Tag,1) + AND + NOT DB_ReactingCharOutOfRegion(_Arrester,_) + THEN + ProcCrimeInterruptStoryDialogs(_CrimeID,_ArrestDialog,1,_Arrester,_Criminal,_Criminal2,_Criminal3,_Criminal4); + ProcCheckStartArrestInterrogation(_CrimeID,_ArrestDialog,_Arrester,_Criminal,_Criminal2,_Criminal3,_Criminal4); + DB_ReactingCharOutOfRegion(_Arrester,_Tag); + + + // We change crime dialog in the next section, so replace it in the DB which is used to clean some stuff on crime end + PROC + ProcCrimeStartRegionArrestInterrogation((STRING)_RegionID,(INTEGER)_CrimeID,(STRING)_ArrestDialog,(CHARACTERGUID)_Arrester,(CHARACTERGUID)_Criminal,(CHARACTERGUID)_Criminal2,(CHARACTERGUID)_Criminal3,(CHARACTERGUID)_Criminal4) + AND + _RegionID == "" + AND + NOT DB_ReactingCharOutOfRegion(_Arrester,_) + THEN + ProcCrimeStartRegionArrestInterrogation_SubActions(_RegionID,_CrimeID,_ArrestDialog,_Arrester,_Criminal,_Criminal2,_Criminal3,_Criminal4); + + PROC + ProcCrimeStartRegionArrestInterrogation_SubActions((STRING)_RegionID,(INTEGER)_CrimeID,(STRING)_ArrestDialog,(CHARACTERGUID)_Arrester,(CHARACTERGUID)_Criminal,(CHARACTERGUID)_Criminal2,(CHARACTERGUID)_Criminal3,(CHARACTERGUID)_Criminal4) + AND + DB_Crime_RequestedDialogWithTension(_CrimeName,_ArrestDialog,_Arrester,_Criminal,_Criminal2,_Criminal3,_Criminal4) + THEN + NOT DB_Crime_RequestedDialogWithTension(_CrimeName,_ArrestDialog,_Arrester,_Criminal,_Criminal2,_Criminal3,_Criminal4); + DB_Crime_RequestedDialogWithTension(_CrimeName,"GEB_Attack",_Arrester,_Criminal,_Criminal2,_Criminal3,_Criminal4); + + PROC + ProcCrimeStartRegionArrestInterrogation_SubActions((STRING)_RegionID,(INTEGER)_CrimeID,(STRING)_ArrestDialog,(CHARACTERGUID)_Arrester,(CHARACTERGUID)_Criminal,(CHARACTERGUID)_Criminal2,(CHARACTERGUID)_Criminal3,(CHARACTERGUID)_Criminal4) + THEN + ProcCrimeInterruptStoryDialogs(_CrimeID,"GEB_Attack",1,_Arrester,_Criminal,_Criminal2,_Criminal3,_Criminal4); + ProcCheckStartArrestInterrogation(_CrimeID,"GEB_Attack",_Arrester,_Criminal,_Criminal2,_Criminal3,_Criminal4); + + PROC + ProcCrimeStartRegionArrestInterrogation((STRING)_RegionID,(INTEGER)_CrimeID,(STRING)_ArrestDialog,(CHARACTERGUID)_Arrester,(CHARACTERGUID)_Criminal,(CHARACTERGUID)_Criminal2,(CHARACTERGUID)_Criminal3,(CHARACTERGUID)_Criminal4) + AND + DB_ReactingCharOutOfRegion(_Arrester,_Tag) + THEN + NOT DB_ReactingCharOutOfRegion(_Arrester,_Tag); + + PROC + ProcCrimeStartArrestInterrogation((INTEGER)_CrimeID,(CHARACTERGUID)_Arrester,(CHARACTERGUID)_Criminal,(CHARACTERGUID)_Criminal2,(CHARACTERGUID)_Criminal3,(CHARACTERGUID)_Criminal4,_) + AND + DB_EvidenceFound(_CrimeID,_Interrogator) + AND + DB_Crime_CallingGuards(_Interrogator,_Criminal,_ID) + THEN + NOT DB_Crime_CallingGuards(_Interrogator,_Criminal,_ID); + NOT DB_EvidenceFound(_CrimeID,_Interrogator); + DB_EvidenceFound(_CrimeID,_Arrester); + + PROC + ProcCrimeStartArrestInterrogation((INTEGER)_CrimeID,(CHARACTERGUID)_Arrester,(CHARACTERGUID)_Criminal,(CHARACTERGUID)_Criminal2,(CHARACTERGUID)_Criminal3,(CHARACTERGUID)_Criminal4,1) + AND + DB_EvidenceFound(_CrimeID,_Interrogator) + AND + DB_Crime_CallingGuards(_Interrogator,_Criminal,_ID) + THEN + DB_StopInterrogationAfterDialog(_CrimeID,_Arrester,_Criminal,_Criminal2,_Criminal3,_Criminal4); + + PROC + ProcCrimeStartArrestInterrogation((INTEGER)_CrimeID,(CHARACTERGUID)_Arrester,(CHARACTERGUID)_Criminal,(CHARACTERGUID)_Criminal2,(CHARACTERGUID)_Criminal3,(CHARACTERGUID)_Criminal4,0) + AND + DB_Crime_Interrogation(_CrimeID,_,_,_,_,_,_) + THEN + ProcStopInterrogation(_CrimeID,_Arrester,_Criminal,_Criminal2,_Criminal3,_Criminal4); + + IF + DialogEnded(_,_Instance) + AND + DB_DialogPlayers(_Instance,_Player,1) + AND + DB_DialogNPCs(_Instance,_NPC,1) + AND + ObjectGetFlag(_Player,"Resist_Arrest",1) + AND + DB_DialogPlayers(_Instance,_Players,_) + THEN + ObjectClearFlag(_Player,"Resist_Arrest",0); + proc_CriminalMakeHostileAfterDialog((CHARACTERGUID)_Players,(CHARACTERGUID)_NPC); + + PROC + proc_CriminalMakeHostileAfterDialog((CHARACTERGUID)_Player,(CHARACTERGUID)_NPC) + AND + _Player != NULL_00000000-0000-0000-0000-000000000000 + THEN + ProcMakeNPCHostile((CHARACTERGUID)_Player,(CHARACTERGUID)_NPC); + + IF + DialogEnded(_,_Instance) + AND + DB_ArrestDialog(_Player,_Instance) + THEN + ObjectClearFlag(_Player,"Allow_Arrest",0); + NOT DB_ArrestDialog(_Player,_Instance); + ProcCrimeStartArrest((CHARACTERGUID)_Player,_Instance); + + IF + DialogEnded(_,_Instance) + AND + DB_DialogNPCs(_Instance,_NPC,1) + AND + DB_StopInterrogationAfterDialog(_CrimeID,(CHARACTERGUID)_NPC,_Criminal,_Criminal2,_Criminal3,_Criminal4) + THEN + ProcStopInterrogation(_CrimeID,_NPC,_Criminal,_Criminal2,_Criminal3,_Criminal4); + NOT DB_StopInterrogationAfterDialog(_CrimeID,_NPC,_Criminal,_Criminal2,_Criminal3,_Criminal4); + + // TODO Remove the DB_ReactingCharOutOfRegion(_Arrester,_Tag) On Tag Removed + //END_REGION + + //REGION Arrest Play As AD + IF + StoryEvent((CHARACTERGUID)_Arrester,"GLO_PlayArrestAsAD") + AND + GetVarString(_Arrester,"ArrestDialog",_ArrestDialog) + THEN + Proc_StartDialog(1,_ArrestDialog,_Arrester); + //END_REGION + + //REGION Escape Prison + IF + CharacterEnteredTrigger(_Player,_PrisonTrigger) + AND + DB_IsArrested(_Player) + AND + DB_IsPlayer(_Player) + AND + DB_PlayerPrison_Door(_PrisonTrigger,(ITEMGUID)_CellDoor,(STRING)_KeyName) + THEN + ItemCloseAndLock(_CellDoor,_KeyName); + + IF + CharacterLeftTrigger(_Player,_PrisonTrigger) + AND + DB_IsArrested(_Player) + AND + DB_IsPlayer(_Player) + AND + DB_PlayerPrison((TRIGGERGUID)_PrisonTrigger,(TRIGGERGUID)_CellDoorTrigger,(STRING)_PrisonCrimeName,(STRING)_PrisonCrimeNameAD) + AND + GetPosition(_CellDoorTrigger,_x,_y,_z) + THEN + //ProcRestoreGenericBehaviourAfterScene(_Player); + DB_PlayerEscapedPrison(_Player,_PrisonCrimeName,_PrisonCrimeNameAD); + ApplyStatus(_Player,"FUGITIVE",100.0,1); + ObjectClearFlag(_Player,"IsInPrison",0); + CharacterRegisterCrimeWithPosition(_Player,"EscapedPrison",NULL_00000000-0000-0000-0000-000000000000,NULL_00000000-0000-0000-0000-000000000000,_x,_y,_z,0); + NOT DB_IsArrested(_Player); + + IF + CharacterStatusApplied(_Player,"FUGITIVE",_) + AND + DB_IsPlayer(_Player) + AND + DB_PlayerEscapedPrison(_Player,_PrisonCrimeName,_PrisonCrimeNameAD) + THEN + ObjectSetFlag(_Player,"IsFugitive"); // This is for Arrest_EscapedPrison Dialog + CharacterRegisterCrime(_Player,_PrisonCrimeName,NULL_00000000-0000-0000-0000-000000000000,NULL_00000000-0000-0000-0000-000000000000,0); + + IF + CharacterStatusApplied(_Player,"FUGITIVE",_) + AND + DB_IsPlayer(_Player) + AND + DB_PlayerEscapedPrison(_Player,_PrisonCrimeName,_PrisonCrimeNameAD) + AND + _PrisonCrimeNameAD != "" + THEN + CharacterRegisterCrime(_Player,_PrisonCrimeNameAD,NULL_00000000-0000-0000-0000-000000000000,NULL_00000000-0000-0000-0000-000000000000,0); + + IF + CharacterStatusRemoved(_Player,"FUGITIVE",_) + AND + DB_IsPlayer(_Player) + AND + DB_PlayerEscapedPrison(_Player,_PrisonCrimeName,_PrisonCrimeNameAD) + THEN + NOT DB_PlayerEscapedPrison(_Player,_PrisonCrimeName,_PrisonCrimeNameAD); + CharacterStopCrime(_Player,_PrisonCrimeName,NULL_00000000-0000-0000-0000-000000000000); + CharacterStopCrime(_Player,_PrisonCrimeNameAD,NULL_00000000-0000-0000-0000-000000000000); + ObjectClearFlag(_Player,"IsFugitive",0); + + IF + ObjectFlagSet("GEB_ClearTag_FUGITIVE",_Player,_) //set in GEB_Arrest_EscapedPrison + AND + HasActiveStatus(_Player,"FUGITIVE",1) + THEN + RemoveStatus(_Player,"FUGITIVE"); + + IF + ObjectFlagSet("GEB_ClearTag_FUGITIVE",_Player,_ID) //set in GEB_Arrest_EscapedPrison + AND + DB_DialogNPCs(_ID,_NPC,1) + THEN + ProcCrimeBribedGuards(_NPC,_Player); + + PROC + ProcCrimeBribedGuards((GUIDSTRING)_NPC,(GUIDSTRING)_Player) + THEN + CrimeIgnoreAllCrimesForCriminal(_Player,_NPC,300000); + DB_CrimeBribeSource(_Player); + CharacterLaunchIteratorAroundCharacter((CHARACTERGUID)_Player,20.0,"GEB_CrimeBribeGuards"); + FireOsirisEvents(); + NOT DB_CrimeBribeSource(_Player); + + IF + StoryEvent((CHARACTERGUID)_NPC,"GEB_CrimeBribeGuards") + AND + NOT DB_CombatCharacters(_NPC,_) + AND + DB_CrimeBribeSource(_Player) + AND + NOT DB_Dead(_NPC) + AND + CharacterIsPlayer(_NPC,0) + AND + QryNPCIsGuard(_NPC) + THEN + CrimeIgnoreAllCrimesForCriminal(_Player,_NPC,300000); + + IF + ObjectFlagSet("GEB_ClearTag_FUGITIVE",_Player,_) //set in GEB_Arrest_EscapedPrison + THEN + ObjectClearFlag(_Player,"GEB_ClearTag_FUGITIVE",0); + + //END_REGION + + //REGION GUARD CALLING + IF + ObjectFlagSet("GEB_Arrest_Player",_NPC,_Inst) + AND + DB_DialogNPCs(_Inst,_OldLead,1) + THEN + DB_CrimeArrestCallingGuards((CHARACTERGUID)_OldLead,_Inst); + + IF + ObjectFlagSet("GEB_Arrest_Player",_NPC,_Inst) + AND + DB_DialogNPCs(_Inst,_OldLead,1) + AND + NOT DB_CombatCharacters((CHARACTERGUID)_NPC,_) + AND + NOT DB_PickedLeadToHelp(_,_Inst) + AND + GetVarInteger(_OldLead,"CrimeID",_CrimeID) + AND + CrimeTransferLeadershipTo((CHARACTERGUID)_OldLead,_CrimeID,_NPC,1) + AND + GetVarObject(_OldLead,"Criminal1",_Criminal1) + AND + GetVarObject(_OldLead,"Criminal2",_Criminal2) + AND + GetVarObject(_OldLead,"Criminal3",_Criminal3) + AND + GetVarObject(_OldLead,"Criminal4",_Criminal4) + AND + GetVarFixedString(_OldLead,"RegionID",_RegionID) + THEN + SetHasDialog(_OldLead,0); + CharacterDisableAllCrimes(_OldLead); + CharacterEnableCrime(_OldLead,"Assault"); + CharacterEnableCrime(_OldLead,"SummonAssault"); + CharacterEnableCrime(_OldLead,"IncapacitatedAssault"); + DB_GuardCaller(_CrimeID,_OldLead,_NPC); + CharacterSetReactionPriority(_OldLead,"CRIME_Flee",2800); + DB_PickedLeadToHelp(_NPC,_Inst); + ProcStoreFightMode(_Npc); + CharacterSetFightMode(_NPC,1,0); + SetVarObject(_NPC,"Criminal1",_Criminal1); + SetVarObject(_NPC,"Criminal2",_Criminal2); + SetVarObject(_NPC,"Criminal3",_Criminal3); + SetVarObject(_NPC,"Criminal4",_Criminal4); + SetVarString(_NPC,"ArrestDialog","GEB_Arrest"); + SetVarFixedString(_NPC,"RegionID",_RegionID); + SetVarInteger(_NPC,"CrimeID",_CrimeID); + SetStoryEvent(_NPC,"CRIME_Perform_Arrest"); + + IF + DialogEnded(_Dialog,_Inst) + AND + DB_CrimeArrestCallingGuards(_OldLead,_Inst) + AND + NOT DB_PickedLeadToHelp(_,_Inst) + AND + DB_DialogPlayers(_Inst,_Player,1) + THEN + NOT DB_CrimeArrestCallingGuards(_OldLead,_Inst); + //failed to transfer leadership, start combat as fallback + Proc_StartDialog(1,"GEB_AD_DealWithCrime",_OldLead); + CharacterSetTemporaryHostileRelation((CHARACTERGUID)_Player,_OldLead); + + IF + DialogEnded(_Dialog,_Inst) + AND + DB_PickedLeadToHelp(_NPC,_Inst) + AND + DB_DialogNPCs(_Inst,_OldLead,1) + THEN + NOT DB_PickedLeadToHelp(_NPC,_Inst); + NOT DB_CrimeArrestCallingGuards((CHARACTERGUID)_OldLead,_Inst); + + IF + OnCrimeConfrontationDone(_CrimeID,_OldLead,_,_,_,_,_) + AND + DB_GuardCaller(_CrimeID,_OldLead,_NPC) + THEN + SetHasDialog(_OldLead,1); + ProcRestoreGenericBehaviour(_OldLead); + NOT DB_GuardCaller(_CrimeID,_OldLead,_NPC); + + + + //END_REGION + + + //REGION Debug + + IF + TextEventSet("LookatPrison") + THEN + CharacterLookAt(S_FTJ_PrisonGuardTest_0ad9aa6c-c8df-4591-be1a-d4f0a94d89ac,S_FortJoy_PlayerPrisonDoor_2dd80040-28aa-4445-8438-1e2424d7228f); + + IF + TextEventSet("Lookatme") + THEN + CharacterLookAt(S_FTJ_PrisonGuardTest_0ad9aa6c-c8df-4591-be1a-d4f0a94d89ac,CHARACTERGUID_Player_Ifan_ad9a3327-4456-42a7-9bf4-7ad60cc9e54f); + + //END_REGION + + } + EXIT + { + + } +} +Goal(18).Title("_Global"); +Goal(18) +{ + INIT + { + + } + KB + { + /////////////////////////////////////////////////////////////////////// + // Story call to forward the clock to _NewHour. + // The clock will be advanced with max 24 hours. This happens when you call SetGameClock(_H) AND Time(_,_H,_). + // + // DO NOT USE THE ENGINE CALL SetTime DIRECTLY! + // + PROC + SetGameClock((INTEGER)_NewHour) + AND + DB_Time(_Day,_H,_) + AND + _NewHour > _H + // day stays today + THEN + UpdateTime(_Day,_NewHour); + + PROC + SetGameClock((INTEGER)_NewHour) + AND + DB_Time(_Day,_H,_) + AND + _NewHour <= _H + AND + // day forwards +1 + IntegerSum(_Day,1,_DayP1) + THEN + UpdateTime(_DayP1,_NewHour); + + PROC + ProcDisableFollow((CHARACTERGUID)_Char) + THEN + CharacterDetachFromGroup(_Char); + //CharacterLockGroup(_Char,1); + + IF + CharacterUsedItemFailed(_Player,_Item) + AND + _Player.DB_IsPlayer() + THEN + ProcShowForbiddenItemText(_Player,_Item); + + PROC + ProcShowForbiddenItemText((CHARACTERGUID)_Player,(ITEMGUID)_Item) + AND + ItemIsLocked(_Item, 0) + AND + NOT DB_NoForbiddenText(_Item) + THEN + Proc_StartDialog(1,"GLO_AD_ForbiddenItem",_Player); + + PROC + ProcShowForbiddenItemText((CHARACTERGUID)_Player,(ITEMGUID)_Item) + AND + ItemIsLocked(_Item, 1) + THEN + CharacterItemSetEvent(_Player, _Item, "GLO_UsedLockedItem"); + + PROC + ProcShowMarker((CHARACTERGUID)_Player,(STRING)_Marker) + AND + NOT DB_ActivePlayerMarker(_Player,_Marker) + THEN + DB_ActivePlayerMarker(_Player,_Marker); + ShowMapMarker(_Player,_Marker,1); + + PROC + ProcHideMarker((CHARACTERGUID)_Player,(STRING)_Marker) + AND + DB_ActivePlayerMarker(_Player,_Marker) + THEN + ShowMapMarker(_Player,_Marker,0); + + PROC + ProcFreezePlayers() + AND + _Player.DB_IsPlayer() + THEN + CharacterFreeze(_Player); + + PROC + ProcUnfreezePlayers() + AND + _Player.DB_IsPlayer() + THEN + CharacterUnfreeze(_Player); + + //REGION moving items default functions + PROC + MoveAllItemsTo((GUIDSTRING)_Source,(GUIDSTRING)_Target) + THEN + MoveAllItemsTo(_Source,_Target,1,1,1); + //END_REGION + + } + EXIT + { + + } +} +Goal(19).Title("_Global_Autosave"); +Goal(19) +{ + INIT + { + + } + KB + { + IF + DB_AutoSaveTrigger((TRIGGERGUID)_Trigger) + AND + NOT DB_Subregion(_Trigger,_,_) //Subregions can function as autosaves the first time they are entered + THEN + DB_OneShotPlayerTrigger(_Trigger); + + //retro-active check, if a trigger is a sub-region, it should not be oneshot + IF + DB_Subregion(_Trigger,_,_) + AND + DB_OneShotPlayerTrigger(_Trigger) + THEN + NOT DB_OneShotPlayerTrigger(_Trigger); + + PROC + ProcOneShotTriggerEntered(_,_Trigger) + AND + DB_AutoSaveTrigger(_Trigger) + AND + DB_AutosaveGroup(_Trigger,_Group) + AND + DB_AutosaveGroup(_AnotherTrigger,_Group) + AND + _AnotherTrigger != _Trigger + THEN + NOT DB_AutoSaveTrigger(_AnotherTrigger); + NOT DB_OneShotPlayerTrigger(_AnotherTrigger); + ProcTriggerUnregisterForPlayers(_AnotherTrigger); + + PROC + ProcOneShotTriggerEntered(_,_Trigger) + AND + DB_AutoSaveTrigger(_Trigger) + THEN + NOT DB_AutoSaveTrigger(_Trigger); + AutoSave(); + + //For use of Autosave triggers that are attached to non-oneshots such as Subregions + IF + CharacterEnteredTrigger((CHARACTERGUID)_Player,(TRIGGERGUID)_Trigger) + AND + DB_AutoSaveTrigger(_Trigger) + AND + NOT DB_OneShotPlayerTrigger(_Trigger) + THEN + NOT DB_AutoSaveTrigger(_Trigger); + AutoSave(); + + IF + DB_AutosaveGroup((TRIGGERGUID)_Trigger,(STRING)_ID) + THEN + DB_AutoSaveTrigger(_Trigger); + + + + } + EXIT + { + + } +} +Goal(20).Title("_Global_CharacterAnimations"); +Goal(20) +{ + INIT + { + // Standard animations + DB_GLO_CharacterAnimation("PlayAnim_attack1","attack1",0); + DB_GLO_CharacterAnimation("PlayAnim_attack2","attack2",0); + DB_GLO_CharacterAnimation("PlayAnim_attack3","attack3",0); + DB_GLO_CharacterAnimation("PlayAnim_attack_ground","attack_ground",0); + DB_GLO_CharacterAnimation("PlayAnim_Loop_thrown_loop","thrown_loop",1); + DB_GLO_CharacterAnimation("PlayAnim_thrown_land","thrown_land",0); + DB_GLO_CharacterAnimation("PlayAnim_knockdown_fall","knockdown_fall",0); + DB_GLO_CharacterAnimation("PlayAnim_Loop_knockdown_loop","knockdown_loop",1); + DB_GLO_CharacterAnimation("PlayAnim_knockdown_getup","knockdown_getup",0); + DB_GLO_CharacterAnimation("PlayAnim_barf","barf",0); + DB_GLO_CharacterAnimation("PlayAnim_die_melee","die_melee",0); + DB_GLO_CharacterAnimation("PlayAnim_die_slash","die_slash",0); + DB_GLO_CharacterAnimation("PlayAnim_die_crush","die_crush",0); + DB_GLO_CharacterAnimation("PlayAnim_die_pierce","die_pierce",0); + DB_GLO_CharacterAnimation("PlayAnim_die_arrow","die_arrow",0); + DB_GLO_CharacterAnimation("PlayAnim_die_dot","die_dot",0); + DB_GLO_CharacterAnimation("PlayAnim_die_incinerate","die_incinerate",0); + DB_GLO_CharacterAnimation("PlayAnim_die_acid","die_acid",0); + DB_GLO_CharacterAnimation("PlayAnim_die_electrocution","die_electrocution",0); + DB_GLO_CharacterAnimation("PlayAnim_die_frozenshatter","die_frozenshatter",0); + DB_GLO_CharacterAnimation("PlayAnim_die_hang","die_hang",0); + DB_GLO_CharacterAnimation("PlayAnim_hit","hit",0); + DB_GLO_CharacterAnimation("PlayAnim_hit_backstab","hit_backstab",0); + DB_GLO_CharacterAnimation("PlayAnim_hit_magic","hit_magic",0); + DB_GLO_CharacterAnimation("PlayAnim_block","block",0); + DB_GLO_CharacterAnimation("PlayAnim_dodge","dodge",0); + DB_GLO_CharacterAnimation("PlayAnim_dodge_backstab","dodge_backstab",0); + DB_GLO_CharacterAnimation("PlayAnim_Loop_idle1","idle1",1); + DB_GLO_CharacterAnimation("PlayAnim_Loop_idle2","idle2",1); + DB_GLO_CharacterAnimation("PlayAnim_Loop_idle3","idle3",1); + DB_GLO_CharacterAnimation("PlayAnim_Loop_walk","walk",1); + DB_GLO_CharacterAnimation("PlayAnim_Loop_sneak","sneak",1); + DB_GLO_CharacterAnimation("PlayAnim_Loop_run","run",1); + DB_GLO_CharacterAnimation("PlayAnim_Loop_still","still",1); + DB_GLO_CharacterAnimation("PlayAnim_Loop_stillsneaking","stillsneaking",1); + DB_GLO_CharacterAnimation("PlayAnim_Loop_stilldiseased","stilldiseased",1); + DB_GLO_CharacterAnimation("PlayAnim_Loop_stillchilled","stillchilled",1); + DB_GLO_CharacterAnimation("PlayAnim_Loop_stillcrippled","stillcrippled",1); + DB_GLO_CharacterAnimation("PlayAnim_Loop_stillblind","stillblind",1); + DB_GLO_CharacterAnimation("PlayAnim_Loop_stilldrunk","stilldrunk",1); + DB_GLO_CharacterAnimation("PlayAnim_Loop_stillelectrified","stillelectrified",1); + DB_GLO_CharacterAnimation("PlayAnim_Loop_walk_wings","walk_wings",1); + DB_GLO_CharacterAnimation("PlayAnim_Loop_run_wings","run_wings",1); + DB_GLO_CharacterAnimation("PlayAnim_Loop_still_wings","still_wings",1); + DB_GLO_CharacterAnimation("PlayAnim_Loop_stillmental","stillmental",1); + + DB_GLO_CharacterAnimation("PlayAnim_Loop_cower","cower",1); + DB_GLO_CharacterAnimation("PlayAnim_spawn","spawn",0); + DB_GLO_CharacterAnimation("PlayAnim_polymorphed","polymorphed",0); + DB_GLO_CharacterAnimation("PlayAnim_resurrect","resurrect",0); + DB_GLO_CharacterAnimation("PlayAnim_use_inspect","use_inspect",0); + DB_GLO_CharacterAnimation("PlayAnim_use_activate","use_activate",0); + DB_GLO_CharacterAnimation("PlayAnim_use_loot","use_loot",0); + DB_GLO_CharacterAnimation("PlayAnim_use_eat","use_eat",0); + DB_GLO_CharacterAnimation("PlayAnim_use_drink","use_drink",0); + DB_GLO_CharacterAnimation("PlayAnim_use_craft","use_craft",0); + DB_GLO_CharacterAnimation("PlayAnim_use_dig","use_dig",0); + DB_GLO_CharacterAnimation("PlayAnim_skill_stance_start","skill_stance_start",0); + DB_GLO_CharacterAnimation("PlayAnim_Loop_skill_stance_loop","skill_stance_loop",1); + DB_GLO_CharacterAnimation("PlayAnim_skill_stance_cast","skill_stance_cast",0); + DB_GLO_CharacterAnimation("PlayAnim_skill_prepare_weapon_01_start","skill_prepare_weapon_01_start",0); + DB_GLO_CharacterAnimation("PlayAnim_Loop_skill_prepare_weapon_01_loop","skill_prepare_weapon_01_loop",1); + DB_GLO_CharacterAnimation("PlayAnim_skill_attack_flurry_01_cast","skill_attack_flurry_01_cast",0); + DB_GLO_CharacterAnimation("PlayAnim_skill_attack_precision_01_cast","skill_attack_precision_01_cast",0); + DB_GLO_CharacterAnimation("PlayAnim_skill_attack_power_01_cast","skill_attack_power_01_cast",0); + DB_GLO_CharacterAnimation("PlayAnim_skill_attack_round_01_cast","skill_attack_round_01_cast",0); + DB_GLO_CharacterAnimation("PlayAnim_skill_attack_multi_01_cast","skill_attack_multi_01_cast",0); + DB_GLO_CharacterAnimation("PlayAnim_skill_attack_multi_02_cast","skill_attack_multi_02_cast",0); + DB_GLO_CharacterAnimation("PlayAnim_skill_attack_aoe_01_cast","skill_attack_aoe_01_cast",0); + DB_GLO_CharacterAnimation("PlayAnim_skill_attack_offhand_01_cast","skill_attack_offhand_01_cast",0); + DB_GLO_CharacterAnimation("PlayAnim_skill_attack_stance_01_cast","skill_attack_stance_01_cast",0); + + DB_GLO_CharacterAnimation("PlayAnim_skill_attack_projectile_01_cast","skill_attack_projectile_01_cast",0); + DB_GLO_CharacterAnimation("PlayAnim_skill_attack_projectile_02_cast","skill_attack_projectile_02_cast",0); + ////////////////////////////////////////////////////////////////////////// + // Ranger tricks: + ////////////////////////////////////////////////////////////////////////// + DB_GLO_CharacterAnimation("PlayAnim_skill_arrow_start","skill_arrow_start",0); + DB_GLO_CharacterAnimation("PlayAnim_Loop_skill_arrow_loop","skill_arrow_loop",1); + DB_GLO_CharacterAnimation("PlayAnim_skill_arrow_cast","skill_arrow_cast",0); + + DB_GLO_CharacterAnimation("PlayAnim_skill_rainofarrows_start","skill_rainofarrows_start",0); + DB_GLO_CharacterAnimation("PlayAnim_Loop_skill_rainofarrows_loop","skill_rainofarrows_loop",1); + DB_GLO_CharacterAnimation("PlayAnim_skill_rainofarrows_cast","skill_rainofarrows_cast",0); + + DB_GLO_CharacterAnimation("PlayAnim_skill_splittingarrows_start","skill_splittingarrows_start",0); + DB_GLO_CharacterAnimation("PlayAnim_Loop_skill_splittingarrows_loop","skill_splittingarrows_loop",1); + DB_GLO_CharacterAnimation("PlayAnim_skill_splittingarrows_cast","skill_splittingarrows_cast",0); + + DB_GLO_CharacterAnimation("PlayAnim_skill_multishot_lvl1_cast","skill_multishot_lvl1_cast",0); + DB_GLO_CharacterAnimation("PlayAnim_skill_multishot_lvl2_cast","skill_multishot_lvl2_cast",0); + DB_GLO_CharacterAnimation("PlayAnim_skill_multishot_lvl3_cast","skill_multishot_lvl3_cast",0); + DB_GLO_CharacterAnimation("PlayAnim_skill_multishot_lvl4_cast","skill_multishot_lvl4_cast",0); + DB_GLO_CharacterAnimation("PlayAnim_skill_multishot_lvl5_cast","skill_multishot_lvl5_cast",0); + DB_GLO_CharacterAnimation("PlayAnim_skill_multishot_lvl6_cast","skill_multishot_lvl6_cast",0); + DB_GLO_CharacterAnimation("PlayAnim_skill_multishot_lvl7_cast","skill_multishot_lvl7_cast",0); + DB_GLO_CharacterAnimation("PlayAnim_skill_multishot_lvl8_cast","skill_multishot_lvl8_cast",0); + DB_GLO_CharacterAnimation("PlayAnim_skill_multishot_lvl9_cast","skill_multishot_lvl9_cast",0); + DB_GLO_CharacterAnimation("PlayAnim_skill_multishot_lvl10_cast","skill_multishot_lvl10_cast",0); + ////////////////////////////////////////////////////////////////////////// + // Warrior tricks: + ////////////////////////////////////////////////////////////////////////// + DB_GLO_CharacterAnimation("PlayAnim_skill_flurry_start","skill_flurry_start",0); + DB_GLO_CharacterAnimation("PlayAnim_Loop_skill_flurry_loop","skill_flurry_loop",1); + DB_GLO_CharacterAnimation("PlayAnim_skill_flurry_cast","skill_flurry_cast",0); + + DB_GLO_CharacterAnimation("PlayAnim_skill_powerattack_start","skill_powerattack_start",0); + DB_GLO_CharacterAnimation("PlayAnim_Loop_skill_powerattack_loop","skill_powerattack_loop",1); + DB_GLO_CharacterAnimation("PlayAnim_skill_powerattack_cast","skill_powerattack_cast",0); + + DB_GLO_CharacterAnimation("PlayAnim_skill_whirlwind_start","skill_whirlwind_start",0); + DB_GLO_CharacterAnimation("PlayAnim_Loop_skill_whirlwind_loop","skill_whirlwind_loop",1); + DB_GLO_CharacterAnimation("PlayAnim_skill_whirlwind_cast","skill_whirlwind_cast",0); + + DB_GLO_CharacterAnimation("PlayAnim_skill_fatality_start","skill_fatality_start",0); + DB_GLO_CharacterAnimation("PlayAnim_Loop_skill_fatality_loop","skill_fatality_loop",1); + DB_GLO_CharacterAnimation("PlayAnim_skill_fatality_cast","skill_fatality_cast",0); + ////////////////////////////////////////////////////////////////////////// + // Scoundrel tricks: + ////////////////////////////////////////////////////////////////////////// + DB_GLO_CharacterAnimation("PlayAnim_skill_shadowstrike_start","skill_shadowstrike_start",0); + DB_GLO_CharacterAnimation("PlayAnim_Loop_skill_shadowstrike_loop","skill_shadowstrike_loop",1); + DB_GLO_CharacterAnimation("PlayAnim_skill_shadowstrike_cast","skill_shadowstrike_cast",0); + + DB_GLO_CharacterAnimation("PlayAnim_skill_lacerate_start","skill_lacerate_start",0); + DB_GLO_CharacterAnimation("PlayAnim_Loop_skill_lacerate_loop","skill_lacerate_loop",1); + DB_GLO_CharacterAnimation("PlayAnim_skill_lacerate_cast","skill_lacerate_cast",0); + ////////////////////////////////////////////////////////////////////////// + // Emotions: + ////////////////////////////////////////////////////////////////////////// + DB_GLO_CharacterAnimation("PlayAnim_emotion_normal","emotion_normal",0); + DB_GLO_CharacterAnimation("PlayAnim_Loop_emotion_normal_looping1","emotion_normal_looping1",1); + DB_GLO_CharacterAnimation("PlayAnim_Loop_emotion_normal_looping2","emotion_normal_looping2",1); + DB_GLO_CharacterAnimation("PlayAnim_Loop_emotion_normal_looping3","emotion_normal_looping3",1); + DB_GLO_CharacterAnimation("PlayAnim_emotion_angry","emotion_angry",0); + DB_GLO_CharacterAnimation("PlayAnim_Loop_emotion_angry_looping1","emotion_angry_looping1",1); + DB_GLO_CharacterAnimation("PlayAnim_Loop_emotion_angry_looping2","emotion_angry_looping2",1); + DB_GLO_CharacterAnimation("PlayAnim_Loop_emotion_angry_looping3","emotion_angry_looping3",1); + DB_GLO_CharacterAnimation("PlayAnim_emotion_boasting_bragging","emotion_boasting_bragging",0); + DB_GLO_CharacterAnimation("PlayAnim_Loop_emotion_boasting_bragging_looping1","emotion_boasting_bragging_looping1",1); + DB_GLO_CharacterAnimation("PlayAnim_Loop_emotion_boasting_bragging_looping2","emotion_boasting_bragging_looping2",1); + DB_GLO_CharacterAnimation("PlayAnim_Loop_emotion_boasting_bragging_looping3","emotion_boasting_bragging_looping3",1); + DB_GLO_CharacterAnimation("PlayAnim_emotion_clueless","emotion_clueless",0); + DB_GLO_CharacterAnimation("PlayAnim_Loop_emotion_clueless_looping1","emotion_clueless_looping1",1); + DB_GLO_CharacterAnimation("PlayAnim_Loop_emotion_clueless_looping2","emotion_clueless_looping2",1); + DB_GLO_CharacterAnimation("PlayAnim_Loop_emotion_clueless_looping3","emotion_clueless_looping3",1); + DB_GLO_CharacterAnimation("PlayAnim_emotion_cower_scared","emotion_cower_scared",0); + DB_GLO_CharacterAnimation("PlayAnim_Loop_emotion_cower_scared_looping1","emotion_cower_scared_looping1",1); + DB_GLO_CharacterAnimation("PlayAnim_Loop_emotion_cower_scared_looping2","emotion_cower_scared_looping2",1); + DB_GLO_CharacterAnimation("PlayAnim_Loop_emotion_cower_scared_looping3","emotion_cower_scared_looping3",1); + DB_GLO_CharacterAnimation("PlayAnim_emotion_greeting","emotion_greeting",0); + DB_GLO_CharacterAnimation("PlayAnim_Loop_emotion_greeting_looping1","emotion_greeting_looping1",1); + DB_GLO_CharacterAnimation("PlayAnim_Loop_emotion_greeting_looping2","emotion_greeting_looping2",1); + DB_GLO_CharacterAnimation("PlayAnim_Loop_emotion_greeting_looping3","emotion_greeting_looping3",1); + DB_GLO_CharacterAnimation("PlayAnim_emotion_happy_cheerful","emotion_happy_cheerful",0); + DB_GLO_CharacterAnimation("PlayAnim_Loop_emotion_happy_cheerful_looping1","emotion_happy_cheerful_looping1",1); + DB_GLO_CharacterAnimation("PlayAnim_Loop_emotion_happy_cheerful_looping2","emotion_happy_cheerful_looping2",1); + DB_GLO_CharacterAnimation("PlayAnim_Loop_emotion_happy_cheerful_looping3","emotion_happy_cheerful_looping3",1); + DB_GLO_CharacterAnimation("PlayAnim_emotion_ignore_dismiss","emotion_ignore_dismiss",0); + DB_GLO_CharacterAnimation("PlayAnim_Loop_emotion_ignore_dismiss_looping1","emotion_ignore_dismiss_looping1",1); + DB_GLO_CharacterAnimation("PlayAnim_Loop_emotion_ignore_dismiss_looping2","emotion_ignore_dismiss_looping2",1); + DB_GLO_CharacterAnimation("PlayAnim_Loop_emotion_ignore_dismiss_looping3","emotion_ignore_dismiss_looping3",1); + DB_GLO_CharacterAnimation("PlayAnim_emotion_panic_distress","emotion_panic_distress",0); + DB_GLO_CharacterAnimation("PlayAnim_Loop_emotion_panic_distress_looping1","emotion_panic_distress_looping1",1); + DB_GLO_CharacterAnimation("PlayAnim_Loop_emotion_panic_distress_looping2","emotion_panic_distress_looping2",1); + DB_GLO_CharacterAnimation("PlayAnim_Loop_emotion_panic_distress_looping3","emotion_panic_distress_looping3",1); + DB_GLO_CharacterAnimation("PlayAnim_emotion_insulting","emotion_insulting",0); + DB_GLO_CharacterAnimation("PlayAnim_Loop_emotion_insulting_looping1","emotion_insulting_looping1",1); + DB_GLO_CharacterAnimation("PlayAnim_Loop_emotion_insulting_looping2","emotion_insulting_looping2",1); + DB_GLO_CharacterAnimation("PlayAnim_Loop_emotion_insulting_looping3","emotion_insulting_looping3",1); + DB_GLO_CharacterAnimation("PlayAnim_emotion_sad","emotion_sad",0); + DB_GLO_CharacterAnimation("PlayAnim_Loop_emotion_sad_looping1","emotion_sad_looping1",1); + DB_GLO_CharacterAnimation("PlayAnim_Loop_emotion_sad_looping2","emotion_sad_looping2",1); + DB_GLO_CharacterAnimation("PlayAnim_Loop_emotion_sad_looping3","emotion_sad_looping3",1); + DB_GLO_CharacterAnimation("PlayAnim_emotion_thankful","emotion_thankful",0); + DB_GLO_CharacterAnimation("PlayAnim_Loop_emotion_thankful_looping1","emotion_thankful_looping1",1); + DB_GLO_CharacterAnimation("PlayAnim_Loop_emotion_thankful_looping2","emotion_thankful_looping2",1); + DB_GLO_CharacterAnimation("PlayAnim_Loop_emotion_thankful_looping3","emotion_thankful_looping3",1); + DB_GLO_CharacterAnimation("PlayAnim_emotion_sly_scheming","emotion_sly_scheming",0); + DB_GLO_CharacterAnimation("PlayAnim_Loop_emotion_sly_scheming_looping1","emotion_sly_scheming_looping1",1); + DB_GLO_CharacterAnimation("PlayAnim_Loop_emotion_sly_scheming_looping2","emotion_sly_scheming_looping2",1); + DB_GLO_CharacterAnimation("PlayAnim_Loop_emotion_sly_scheming_looping3","emotion_sly_scheming_looping3",1); + DB_GLO_CharacterAnimation("PlayAnim_emotion_sarcasm_haughty","emotion_sarcasm_haughty",0); + DB_GLO_CharacterAnimation("PlayAnim_Loop_emotion_sarcasm_haughty_looping1","emotion_sarcasm_haughty_looping1",1); + DB_GLO_CharacterAnimation("PlayAnim_Loop_emotion_sarcasm_haughty_looping2","emotion_sarcasm_haughty_looping2",1); + DB_GLO_CharacterAnimation("PlayAnim_Loop_emotion_sarcasm_haughty_looping3","emotion_sarcasm_haughty_looping3",1); + DB_GLO_CharacterAnimation("PlayAnim_emotion_flirt","emotion_flirt",0); + DB_GLO_CharacterAnimation("PlayAnim_Loop_emotion_flirt_looping1","emotion_flirt_looping1",1); + DB_GLO_CharacterAnimation("PlayAnim_Loop_emotion_flirt_looping2","emotion_flirt_looping2",1); + DB_GLO_CharacterAnimation("PlayAnim_Loop_emotion_flirt_looping3","emotion_flirt_looping3",1); + ////////////////////////////////////////////////////////////////////////// + // Climbing + ////////////////////////////////////////////////////////////////////////// + DB_GLO_CharacterAnimation("PlayAnim_climb_UpAttach","climb_UpAttach",0); + DB_GLO_CharacterAnimation("PlayAnim_Loop_climb_Up","climb_Up",1); + DB_GLO_CharacterAnimation("PlayAnim_climb_UpDetach","climb_UpDetach",0); + DB_GLO_CharacterAnimation("PlayAnim_climb_DownAttach","climb_DownAttach",0); + DB_GLO_CharacterAnimation("PlayAnim_Loop_climb_Down","climb_Down",1); + DB_GLO_CharacterAnimation("PlayAnim_climb_DownDetach","climb_DownDetach",0); + DB_GLO_CharacterAnimation("PlayAnim_cast_target_start","cast_target_start",0); + DB_GLO_CharacterAnimation("PlayAnim_Loop_cast_target_loop","cast_target_loop",1); + DB_GLO_CharacterAnimation("PlayAnim_cast_target_cast","cast_target_cast",0); + DB_GLO_CharacterAnimation("PlayAnim_cast_shout_start","cast_shout_start",0); + DB_GLO_CharacterAnimation("PlayAnim_Loop_cast_shout_loop","cast_shout_loop",1); + + // Custom animations + DB_GLO_CharacterAnimation("PlayAnim_Activate_01","Activate_01",0); + DB_GLO_CharacterAnimation("PlayAnim_Annoyed_01","Annoyed_01",0); + DB_GLO_CharacterAnimation("PlayAnim_Attention_01","Attention_01",0); + DB_GLO_CharacterAnimation("PlayAnim_Attention_02","Attention_02",0); + DB_GLO_CharacterAnimation("PlayAnim_Attention_03","Attention_03",0); + DB_GLO_CharacterAnimation("PlayAnim_Attentive_01","Attentive_01",0); + DB_GLO_CharacterAnimation("PlayAnim_Beg_01","Beg_01",0); + DB_GLO_CharacterAnimation("PlayAnim_BehindBars_01","BehindBars_01",0); + DB_GLO_CharacterAnimation("PlayAnim_BehindBars_02","BehindBars_02",0); + DB_GLO_CharacterAnimation("PlayAnim_Bellringer_01","Bellringer_01",0); + DB_GLO_CharacterAnimation("PlayAnim_Blacksmith_01","Blacksmith_01",0); + DB_GLO_CharacterAnimation("PlayAnim_Blacksmith_02","Blacksmith_02",0); + DB_GLO_CharacterAnimation("PlayAnim_Bored_01","Bored_01",0); + DB_GLO_CharacterAnimation("PlayAnim_Bored_02","Bored_02",0); + DB_GLO_CharacterAnimation("PlayAnim_Bored_03","Bored_03",0); + DB_GLO_CharacterAnimation("PlayAnim_Bow_01","Bow_01",0); + DB_GLO_CharacterAnimation("PlayAnim_Bow_02","Bow_02",0); + DB_GLO_CharacterAnimation("PlayAnim_Bow_03","Bow_03",0); + DB_GLO_CharacterAnimation("PlayAnim_Bow_04","Bow_04",0); + DB_GLO_CharacterAnimation("PlayAnim_Bow_05","Bow_05",0); + DB_GLO_CharacterAnimation("PlayAnim_Cast_Ritual_01","Cast_Ritual_01",0); + DB_GLO_CharacterAnimation("PlayAnim_Check_Neck_01","Check_Neck_01",0); + DB_GLO_CharacterAnimation("PlayAnim_Cheer_01","Cheer_01",0); + DB_GLO_CharacterAnimation("PlayAnim_Cheer_02","Cheer_02",0); + DB_GLO_CharacterAnimation("PlayAnim_Cheer_03","Cheer_03",0); + DB_GLO_CharacterAnimation("PlayAnim_Chicken_01","Chicken_01",0); + DB_GLO_CharacterAnimation("PlayAnim_Chicken_02","Chicken_02",0); + DB_GLO_CharacterAnimation("PlayAnim_Chicken_03","Chicken_03",0); + DB_GLO_CharacterAnimation("PlayAnim_Chicken_Idle_01","Chicken_Idle_01",0); + DB_GLO_CharacterAnimation("PlayAnim_Chuckle_01","Chuckle_01",0); + DB_GLO_CharacterAnimation("PlayAnim_Chuckle_02","Chuckle_02",0); + DB_GLO_CharacterAnimation("PlayAnim_Clap_01","Clap_01",0); + DB_GLO_CharacterAnimation("PlayAnim_Clap_02","Clap_02",0); + DB_GLO_CharacterAnimation("PlayAnim_Clap_03","Clap_03",0); + DB_GLO_CharacterAnimation("PlayAnim_Clap_04","Clap_04",0); + DB_GLO_CharacterAnimation("PlayAnim_Clean_Floor_01","Clean_Floor_01",0); // With mop & water + DB_GLO_CharacterAnimation("PlayAnim_Clean_Floor_02","Clean_Floor_02",0); // With broom + DB_GLO_CharacterAnimation("PlayAnim_Cooking_01","Cooking_01",0); + DB_GLO_CharacterAnimation("PlayAnim_Cower_01","Cower_01",0); + DB_GLO_CharacterAnimation("PlayAnim_Cower_02","Cower_02",0); + DB_GLO_CharacterAnimation("PlayAnim_Crying_01","Crying_01",0); + DB_GLO_CharacterAnimation("PlayAnim_Curtsey_01","Curtsey_01",0); + DB_GLO_CharacterAnimation("PlayAnim_Dance_01","Dance_01",0); + DB_GLO_CharacterAnimation("PlayAnim_Depressed_01","Depressed_01",0); + DB_GLO_CharacterAnimation("PlayAnim_Dust_Off_01","Dust_Off_01",0); + DB_GLO_CharacterAnimation("PlayAnim_Fidget_Book_01","Fidget_Book_01",0); + DB_GLO_CharacterAnimation("PlayAnim_Fidget_Forward_01","Fidget_Forward_01",0); + DB_GLO_CharacterAnimation("PlayAnim_Fidget_Forward_02","Fidget_Forward_02",0); + DB_GLO_CharacterAnimation("PlayAnim_Fidget_High_01","Fidget_High_01",0); + DB_GLO_CharacterAnimation("PlayAnim_Fidget_High_02","Fidget_High_02",0); + DB_GLO_CharacterAnimation("PlayAnim_Fidget_Low_01","Fidget_Low_01",0); + DB_GLO_CharacterAnimation("PlayAnim_Fidget_Low_02","Fidget_Low_02",0); + DB_GLO_CharacterAnimation("PlayAnim_Fidget_Low_03","Fidget_Low_03",0); + DB_GLO_CharacterAnimation("PlayAnim_Flex_01","Flex_01",0); + DB_GLO_CharacterAnimation("PlayAnim_Flirt_01","Flirt_01",0); + DB_GLO_CharacterAnimation("PlayAnim_Fumble_01","Fumble_01",0); + DB_GLO_CharacterAnimation("PlayAnim_Halt_01","Halt_01",0); + DB_GLO_CharacterAnimation("PlayAnim_Ignore_01","Ignore_01",0); + DB_GLO_CharacterAnimation("PlayAnim_Intimidate_01","Intimidate_01",0); + DB_GLO_CharacterAnimation("PlayAnim_Intimidate_02","Intimidate_02",0); + DB_GLO_CharacterAnimation("PlayAnim_Kneel_01","Kneel_01",0); + DB_GLO_CharacterAnimation("PlayAnim_Kneel_02","Kneel_02",0); + DB_GLO_CharacterAnimation("PlayAnim_Laugh_01","Laugh_01",0); + DB_GLO_CharacterAnimation("PlayAnim_Laugh_02","Laugh_02",0); + DB_GLO_CharacterAnimation("PlayAnim_Listen_01","Listen_01",0); + DB_GLO_CharacterAnimation("PlayAnim_LoadingCart_01","LoadingCart_01",0); + DB_GLO_CharacterAnimation("PlayAnim_Look_Down_Long_01","Look_Down_Long_01",0); + DB_GLO_CharacterAnimation("PlayAnim_Look_Down_Short_01","Look_Down_Short_01",0); + DB_GLO_CharacterAnimation("PlayAnim_Look_Left_Long_01","Look_Left_Long_01",0); + DB_GLO_CharacterAnimation("PlayAnim_Look_Left_Short_01","Look_Left_Short_01",0); + DB_GLO_CharacterAnimation("PlayAnim_Look_Right_Long_01","Look_Right_Long_01",0); + DB_GLO_CharacterAnimation("PlayAnim_Look_Right_Short_01","Look_Right_Short_01",0); + DB_GLO_CharacterAnimation("PlayAnim_Look_Up_Long_01","Look_Up_Long_01",0); + DB_GLO_CharacterAnimation("PlayAnim_Look_Up_Short_01","Look_Up_Short_01",0); + DB_GLO_CharacterAnimation("PlayAnim_Look_Up_Short_02","Look_Up_Short_02",0); + DB_GLO_CharacterAnimation("PlayAnim_Mindcontrol_01","Mindcontrol_01",0); + DB_GLO_CharacterAnimation("PlayAnim_Mine_01","Mine_01",0); + DB_GLO_CharacterAnimation("PlayAnim_No_01","No_01",0); + DB_GLO_CharacterAnimation("PlayAnim_No_02","No_02",0); + DB_GLO_CharacterAnimation("PlayAnim_No_03","No_03",0); + DB_GLO_CharacterAnimation("PlayAnim_No_04","No_04",0); + DB_GLO_CharacterAnimation("PlayAnim_No_05","No_05",0); + DB_GLO_CharacterAnimation("PlayAnim_No_06","No_06",0); + DB_GLO_CharacterAnimation("PlayAnim_Perform_01","Perform_01",0); + DB_GLO_CharacterAnimation("PlayAnim_Perform_02","Perform_02",0); + DB_GLO_CharacterAnimation("PlayAnim_Perform_03","Perform_03",0); + DB_GLO_CharacterAnimation("PlayAnim_Pickpocket_01","Pickpocket_01",0); + DB_GLO_CharacterAnimation("PlayAnim_Point_01","Point_01",0); + DB_GLO_CharacterAnimation("PlayAnim_Potion_Rub_01","Potion_Rub_01",0); + DB_GLO_CharacterAnimation("PlayAnim_Pray_01","Pray_01",0); + DB_GLO_CharacterAnimation("PlayAnim_Pst_01","Pst_01",0); + DB_GLO_CharacterAnimation("PlayAnim_Pst_02","Pst_02",0); + DB_GLO_CharacterAnimation("PlayAnim_Pst_03","Pst_03",0); + DB_GLO_CharacterAnimation("PlayAnim_Pst_04","Pst_04",0); + DB_GLO_CharacterAnimation("PlayAnim_Repair_01","Repair_01",0); + DB_GLO_CharacterAnimation("PlayAnim_Revolt_01","Revolt_01",0); + DB_GLO_CharacterAnimation("PlayAnim_Revolt_02","Revolt_02",0); + DB_GLO_CharacterAnimation("PlayAnim_Salute_01","Salute_01",0); + DB_GLO_CharacterAnimation("PlayAnim_Salute_02","Salute_02",0); + DB_GLO_CharacterAnimation("PlayAnim_Salute_03","Salute_03",0); + DB_GLO_CharacterAnimation("PlayAnim_Salute_04","Salute_04",0); + DB_GLO_CharacterAnimation("PlayAnim_Salute_05","Salute_05",0); + DB_GLO_CharacterAnimation("PlayAnim_Salute_06","Salute_06",0); + DB_GLO_CharacterAnimation("PlayAnim_Salute_07","Salute_07",0); + DB_GLO_CharacterAnimation("PlayAnim_Shout_01","Shout_01",0); + DB_GLO_CharacterAnimation("PlayAnim_Shrug_01","Shrug_01",0); + DB_GLO_CharacterAnimation("PlayAnim_Sigh_01","Sigh_01",0); + DB_GLO_CharacterAnimation("PlayAnim_Sooth_01","Sooth_01",0); + DB_GLO_CharacterAnimation("PlayAnim_Sow_02","Sow_02",0); + DB_GLO_CharacterAnimation("PlayAnim_Stand_Read_01","Stand_Read_01",0); + DB_GLO_CharacterAnimation("PlayAnim_Surprise_01","Surprise_01",0); + DB_GLO_CharacterAnimation("PlayAnim_Surprise_02","Surprise_02",0); + DB_GLO_CharacterAnimation("PlayAnim_Tapping_Beer_01","Tapping_Beer_01",0); + DB_GLO_CharacterAnimation("PlayAnim_Tapping_Beer_02","Tapping_Beer_02",0); + DB_GLO_CharacterAnimation("PlayAnim_Taunt_01","Taunt_01",0); + DB_GLO_CharacterAnimation("PlayAnim_Taunt_02","Taunt_02",0); + DB_GLO_CharacterAnimation("PlayAnim_Teleport_01","Teleport_01",0); + DB_GLO_CharacterAnimation("PlayAnim_Think_01","Think_01",0); + DB_GLO_CharacterAnimation("PlayAnim_Think_02","Think_02",0); + DB_GLO_CharacterAnimation("PlayAnim_Throw_Away_01","Throw_Away_01",0); + DB_GLO_CharacterAnimation("PlayAnim_Throw_Up_01","Throw_Up_01",0); + DB_GLO_CharacterAnimation("PlayAnim_Throw_Water_01","Throw_Water_01",0); + DB_GLO_CharacterAnimation("PlayAnim_Tired_01","Tired_01",0); + DB_GLO_CharacterAnimation("PlayAnim_Trader_01","Trader_01",0); + DB_GLO_CharacterAnimation("PlayAnim_Trader_02","Trader_02",0); + DB_GLO_CharacterAnimation("PlayAnim_Trader_03","Trader_03",0); + DB_GLO_CharacterAnimation("PlayAnim_Use_01","Use_01",0); + DB_GLO_CharacterAnimation("PlayAnim_Use_02","Use_02",0); + DB_GLO_CharacterAnimation("PlayAnim_Victory_01","Victory_01",0); + DB_GLO_CharacterAnimation("PlayAnim_Victory_02","Victory_02",0); + DB_GLO_CharacterAnimation("PlayAnim_Warm_Hands_01","Warm_Hands_01",0); + DB_GLO_CharacterAnimation("PlayAnim_Wave_01","Wave_01",0); + DB_GLO_CharacterAnimation("PlayAnim_Wave_02","Wave_02",0); + DB_GLO_CharacterAnimation("PlayAnim_Wave_03","Wave_03",0); + DB_GLO_CharacterAnimation("PlayAnim_WipeTable_01","WipeTable_01",0); + DB_GLO_CharacterAnimation("PlayAnim_Worship_01","Worship_01",0); + DB_GLO_CharacterAnimation("PlayAnim_Yawn_01","Yawn_01",0); + DB_GLO_CharacterAnimation("PlayAnim_Yes_01","Yes_01",0); + DB_GLO_CharacterAnimation("PlayAnim_Yes_02","Yes_02",0); + DB_GLO_CharacterAnimation("PlayAnim_Yes_03","Yes_03",0); + DB_GLO_CharacterAnimation("PlayAnim_Yes_04","Yes_04",0); + DB_GLO_CharacterAnimation("PlayAnim_Yes_05","Yes_05",0); + DB_GLO_CharacterAnimation("PlayAnim_Sit_Angry_01","Sit_Angry_01",0); + DB_GLO_CharacterAnimation("PlayAnim_Sit_Annoyed_01","Sit_Annoyed_01",0); + DB_GLO_CharacterAnimation("PlayAnim_Sit_Bored_01","Sit_Bored_01",0); + DB_GLO_CharacterAnimation("PlayAnim_Sit_Bored_02","Sit_Bored_02",0); + DB_GLO_CharacterAnimation("PlayAnim_Sit_Chuckle_01","Sit_Chuckle_01",0); + DB_GLO_CharacterAnimation("PlayAnim_Sit_Drink_01","Sit_Drink_01",0); + DB_GLO_CharacterAnimation("PlayAnim_Sit_Laugh_01","Sit_Laugh_01",0); + DB_GLO_CharacterAnimation("PlayAnim_Sit_Laugh_02","Sit_Laugh_02",0); + DB_GLO_CharacterAnimation("PlayAnim_Sit_Look_Left_01","Sit_Look_Left_01",0); + DB_GLO_CharacterAnimation("PlayAnim_Sit_Look_Right_01","Sit_Look_Right_01",0); + DB_GLO_CharacterAnimation("PlayAnim_Sit_No_01","Sit_No_01",0); + DB_GLO_CharacterAnimation("PlayAnim_Sit_No_02","Sit_No_02",0); + DB_GLO_CharacterAnimation("PlayAnim_Sit_Praise_01","Sit_Praise_01",0); + DB_GLO_CharacterAnimation("PlayAnim_Sit_Pray_01","Sit_Pray_01",0); + DB_GLO_CharacterAnimation("PlayAnim_Sit_Read_01","Sit_Read_01",0); + DB_GLO_CharacterAnimation("PlayAnim_Sit_Reject_01","Sit_Reject_01",0); + DB_GLO_CharacterAnimation("PlayAnim_Sit_Salute_01","Sit_Salute_01",0); + DB_GLO_CharacterAnimation("PlayAnim_Sit_Sigh_01","Sit_Sigh_01",0); + DB_GLO_CharacterAnimation("PlayAnim_Sit_Surprise_01","Sit_Surprise_01",0); + DB_GLO_CharacterAnimation("PlayAnim_Sit_Tired_01","Sit_Tired_01",0); + DB_GLO_CharacterAnimation("PlayAnim_Sit_Yes_01","Sit_Yes_01",0); + DB_GLO_CharacterAnimation("PlayAnim_Loop_BarStand_01_Loop","BarStand_01_Loop",1); + DB_GLO_CharacterAnimation("PlayAnim_Loop_Lie_Relax_01_Loop","Lie_Relax_01_Loop",1); + DB_GLO_CharacterAnimation("PlayAnim_Loop_Lie_Suffer_01_Loop","Lie_Suffer_01_Loop",1); + DB_GLO_CharacterAnimation("PlayAnim_Loop_Lie_Suffer_02_Loop","Lie_Suffer_02_Loop",1); + DB_GLO_CharacterAnimation("PlayAnim_Loop_Lie_Suffer_03_Loop","Lie_Suffer_03_Loop",1); + DB_GLO_CharacterAnimation("PlayAnim_Loop_Chained_01_Loop","Chained_01_Loop",1); + DB_GLO_CharacterAnimation("PlayAnim_Loop_Cower_03_Loop","Cower_03_Loop",1); + DB_GLO_CharacterAnimation("PlayAnim_Loop_Crucified_01_Loop","Crucified_01_Loop",1); + DB_GLO_CharacterAnimation("PlayAnim_Loop_Dig_01_Loop","Dig_01_Loop",1); + DB_GLO_CharacterAnimation("PlayAnim_Loop_Lie_Dead_01_Loop","Lie_Dead_01_Loop",1); + DB_GLO_CharacterAnimation("PlayAnim_Loop_Lie_Relax_02_Loop","Lie_Relax_02_Loop",1); + DB_GLO_CharacterAnimation("PlayAnim_Loop_Lie_Wounded_01_Loop","Lie_Wounded_01_Loop",1); + DB_GLO_CharacterAnimation("PlayAnim_Loop_Lie_Wounded_02_Loop","Lie_Wounded_02_Loop",1); + DB_GLO_CharacterAnimation("PlayAnim_Loop_Lie_Wounded_03_Loop","Lie_Wounded_03_Loop",1); + DB_GLO_CharacterAnimation("PlayAnim_Loop_Guard_01_Loop","Guard_01_Loop",1); + DB_GLO_CharacterAnimation("PlayAnim_Loop_Hung_01_Loop","Hung_01_Loop",1); + DB_GLO_CharacterAnimation("PlayAnim_Loop_Hung_02_Loop","Hung_02_Loop",1); // dead + DB_GLO_CharacterAnimation("PlayAnim_Loop_Hung_03_Loop","Hung_03_Loop",1); // alive & kicking by the neck + DB_GLO_CharacterAnimation("PlayAnim_Loop_Injured_01_Loop","Injured_01_Loop",1); + DB_GLO_CharacterAnimation("PlayAnim_Loop_Insane_01_Loop","Insane_01_Loop",1); + DB_GLO_CharacterAnimation("PlayAnim_Loop_Kneel_01_Loop","Kneel_01_Loop",1); + DB_GLO_CharacterAnimation("PlayAnim_Loop_Lie_Tied_Up_01_Loop","Lie_Tied_Up_01_Loop",1); + DB_GLO_CharacterAnimation("PlayAnim_Loop_Lie_Tied_Up_01_End","Lie_Tied_Up_01_End",1); + DB_GLO_CharacterAnimation("PlayAnim_Loop_Rake_01_Loop","Rake_01_Loop",1); + DB_GLO_CharacterAnimation("PlayAnim_Loop_Sing_01_Loop","Sing_01_Loop",1); + DB_GLO_CharacterAnimation("PlayAnim_Loop_Sad_01_Loop","Sad_01_Loop",1); + DB_GLO_CharacterAnimation("PlayAnim_Loop_Search_Book_01_Loop","Search_Book_01_Loop",1); + DB_GLO_CharacterAnimation("PlayAnim_Loop_Sit_Sick_01_Loop","Sit_Sick_01_Loop",1); + DB_GLO_CharacterAnimation("PlayAnim_Loop_Sit_Sleep_01_Loop","Sit_Sleep_01_Loop",1); + DB_GLO_CharacterAnimation("PlayAnim_Loop_Sit_Sleep_02_Loop","Sit_Sleep_02_Loop",1); + DB_GLO_CharacterAnimation("PlayAnim_Loop_Sit_Write_01_Loop","Sit_Write_01_Loop",1); + DB_GLO_CharacterAnimation("PlayAnim_Loop_Sow_01_Loop","Sow_01_Loop",1); + DB_GLO_CharacterAnimation("PlayAnim_Loop_Stand_Drink_01_Loop","Stand_Drink_01_Loop",1); + DB_GLO_CharacterAnimation("PlayAnim_Loop_Stand_Drink_02_Loop","Stand_Drink_02_Loop",1); + DB_GLO_CharacterAnimation("PlayAnim_Loop_Stand_Read_01_Loop","Stand_Read_01_Loop",1); + DB_GLO_CharacterAnimation("PlayAnim_Loop_Stare_01_Loop","Stare_01_Loop",1); + DB_GLO_CharacterAnimation("PlayAnim_Loop_Tied_Up_01_Loop","Tied_Up_01_Loop",1); + DB_GLO_CharacterAnimation("PlayAnim_Loop_Training_2HS_01_Loop","Training_2HS_01_Loop",1); + DB_GLO_CharacterAnimation("PlayAnim_Loop_Training_Bow_01_Loop","Training_Bow_01_Loop",1); + + DB_GLO_CharacterAnimation("PlayAnim_skill_cast_throw_arc_02_cast","skill_cast_throw_arc_02_cast"); + + } + KB + { + //REGION Play animation when flag is set + IF + ObjectFlagSet(_Flag,(CHARACTERGUID)_Character,_Dialog) + AND + DB_GLO_CharacterAnimation(_Flag,_Animation,_Looping) + THEN + ObjectClearFlag(_Character,_Flag); + PROC_GLO_CharacterAnimationStart(_Character,_Animation,_Dialog,_Looping); + + PROC + PROC_GLO_CharacterAnimationStart((CHARACTERGUID)_Character,(STRING)_Animation,(INTEGER)_Dialog,0) + AND + NOT DB_GLO_CharacterAnimationForDialog(_Character,_) + THEN + DB_GLO_CharacterAnimationForDialog(_Character,_Dialog); + PlayAnimation(_Character,_Animation,"GLO_CharacterAnimationFinished"); + + IF + StoryEvent((CHARACTERGUID)_Character,"GLO_CharacterAnimationFinished") + AND + DB_GLO_CharacterAnimationForDialog(_Character,_Dialog) + THEN + NOT DB_GLO_CharacterAnimationForDialog(_Character,_Dialog); + + PROC + PROC_GLO_CharacterAnimationStart((CHARACTERGUID)_Character,(STRING)_Animation,(INTEGER)_Dialog,1) + THEN + CharacterSetAnimationOverride(_Character,_Animation); + + IF + ObjectFlagSet("PlayAnim_StopLoop",(CHARACTERGUID)_Character,_) + THEN + ObjectClearFlag(_Character,"PlayAnim_StopLoop"); + CharacterSetAnimationOverride(_Character,""); + + IF + ObjectFlagSet("PlayAnim_DrawWeapons",(CHARACTERGUID)_Character,_) + AND + HasActiveStatus(_Character,"SITTING",1) + THEN + RemoveStatus(_Character,"SITTING"); + + IF + ObjectFlagSet("PlayAnim_DrawWeapons",(CHARACTERGUID)_Character,_) + THEN + ObjectClearFlag(_Character,"PlayAnim_DrawWeapons"); + CharacterSetFightMode(_Character,1,0); + + IF + ObjectFlagSet("PlayAnim_SheathWeapons",(CHARACTERGUID)_Character,_) + THEN + ObjectClearFlag(_Character,"PlayAnim_SheathWeapons"); + CharacterSetFightMode(_Character,0,0); + //END_REGION + + } + EXIT + { + + } +} +Goal(21).Title("_Global_Counter"); +Goal(21) +{ + INIT + { + + } + KB + { + PROC + ProcRemoveCounter((STRING)_Name) + AND + DB_GlobalCounter(_Name,_Count) + THEN + NOT DB_GlobalCounter(_Name,_Count); + + PROC + ProcDeclareCounter((STRING)_Name) + THEN + ProcRemoveCounter(_Name); + DB_GlobalCounter(_Name,0); + + PROC + ProcIncreaseCounter((STRING)_Name) + THEN + ProcIncreaseCounter(_Name,1); + + PROC + ProcIncreaseCounter((STRING)_Name,(INTEGER)_Amount) + AND + DB_GlobalCounter(_Name,_Count) + AND + IntegerSum(_Count,_Amount,_NewCount) + THEN + NOT DB_GlobalCounter(_Name,_Count); + DB_GlobalCounter(_Name,_NewCount); + + PROC + ProcDecreaseCounter((STRING)_Name) + THEN + ProcDecreaseCounter(_Name,1); + + PROC + ProcDecreaseCounter((STRING)_Name,(INTEGER)_Amount) + AND + DB_GlobalCounter(_Name,_Count) + AND + IntegerSubtract(_Count,_Amount,_NewCount) + THEN + NOT DB_GlobalCounter(_Name,_Count); + DB_GlobalCounter(_Name,_NewCount); + + } + EXIT + { + + } +} +Goal(22).Title("_GLOBAL_Effects"); +Goal(22) +{ + INIT + { + PROC_CleanUpEffects(); + + } + KB + { + //REGION Savegame handle fixes + + IF + DB_CurrentLevel(_) + THEN + PROC_InvalidateLoopEffects(); + PROC_InvalidateLoopBeamEffects(); + + PROC + PROC_InvalidateLoopEffects() + AND + DB_LoopEffect((GUIDSTRING)_object, (INTEGER64) _fxHandle,(STRING)_ID,(STRING)_Region,(STRING)_effect,(STRING)_BoneName) + THEN + NOT DB_LoopEffect(_object, _fxHandle,_ID,_Region,_effect,_BoneName); + DB_LoopEffect(_object, (INTEGER64)-1,_ID,_Region,_effect,_BoneName); + + PROC + PROC_InvalidateLoopBeamEffects() + AND + DB_LoopBeamEffect((GUIDSTRING)_Source,(GUIDSTRING)_Target,(INTEGER64)_EffectHandle,(STRING)_ID,(STRING)_Region,(STRING)_effect,(STRING)_SrcBone,(STRING)_TargetBone) + THEN + NOT DB_LoopBeamEffect(_Source,_Target,_EffectHandle,_ID,_Region,_effect,_SrcBone,_TargetBone); + DB_LoopBeamEffect(_Source,_Target,(INTEGER64)-1,_ID,_Region,_effect,_SrcBone,_TargetBone); + //END_REGION + + /***************************/ + /*** EFFECT REGISTRATION ***/ + /***************************/ + + PROC + PROC_LoopEffect((STRING)_effect, (GUIDSTRING)_Source,(STRING)_ID,(STRING)_Region,(STRING)_BoneName) + AND + DB_CurrentLevel(_Region) + AND + PlayLoopEffect(_Source, _effect, _BoneName, _fxHandle) + THEN + DB_LoopEffect(_Source, _fxHandle,_ID,_Region,_effect, _BoneName); + + PROC + PROC_LoopEffect((STRING)_effect, (GUIDSTRING)_Source,(STRING)_ID,(STRING)_Region,(STRING)_BoneName) + AND + _Region!="__ANY__" + AND + NOT DB_CurrentLevel(_Region) + THEN + DB_LoopEffect(_Source, (INTEGER64)-1,_ID,_Region,_effect, _BoneName); + + PROC + PROC_LoopEffect((STRING)_effect, (GUIDSTRING)_Source,(STRING)_ID,"__ANY__",(STRING)_BoneName) + AND + DB_CurrentLevel(_) + AND + NOT QryBlockEffectInCombat(_effect,_Source) + AND + PlayLoopEffect(_Source, _effect, _BoneName, _fxHandle) + THEN + DB_LoopEffect(_Source, _fxHandle,_ID,"__ANY__",_effect, _BoneName); + + PROC + PROC_LoopEffect((STRING)_effect, (GUIDSTRING)_Source,(STRING)_ID,"__ANY__",(STRING)_BoneName) + AND + DB_CurrentLevel(_) + AND + QryBlockEffectInCombat(_effect,_Source) + THEN + DB_LoopEffect(_Source, (INTEGER64)-1,_ID,"__ANY__",_effect, _BoneName); + + PROC + PROC_LoopEffect((STRING)_effect, (GUIDSTRING)_Source,(STRING)_ID,"__ANY__",(STRING)_BoneName) + AND + NOT DB_CurrentLevel(_) + THEN + DB_LoopEffect(_Source, (INTEGER64)-1,_ID,"__ANY__",_effect, _BoneName); + + PROC + PROC_LoopBeamEffect((STRING)_effect, (GUIDSTRING)_Source,(GUIDSTRING)_Target,(STRING)_SrcBone,(STRING)_TargetBone,(STRING)_ID,(STRING)_Region) + AND + _Region!="__ANY__" + AND + NOT DB_CurrentLevel(_Region) + THEN + DB_LoopBeamEffect(_Source,_Target,(INTEGER64)-1,_ID,_Region,_effect,_SrcBone,_TargetBone); + + PROC + PROC_LoopBeamEffect((STRING)_effect, (GUIDSTRING)_Source,(GUIDSTRING)_Target,(STRING)_SrcBone,(STRING)_TargetBone,(STRING)_ID,(STRING)_Region) + AND + DB_CurrentLevel(_Region) + AND + PlayLoopBeamEffect(_Source,_Target,_effect,_SrcBone,_TargetBone,_EffectHandle) + THEN + DB_LoopBeamEffect(_Source,_Target,_EffectHandle,_ID,_Region,_effect,_SrcBone,_TargetBone); + + PROC + PROC_LoopBeamEffect((STRING)_effect, (GUIDSTRING)_Source,(GUIDSTRING)_Target,(STRING)_SrcBone,(STRING)_TargetBone,(STRING)_ID,"__ANY__") + AND + PlayLoopBeamEffect(_Source,_Target,_effect,_SrcBone,_TargetBone,_EffectHandle) + THEN + DB_LoopBeamEffect(_Source,_Target,_EffectHandle,_ID,"__ANY__",_effect,_SrcBone,_TargetBone); + + /***********************/ + /*** EFFECT DELETION ***/ + /***********************/ + + PROC + ProcStopLoopEffect((INTEGER64)_fxHandle) + AND + _fxHandle!=-1 + THEN + StopLoopEffect(_fxHandle); + + PROC + PROC_StopLoopEffect((GUIDSTRING)_Source,(STRING)_ID) + AND + DB_LoopEffect(_Source, _fxHandle,_ID,_Region,_effect, _BoneName) + THEN + ProcStopLoopEffect(_fxHandle); + NOT DB_LoopEffect(_Source, _fxHandle,_ID,_Region,_effect, _BoneName); + + PROC + PROC_StopLoopBeamEffect((GUIDSTRING)_Source,(STRING)_ID) + AND + DB_LoopBeamEffect(_Source,_Target,_EffectHandle,_ID,_Region,_effect,_SrcBone,_TargetBone) + THEN + ProcStopLoopEffect(_EffectHandle); + NOT DB_LoopBeamEffect(_Source,_Target,_EffectHandle,_ID,_Region,_effect,_SrcBone,_TargetBone); + + /************************/ + /*** EFFECTS CLEAN-UP ***/ + /************************/ + + PROC + PROC_CleanUpEffects() + THEN + PROC_CleanUpLoopEffects(); + PROC_CleanUpLoopBeamEffects(); + + PROC + PROC_CleanUpLoopEffects() + AND + DB_LoopEffect(_Object, _,_ID,_,_,_) + THEN + PROC_StopLoopEffect(_Object,_ID); + + PROC + PROC_CleanUpLoopBeamEffects() + AND + DB_LoopBeamEffect(_Source,_Target,_EffectHandle,_ID,_Region,_effect,_SrcBone,_TargetBone) + THEN + PROC_StopLoopBeamEffect(_Source,_ID); + + + + /************************/ + + //REGION stopping effects for a region + + PROC + ProcStopEffectsForRegion((STRING)_Region) + THEN + ProcStopLoopEffectsForRegion(_Region); + ProcStopLoopBeamEffectsForRegion(_Region); + + PROC + ProcStopLoopEffectsForRegion((STRING)_Region) + AND + DB_LoopEffect(_Source, _fxHandle,_ID,_Region,_effect, _BoneName) + THEN + ProcStopLoopEffect(_fxHandle); + NOT DB_LoopEffect(_Source, _fxHandle,_ID,_Region,_effect, _BoneName); + DB_LoopEffect(_Source,(INTEGER64)-1,_ID,_Region,_effect, _BoneName); + + PROC + ProcStopLoopBeamEffectsForRegion((STRING)_Region) + AND + DB_LoopBeamEffect(_Source,_Target,_EffectHandle,_ID,_Region,_effect,_SrcBone,_TargetBone) + THEN + ProcStopLoopEffect(_EffectHandle); + NOT DB_LoopBeamEffect(_Source,_Target,_EffectHandle,_ID,_Region,_effect,_SrcBone,_TargetBone); + DB_LoopBeamEffect(_Source,_Target,(INTEGER64)-1,_ID,_Region,_effect,_SrcBone,_TargetBone); + + PROC + ProcStopLoopEffectsForRegion((STRING)_) + AND + DB_LoopEffect(_Source, _fxHandle,_ID,"__ANY__",_effect, _BoneName) + THEN + ProcStopLoopEffect(_fxHandle); + NOT DB_LoopEffect(_Source, _fxHandle,_ID,"__ANY__",_effect, _BoneName); + DB_LoopEffect(_Source, (INTEGER64)-1,_ID,"__ANY__",_effect, _BoneName); + + PROC + ProcStopLoopBeamEffectsForRegion((STRING)_) + AND + DB_LoopBeamEffect(_Source,_Target,_EffectHandle,_ID,"__ANY__",_effect,_SrcBone,_TargetBone) + THEN + ProcStopLoopEffect(_EffectHandle); + NOT DB_LoopBeamEffect(_Source,_Target,_EffectHandle,_ID,"__ANY__",_effect,_SrcBone,_TargetBone); + DB_LoopBeamEffect(_Source,_Target,(INTEGER64)-1,_ID,"__ANY__",_effect,_SrcBone,_TargetBone); + //END_REGION + + //REGION starting effects for a region + + PROC + ProcStartEffectsForRegion((STRING)_Region) + THEN + ProcStartLoopEffectsForRegion(_Region); + ProcStartLoopBeamEffectsForRegion(_Region); + + PROC + ProcStartLoopEffectsForRegion((STRING)_Region) + AND + DB_LoopEffect(_Source, _fxHandle,_ID,_Region,_effect, _BoneName) + AND + NOT QryBlockEffectInCombat(_effect,_Source) + AND + PlayLoopEffect(_Source, _effect, _BoneName, _NewfxHandle) + THEN + ProcStopLoopEffect(_fxHandle); + NOT DB_LoopEffect(_Source, _fxHandle,_ID,_Region,_effect, _BoneName); + DB_LoopEffect(_Source, _NewfxHandle,_ID,_Region,_effect, _BoneName); + + PROC + ProcStartLoopBeamEffectsForRegion((STRING)_Region) + AND + DB_LoopBeamEffect(_Source,_Target,_EffectHandle,_ID,_Region,_effect,_SrcBone,_TargetBone) + AND + PlayLoopBeamEffect(_Source,_Target,_effect,_SrcBone,_TargetBone,_NewEffectHandle) + THEN + ProcStopLoopEffect(_EffectHandle); + NOT DB_LoopBeamEffect(_Source,_Target,_EffectHandle,_ID,_Region,_effect,_SrcBone,_TargetBone); + DB_LoopBeamEffect(_Source,_Target,_NewEffectHandle,_ID,_Region,_effect,_SrcBone,_TargetBone); + + /////// + + PROC + ProcStartLoopEffectsForRegion((STRING)_) + AND + DB_LoopEffect(_Source, _fxHandle,_ID,"__ANY__",_effect, _BoneName) + AND + NOT QryBlockEffectInCombat(_effect,_Source) + AND + PlayLoopEffect(_Source, _effect, _BoneName, _NewfxHandle) + THEN + ProcStopLoopEffect(_fxHandle); + NOT DB_LoopEffect(_Source, _fxHandle,_ID,"__ANY__",_effect, _BoneName); + DB_LoopEffect(_Source, _NewfxHandle,_ID,"__ANY__",_effect, _BoneName); + + PROC + ProcStartLoopBeamEffectsForRegion((STRING)_Region) + AND + DB_LoopBeamEffect(_Source,_Target,_EffectHandle,_ID,"__ANY__",_effect,_SrcBone,_TargetBone) + AND + PlayLoopBeamEffect(_Source,_Target,_effect,_SrcBone,_TargetBone,_NewEffectHandle) + THEN + ProcStopLoopEffect(_EffectHandle); + NOT DB_LoopBeamEffect(_Source,_Target,_EffectHandle,_ID,"__ANY__",_effect,_SrcBone,_TargetBone); + DB_LoopBeamEffect(_Source,_Target,_NewEffectHandle,_ID,"__ANY__",_effect,_SrcBone,_TargetBone); + + //END_REGION + + IF + RegionEnded(_Region) + THEN + ProcStopEffectsForRegion(_Region); + + // At GameStarted instead of RegionStarted, because sometimes characters are teleported to the current region + // only during RegionStarted, and this RegionStarted handled may be exectuted before the one doing that + // (which results in playing the effect on the character while they are still in the previous level, and that + // triggers an assert) + IF + GameStarted(_Region,_) + THEN + ProcStartEffectsForRegion(_Region); + + //REGION Special case: Some effects should stop during combat + IF + ObjectEnteredCombat(_Character,_) + AND + DB_LoopEffect(_Character, _fxHandle,_ID,_Region,_EffectName,_BoneName) + AND + DB_LoopEffectDisabledInCombat(_EffectName) + THEN + ProcStopLoopEffect(_fxHandle); + NOT DB_LoopEffect(_Character, _fxHandle,_ID,_Region,_EffectName,_BoneName); + DB_LoopEffect(_Character, (INTEGER64)-1, _ID,_Region,_EffectName,_BoneName); + + IF + ObjectLeftCombat((CHARACTERGUID)_Character,_) + AND + NOT DB_Dead(_Character) + AND + DB_LoopEffect(_Character, (INTEGER64)-1, _ID,_Region,_EffectName,_BoneName) + AND + DB_LoopEffectDisabledInCombat(_EffectName) + AND + PlayLoopEffect(_Character, _EffectName, _BoneName,_NewfxHandle) + THEN + NOT DB_LoopEffect(_Character, (INTEGER64)-1, _ID,_Region,_EffectName,_BoneName); + DB_LoopEffect(_Character,_NewfxHandle,_ID,_Region,_EffectName,_BoneName); + + QRY + QryBlockEffectInCombat((STRING)_EffectName,(GUIDSTRING)_Object) + AND + DB_LoopEffectDisabledInCombat(_EffectName) + AND + DB_CombatObjects(_Object,_) + THEN + DB_NOOP(1); + + //END_REGION + + } + EXIT + { + + } +} +Goal(23).Title("_Global_Homestead"); +Goal(23) +{ + INIT + { + + } + KB + { + //REGION Character creation mirror (respec) + IF + CharacterItemEvent(_Player,_Mirror,"GEN_Homestead_CharCreationMirror") + AND + CharacterAddToCharacterCreation(_Player,2,1) + THEN + DB_Illusionist(_Player,_Mirror); + + IF + CharacterItemEvent(_Player,_Mirror,"GEN_Homestead_CharCreationMirror") + AND + HasActiveStatus(_Player,"PLAY_DEAD",1) + THEN + RemoveStatus(_Player,"PLAY_DEAD"); + + //REGION Save while using the mirror -> re-add to character creation after loading (DOSTWO-9410) + IF + SavegameLoaded(_,_,_,_) + AND + DB_Illusionist(_Player,_Mirror) + AND + CharacterAddToCharacterCreation(_Player,2,0) + THEN + DebugBreak("Failed to re-add player to character creation after reload"); + //END_REGION + + //REGION Handle teleportation pyramids + // Avoid other players teleporting into the character creation "level" + IF + DB_Illusionist(_Player,_) + AND + DB_TeleporterPyramid(_Pyramid) + AND + ItemIsInCharacterInventory(_Pyramid,_Player,1) + THEN + Proc_PyramidCustomBlockAdd(_Pyramid,"LV_Mirror"); + + IF + ItemAddedToCharacter(_Pyramid,_Player) + AND + DB_TeleporterPyramid(_Pyramid) + AND + DB_Illusionist(_Player,_Mirror) + THEN + Proc_PyramidCustomBlockAdd(_Pyramid,"LV_Mirror"); + + IF + ItemRemovedFromCharacter(_Pyramid,_Player) + AND + DB_TeleporterPyramid(_Pyramid) + AND + DB_Illusionist(_Player,_Mirror) + THEN + Proc_PyramidCustomBlockRemove(_Pyramid,"LV_Mirror"); + + IF + CharacterCreationFinished(_Player) + AND + DB_Illusionist(_Player,_Mirror) + AND + DB_TeleporterPyramid(_Pyramid) + AND + ItemIsInCharacterInventory(_Pyramid,_Player,1) + THEN + Proc_PyramidCustomBlockRemove(_Pyramid,"LV_Mirror"); + //END_REGION + + //REGION Finished respec + PROC + Proc_HomesteadTeleportAfterMirror((CHARACTERGUID)_Player,(ITEMGUID)_Mirror,(TRIGGERGUID)_Trigger) + AND + _Trigger!=NULL_00000000-0000-0000-0000-000000000000 + THEN + TeleportTo(_Player,_Trigger,"",0); + + PROC + Proc_HomesteadTeleportAfterMirror((CHARACTERGUID)_Player,(ITEMGUID)_Mirror,(TRIGGERGUID)_Trigger) + AND + _Trigger==NULL_00000000-0000-0000-0000-000000000000 + THEN + TeleportTo(_Player,_Mirror,"",0); + + IF + CharacterCreationFinished(_Player) + AND + DB_Illusionist(_Player,_Mirror) + AND + GetVarObject(_Mirror,"PlayerPositionAfterCreation",_Trigger) + THEN + NOT DB_Illusionist(_Player,_Mirror); + Proc_HomesteadTeleportAfterMirror(_Player,_Mirror,(TRIGGERGUID)_Trigger); + //END_REGION + + //END_REGION + + } + EXIT + { + + } +} +Goal(24).Title("_GLOBAL_ItemEvents"); +Goal(24) +{ + INIT + { + /* + Databases used for this events + DB_HasStoryEvent((ITEMGUID)_Item,_HasItemEvent); + DB_GiveItemToEvent(_Item,_GiveItemToStoryEvent); + */ + ///Please Add any Item tags that you want to be tracked HERE!! + //when declaredo n the fly only players will be checked + DB_TaggedItemInitialSetup(1); + DB_TaggedItemTracker("HEALING_POTION"); + DB_TaggedItemTracker("BODYPART"); + DB_TaggedItemTracker("MEAT"); + DB_TaggedItemTracker("FISH"); + DB_TaggedItemTracker("BOOK"); + DB_TaggedItemTracker("QUEST_ANCESTOR_TREE_BRANCH"); + DB_TaggedItemTracker("FOOD"); + DB_TaggedItemTracker("BONE"); + DB_TaggedItemTracker("TOY"); + DB_TaggedItemTracker("ALCOHOL"); + DB_TaggedItemTracker("CHEESE"); + DB_TaggedItemTracker("VEGETABLE"); + DB_TaggedItemTracker("DRUDANAE"); + NOT DB_TaggedItemInitialSetup(1); + + DB_ItemEvents_TransferFlagToMoneyVarIndex("GEN_TransferNPCPayment",1); + DB_ItemEvents_TransferFlagToMoneyVarIndex("GEN_TransferNPCPayment_2",2); + DB_ItemEvents_TransferFlagToMoneyVarIndex("GEN_TransferNPCPayment_3",3); + DB_ItemEvents_TransferFlagToMoneyVarIndex("GEN_TransferNPCPayment_4",4); + DB_ItemEvents_TransferFlagToMoneyVarIndex("GEN_TransferNPCPayment_5",5); + + } + KB + { + //REGION Event Recheck triggers + //see also ZZZ_ItemEvents for a trigger + IF + CharacterMadePlayer(_Player) + THEN + DB_IgnoreReservedChanged(_Player); + + IF + DB_CharacterCreationDummy((CHARACTERGUID)_Char) + THEN + DB_IgnoreReservedChanged(_Char); + + IF + CharacterReservedUserIDChanged(_Char,_) + AND + DB_IsPlayer(_Char) + AND + NOT DB_IgnoreReservedChanged(_Char) + THEN + Proc_ItemEventCheck(); + + IF + CharacterReservedUserIDChanged(_Char,_) + THEN + NOT DB_IgnoreReservedChanged(_Char); + + IF + SavegameLoaded(_,_,_,_) + THEN + Proc_ItemEventCheck(); + + PROC + PROC_GLO_PartyMembers_Add((CHARACTERGUID)_Origin,(CHARACTERGUID)_) + THEN + Proc_ItemEventCheck(); + + //END_REGION + + //REGION Private + PROC + ProcSetMagicPocketsItemTemplatecount((CHARACTERGUID)_Player,(STRING)_Template,(INTEGER)_) + AND + DB_MagicPocketsItemTemplateCount(_Player,_Template,_Count) + THEN + NOT DB_MagicPocketsItemTemplateCount(_Player,_Template,_Count); + + PROC + ProcSetMagicPocketsItemTemplatecount((CHARACTERGUID)_Player,(STRING)_Template,(INTEGER)_Count) + THEN + DB_MagicPocketsItemTemplateCount(_Player,_Template,_Count); + //END_REGION + + //REGION Eating a bodypart + IF + StoryEvent(_Player,"GEN_CloseInventory") + THEN + CloseUI((CHARACTERGUID)_Player,"Inventory"); + + IF + CharacterItemEvent(_Char,_BodyPart,_Dialog) + AND + DB_AwaitingBodypartDialog(_Char,_BodyPart) + THEN + NOT DB_AwaitingBodypartDialog(_Char,_BodyPart); + ProcCheckBodyPartDialog(_Char,_Dialog); + + IF + CharacterItemEvent(_Char,_BodyPart,"GLO_AteBodyPart") + THEN + DB_AwaitingBodypartDialog(_Char,_BodyPart); + + PROC + ProcCheckBodyPartDialog((CHARACTERGUID)_Char,(STRING)_Dialog) + AND + _Dialog!="" + THEN + ProcTriggerLimbEatingDialog(_Char,_Dialog); + + PROC + ProcTriggerLimbEatingDialog((CHARACTERGUID)_Char,(STRING)_Dialog) + AND + CharacterIsInCombat(_Char,0) + THEN + Proc_StartDialog(0,_Dialog,_Char); + + PROC + ProcTriggerLimbEatingDialog((CHARACTERGUID)_Char,(STRING)_Dialog) + AND + CharacterIsInCombat(_Char,1) + THEN + CloseUI(_Char,"Inventory"); + + IF + DialogEnded("GEN_Limb_RottenLimb", _ID) + AND + DB_DialogPlayers(_ID, _Player, _) + AND + CharacterConsume((CHARACTERGUID)_Player, "CON_BodyPart_Harm", _) + THEN + DB_NOOP(0); + //END_REGION + + //REGION Magic Pockets procedures + QRY + QryItemInMagicPockets((CHARACTERGUID)_Char,(ITEMGUID)_Item) + AND + ItemIsInUserInventory(_Item,_Char,0,1) + THEN + DB_NOOP(1); + + QRY + QryItemTemplateInMagicPockets((CHARACTERGUID)_Char,(STRING)_ItemTemplate) + AND + ItemTemplateIsInUserInventory(_Char,_ItemTemplate,0,_Count) + AND + _Count > 0 + THEN + DB_NOOP(1); + + QRY + QryTaggedItemInMagicPockets((CHARACTERGUID)_Character,(STRING)_Tag) + AND + UserFindTaggedItem(_Character,_Tag,0,_Item) + THEN + DB_NOOP(1); + + QRY + QryItemTemplateInMagicPocketsCount((CHARACTERGUID)_Player,(STRING)_Template) + AND + ItemTemplateIsInUserInventory(_Player,_Template,0,_Count) + AND + _Count > 0 + THEN + ProcSetMagicPocketsItemTemplatecount(_Player,_Template,_Count); + + QRY + QryRemoveTaggedLocalItemsFromMagicPockets((CHARACTERGUID)_Player,(STRING)_Tag,(INTEGER)_Amount) + AND + UserRemoveTaggedLocalItems(_Player,_Tag,_Amount,_Count) + AND + _Count == _Amount + THEN + DB_NOOP(1); + + QRY + QryTakeItemFromMagicPockets((CHARACTERGUID)_Player,(ITEMGUID)_Item,(CHARACTERGUID)_TargetCharacter) + AND + ItemIsInUserInventory(_Item,_Player, 1, 1) + THEN + ItemToInventory(_Item,_TargetCharacter); + + QRY + QryTakeItemTemplateFromMagicPockets((CHARACTERGUID)_Character,(STRING)_ItemTemplate,(INTEGER)_Amount,(CHARACTERGUID)_TargetCharacter) + AND + ItemTemplateIsInUserInventory(_Character,_ItemTemplate,1,_Amount) + THEN + ItemTemplateRemoveFromUser(_ItemTemplate,_Character,_Amount); + ItemTemplateAddTo(_ItemTemplate,_TargetCharacter,_Amount); + + QRY + QryRemoveItemTemplateFromMagicPockets((CHARACTERGUID)_Character,(STRING)_ItemTemplate,(INTEGER)_Amount) + AND + ItemTemplateIsInUserInventory(_Character,_ItemTemplate,1,_HaveAmount) + AND + _HaveAmount >= _Amount + THEN + ItemTemplateRemoveFromUser(_ItemTemplate,_Character,_Amount); + + //TODO: take amount into account + QRY + QryRemoveTaggedItemFromMagicPockets((CHARACTERGUID)_Character,(STRING)_Tag, (INTEGER)_Amount) + AND + UserFindTaggedItem(_Character,_Tag,1,_Item) + THEN + ItemRemove(_Item); + + QRY + QryEvaluateMagicPocketGold((CHARACTERGUID)_Character,"<",(INTEGER)_Amount) + AND + UserGetGold(_Character,_Gold) + AND + _Gold < _Amount + THEN + DB_NOOP(1); + + QRY + QryEvaluateMagicPocketGold((CHARACTERGUID)_Character,"<=",(INTEGER)_Amount) + AND + UserGetGold(_Character,_Gold) + AND + _Gold <= _Amount + THEN + DB_NOOP(1); + + QRY + QryEvaluateMagicPocketGold((CHARACTERGUID)_Character,">",(INTEGER)_Amount) + AND + UserGetGold(_Character,_Gold) + AND + _Gold > _Amount + THEN + DB_NOOP(1); + + QRY + QryEvaluateMagicPocketGold((CHARACTERGUID)_Character,">=",(INTEGER)_Amount) + AND + UserGetGold(_Character,_Gold) + AND + _Gold >= _Amount + THEN + DB_NOOP(1); + + QRY + QryEvaluateMagicPocketGold((CHARACTERGUID)_Character,"==",(INTEGER)_Amount) + AND + UserGetGold(_Character,_Gold) + AND + _Gold == _Amount + THEN + DB_NOOP(1); + + PROC + ProcEvaluateMagicPocketGold((CHARACTERGUID)_Character,"<",(INTEGER)_Amount,(STRING)_ObjectFlag) + AND + UserGetGold(_Character,_Gold) + AND + _Gold < _Amount + THEN + ObjectSetFlag(_Character,_ObjectFlag); + + PROC + ProcEvaluateMagicPocketGold((CHARACTERGUID)_Character,"<=",(INTEGER)_Amount,(STRING)_ObjectFlag) + AND + UserGetGold(_Character,_Gold) + AND + _Gold <= _Amount + THEN + ObjectSetFlag(_Character,_ObjectFlag); + + PROC + ProcEvaluateMagicPocketGold((CHARACTERGUID)_Character,"==",(INTEGER)_Amount,(STRING)_ObjectFlag) + AND + UserGetGold(_Character,_Gold) + AND + _Gold == _Amount + THEN + ObjectSetFlag(_Character,_ObjectFlag); + + PROC + ProcEvaluateMagicPocketGold((CHARACTERGUID)_Character,">",(INTEGER)_Amount,(STRING)_ObjectFlag) + AND + UserGetGold(_Character,_Gold) + AND + _Gold > _Amount + THEN + ObjectSetFlag(_Character,_ObjectFlag); + + PROC + ProcEvaluateMagicPocketGold((CHARACTERGUID)_Character,">=",(INTEGER)_Amount,(STRING)_ObjectFlag) + AND + UserGetGold(_Character,_Gold) + AND + _Gold >= _Amount + THEN + ObjectSetFlag(_Character,_ObjectFlag); + + PROC + ProcAddGoldToMagicPockets((CHARACTERGUID)_Char,(INTEGER)_Gold) + THEN + UserAddGold(_Char,_Gold); + + PROC + ProcLaunchMagicPocketIterator((CHARACTERGUID)_Character,(STRING)_Event, (STRING)_CompletionEvent) + AND + CharacterGetReservedUserID(_Character,_OwnerUser) + AND + DB_IsPlayer(_Player) + AND + CharacterGetReservedUserID(_Player,_OwnerUser) + THEN + InventoryLaunchIterator(_Player, _Event, ""); + + PROC + ProcLaunchMagicPocketIterator((CHARACTERGUID)_Character,(STRING)_Event, (STRING)_CompletionEvent) + AND + _CompletionEvent != "" + THEN + GlobalSetFlag(_CompletionEvent); + + PROC + ProcLaunchMagicPocketTagIterator((CHARACTERGUID)_Character,(STRING)_TagA,(STRING)_TagB,(STRING)_Event,(STRING)_CompletionEvent) + AND + CharacterGetReservedUserID(_Character,_OwnerUser) + AND + DB_IsPlayer(_Player) + AND + CharacterGetReservedUserID(_Player,_OwnerUser) + THEN + InventoryLaunchTagIterator(_Player,_TagA,_TagB,_Event,""); + + PROC + ProcLaunchMagicPocketTagIterator((CHARACTERGUID)_Character,(STRING)_TagA,(STRING)_TagB,(STRING)_Event,(STRING)_CompletionEvent) + AND + _CompletionEvent != "" + THEN + GlobalSetFlag(_CompletionEvent); + + PROC + ProcSetMagicPocketsOwnershipFlag((CHARACTERGUID)_Char,(STRING)_Flag) + AND + DB_IsPlayer(_Char) + THEN + UserSetFlag(_Char,_Flag,0); + + PROC + ProcSetMagicPocketsOwnershipFlag((CHARACTERGUID)_Char,(STRING)_Flag) + AND + NOT DB_IsPlayer(_Char) + THEN + ObjectSetFlag(_Char,_Flag); + + PROC + ProcClearMagicPocketsOwnershipFlag((CHARACTERGUID)_Char,(STRING)_Flag) + AND + DB_IsPlayer(_Char) + THEN + UserClearFlag(_Char,_Flag,0); + + PROC + ProcClearMagicPocketsOwnershipFlag((CHARACTERGUID)_Char,(STRING)_Flag) + AND + NOT DB_IsPlayer(_Char) + THEN + ObjectClearFlag(_Char,_Flag,0); + + //REGION General Give - Take Gold in dialogs + + // Usage: + // DB_DialogMoneyTransfer(_MoneyVarIndex,_Dialog,_Amount) + // DB_DialogMoneyTransfer(_MoneyVarIndex,_Dialog,_Amount,_CheckSpeakerIndex) + // DB_DialogMoneyTransfer(_MoneyVarIndex,_Dialog,_Amount,_CheckSpeakerIndex,_TargetDBIndex) + // + // Paramters: + // - _MoneyVarIndex: 1 - 5 (use different GEN_CheckMagicPocketGold variables) + // - _Dialog: name of the dialog + // - _Amount: amount of money transfer + // - _CheckSpeakerIndex: the speaker index on which the money should be checked by the + // GEN_CheckPocketGold* script flags. If not specified, defaults to 2 (player in most cases). + // ** NOTE: this Speaker index is a dialog speaker index, as assigned in the dialog + // - _TargetDBIndex: when the GEN_TransferNPCPayment/GEN_TransferNPCPayment_2/../GEN_TransferNPCPayment_5 flag + // is set on a player in a dialog, transfer the money from that player to DB_DialogNPCs(_ID,_NPC,_TargetDBIndex). + // When set on an NPC in a dialog, transfer from that NPC to DB_DialogPlayers(_ID,_Player,_TargetDBIndex). + // If not specified, defaults to 1 (first NPC/player in dialog) + // ** NOTE: this index is an index in DB_DialogNPCs resp. DB_DialogPlayers, and hence is unrelated to the + // the speaker indices in the dialog editor! + // + // If someone does not have enough gold and a transfer is requested, all of their gold will be transferred instead. + + //REGION Default speaker to check and target Player/NPC for the money transfer if unspecified + PROC + Proc_ItemEvents_DialogMoneyTransfer_New((INTEGER)_MoneyVarIndex,(STRING)_Dialog,(INTEGER)_Amount,(INTEGER)_CheckSpeakerIndex,(INTEGER)_TargetDBIndex) + AND + DB_DialogMoneyTransfer(_MoneyVarIndex,_Dialog,_AnyAmount,_AnyCheckSpeakerIndex,_AnyTargetDBIndex) + THEN + NOT DB_DialogMoneyTransfer(_MoneyVarIndex,_Dialog,_AnyAmount,_AnyCheckSpeakerIndex,_AnyTargetDBIndex); + + PROC + Proc_ItemEvents_DialogMoneyTransfer_New((INTEGER)_MoneyVarIndex,(STRING)_Dialog,(INTEGER)_Amount,(INTEGER)_CheckSpeakerIndex,(INTEGER)_TargetDBIndex) + THEN + DB_DialogMoneyTransfer(_MoneyVarIndex,_Dialog,_Amount,_CheckSpeakerIndex,_TargetDBIndex); + + // Default to most common player speaker index (2) and transfer to the first NPC or Player in the dialog (1) + IF + DB_DialogMoneyTransfer((INTEGER)_MoneyVarIndex,(STRING)_Dialog,(INTEGER)_Amount) + THEN + Proc_ItemEvents_DialogMoneyTransfer_New(_MoneyVarIndex,_Dialog,_Amount,2,1); + + IF + DB_DialogMoneyTransfer((INTEGER)_MoneyVarIndex,(STRING)_Dialog,(INTEGER)_Amount,(INTEGER)_CheckSpeakerIndex) + THEN + Proc_ItemEvents_DialogMoneyTransfer_New(_MoneyVarIndex,_Dialog,_Amount,_CheckSpeakerIndex,1); + //END_REGION + + //REGION Transfer gold between player and NPC + // If the transfer flag is set on a player, transfer from player to NPC. Otherwise vice versa. + + PROC + Proc_GiveNPCGold((CHARACTERGUID)_Player,(CHARACTERGUID)_NPC,(INTEGER)_Amount) + AND + IntegerSubtract(0,_Amount,_RemoveGold) + THEN + ProcAddGoldToMagicPockets(_Player,_RemoveGold); + CharacterAddGold(_NPC,_Amount); + + PROC + Proc_TakeNPCGold((CHARACTERGUID)_Player,(CHARACTERGUID)_NPC,(INTEGER)_Amount) + AND + IntegerSubtract(0,_Amount,_RemoveGold) + THEN + ProcAddGoldToMagicPockets(_Player,_Amount); + CharacterAddGold(_NPC,_RemoveGold); + + PROC + Proc_ItemEventsTransferMoney((INTEGER)_ID,(INTEGER)_Amount,(CHARACTERGUID)_Source,(INTEGER)_TargetIndex) + AND + DB_DialogPlayers(_ID,_Source,_) + AND + DB_DialogNPCs(_ID,_NPC,_TargetIndex) + THEN + Proc_GiveNPCGold(_Source,(CHARACTERGUID)_NPC,_Amount); + + PROC + Proc_ItemEventsTransferMoney(_ID,_Amount,_Source,_TargetIndex) + AND + DB_DialogNPCs(_ID,_Source,_) + AND + DB_DialogPlayers(_ID,_Player,_TargetIndex) + AND + CharacterGetGold(_Source,_NPCGold) + AND + IntegerMin(_NPCGold,_Amount,_TransferAmount) + THEN + Proc_TakeNPCGold((CHARACTERGUID)_Player,_Source,_TransferAmount); + + IF + ObjectFlagSet(_TransferFlag,_Source,_ID) + AND + DB_ItemEvents_TransferFlagToMoneyVarIndex(_TransferFlag,_MoneyVarIndex) + AND + DB_DialogName(_Dialog,_ID) + AND + DB_DialogMoneyTransfer(_MoneyVarIndex,_Dialog,_Amount,_CheckSpeakerIndex,_TargetDBIndex) + THEN + Proc_ItemEventsTransferMoney(_ID,_Amount,(CHARACTERGUID)_Source,_TargetDBIndex); + + //END_REGION + + //REGION Reset transfer flag + IF + ObjectFlagSet(_TransferFlag,_Char,_ID) + AND + DB_ItemEvents_TransferFlagToMoneyVarIndex(_TransferFlag,_) + THEN + ObjectClearFlag(_Char,_TransferFlag); + //END_REGION + + //END_REGION + + //REGION Has, lose and give items + IF + ItemAddedToCharacter(_Item,_Char) + AND + DB_HasStoryEvent((ITEMGUID)_Item,_Event) + THEN + SetOnStage(_Item,1); + ProcSetMagicPocketsOwnershipFlag(_Char,_Event); + + IF + ItemRemovedFromCharacter(_Item,_Char) + AND + DB_HasStoryEvent((ITEMGUID)_Item,_Event) + THEN + ProcClearMagicPocketsOwnershipFlag(_Char,_Event); + + IF + DB_HasStoryEvent((ITEMGUID)_Item,(STRING)_Event) + AND + ItemGetOwner(_Item,_Owner) + AND + _Owner != NULL_00000000-0000-0000-0000-000000000000 + AND + ItemIsInCharacterInventory(_Item,_Owner,1) + THEN + ProcSetMagicPocketsOwnershipFlag(_Owner,_Event); + + IF + ObjectFlagSet(_Event,_Char,_) + AND + DB_GiveItemToEvent((ITEMGUID)_Item,_Event) + THEN + ItemToInventory(_Item,_Char,1); + + //For giving the same item multiple times + IF + ObjectFlagSet(_Event,_Char,_) + AND + DB_GiveItemToEventWithClear((ITEMGUID)_Item,_Event) + THEN + ItemToInventory(_Item,_Char,1); + ObjectClearFlag(_Char,_Event,0); + + PROC + Proc_ItemEventCheck() + AND + DB_IsPlayer(_Char) + AND + DB_HasStoryEvent(_,_Event) + THEN + ProcClearMagicPocketsOwnershipFlag(_Char,_Event); + + PROC + Proc_ItemEventCheck() + AND + DB_HasStoryEvent((ITEMGUID)_Item,_Event) + AND + ObjectExists(_Item,1) + AND + ItemIsInInventory(_Item,1) + AND + ItemGetOwner(_Item,_Char) + AND + DB_IsPlayer(_Char) + THEN + ProcSetMagicPocketsOwnershipFlag(_Char,_Event); + + //END_REGION + + //REGION Check for Tagged Items in Characters Inventory + //If You want to Keep track of a specific Item tag going in and out of the inventory, add that tag to DB_TaggedItemTracker(_String) + + IF + ItemAddedToCharacter(_Item,_Char) + AND + ObjectExists(_Item,1) //prevent stacked items from asserting all over the place + AND + DB_TaggedItemTracker((STRING)_Tag) + AND + IsTagged(_Item,_Tag,1) + AND + StringConcatenate("Has_TaggedItem_",_Tag,_Flag) + THEN + ProcSetMagicPocketsOwnershipFlag(_Char,_Flag); + DB_HasTaggedItem(_Char,_Item,_Tag,_Flag); + + IF + DB_TaggedItemInitialSetup(1) + THEN + DB_TaggedItemRecheck(1); + + IF + DB_TaggedItemRecheck(1) + AND + NOT DB_TaggedItemInitialSetup(1) + THEN + NOT DB_TaggedItemRecheck(1); + ProcCheckItemTags(); + + PROC + ProcCheckItemTags() + AND + DB_IsPlayer(_Player) + AND + DB_TaggedItemTracker(_Tag) + THEN + DB_ItemTagCheckingPlayer((CHARACTERGUID)_Player,_Tag); + InventoryLaunchTagIterator(_Player,_Tag,"","_Check_Item_Tags_",""); + FireOsirisEvents(); + NOT DB_ItemTagCheckingPlayer(_Player,_Tag); + + IF + StoryEvent((ITEMGUID)_Item,"_Check_Item_Tags_") + AND + DB_ItemTagCheckingPlayer(_Player,_Tag) + AND + StringConcatenate("Has_TaggedItem_",_Tag,_Flag) + THEN + ProcSetMagicPocketsOwnershipFlag(_Player,_Flag); + DB_HasTaggedItem(_Player,_Item,_Tag,_Flag); + + IF + DB_TaggedItemTracker(_Tag) + AND + NOT DB_TaggedItemInitialSetup(1) + THEN + ProcCheckInventoriesForTag(_Tag); + + PROC + ProcCheckInventoriesForTag((STRING)_Tag) + AND + DB_IsPlayer(_Player) + THEN + DB_ItemTagCheckingPlayer(_Player,_Tag); + InventoryLaunchTagIterator(_Player,_Tag,"","_Check_Item_Tag_",""); + FireOsirisEvents(); + NOT DB_ItemTagCheckingPlayer(_Player,_Tag); + + IF + StoryEvent((ITEMGUID)_Item, "_Check_Item_Tag_") + AND + DB_ItemTagCheckingPlayer(_Char,_Tag) + AND + StringConcatenate("Has_TaggedItem_",_Tag,_Flag) + THEN + ProcSetMagicPocketsOwnershipFlag(_Char,_Flag); + DB_HasTaggedItem(_Char,_Item,_Tag,_Flag); + + + IF + ItemStackedWith(_Item,_) + AND + DB_HasTaggedItem(_Char,_Item,_String,_Flag) + THEN + NOT DB_HasTaggedItem(_Char,_Item,_String,_Flag); + + IF + ItemRemovedFromCharacter(_Item,_Char) + AND + DB_HasTaggedItem((CHARACTERGUID)_Char,_Item,_String,_Flag) + THEN + NOT DB_HasTaggedItem(_Char,_Item,_String,_Flag); + Proc_CheckForItemTagInMagicPockets(_Char,_String); + + PROC + Proc_CheckForItemTagInMagicPockets((CHARACTERGUID)_Char,(STRING)_Tag) + AND + NOT QryTaggedItemInMagicPockets(_Char,_Tag) + AND + StringConcatenate("Has_TaggedItem_",_Tag,_Flag) + THEN + ProcClearMagicPocketsOwnershipFlag(_Char,_Flag); + + PROC + Proc_ItemEventCheck() + AND + DB_HasTaggedItem(_,_,_Tag,_Flag) + AND + DB_IsPlayer(_Char) + THEN + ProcClearMagicPocketsOwnershipFlag(_Char,_Flag); + + IF + RegionStarted(_) + AND + DB_HasTaggedItem(_Char,_Item,_Tag,_Flag) + AND + ObjectExists(_Char,0) + THEN + NOT DB_HasTaggedItem(_Char,_Item,_Tag,_Flag); + + PROC + Proc_ItemEventCheck() + AND + DB_HasTaggedItem(_Char,_Item,_Tag,_Flag) + THEN + ProcSetMagicPocketsOwnershipFlag(_Char,_Flag); + + //END_REGION + + + //REGION On item template on gain and lose + //DB_HasTemplateItem((STRING)_template,(STRING)_HasItemFlag) + //If item is added to inventory + IF + ItemTemplateAddedToCharacter(_templateGUID,_item,_char) + AND + String(_templateGUID,_template) + AND + DB_HasTemplateItem((STRING)_template,(STRING)_flag) + THEN + Proc_OnAddItemTemplateToChar(_char,_template,_flag); + + //If item is removed from inventory + IF + ItemTemplateRemovedFromCharacter(_template,_item,_char) + AND + DB_HasTemplateItem((STRING)_template,(STRING)_flag) + THEN + Proc_OnRemoveItemTemplateFromChar(_char,_template,_flag); + + IF + DB_HasTemplateItem((STRING)_template,(STRING)_flag) + AND + DB_IsPlayer(_Player) + THEN + Proc_OnAddItemTemplateToChar(_Player,_template,_flag); + + PROC + Proc_ItemEventCheck() + AND + DB_HasTemplateItem((STRING)_template,(STRING)_flag) + AND + DB_IsPlayer(_Player) + THEN + ProcClearMagicPocketsOwnershipFlag(_Player,_flag); + Proc_OnAddItemTemplateToChar(_Player,_template,_flag); + + //Proc on template add + PROC + Proc_OnAddItemTemplateToChar((CHARACTERGUID)_char,(STRING)_template,(STRING)_flag) + AND + ItemTemplateIsInUserInventory(_char,_template,0,_amount) + AND + _amount > 0 + THEN + ProcSetMagicPocketsOwnershipFlag(_char,_flag); + + //Proc on template remove + PROC + Proc_OnRemoveItemTemplateFromChar((CHARACTERGUID)_char,(STRING)_template,(STRING)_flag) + AND + ItemTemplateIsInUserInventory(_char,_template,0,_amount) + AND + _amount < 1 + THEN + ProcClearMagicPocketsOwnershipFlag(_char,_flag); + + //END_REGION + + //REGION Give/remove Item template + //Gives item when flag is set + IF + ObjectFlagSet(_event,(CHARACTERGUID)_target,_inst) + AND + DB_GiveTemplateFromPlayerDialogEvent((STRING)_template,(STRING)_event,(STRING)_success) + AND + DialogGetInvolvedPlayer(_inst, 1, (CHARACTERGUID)_player) + THEN + ObjectClearFlag(_target,_event,0); //Flag is cleared again + Proc_PlayerGivesItemFromTemplate(_player,_target,_template,_success); + + + //Gives item when flag is set + IF + ObjectFlagSet(_event,(CHARACTERGUID)_target,_inst) + AND + DB_GiveTemplateFromNpcToPlayerDialogEvent((STRING)_template,(STRING)_event,(STRING)_success) + AND + DialogGetInvolvedPlayer(_inst, 1, (CHARACTERGUID)_player) + THEN + ObjectClearFlag(_target,_event,0); //Flag is cleared again + Proc_NPCGivesItemFromTemplate(_target,_player,_template,_success); + + + //Gives item when flag is set + IF + ObjectFlagSet(_event,_char,_) + AND + DB_GiveNewItemFromTemplateEvent((STRING)_template,(STRING)_event) + THEN + ObjectClearFlag((CHARACTERGUID)_char,(STRING)_event,0); //Flag is cleared again + ItemTemplateAddTo(_template,_char,1); + + + //Remove item when flag set + IF + ObjectFlagSet(_event,(CHARACTERGUID)_char,_) + AND + DB_RemoveItemFromTemplateEvent((STRING)_template,(STRING)_event,(STRING)_success) + THEN + ObjectClearFlag(_char,_event,0); //Flag is cleared again + Proc_RemoveItemFromTemplate(_char,_template,_success); + + //END_REGION + + //REGION Give/Remove Template Proc + //Giving Item + PROC + Proc_PlayerGivesItemFromTemplate((CHARACTERGUID)_Player,(CHARACTERGUID)_target,(STRING)_template,(STRING)_success) + AND + QryRemoveItemTemplateFromMagicPockets(_Player,_Template,1) + THEN + ItemTemplateAddTo(_template,_target,1); + ObjectSetFlag(_target,_success); + + //Mirrored Give to set _success on NPC instead of player + PROC + Proc_NPCGivesItemFromTemplate((CHARACTERGUID)_giver,(CHARACTERGUID)_target,(STRING)_template,(STRING)_success) + AND + ItemTemplateIsInCharacterInventory(_giver,_template,_amount) + AND + _amount > 0 + THEN + ItemTemplateRemoveFrom(_template,_giver,1); + ItemTemplateAddTo(_template,_target,1); + ObjectSetFlag(_giver,_success); + + //Remove Item + PROC + Proc_RemoveItemFromTemplate((CHARACTERGUID)_char,(STRING)_template,(STRING)_success) + AND + DB_IsPlayer(_Char) + AND + QryRemoveItemTemplateFromMagicPockets(_char,_template,1) + THEN + ObjectSetFlag(_char,_success); + + PROC + Proc_RemoveItemFromTemplate((CHARACTERGUID)_char,(STRING)_template,(STRING)_success) + AND + NOT DB_IsPlayer(_Char) + AND + ItemTemplateIsInCharacterInventory(_char,_template,_amount) + AND + _amount > 0 + THEN + ItemTemplateRemoveFrom(_template,_char,1); + ObjectSetFlag(_char,_success); + //END_REGION + + IF + DialogEnded(_,_ID) + AND + DialogGetInvolvedNPC(_ID,1,(ITEMGUID)_Item) + AND + ObjectIsItem(_Item,1) + AND + ObjectGetFlag(_Item,"ItemPickup",1) + AND + DialogGetInvolvedPlayer(_ID,1,(CHARACTERGUID)_Player) + THEN + CharacterPickupItem(_Player,_Item,""); + ObjectClearFlag(_Item,"ItemPickup",0); + + //REGION Play Activate animation from Script + + IF + StoryEvent((CHARACTERGUID)_Player,"GEN_Animation_Activate") + THEN + PlayAnimation(_Player,"use_activate"); + + //END_REGION + + + + } + EXIT + { + + } +} +Goal(25).Title("_Global_JournalHelper"); +Goal(25) +{ + INIT + { + + } + KB + { + //REGION Init + + PROC + ProcCheckDefaultQuestAdd((STRING)_QuestName) + AND + NOT DB_QuestDef_AddEvent(_QuestName,_) + AND + StringConcatenate("QuestAdd_",_QuestName,_AddEvent) + THEN + DB_QuestDef_AddEvent(_QuestName,_AddEvent); + + PROC + ProcCheckDefaultQuestClose((STRING)_QuestName) + AND + NOT DB_QuestDef_CloseEvent(_QuestName,_) + AND + StringConcatenate("QuestClose_",_QuestName,_CloseEvent) + THEN + DB_QuestDef_CloseEvent(_QuestName,_CloseEvent); + + IF + DB_QuestDef_State((STRING)_QuestName,(STRING)_QuestState, 1) //Indices allow you to define multiple start and end events for every quest. + AND + StringConcatenate("QuestUpdate_",_QuestName,_String1) + AND + StringConcatenate(_String1,"_",_String2) + AND + StringConcatenate(_String2,_QuestState,_AddEvent) + THEN + DB_QuestDef_AddEvent(_QuestName,_AddEvent); + DB_QuestDef_State((STRING)_QuestName,(STRING)_QuestState); + + IF + DB_QuestDef_State((STRING)_QuestName,(STRING)_QuestState, -1) + AND + StringConcatenate("QuestUpdate_",_QuestName,_String1) + AND + StringConcatenate(_String1,"_",_String2) + AND + StringConcatenate(_String2,_QuestState,_CloseEvent) + THEN + DB_QuestDef_CloseEvent(_QuestName,_CloseEvent); + DB_QuestDef_State((STRING)_QuestName,(STRING)_QuestState); + + IF + DB_QuestDef_State((STRING)_QuestName,(STRING)_QuestState) + AND + NOT DB_QuestDef_UpdateEvent(_QuestName,_QuestState, _) + AND + StringConcatenate("QuestUpdate_",_QuestName,_String1) + AND + StringConcatenate(_String1,"_",_String2) + AND + StringConcatenate(_String2,_QuestState,_UpdateEvent) + THEN + ProcCheckDefaultQuestAdd(_QuestName); + ProcCheckDefaultQuestClose(_QuestName); + DB_QuestDef_UpdateEvent(_QuestName,_QuestState,_UpdateEvent); + //END_REGION + + //REGION Unlocking quests + IF + ObjectFlagSet(_QuestAddEvent,(CHARACTERGUID)_Player, _) + AND + DB_QuestDef_AddEvent(_QuestName,_QuestAddEvent) + AND + DB_IsPlayer(_Player) + THEN + QuestAdd(_Player,_QuestName); + DB_ActivatedQuests(_QuestName); + ProcSetQuestNPCFlag(_QuestName,_QuestAddEvent); + ProcCheckMigrateQuestAddFlag(_Player,_QuestName,_QuestAddEvent); + + //don't migrate quest add flags that also trigger an update, since we'll be trying ot share the update already before the original player + //had a chance to get the update + PROC + ProcCheckMigrateQuestAddFlag((CHARACTERGUID)_Player,(STRING)_QuestName,(STRING)_QuestAddEvent) + AND + NOT DB_QuestDef_UpdateEvent(_QuestName,_,_QuestAddEvent) + THEN + ProcMigrateQuestFlag(_Player,_QuestName,_QuestAddEvent); + + IF + ObjectFlagSet(_QuestUpdateEvent,(CHARACTERGUID)_Player, _) + AND + DB_QuestDef_UpdateEvent(_QuestName,_QuestState,_QuestUpdateEvent) + AND + DB_IsPlayer(_Player) + AND + QuestIsClosed(_Player,_QuestName,0) + THEN + QuestUpdate(_Player,_QuestName,_QuestState); + ProcMigrateQuestFlag(_Player,_QuestName,_QuestUpdateEvent); + ProcSetQuestNPCFlag(_QuestName,_QuestUpdateEvent); + + IF + ObjectFlagSet(_QuestCloseEvent,(CHARACTERGUID)_Player, _) + AND + DB_QuestDef_CloseEvent(_QuestName,_QuestCloseEvent) + AND + DB_IsPlayer(_Player) + THEN + QuestClose(_Player,_QuestName); + ProcMigrateQuestFlag(_Player,_QuestName,_QuestCloseEvent); + ProcSetQuestNPCFlag(_QuestName,_QuestCloseEvent); + + PROC + ProcSetQuestNPCFlag((STRING)_QuestName,(STRING)_QuestEvent) + AND + DB_QuestNPC(_QuestName,(CHARACTERGUID)_NPC) + THEN + ObjectSetFlag(_NPC,_QuestEvent,0); + + + //END_REGION + + //REGION Merging quests + + IF + CharacterMadePlayer(_Player) + THEN + DB_JournalIgnoreReservedChanged(_Player); + + IF + CharacterReservedUserIDChanged(_Char,_NewUser) + AND + DB_IsPlayer(_Char) + AND + NOT DB_JournalIgnoreReservedChanged(_Char) + THEN + ProcMigrateQuestsForUser(_NewUser,_Char); + + IF + CharacterReservedUserIDChanged(_Char,_) + THEN + NOT DB_JournalIgnoreReservedChanged(_Char); + + IF + CharacterJoinedParty(_Joiner) + AND + DB_IsPlayer(_Joiner) + AND + DB_ActivatedQuests(_Quest) + THEN + ProcMigrateQuest(_Joiner,_Quest); + + IF + CharacterJoinedParty(_Joiner) + AND + DB_IsPlayer(_Joiner) + THEN + ProcShareQuestsWithParty(_Joiner); + + PROC + ProcShareQuestsWithParty((CHARACTERGUID)_Joiner) + AND + DB_IsPlayer(_Player) + AND + NOT DB_MigratedNewPartyFlags(1) + AND + _Player != _Joiner + AND + CharacterIsInPartyWith(_Player,_Joiner,1) + AND + DB_ActivatedQuests(_Quest) + AND + DB_SharedQuestFlag(_Player,_Quest,_QuestFlag) + THEN + DB_MigratedNewPartyFlags(1); + ProcShareQuestflagWith(_Player,_Quest,_QuestFlag,_Joiner); + + PROC + ProcShareQuestsWithParty((CHARACTERGUID)_Joiner) + THEN + NOT DB_MigratedNewPartyFlags(1); + + //always share quests with the owning user + IF + CharacterJoinedParty(_Joiner) + AND + DB_IsPlayer(_Joiner) + AND + CharacterGetReservedUserID(_Joiner,_User) + THEN + ProcMigrateQuestsForUser(_User,_Joiner); + + //handle the case where an MP savegame is loaded in SP + IF + SavegameLoaded(_,_,_,_) + AND + DB_IsPlayer(_Joiner) + AND + CharacterGetReservedUserID(_Joiner,_User) + THEN + ProcMigrateQuestsForUser(_User,_Joiner); + ProcShareQuestsWithParty(_Joiner); + + PROC + ProcMigrateQuestsForUser((INTEGER)_User,(CHARACTERGUID)_Joiner) + AND + DB_IsPlayer(_Other) + AND + CharacterGetReservedUserID(_Other,_User) + AND + DB_ActivatedQuests(_Quest) + AND + QuestGetBroadcastLevel(_Quest,_Level) + AND + _Level != "Character" + AND + DB_PrivateQuestFlag(_Other,_Quest,_QuestFlag) + THEN + ProcShareQuestflagWith(_Other,_Quest,_QuestFlag,_Joiner); + + IF + QuestShared(_SrcCharacter,"",1) + AND + DB_IsPlayer(_SrcCharacter) + AND + DB_ActivatedQuests(_Quest) + THEN + ProcMigrateQuest(_SrcCharacter,_Quest); + + IF + QuestShared(_SrcCharacter,_Quest,1) + AND + _Quest != "" + THEN + ProcMigrateQuestFlagsFor(_SrcCharacter,_Quest); + + PROC + ProcMigrateQuest((CHARACTERGUID)_SrcPlayer,(STRING)_Quest) + AND + QuestAccepted(_SrcPlayer,_Quest,1) + AND + QuestIsShared(_SrcPlayer,_Quest,1) + THEN + ProcMigrateQuestFlagsFor(_SrcPlayer,_Quest); + + PROC + ProcMigrateQuestFlagsFor((CHARACTERGUID)_SrcPlayer,(STRING)_Quest) + AND + DB_PrivateQuestFlag(_SrcPlayer,_Quest,_QuestFlag) + THEN + DB_SharedQuestFlag(_SrcPlayer,_Quest,_QuestFlag); + NOT DB_PrivateQuestFlag(_SrcPlayer,_Quest,_QuestFlag); + + PROC + ProcMigrateQuestFlagsFor((CHARACTERGUID)_SrcChar,(STRING)_Quest) + AND + DB_SharedQuestFlag(_SrcChar,_Quest,_QuestFlag) + THEN + ProcShareQuestFlag(_SrcChar,_Quest,_QuestFlag); + + PROC + ProcShareQuestFlag((CHARACTERGUID)_SrcPlayer,(STRING)_Quest,(STRING)_QuestFlag) + AND + DB_IsPlayer(_Player) + AND + _Player!=_SrcPlayer + AND + CharacterIsInPartyWith(_Player,_SrcPlayer,1) + AND + ObjectGetFlag(_Player,_QuestFlag,0) //checked for when story gives the flag to several players in a row. Otherwise you won't have a chance to give the update in case someone shares the quest + THEN + ProcShareQuestflagWith(_SrcPlayer,_Quest,_QuestFlag,_Player); + + PROC + ProcShareQuestflagWith((CHARACTERGUID)_SrcPlayer,(STRING)_Quest,(STRING)_QuestFlag,(CHARACTERGUID)_Player) + THEN + ProcStoreFlagState(_Player,_QuestFlag); + ProcShareQuestUpdate(_SrcPlayer,_Quest,_QuestFlag,_Player); + ObjectShareFlag(_Player,_QuestFlag); + ProcStoreSharedQuestFlag(_SrcPlayer,_Quest,_QuestFlag,_Player); + ProcVerifyFlagState(_SrcPlayer,_Player,_QuestFlag); + + PROC + ProcStoreSharedQuestFlag((CHARACTERGUID)_SrcPlayer,(STRING)_Quest,(STRING)_QuestFlag,(CHARACTERGUID)_Player) + AND + DB_SharedQuestFlag(_SrcPlayer,_Quest,_QuestFlag) + THEN + DB_SharedQuestFlag(_Player,_Quest,_QuestFlag); + + PROC + ProcStoreSharedQuestFlag((CHARACTERGUID)_SrcPlayer,(STRING)_Quest,(STRING)_QuestFlag,(CHARACTERGUID)_Player) + AND + DB_PrivateQuestFlag(_SrcPlayer,_Quest,_QuestFlag) + THEN + DB_PrivateQuestFlag(_Player,_Quest,_QuestFlag); + + PROC + ProcShareQuestUpdate((CHARACTERGUID)_SrcPlayer,(STRING)_Quest,(STRING)_QuestFlag,(CHARACTERGUID)_Player) + AND + QuestAccepted(_SrcPlayer,_Quest,1) + AND + QuestAccepted(_Player,_Quest,0) + THEN + QuestAdd(_Player,_Quest); + ProcMigrateQuestFlagsFor(_Player,_Quest); + + PROC + ProcShareQuestUpdate((CHARACTERGUID)_SrcPlayer,(STRING)_Quest,(STRING)_QuestFlag,(CHARACTERGUID)_Player) + AND + DB_QuestDef_UpdateEvent(_Quest,_QuestState,_QuestFlag) + AND + QuestHasUpdate(_SrcPlayer,_Quest,_QuestState,1) + THEN + QuestReceiveSharedUpdate(_SrcPlayer,_Player,_Quest,_QuestState); + + PROC + ProcShareQuestUpdate((CHARACTERGUID)_SrcPlayer,(STRING)_Quest,(STRING)_QuestFlag,(CHARACTERGUID)_Player) + AND + DB_QuestDef_CloseEvent(_Quest,_QuestFlag) + THEN + QuestClose(_Player,_Quest); + + PROC + ProcMigrateQuestFlag((CHARACTERGUID)_SrcPlayer,(STRING)_Quest,(STRING)_QuestFlag) + AND + QuestIsShared(_SrcPlayer,_Quest,0) + THEN + DB_PrivateQuestFlag(_SrcPlayer,_Quest,_QuestFlag); + ProcSharePrivateFlags(_SrcPlayer,_Quest,_QuestFlag); + + PROC + ProcMigrateQuestFlag((CHARACTERGUID)_SrcPlayer,(STRING)_Quest,(STRING)_QuestFlag) + AND + QuestIsShared(_SrcPlayer,_Quest,1) + THEN + DB_SharedQuestFlag(_SrcPlayer,_Quest,_QuestFlag); + ProcShareQuestFlag(_SrcPlayer,_Quest,_QuestFlag); + + PROC + ProcSharePrivateFlags((CHARACTERGUID)_SrcPlayer,(STRING)_Quest,(STRING)_QuestFlag) + AND + QuestGetBroadcastLevel(_Quest,"User") + AND + CharacterGetReservedUserID(_SrcPlayer,_User) + AND + DB_IsPlayer(_Char) + AND + _Char != _SrcPlayer + AND + CharacterGetReservedUserID(_Char,_User) + THEN + DB_PrivateQuestFlag(_SrcPlayer,_Quest,_QuestFlag); + ObjectShareFlag(_Char,_QuestFlag); + + PROC + ProcStoreFlagState((CHARACTERGUID)_Player,(STRING)_QuestFlag) + AND + ObjectGetFlag(_Player,_Questflag,_State) + THEN + DB_TargetQuestFlagState(_Player,_QuestFlag,_State); + + //if a quest flag was cleared and we didn't have it set before sharing clear it as well + //this will make flags couple to inventory events work correctly + PROC + ProcVerifyFlagState((CHARACTERGUID)_SrcPlayer,(CHARACTERGUID)_Player,(STRING)_QuestFlag) + AND + ObjectGetFlag(_SrcPlayer,_Questflag,0) + AND + DB_TargetQuestFlagState(_Player,_QuestFlag,0) + THEN + ObjectClearFlag(_Player,_QuestFlag,0); + + PROC + ProcVerifyFlagState((CHARACTERGUID)_SrcPlayer,(CHARACTERGUID)_Player,(STRING)_QuestFlag) + AND + ObjectGetFlag(_SrcPlayer,_Questflag,1) + AND + DB_HasStoryEvent((ITEMGUID)_Item,_Questflag) + AND + NOT QryItemInMagicPockets(_Player,_Item) + THEN + ObjectClearFlag(_Player,_QuestFlag,0); + + PROC + ProcVerifyFlagState((CHARACTERGUID)_SrcPlayer,(CHARACTERGUID)_Player,(STRING)_QuestFlag) + AND + ObjectGetFlag(_SrcPlayer,_Questflag,1) + AND + DB_HasTemplateItem(_Template,_Questflag) + AND + NOT QryItemTemplateInMagicPockets(_Player,_Template) + THEN + ObjectClearFlag(_Player,_QuestFlag,0); + + //TODO: this does not handle events outside of the DB_HasStoryEvent or the TaggedItemTracker + + PROC + ProcVerifyFlagState(_,_Player,_QuestFlag) + AND + DB_TargetQuestFlagState(_Player,_QuestFlag,_State) + THEN + NOT DB_TargetQuestFlagState(_Player,_QuestFlag,_State); + //END_REGION + + PROC + ProcSetFlagOnAll((STRING)_Flag) + AND + DB_IsPlayer(_Player) + THEN + ObjectSetFlag(_Player,_Flag); + + //REGION end of region + + PROC + ProcRegionEnded(_Region) + AND + DB_QuestDef_CloseAtRegionEnded((STRING)_Quest,(STRING)_Flag,(STRING)_Region) + AND + DB_IsPlayer(_Char) + AND + QuestAccepted(_Char,_Quest,1) + THEN + ObjectSetFlag(_Char,_Flag); + QuestArchive(_Char,_Quest,1); + + PROC + ProcRegionEnded(_Region) + AND + DB_QuestDef_CloseAtRegionEnded_Conditions_FalseGlobalFlag_TrueObjFlag((STRING)_Quest,(STRING)_SetFlag,_Region,(STRING)_GloFlag,(STRING)_ObjFlag) + AND + GlobalGetFlag(_GloFlag,0) + AND + DB_IsPlayer(_Char) + AND + QuestAccepted(_Char,_Quest,1) + AND + UserGetFlag(_Char,_ObjFlag,1) + AND + ObjectGetFlag(_Char,_SetFlag,0) + THEN + ObjectSetFlag(_Char,_SetFlag); + QuestArchive(_Char,_Quest,1); + + PROC + ProcRegionEnded(_Region) + AND + DB_QuestDef_CloseAtRegionEnded_Conditions_FalseGlobalFlag_FalseObjFlag((STRING)_Quest,(STRING)_SetFlag,_Region,(STRING)_GloFlag,(STRING)_ObjFlag) + AND + GlobalGetFlag(_GloFlag,0) + AND + DB_IsPlayer(_Char) + AND + QuestAccepted(_Char,_Quest,1) + AND + UserGetFlag(_Char,_ObjFlag,0) + AND + ObjectGetFlag(_Char,_SetFlag,0) + THEN + ObjectSetFlag(_Char,_SetFlag); + QuestArchive(_Char,_Quest,1); + + PROC + ProcRegionEnded(_Region) + AND + DB_QuestDef_CloseAtRegionEnded_Condition_FalseGlobalFlag_OrClose((STRING)_Quest,(STRING)_SetFlag,_Region,(STRING)_GlobalFlag) + AND + DB_IsPlayer(_Char) + AND + QuestAccepted(_Char,_Quest,1) + AND + GlobalGetFlag(_GlobalFlag,0) + THEN + ObjectSetFlag(_Char,_SetFlag); + QuestArchive(_Char,_Quest,1); + + PROC + ProcRegionEnded(_Region) + AND + DB_QuestDef_CloseAtRegionEnded_Condition_FalseGlobalFlag_OrClose((STRING)_Quest,(STRING)_SetFlag,_Region,(STRING)_GlobalFlag) + AND + DB_IsPlayer(_Char) + AND + QuestAccepted(_Char,_Quest,1) + AND + GlobalGetFlag(_GlobalFlag,1) + THEN + QuestClose(_Char,_Quest); + QuestArchive(_Char,_Quest,1); + + PROC + ProcRegionEnded(_Region) + AND + DB_QuestDef_CloseAtRegionEnded_Condition_FalseGlobalFlag((STRING)_Quest,(STRING)_SetFlag,_Region,(STRING)_GlobalFlag) + AND + DB_IsPlayer(_Char) + AND + QuestAccepted(_Char,_Quest,1) + AND + GlobalGetFlag(_GlobalFlag,0) + THEN + ObjectSetFlag(_Char,_SetFlag); + QuestArchive(_Char,_Quest,1); + + PROC + ProcRegionEnded(_Region) + AND + DB_QuestDef_CloseAtRegionEnded_Condition_TrueGlobalFlag((STRING)_Quest,(STRING)_SetFlag,_Region,(STRING)_GlobalFlag) + AND + DB_IsPlayer(_Char) + AND + QuestAccepted(_Char,_Quest,1) + AND + GlobalGetFlag(_GlobalFlag,1) + THEN + ObjectSetFlag(_Char,_SetFlag); + QuestArchive(_Char,_Quest,1); + + + PROC + ProcRegionEnded(_Region) + AND + DB_QuestDef_CloseAtRegionEnded_Conditions_FalseObjFlag((STRING)_Quest,(STRING)_SetFlag,_Region,(STRING)_ObjFlag) + AND + DB_IsPlayer(_Char) + AND + QuestAccepted(_Char,_Quest,1) + AND + UserGetFlag(_Char,_ObjFlag,0) + AND + ObjectGetFlag(_Char,_SetFlag,0) + THEN + ObjectSetFlag(_Char,_SetFlag); + QuestArchive(_Char,_Quest,1); + + PROC + ProcRegionEnded(_Region) + AND + DB_QuestDef_CloseAtRegionEnded_Conditions_TrueObjFlag((STRING)_Quest,(STRING)_SetFlag,_Region,(STRING)_ObjFlag) + AND + DB_IsPlayer(_Char) + AND + QuestAccepted(_Char,_Quest,1) + AND + UserGetFlag(_Char,_ObjFlag,1) + AND + ObjectGetFlag(_Char,_SetFlag,0) + THEN + ObjectSetFlag(_Char,_SetFlag); + QuestArchive(_Char,_Quest,1); + + PROC + ProcRegionEnded(_Region) + AND + DB_QuestDef_CloseAtRegionEnded_Conditions_FalseObjFlag_TrueObjFlag((STRING)_Quest,(STRING)_SetFlag,_Region,(STRING)_FalseObjFlag,(STRING)_TrueObjFlag) + AND + DB_IsPlayer(_Char) + AND + QuestAccepted(_Char,_Quest,1) + AND + UserGetFlag(_Char,_FalseObjFlag,0) + AND + UserGetFlag(_Char,_TrueObjFlag,1) + AND + ObjectGetFlag(_Char,_SetFlag,0) + THEN + ObjectSetFlag(_Char,_SetFlag); + QuestArchive(_Char,_Quest,1); + + /* + PROC + ProcRegionEnded(_Region) + AND + DB_QuestDef_CloseAtRegionEnded_Conditions_FalseObjFlag_FalseObjFlag(_Quest,_SetFlag,_Region,_FalseObjFlag1,_FalseObjFlag2) + AND + DB_IsPlayer(_Char) + AND + QuestAccepted(_Char,_Quest,1) + AND + UserGetFlag(_Char,_FalseObjFlag1,0) + AND + UserGetFlag(_Char,_FalseObjFlag2,0) + AND + ObjectGetFlag(_Char,_SetFlag,0) + THEN + ObjectSetFlag(_Char,_SetFlag); + QuestArchive(_Char,_Quest,1); + */ + + //END_REGION + + //REGION set category on region started + + PROC + ProcRegionStarted((STRING)_Region) + AND + DB_RegionQuestCategory(_Region,(STRING)_Category) + AND + DB_RegionQuestCategory_Swapped((STRING)_Quest) + THEN + ProcQuestSetCategory(_Quest,_Category); + + + PROC + ProcQuestSetCategory((STRING)_Quest,(STRING)_Category) + AND + DB_IsPlayer(_Char) + AND + QuestIsClosed(_Char,_Quest,0) + THEN + QuestSetCategory(_Quest,_Category); + ProcQuestCategorySet(_Quest,_Category,_Char); + + + PROC + ProcQuestCategorySet((STRING)_Quest,(STRING)_Category,(CHARACTERGUID)_Char) + THEN + DB_NOOP(1); + + //END_REGION + + //REGION QuestReward + + IF + ObjectFlagSet(_Flag,(CHARACTERGUID)_Player,_Inst) + AND + DB_IsPlayer(_Player) + AND + DB_QuestDef_QuestReward((STRING)_Quest,(STRING)_RewardState,_Flag) + AND + QuestAccepted(_Player,_Quest,1) + AND + _Inst != 0 + THEN + DB_GiveQuestRewardAfterDialog(_Player,_Quest,_RewardState,_Inst); + + IF + DialogEnded(_,_Inst) + AND + DB_GiveQuestRewardAfterDialog(_Player,_Quest,_RewardState,_Inst) + THEN + ProcGiveQuestReward(_Player,_Quest,_RewardState); + NOT DB_GiveQuestRewardAfterDialog(_Player,_Quest,_RewardState,_Inst); + + PROC + ProcGiveQuestReward((CHARACTERGUID)_Player,(STRING)_Quest,(STRING)_RewardState) + AND + NOT DB_QuestRewardGiven(_Player,_Quest,_RewardState) + THEN + CharacterGiveQuestReward(_Player,_Quest,_RewardState); + ProcMarkPartyAsGivenReward(_Player,_Quest,_RewardState); + + PROC + ProcMarkPartyAsGivenReward((CHARACTERGUID)_Player,(STRING)_Quest,(STRING)_RewardState) + AND + DB_IsPlayer(_OtherPlayer) + AND + CharacterIsInPartyWith(_Player,_OtherPlayer,1) + THEN + DB_QuestRewardGiven(_OtherPlayer,_Quest,_RewardState); + + //END_REGION + + } + EXIT + { + + } +} +Goal(26).Title("_Global_Procedure"); +Goal(26) +{ + INIT + { + + } + KB + { + //REGION Character DB_KillCounter + PROC + ProcCheckCounter((INTEGER)_Count,(STRING)_CounterDB) + AND + DB_KillCounterCounts(_CounterDB,_OldCount) + THEN + NOT DB_KillCounterCounts(_CounterDB,_OldCount); + DB_KillCounterCounts(_CounterDB,_Count); + + PROC + ProcClearCounterDB((STRING)_CounterDB) + AND + DB_KillCounter_Internal(_Character,_CounterDB) + THEN + NOT DB_KillCounter_Internal(_Character,_CounterDB); + + PROC + ProcClearCounterDB((STRING)_CounterDB) + AND + DB_KillCounterDied(_Char,_CounterDB) + THEN + NOT DB_KillCounterDied(_Char,_CounterDB); + + PROC + ProcCheckCounter(_Count,_CounterDB) + AND + DB_KillCounter((STRING)_CounterDB,(INTEGER)_TargetCount) + AND + _Count == _TargetCount + THEN + //NOT DB_KillCounter(_CounterDB,_TargetCount); + ProcClearCounterDB(_CounterDB); + ProcKillCounterReached(_CounterDB); + NOT DB_KillCounterCounterDefined(_CounterDB); + //NOT DB_KillCounterCounts(_CounterDB,_TargetCount); + + PROC + ProcCheckCounter(_Count, _CounterDB) + AND + DB_KillCounter((STRING)_CounterDB,(INTEGER)_TargetCount) + AND + IntegerSubtract(_TargetCount, 1, _New) + AND + _Count == _New + AND + DB_KillCounter_Internal(_LastCharacter, _CounterDB) + THEN + DB_LastManStanding((CHARACTERGUID)_LastCharacter, (STRING)_CounterDB); + + IF + CharacterDying(_Character) + AND + DB_KillCounter_Internal(_Character,_CounterDB) + AND + DB_CombatCharacters(_Character,_ID) + THEN + ProcSetCounterCombatID(_CounterDB,_ID); + + PROC + ProcClearCounterCombatID((STRING)_Counter) + AND + DB_CounterCombatID(_Counter,_OldID) + THEN + NOT DB_CounterCombatID(_Counter,_OldID); + + PROC + ProcSetCounterCombatID((STRING)_Counter,(INTEGER)_ID) + THEN + ProcClearCounterCombatID(_Counter); + DB_CounterCombatID(_Counter,_ID); + + IF + ObjectEnteredCombat((CHARACTERGUID)_Char, _ID) + AND + DB_KillCounter_Internal(_Char, _Counter) + THEN + ProcClearCounterCombatID(_Counter); + + IF + ObjectEnteredCombat((CHARACTERGUID)_LastMan, _ID) + AND + DB_KillCounter_Internal(_LastMan, _Counter) + AND + DB_KillCounter(_Counter,1) + THEN + DB_LastManStanding((CHARACTERGUID)_LastMan, (STRING)_Counter); + + IF + CharacterKilledBy(_Defender, _AttackerOwner, _Attacker) + AND + DB_LastManStanding(_Defender, _CounterDB) + THEN + NOT DB_LastManStanding(_Defender, _CounterDB); + DB_LastManInCombatKilledBy(_AttackerOwner, (STRING)_CounterDB); + + IF + CharacterDied(_Character) + AND + DB_KillCounter_Internal(_Character,_CounterDB) + AND + DB_KillCounterCounts(_CounterDB,_Count) + AND + IntegerSum(_Count,1,_NewCount) + THEN + NOT DB_KillCounter_Internal(_Character,_CounterDB); + DB_KillCounterDied(_Character,_CounterDB); + ProcCheckCounter(_NewCount,_CounterDB); + + IF + CharacterResurrected(_Character) + AND + DB_KillCounterDied(_Character,_CounterDB) + AND + DB_KillCounterCounts(_CounterDB,_Count) + AND + IntegerSubtract(_Count,1,_NewCount) + THEN + DB_KillCounter_Internal(_Character,_CounterDB); + NOT DB_KillCounterDied(_Character,_CounterDB); + ProcCheckCounter(_NewCount,_CounterDB); + + IF + DB_KillCounter_Internal(_Character,_CounterDB) + AND + NOT DB_KillCounterCounterDefined(_CounterDB) + THEN + DB_KillCounterCounts(_CounterDB,0); + DB_KillCounterCounterDefined(_CounterDB); + + IF + CombatEnded(_CombatID) + AND + DB_CounterCombatID(_CounterDB,_CombatID) + AND + DB_KillCounterCounts(_CounterDB,_Count) + THEN + ProcCheckCounterCombatOver(_CounterDB,_CombatID); + ProcCheckCounter(_Count,_CounterDB); + + IF + DB_CombatCharacters(_Char, _CombatID) + AND + DB_KillCounter_Internal(_Char,_CounterDB) + THEN + ProcClearCombatOverDB(_CounterDB); + + PROC + ProcClearCombatOverDB((STRING)_CounterDB) + AND + DB_KillCounterCombatOver(_OldCombatID, _CounterDB) + THEN + NOT DB_KillCounterCombatOver(_OldCombatID, _CounterDB); + + //killed people outside of combat + PROC + ProcKillCounterReached((STRING)_CounterDB) + AND + NOT DB_CounterCombatID(_CounterDB,_) + THEN + ReactOnKillCounter(_CounterDB); + + PROC + ProcKillCounterReached((STRING)_CounterDB) + AND + DB_KillCounterCombatOver(_CombatID, _CounterDB) + THEN + ReactOnKillCounter(_CounterDB); + NOT DB_KillCounterCombatOver(_CombatID, _CounterDB); + + //Wasn't involved in combat + PROC + ProcCheckCounterCombatOver((STRING)_CounterDB,(INTEGER)_CombatID) + THEN + NOT DB_KillCounterCombatOver(_CombatID, _CounterDB); + + PROC + ProcCheckCounterCombatOver((STRING)_CounterDB,(INTEGER)_CombatID) + AND + CombatGetNumberOfInvolvedPartyMembers(_CombatID, 0) + THEN + DB_KillCounterCombatOver(_CombatID, _CounterDB); + + + PROC + ReactOnKillCounter((STRING)_CounterDB) + AND + DB_KillCounterCounts(_CounterDB,_Count) + AND + DB_KillCounter(_CounterDB,_TargetCount) + THEN + NOT DB_KillCounterCounts(_CounterDB,_Count); + NOT DB_KillCounter(_CounterDB,_TargetCount); + + //END_REGION + + //REGION Item Destroy Counter + PROC + CheckItemCounter((INTEGER)_Count,(STRING)_CounterDB) + AND + DB_ItemDestroyCounterCounts(_CounterDB,_OldCount) + THEN + NOT DB_ItemDestroyCounterCounts(_CounterDB,_OldCount); + DB_ItemDestroyCounterCounts(_CounterDB,_Count); + + PROC + ClearDB_ItemDestroyCounter_Internal((STRING)_CounterDB) + AND + DB_ItemDestroyCounter_Internal(_Item,_CounterDB) + THEN + NOT DB_ItemDestroyCounter_Internal(_Item,_CounterDB); + + PROC + CheckItemCounter(_Count,_CounterDB) + AND + DB_ItemDestroyCounter((STRING)_CounterDB,(INTEGER)_TargetCount) + AND + _Count == _TargetCount + THEN + NOT DB_ItemDestroyCounter(_CounterDB,_TargetCount); + ClearDB_ItemDestroyCounter_Internal(_CounterDB); + NOT DB_ItemDestroyCounterCounterDefined(_CounterDB); + NOT DB_ItemDestroyCounter(_CounterDB,_TargetCount); + + IF + ItemDestroyed(_Item) + AND + DB_ItemDestroyCounter_Internal(_Item,_CounterDB) + AND + DB_ItemDestroyCounterCounts(_CounterDB,_Count) + AND + IntegerSum(_Count,1,_NewCount) + THEN + NOT DB_ItemDestroyCounter_Internal(_Item,_CounterDB); + CheckItemCounter(_NewCount,_CounterDB); + + IF + DB_ItemDestroyCounter_Internal(_Item,_CounterDB) + AND + NOT DB_ItemDestroyCounterCounterDefined(_CounterDB) + THEN + DB_ItemDestroyCounterCounts(_CounterDB,0); + DB_ItemDestroyCounterCounterDefined(_CounterDB); + //END_REGION + + //REGION Comment for hidden effect + PROC + PROC_CommentHiddenEffect((CHARACTERGUID)_Player) + THEN + Proc_StartDialog(1,"GLO_AD_ActivatedSwitch", _Player); + //END_REGION + + //REGION SneakTrigger + IF + DB_SneakTriggerSpotter((TRIGGERGUID)_Trigger, (CHARACTERGUID)_Char) + THEN + SetVarObject(_Char, "SpottedTrigger", _Trigger); + ProcTriggerRegisterForPlayers(_Trigger); + + PROC + ProcHandleSneakSpotted((CHARACTERGUID)_Char) + THEN + DB_NOOP(1); + + IF + StoryEvent((CHARACTERGUID)_Char, "GLO_SpotterSneaker") + AND + DB_SneakTriggerSpotter(_Trigger, _Char) + AND + GetVarObject(_Char, "SpottedDude", _Player) + THEN + ProcCharInTriggerSpotted((CHARACTERGUID)_Player,_Trigger); + ProcCharInTriggerSpottedByChar((CHARACTERGUID)_Player,_Trigger,_Char); + ProcHandleSneakSpotted(_Char); + ProcCleanUpSneakTrigger(_Trigger); + + PROC + ProcCharInTriggerSpottedByChar((CHARACTERGUID)_Player,(TRIGGERGUID)_Trigger,(CHARACTERGUID)_Spotter) + THEN + DB_NOOP(1); + + PROC + ProcCleanUpSneakTrigger((TRIGGERGUID)_Trigger) + AND + DB_SneakTriggerSpotter((TRIGGERGUID)_Trigger, (CHARACTERGUID)_Char) + THEN + SetVarInteger(_Char, "SpottedCounter", 1); + NOT DB_SneakTriggerSpotter(_Trigger, _Char); + + PROC + ProcTriggerUnregisterForPlayers((TRIGGERGUID)_Trigger) + AND + DB_SneakTriggerSpotter(_Trigger, _Char) + THEN + NOT DB_SneakTriggerSpotter(_Trigger, _Char); + //END_REGION + + //REGION DB_AmbushTrigger + IF + DB_AmbushTrigger((TRIGGERGUID)_Trigger, (ITEMGUID)_Helper) + THEN + ProcTriggerRegisterForPlayers(_Trigger); + SetOnStage(_Helper, 1); + + IF + CharacterEnteredTrigger(_Player,_Trigger) + AND + DB_AmbushTrigger(_Trigger, _) + AND + HasActiveStatus(_Player, "INVISIBLE", 0) + AND + HasActiveStatus(_Player, "SNEAKING", 0) + THEN + ProcLaunchAmbush(_Trigger, _Player); + + IF + CharacterStatusRemoved(_Player, "INVISIBLE",_) + AND + DB_InRegion(_Player, _Trigger) + AND + DB_AmbushTrigger(_Trigger, _) + THEN + ProcLaunchAmbush(_Trigger, _Player); + + IF + CharacterStatusRemoved(_Player, "SNEAKING",_) + AND + DB_InRegion(_Player, _Trigger) + AND + DB_AmbushTrigger(_Trigger, _Helper) + THEN + ProcLaunchAmbush(_Trigger, _Player); + + PROC + ProcLaunchAmbush((TRIGGERGUID)_Trigger, (CHARACTERGUID)_Player) + AND + DB_AmbushTrigger(_Trigger, _Helper) + THEN + ProcTriggerUnregisterForPlayers(_Trigger); + NOT DB_AmbushTrigger(_Trigger, _Helper); + SetOnStage(_Helper, 0); + //END_REGION + + //REGION Queuing Dialog + //1 Speaker + PROC + PROC_MandatoryOneSpeakerDialog((STRING)_Dialog,(CHARACTERGUID)_Player) + AND + NOT QRY_SpeakerIsAvailable(_Player) + THEN + DB_QueuedOneSpeakerDialog(_Player,_Dialog); + + PROC + PROC_MandatoryOneSpeakerDialog((STRING)_Dialog,(CHARACTERGUID)_Player) + AND + QRY_SpeakerIsAvailable(_Player) + THEN + MakePlayerActive(_Player); + Proc_StartDialog(0,_Dialog,_Player); + + IF + DialogEnded(_,_) + AND + DB_QueuedOneSpeakerDialog(_Player,_Dialog) + AND + DB_IsPlayer(_Player) + AND + QRY_SpeakerIsAvailable(_Player) + THEN + NOT DB_QueuedOneSpeakerDialog(_Player,_Dialog); + MakePlayerActive(_Player); + Proc_StartDialog(0,_Dialog,_Player); + + IF + ObjectLeftCombat((CHARACTERGUID)_Player,_) + AND + DB_QueuedOneSpeakerDialog(_Player,_Dialog) + AND + DB_IsPlayer(_Player) + AND + QRY_SpeakerIsAvailable(_Player) + THEN + NOT DB_QueuedOneSpeakerDialog(_Player,_Dialog); + MakePlayerActive(_Player); + Proc_StartDialog(0,_Dialog,_Player); + + //END_REGION + + //REGION Quest Reward + PROC + ProcRewardQuestMedium((TRIGGERGUID)_Trigger) + THEN + ItemCreateAtTrigger(_Trigger, "Quest_Reward_Small_94e12298-5e59-405a-9e93-95833e648ce2"); + PlayEffect(_Trigger,"RS3_FX_GP_ScriptedEvent_Teleport_GenericSmoke_01"); + + PROC + ProcRewardQuestBig((TRIGGERGUID)_Trigger) + THEN + ItemCreateAtTrigger(_Trigger, "Quest_Reward_Big_b67596ec-18ca-4790-9273-8af23d8a7a43"); + PlayEffect(_Trigger,"RS3_FX_GP_ScriptedEvent_Teleport_GenericSmoke_01"); + //END_REGION + + + //REGION secret regions + IF + DB_SecretRegions_OnOpenedDoor(_Trig,_) + THEN + LockSecretRegion(_Trig); + + IF + DB_SecretRegions_OnEnteredTrig(_Trig,_OtherTrig) + THEN + ProcTriggerRegisterForPlayers(_OtherTrig); + LockSecretRegion(_Trig); + + IF + CharacterUsedItem(_,_Door) + AND + DB_SecretRegions_OnOpenedDoor(_Trig,_Door) + AND + DoorIsOpening(_Door,1) + THEN + UnlockSecretRegion(_Trig); + NOT DB_SecretRegions_OnOpenedDoor(_Trig,_Door); + + IF + ItemOpened(_Door) + AND + DB_SecretRegions_OnOpenedDoor(_Trig,_Door) + THEN + UnlockSecretRegion(_Trig); + NOT DB_SecretRegions_OnOpenedDoor(_Trig,_Door); + + IF + ItemDestroyed(_Door) + AND + DB_SecretRegions_OnOpenedDoor(_Trig,_Door) + THEN + UnlockSecretRegion(_Trig); + NOT DB_SecretRegions_OnOpenedDoor(_Trig,_Door); + + + IF + CharacterEnteredTrigger(_Char,_EnteredTrig) + AND + DB_SecretRegions_OnEnteredTrig(_Trig,(TRIGGERGUID)_EnteredTrig) + AND + DB_IsPlayer(_Char) + THEN + UnlockSecretRegion(_Trig); + NOT DB_SecretRegions_OnEnteredTrig(_Trig,_EnteredTrig); + + IF + StoryEvent(_,_Event) + AND + DB_SecretRegions_OnStoryEvent(_Trig,_Event) + THEN + UnlockSecretRegion(_Trig); + NOT DB_SecretRegions_OnStoryEvent(_Trig,_Event); + + + //END_REGION + + } + EXIT + { + + } +} +Goal(27).Title("_Global_RunUpAndChat"); +Goal(27) +{ + INIT + { + + } + KB + { + //REGION MoveToAndTalk + + //Start Walking + PROC + ProcCharacterMoveToAndTalk((CHARACTERGUID)_Character,(GUIDSTRING)_Target,(STRING)_Dialog,(INTEGER)_IsAutomated,(STRING)_MoveEvent,(INTEGER)_Running,(REAL)_TimeOut) + THEN + ProcCharacterMoveToAndTalk((CHARACTERGUID)_Character,(GUIDSTRING)_Target,(STRING)_Dialog,(INTEGER)_IsAutomated,(STRING)_MoveEvent,(INTEGER)_Running,(REAL)_TimeOut,0,0); + + PROC + ProcCharacterMoveToAndTalk((CHARACTERGUID)_Character,(GUIDSTRING)_Target,(STRING)_Dialog,(INTEGER)_IsAutomated,(STRING)_MoveEvent,(INTEGER)_Running,(REAL)_TimeOut,(INTEGER)_CheckForFallBackPlayers,(INTEGER)_ForcePartyCheck) + AND + DB_CharacterMoveToAndTalk_CharacerIsMoving(_Character,_Target,_Dialog,_MoveEvent) + THEN + NOT DB_CharacterMoveToAndTalk_CharacerIsMoving(_Character,_Target,_Dialog,_MoveEvent); + + PROC + ProcCharacterMoveToAndTalk((CHARACTERGUID)_Character,(GUIDSTRING)_Target,(STRING)_Dialog,(INTEGER)_IsAutomated,(STRING)_MoveEvent,(INTEGER)_Running,(REAL)_TimeOut,(INTEGER)_CheckForFallBackPlayers,(INTEGER)_ForcePartyCheck) + AND + DB_CharacterMoveToAndTalk_CheckForFallBackCharacters(_Character,_Target,_Dialog,_CheckForFallBackPlayers,_ForcePartCheck) + THEN + NOT DB_CharacterMoveToAndTalk_CheckForFallBackCharacters(_Character,_Target,_Dialog,_CheckForFallBackPlayers,_ForcePartCheck); + + PROC + ProcCharacterMoveToAndTalk((CHARACTERGUID)_Character,(GUIDSTRING)_Target,(STRING)_Dialog,(INTEGER)_IsAutomated,(STRING)_MoveEvent,(INTEGER)_Running,(REAL)_TimeOut,(INTEGER)_CheckForFallBackPlayers,(INTEGER)_ForcePartyCheck) + THEN + CharacterMoveToAndTalk(_Character,_Target,_Dialog,_IsAutomated,_MoveEvent,_Running,_TimeOut); + DB_CharacterMoveToAndTalk_CharacerIsMoving(_Character,_Target,_Dialog,_MoveEvent); + DB_CharacterMoveToAndTalk_CheckForFallBackCharacters(_Character,_Target,_Dialog,_CheckForFallBackPlayers,_ForcePartyCheck); + + //Movement Failed + IF + CharacterMoveToAndTalkFailed((CHARACTERGUID)_Character,(GUIDSTRING)_Target,(STRING)_MoveEvent) + AND + DB_CharacterMoveToAndTalk_CharacerIsMoving(_Character,_Target,_Dialog,_MoveEvent) + THEN + NOT DB_CharacterMoveToAndTalk_CharacerIsMoving(_Character,_Target,_Dialog,_MoveEvent); + + IF + AttackedByObject(_Character,_Source,_Summon,_DamageType,_) + AND + DB_CharacterMoveToAndTalk_CharacerIsMoving((CHARACTERGUID)_Character,_Target,_Dialog,_MoveEvent) + THEN + CharacterMoveToAndTalkRequestDialogFailed(_Character,_Target,_MoveEvent); + + IF + RegionEnded(_) + AND + DB_CharacterMoveToAndTalk_CharacerIsMoving((CHARACTERGUID)_Character,_Target,_Dialog,_MoveEvent) + THEN + CharacterMoveToAndTalkRequestDialogFailed(_Character,_Target,_MoveEvent); + + IF + CharacterLeftRegion(_Characrer,_Region) + AND + DB_CurrentLevel(_Region) + AND + DB_CharacterMoveToAndTalk_CharacerIsMoving((CHARACTERGUID)_Character,_Target,_Dialog,_MoveEvent) + THEN + CharacterMoveToAndTalkRequestDialogFailed(_Character,_Target,_MoveEvent); + + IF + CharacterLeftRegion(_Target,_Region) + AND + DB_CurrentLevel(_Region) + AND + DB_CharacterMoveToAndTalk_CharacerIsMoving((CHARACTERGUID)_Character,_Target,_Dialog,_MoveEvent) + THEN + CharacterMoveToAndTalkRequestDialogFailed(_Character,_Target,_MoveEvent); + + IF + CharacterDied(_Target) + AND + DB_CharacterMoveToAndTalk_CharacerIsMoving((CHARACTERGUID)_Character,_Target,_Dialog,_MoveEvent) + THEN + CharacterMoveToAndTalkRequestDialogFailed(_Character,_Target,_MoveEvent); + + IF + CharacterDied(_Character) + AND + DB_CharacterMoveToAndTalk_CharacerIsMoving((CHARACTERGUID)_Character,_Target,_Dialog,_MoveEvent) + THEN + CharacterMoveToAndTalkRequestDialogFailed(_Character,_Target,_MoveEvent); + + IF + DialogStarted(_,_ID) + AND + DB_DialogNPCS(_ID,_Character,_) + AND + DB_CharacterMoveToAndTalk_CharacerIsMoving((CHARACTERGUID)_Character,_Target,_Dialog,_MoveEvent) + THEN + CharacterMoveToAndTalkRequestDialogFailed(_Character,_Target,_MoveEvent); + + IF + CombatStarted(_ID) + AND + DB_CombatCharacters(_Character, _ID) + AND + DB_CharacterMoveToAndTalk_CharacerIsMoving((CHARACTERGUID)_Character,_Target,_Dialog,_MoveEvent) + THEN + CharacterMoveToAndTalkRequestDialogFailed(_Character,_Target,_MoveEvent); + + + // Movement Finished + IF + CharacterMoveToAndTalkRequestDialog((CHARACTERGUID)_Character,(GUIDSTRING)_Target,(STRING)_Dialog,(INTEGER)_IsAutomated,(STRING)_MoveEvent) + AND + DB_CharacterMoveToAndTalk_CharacerIsMoving(_Character,_Target,_Dialog,_MoveEvent) + THEN + NOT DB_CharacterMoveToAndTalk_CharacerIsMoving(_Character,_Target,_Dialog,_MoveEvent); + + IF + CharacterMoveToAndTalkRequestDialog((CHARACTERGUID)_Character,(GUIDSTRING)_Target,(STRING)_Dialog,(INTEGER)_IsAutomated,(STRING)_MoveEvent) + THEN + SetStoryEvent(_Character,_MoveEvent); + + // Dialog Failed + IF + CharacterMoveToAndTalkRequestDialog((CHARACTERGUID)_Character,(GUIDSTRING)_Target,(STRING)_Dialog,(INTEGER)_IsAutomated,(STRING)_MoveEvent) + AND + NOT QRY_SpeakerIsAvailable(_Target) + AND + DB_CharacterMoveToAndTalk_CheckForFallBackCharacters(_Character,_Target,_Dialog,0,_ForcePartyCheck) + THEN + CharacterMoveToAndTalkRequestDialogFailed(_Character,_Target,_MoveEvent); + + IF + CharacterMoveToAndTalkRequestDialog((CHARACTERGUID)_Character,(GUIDSTRING)_Target,(STRING)_Dialog,(INTEGER)_IsAutomated,(STRING)_MoveEvent) + AND + NOT QRY_SpeakerIsAvailable(_Target) + AND + DB_CharacterMoveToAndTalk_CheckForFallBackCharacters(_Character,(CHARACTERGUID)_Target,_Dialog,1,0) + AND + DB_IsPlayer(_Target) + THEN + ProcGetClosestAvailableCharacterTo(_Target,1); + PROC_CharacterMoveToAndTalkRequestDialog_Closest(_Character,_Target,_Dialog,_IsAutomated,_MoveEvent); + + IF + CharacterMoveToAndTalkRequestDialog((CHARACTERGUID)_Character,(GUIDSTRING)_Target,(STRING)_Dialog,(INTEGER)_IsAutomated,(STRING)_MoveEvent) + AND + NOT QRY_SpeakerIsAvailable(_Target) + AND + DB_CharacterMoveToAndTalk_CheckForFallBackCharacters(_Character,(CHARACTERGUID)_Target,_Dialog,1,1) + AND + DB_IsPlayer(_Target) + THEN + ProcGetClosestAvailableCharacterTo(_Target,1,1,_Target); + PROC_CharacterMoveToAndTalkRequestDialog_Closest(_Character,_Target,_Dialog,_IsAutomated,_MoveEvent); + + // Dialog Success + + IF + CharacterMoveToAndTalkRequestDialog((CHARACTERGUID)_Character,(GUIDSTRING)_Target,(STRING)_Dialog,(INTEGER)_IsAutomated,(STRING)_MoveEvent) + AND + _Dialog!="" + AND + QRY_SpeakerIsAvailable(_Target) + THEN + Proc_StartDialog(_IsAutomated,_Dialog,_Character,_Target); + + PROC + PROC_CharacterMoveToAndTalkRequestDialog_Closest((CHARACTERGUID)_Character,(GUIDSTRING)_Target,(STRING)_Dialog,(INTEGER)_IsAutomated,(STRING)_MoveEvent) + AND + NOT QRY_SpeakerIsAvailable(_Target) + AND + DB_ClosestAvailablePlayer(_Player,(CHARACTERGUID)_Target) + THEN + Proc_StartDialog(_IsAutomated,_Dialog,_Character,_Player); + + PROC + PROC_CharacterMoveToAndTalkRequestDialog_Closest((CHARACTERGUID)_Character,(GUIDSTRING)_Target,(STRING)_Dialog,(INTEGER)_IsAutomated,(STRING)_MoveEvent) + AND + NOT QRY_SpeakerIsAvailable(_Target) + AND + NOT DB_ClosestAvailablePlayer(_,(CHARACTERGUID)_Target) + THEN + CharacterMoveToAndTalkRequestDialogFailed(_Character,_Target,_MoveEvent); + + //END_REGION + + + } + EXIT + { + + } +} +Goal(28).Title("_Global_SavegamePatching"); +Goal(28) +{ + INIT + { + + } + KB + { + //REGION Remove destroyed items that came back + IF + SavegameLoaded(_Major,_Minor,_Rev,_Build) + AND + QRY_VersionIsOlderThan(_Major,_Minor,_Rev,_Build,3,1,5,1) + AND + DB_DestroyedItems((GUIDSTRING)_Item) + AND + ObjectExists((ITEMGUID)_Item,1) + AND + ItemIsDestroyed(_Item,0) + THEN + ItemRemove(_Item); + //END_REGION + + } + EXIT + { + + } +} +Goal(29).Title("_GLOBAL_Subregions"); +Goal(29) +{ + INIT + { + + } + KB + { + IF + DB_Subregion((TRIGGERGUID)_Trigger,(STRING)_,(INTEGER)_) + THEN + ProcTriggerRegisterForPlayers(_Trigger); + + IF + CharacterEnteredTrigger(_Character,_Trigger) + AND + DB_Subregion(_Trigger,_Message,_ShowMarker) + THEN + CharacterEnteredSubRegion(_Character,_Message); + ProcCheckSetRegionMarker(_Message,_ShowMarker); + + PROC + ProcCheckSetRegionMarker((STRING)_Message,1) + AND + DB_IsPlayer(_Player) + THEN + ProcShowMarker(_Player,_Message); + + + + + + } + EXIT + { + + } +} +Goal(30).Title("_GLOBAL_TeleporterPyramids"); +Goal(30) +{ + INIT + { + // PUBLIC PROCs: + // Proc_PyramidBlockerTriggerAdd - add trigger that blocks pyramid usage + // Proc_PyramidBlockerTriggerRemove - remove trigger from blocking list + // Proc_PyramidGlobalBlockerOn - global pyramids blocker turn on + // Proc_PyramidGlobalBlockerOff - global pyramids blocker turn off + // Proc_PyramidCustomBlockAdd - add custom blocker of a pyramid (used for Tenebrium chests) + // Proc_PyramidCustomBlockRemove - remove custom blocker of a pyramid (used for Tenebrium chests) + + + // Inner DBs: + // DB_TeleporterPyramidUnlocked((ITENGUID)_Pyramid) - the ones found by players + // DB_PyramidBlockerTrigger((TRIGGERGUID)_Trigger) - trigger blockers of pyramid usage + // DB_PyramidUsageBlocked(1) - global pyramid blocker + // DB_PyramidInBlockerTrigger((ITEMGUID)_Pyramid,(TRIGGERGUID)_Trigger) - used to store which pyramids are in blocking triggers + // DB_PyramidCustomBlock((ITEMGUID)_Pyramid,(STRING)_Reason) - used to store which pyramids are customly blocked + + } + KB + { + //REGION Debug + //END_REGION + + IF + DB_TeleporterPyramid(_Pyramid) + THEN + ItemSetForceSynch(_Pyramid,1); + + //REGION Unlock pyramids that were found by player + IF + ItemAddedToCharacter(_Pyramid,_Player) + AND + DB_TeleporterPyramid(_Pyramid) + AND + NOT DB_TeleporterPyramidUnlocked(_Pyramid) + AND + _Player.DB_IsPlayer() + THEN + DB_TeleporterPyramidUnlocked(_Pyramid); + + IF + CharacterUsedItem(_Player,_Pyramid) + AND + DB_TeleporterPyramid(_Pyramid) + AND + NOT DB_TeleporterPyramidUnlocked(_Pyramid) + AND + _Player.DB_IsPlayer() + THEN + PROC_CheckPlayTut(_Player,"TUT_PyramidsPickup"); + //END_REGION + + + //REGION Pyramid blockers + PROC + Proc_PyramidGlobalBlockerOn() + THEN + DB_PyramidUsageBlocked(1); + + PROC + Proc_PyramidGlobalBlockerOn() + AND + DB_TeleporterPyramid(_Pyramid) + THEN + DisablePyramid(_Pyramid); + + PROC + Proc_PyramidGlobalBlockerOff() + THEN + NOT DB_PyramidUsageBlocked(1); + + PROC + Proc_PyramidGlobalBlockerOff() + AND + DB_TeleporterPyramid(_Pyramid) + THEN + Proc_PyramidCheckEnabled(_Pyramid); + + + PROC + Proc_PyramidBlockerTriggerAdd((TRIGGERGUID)_Trigger) + THEN + DB_PyramidBlockerTrigger(_Trigger); + TriggerRegisterForItems(_Trigger); + ProcTriggerRegisterForPlayers(_Trigger); + + PROC + Proc_PyramidBlockerTriggerRemove((TRIGGERGUID)_Trigger) + THEN + NOT DB_PyramidBlockerTrigger(_Trigger); + TriggerUnregisterForItems(_Trigger); + ProcTriggerUnregisterForPlayers(_Trigger); + + PROC + Proc_PyramidCustomBlockAdd((ITEMGUID)_Pyramid,(STRING)_Reason) + THEN + DisablePyramid(_Pyramid); + DB_PyramidCustomBlock(_Pyramid,_Reason); + + PROC + Proc_PyramidCustomBlockRemove((ITEMGUID)_Pyramid,(STRING)_Reason) + THEN + NOT DB_PyramidCustomBlock(_Pyramid,_Reason); + Proc_PyramidCheckEnabled(_Pyramid); + + + // Pyramid entered/left blocking trigger inside of character's inventory + IF + CharacterEnteredTrigger(_Char,_Trigger) + AND + DB_PyramidBlockerTrigger(_Trigger) + AND + DB_TeleporterPyramid(_Pyramid) + AND + ItemIsInCharacterInventory(_Pyramid,_Char,1) + THEN + Proc_PyramidEnterBlockerTrigger(_Pyramid,_Trigger); + + IF + CharacterLeftTrigger(_Char,_Trigger) + AND + DB_PyramidBlockerTrigger(_Trigger) + AND + DB_TeleporterPyramid(_Pyramid) + AND + ItemIsInCharacterInventory(_Pyramid,_Char,1) + AND + DB_PyramidInBlockerTrigger(_Pyramid,_Trigger) + THEN + Proc_PyramidLeaveBlockerTrigger(_Pyramid,_Trigger); + + + // Pyramid entered/left blocking trigger + IF + ItemEnteredTrigger(_Pyramid,_Trigger,_) + AND + DB_TeleporterPyramid(_Pyramid) + AND + DB_PyramidBlockerTrigger(_Trigger) + THEN + Proc_PyramidEnterBlockerTrigger(_Pyramid,_Trigger); + + IF + ItemLeftTrigger(_Pyramid,_Trigger,_) + AND + DB_PyramidInBlockerTrigger(_Pyramid,_Trigger) + THEN + Proc_PyramidLeaveBlockerTrigger(_Pyramid,_Trigger); + + + // Pyramid entered/left blocking trigger inside of another container + IF + ItemEnteredTrigger(_Container,_Trigger,_) + AND + DB_PyramidBlockerTrigger(_Trigger) + AND + DB_TeleporterPyramid(_Pyramid) + AND + GetInventoryOwner(_Pyramid,_Container) + THEN + Proc_PyramidEnterBlockerTrigger(_Pyramid,_Trigger); + + IF + ItemLeftTrigger(_Container,_Trigger,_) + AND + DB_PyramidInBlockerTrigger(_Pyramid,_Trigger) + AND + GetInventoryOwner(_Pyramid,_Container) + THEN + Proc_PyramidLeaveBlockerTrigger(_Pyramid,_Trigger); + + + // Added to container - remove old triggers that this container is not inside + IF + ItemAddedToContainer(_Pyramid,_) + AND + DB_TeleporterPyramid(_Pyramid) + AND + GetInventoryOwner(_Pyramid,_TopCont) + AND + DB_PyramidInBlockerTrigger(_Pyramid,_Trigger) + AND + ObjectIsInTrigger(_TopCont,_Trigger,0) + THEN + Proc_PyramidLeaveBlockerTrigger(_Pyramid,_Trigger); + + IF + ItemAddedToContainer(_Pyramid,_) + AND + DB_TeleporterPyramid(_Pyramid) + AND + GetInventoryOwner(_Pyramid,_TopCont) + AND + DB_PyramidBlockerTrigger(_Trigger) + AND + ObjectIsInTrigger(_TopCont,_Trigger,1) + THEN + Proc_PyramidEnterBlockerTrigger(_Pyramid,_Trigger); + + IF + ItemRemovedFromContainer(_Pyramid,_Container) + AND + DB_TeleporterPyramid(_Pyramid) + AND + DB_PyramidInBlockerTrigger(_Pyramid,_Trigger) + AND + ObjectIsInTrigger(_Container,_Trigger,1) + THEN + Proc_PyramidLeaveBlockerTrigger(_Pyramid,_Trigger); + + IF + ItemRemovedFromContainer(_Pyramid,_Container) + AND + DB_TeleporterPyramid(_Pyramid) + AND + DB_PyramidInBlockerTrigger(_Pyramid,_Trigger) + AND + GetInventoryOwner(_Container,_TopCont) + AND + ObjectIsInTrigger(_TopCont,_Trigger,1) + THEN + Proc_PyramidLeaveBlockerTrigger(_Pyramid,_Trigger); + + IF + ItemAddedToCharacter(_Pyramid,_Character) + AND + DB_TeleporterPyramid(_Pyramid) + AND + DB_PyramidBlockerTrigger(_Trigger) + AND + ObjectIsInTrigger(_Character,_Trigger,1) + THEN + Proc_PyramidEnterBlockerTrigger(_Pyramid,_Trigger); + + IF + ItemRemovedFromCharacter(_Pyramid,_Character) + AND + DB_TeleporterPyramid(_Pyramid) + AND + DB_PyramidBlockerTrigger(_Trigger) + AND + ObjectIsInTrigger(_Character,_Trigger,1) + THEN + Proc_PyramidLeaveBlockerTrigger(_Pyramid,_Trigger); + + + PROC + Proc_PyramidEnterBlockerTrigger((ITEMGUID)_Pyramid,(TRIGGERGUID)_Trigger) + THEN + DisablePyramid(_Pyramid); + DB_PyramidInBlockerTrigger(_Pyramid,_Trigger); + + PROC + Proc_PyramidLeaveBlockerTrigger((ITEMGUID)_Pyramid,(TRIGGERGUID)_Trigger) + THEN + NOT DB_PyramidInBlockerTrigger(_Pyramid,_Trigger); + Proc_PyramidCheckEnabled(_Pyramid); + + + PROC + Proc_PyramidCheckEnabled((ITEMGUID)_Pyramid) + AND + NOT DB_PyramidUsageBlocked(1) + AND + DB_TeleporterPyramid(_Pyramid) + AND + NOT DB_PyramidInBlockerTrigger(_Pyramid,_) + AND + NOT DB_PyramidCustomBlock(_Pyramid,_) + THEN + EnablePyramid(_Pyramid); + //END_REGION + + + //REGION Level transitions + IF + RegionEnded(_Region) + AND + DB_TeleporterPyramidUnlocked(_Pyramid) + AND + GetRegion(_Pyramid,_Region) + AND + GetInventoryOwner(_Pyramid,_TopContainer) + AND + NOT DB_IsPlayer((CHARACTERGUID)_TopContainer) + AND + ItemGetOriginalOwner(_Pyramid,_Owner) + THEN + ItemToInventory(_Pyramid,_Owner,-1); + + IF + RegionEnded(_Region) + AND + DB_TeleporterPyramidUnlocked(_Pyramid) + AND + GetRegion(_Pyramid,_Region) + AND + NOT GetInventoryOwner(_Pyramid,_) + AND + ItemGetOriginalOwner(_Pyramid,_Owner) + THEN + ItemToInventory(_Pyramid,_Owner,-1); + + /* + IF + ItemWentOnStage(_Container,0) + AND + DB_TeleporterPyramidUnlocked(_Pyramid) + AND + //TODO: implement this: ItemIsInItemInventory(_Pyramid,_Container,1) + AND + GetPosition(_Container,_X,_Y,_Z) + THEN + TeleportToPosition(_Pyramid,_X,_Y,_Z,"",0); + ItemClearOwner(_Pyramid); + */ + + // Prevent NPCs leaving with Pyramids in their inventory + IF + CharacterWentOnStage(_NPC,0) + AND + // Players can be set off-stage temporarily by story in some cases + NOT DB_IsPlayer(_NPC) + AND + DB_TeleporterPyramid(_Pyramid) + AND + ItemIsInCharacterInventory(_Pyramid,_NPC,1) + AND + GetPosition(_NPC,_X,_Y,_Z) + THEN + TeleportToPosition(_Pyramid,_X,_Y,_Z,"",0); + ItemClearOwner(_Pyramid); + + PROC + Proc_CompanionLeftParty((CHARACTERGUID)_Companion,(CHARACTERGUID)_Player) + AND + DB_TeleporterPyramid(_Pyramid) + AND + ItemIsInCharacterInventory(_Pyramid,_Companion,1) + THEN + ItemToInventory(_Pyramid,_Player,-1); + //END_REGION + + + //REGION Pyramids using (teleportation) + IF + CharacterTeleportToPyramid(_Char,_Pyramid) + AND + PyramidEnabled(_Pyramid) + AND + GetPosition(_Pyramid,_X,_Y,_Z) + THEN + TeleportToPosition(_Char,_X,_Y,_Z,"",1,1); + + IF + CharacterTeleportToPyramid(_Char,_Pyramid) + AND + NOT PyramidEnabled(_Pyramid) + THEN + Proc_StartDialog(1,"GEN_AD_TeleporterPyramidDisabled",_Char); + // TODO: check if this is needed ---> DB_CustomUseItemResponse(_Char,_Pyramid,0); + + PROC + ProcBlockUseOfItem(_Player,_Pyramid) + AND + DB_TeleporterPyramid(_Pyramid) + AND + DB_IsPlayer(_Player) + AND + NOT PyramidEnabled(_Pyramid) + THEN + CloseUI(_Player,"Inventory"); + ObjectSetFlag(_Player,"SourcePyramidBlocked"); + Proc_StartDialog(1,"GEN_AD_TeleporterPyramidDisabled",_Player); + DB_CustomUseItemResponse(_Player,_Pyramid,0); + + PROC + ProcBlockUseOfItem(_Player,_Pyramid) + AND + DB_TeleporterPyramid(_Pyramid) + AND + DB_IsPlayer(_Player) + AND + DB_PyramidBlockerTrigger(_Trigger) + AND + ObjectIsInTrigger(_Player,_Trigger,1) + THEN + CloseUI(_Player,"Inventory"); + ObjectSetFlag(_Player,"SourcePyramidBlocked"); + Proc_StartDialog(1,"GEN_AD_TeleporterPyramidDisabled",_Player); + DB_CustomUseItemResponse(_Player,_Pyramid,0); + //END_REGION + + + IF + ItemAddedToCharacter(_Pyramid,_Player) + AND + DB_TeleporterPyramid(_Pyramid) + AND + DB_IsPlayer(_Player) + THEN + PROC_CheckPlayTutWithDelay(_Player,"TUT_Pyramid",2000); + + + //REGION Enable/disable + //TEMP: needs to be a game calls + PROC + DisablePyramid((ITEMGUID)_Item) + THEN + SetTag(_Item,"PYRAMID_DISABLED"); + + PROC + EnablePyramid((ITEMGUID)_Item) + THEN + ClearTag(_Item,"PYRAMID_DISABLED"); + + QRY + PyramidEnabled((ITEMGUID)_Item) + AND + IsTagged(_Item,"PYRAMID_DISABLED",0) + THEN + DB_NOOP(1); + //END_REGION + + } + EXIT + { + + } +} +Goal(31).Title("_GLOBAL_TutorialMessages"); +Goal(31) +{ + INIT + { + DB_ItemOpened(0); + + /*********************** + * Flags field: a bit field + * 0 : no checks + * 1 : no combat : don't show the tutorial if the character is active in combat + * 2 : no UI : don't show the tutorial if the character has a UI open + * 4 : cancel on condition fail : the tutorial is removed from the queue if the conditions don't match, it can be sent again after that + * 8 : force enqueue (wait at least one frame) + * 16 : newfeature : this tutorial is specific for DOS2 + ***********************/ + DB_TutorialInfo("TUT_Book","TUT_CAT_Inventory","TUT_Book_Title",0,0,-1,1,3,0); + DB_TutorialInfo("TUT_Camera","TUT_CAT_Exploring","TUT_Camera_Title",0,0,6000,1,2,0); + DB_TutorialInfo("TUT_Combat","TUT_CAT_Combat","TUT_Combat_Title",0,0,-1,1,0,0); + DB_TutorialInfo("TUT_Combat_Examine","TUT_CAT_Combat","TUT_Combat_Examine_Title",0,0,20000,2,0,0); + DB_TutorialInfo("TUT_Combat_Guard","TUT_CAT_Combat","TUT_Combat_Guard_Title",0,2,-1,2,0,0); + DB_TutorialInfo("TUT_Combat_TacticalView","TUT_CAT_Combat","TUT_Combat_TacticalView_Title",0,0,20000,2,0,0); + DB_TutorialInfo("TUT_Combat_TargetCycle","TUT_CAT_Combat","TUT_Combat_TargetCycle_Controller_Title",2,0,-1,1,0,0); + DB_TutorialInfo("TUT_Combat_TurnOrder","TUT_CAT_Combat","TUT_Combat_TurnOrder_Title",0,3,-1,1,0,0); + DB_TutorialInfo("TUT_Died","TUT_CAT_Combat","TUT_Died_Title",0,0,-1,1,0,0); + DB_TutorialInfo("TUT_Disarm","TUT_CAT_Exploring","TUT_Disarm_Title",0,0,-1,3,0,0); + DB_TutorialInfo("TUT_DragIcon","TUT_CAT_Exploring","TUT_DragIcon_Title",1,0,-1,1,0,0); + DB_TutorialInfo("TUT_Equipment","TUT_CAT_Inventory","TUT_Equipment_Title",0,0,20000,1,2,0); + DB_TutorialInfo("TUT_Flee","TUT_CAT_Combat","TUT_Flee_Title",0,0,-1,3,4,0); + DB_TutorialInfo("TUT_GasPit","TUT_CAT_Exploring","TUT_GasPit_Title",0,1,-1,3,0,0); + DB_TutorialInfo("TUT_GenericBehaviors","TUT_CAT_Exploring","TUT_GenericBehaviors_Title",0,0,-1,3,0,0); + DB_TutorialInfo("TUT_Hammer","TUT_CAT_Inventory","TUT_Hammer_Title",0,0,-1,1,0,0); + DB_TutorialInfo("TUT_HotbarAssignment","TUT_CAT_Inventory","TUT_HotbarAssignment_Title",0,0,15000,2,6,0); + DB_TutorialInfo("TUT_IdentifyingGlass","TUT_CAT_Inventory","TUT_IdentifyingGlass_Title",0,0,-1,1,0,0); + DB_TutorialInfo("TUT_Inventory","TUT_CAT_Inventory","TUT_Inventory_Title",0,2,20000,1,2,0); + DB_TutorialInfo("TUT_LevelUp","TUT_CAT_Inventory","TUT_LevelUp_Title",0,2,20000,2,3,0); + DB_TutorialInfo("TUT_Locked","TUT_CAT_Exploring","TUT_Locked_Title",0,0,-1,3,0,0); + DB_TutorialInfo("TUT_WoodenDoor","TUT_CAT_Exploring","TUT_WoodenDoor_Title",0,0,-1,3,0,0); + DB_TutorialInfo("TUT_Lockpick","TUT_CAT_Inventory","TUT_Lockpick_Title",0,0,-1,1,0,0); + DB_TutorialInfo("TUT_Mound","TUT_CAT_Exploring","TUT_Mound_Title",0,0,-1,3,0,0); + DB_TutorialInfo("TUT_Movement","TUT_CAT_Exploring","TUT_Movement_Title",0,0,6000,1,0,0); + DB_TutorialInfo("TUT_NoPetPal","TUT_CAT_Social","TUT_PetPal_Title",0,0,-1,2,0,0); + DB_TutorialInfo("TUT_Perception","TUT_CAT_Inventory","TUT_Perception_Title",0,0,-1,1,0,0); + DB_TutorialInfo("TUT_PetPal","TUT_CAT_Social","TUT_PetPal_Title",0,0,-1,2,0,0); + DB_TutorialInfo("TUT_Potion","TUT_CAT_Combat","TUT_Potion_Title",0,1,-1,2,0,0); + DB_TutorialInfo("TUT_Pyramid","TUT_CAT_Exploring","TUT_Pyramid_Title",0,0,-1,1,0,0); + DB_TutorialInfo("TUT_Pyramid_2","TUT_CAT_Exploring","TUT_Pyramid_2_Title",0,0,-1,1,0,0); + DB_TutorialInfo("TUT_PyramidsPickup","TUT_CAT_Exploring","TUT_PyramidsPickup_Title",0,0,-1,1,0,0); + DB_TutorialInfo("TUT_ReflectionDialog","TUT_CAT_Social","TUT_ReflectionDialog_Title",0,0,-1,1,0,0); + DB_TutorialInfo("TUT_SaveLoad","TUT_CAT_General","TUT_SaveLoad_Title",0,0,-1,1,0,0); + DB_TutorialInfo("TUT_Scroll","TUT_CAT_Combat","TUT_Scroll_Title",0,0,-1,2,2,0); + DB_TutorialInfo("TUT_Shovel","TUT_CAT_Exploring","TUT_Shovel_Title",0,0,-1,2,0,0); + DB_TutorialInfo("TUT_Skill","TUT_CAT_Inventory","TUT_Skill_Title",0,0,-1,2,2,0); + DB_TutorialInfo("TUT_SkillUnlock","TUT_CAT_Combat","TUT_SkillUnlock_Title",0,0,-1,2,0,0); + DB_TutorialInfo("TUT_Stealing","TUT_CAT_Exploring","TUT_Stealing_Title",0,0,-1,3,0,0); + DB_TutorialInfo("TUT_Waypoint","TUT_CAT_Exploring","TUT_Waypoint_Title",0,0,-1,2,0,0); + DB_TutorialInfo("TUT_BondedAvatar","TUT_CAT_Social","TUT_BondedAvatar_Title",0,0,-1,1,16,0); + DB_TutorialInfo("TUT_HotbarLocked","TUT_CAT_Combat","TUT_HotbarLocked_Title",0,3,-1,1,0,10); + DB_TutorialInfo("TUT_Tooltips","TUT_CAT_Exploring","TUT_Tooltips_Title",1,0,-1,2,0,0); + DB_TutorialInfo("TUT_Tooltips_Controller","TUT_CAT_Exploring","TUT_Tooltips_Title",2,0,-1,2,0,0); + DB_TutorialInfo("TUT_TagVillain","TUT_CAT_Social","TUT_TagVillain_Title",0,0,-1,2,0,0); + DB_TutorialInfo("TUT_TagHero","TUT_CAT_Social","TUT_TagHero_Title",0,0,-1,2,0,0); + DB_TutorialInfo("TUT_Waypoint","TUT_CAT_Exploring","TUT_Waypoint_Title",0,2,3,1,0,0); + + } + KB + { + //REGION Tutorial Message + PROC + ProcPlayTut((CHARACTERGUID)_Char,(STRING)_Message) + AND + DB_TutorialInfo(_Message,(STRING)_Cat,(STRING)_Title,(INTEGER)_ControllerType,(INTEGER)_ModalType,(INTEGER)_Duration,(INTEGER)_Priority,(INTEGER)_Flags,(INTEGER)_MinimumPlaytime) + THEN + ShowTutorial(_Char,_Message,_Cat,_Title,_ControllerType,_ModalType,_Duration,_Priority,_Flags,_MinimumPlaytime); + + PROC + ProcPlayTut((CHARACTERGUID)_Char,(STRING)_Message) + AND + DB_TutorialInfo_MsgBox(_Message,(STRING)_Cat,(STRING)_Title,(INTEGER)_ControllerType,(INTEGER)_ModalType,(INTEGER)_Duration,(INTEGER)_Priority,(INTEGER)_Flags,(INTEGER)_MinimumPlaytime) + THEN + OpenMessageBox(_Char,_Message); + + PROC + ProcPlayTut((CHARACTERGUID)_Char,(STRING)_Message) + AND + NOT DB_TutorialInfo(_Message,_,_,_,_,_,_,_,_) + AND + NOT DB_TutorialInfo_MsgBox(_Message,_,_,_,_,_,_,_,_) + AND + StringConcatenate("Tutorial Message without info: ",_Message,_ErrorMessage) + THEN + DebugBreak(_ErrorMessage); + ShowTutorial(_Char,_Message,"","",0,0,-1,3,0,0); + //END_REGION + + //REGION Tutorial From ProximityTutorial.itemscript + IF + CharacterItemEvent(_Character,_Item,"LaunchTutorialMessage") + AND + GetVarString(_Item,"TutorialMessage",_TutorialName) + THEN + PROC_CheckPlayTut(_Character,_TutorialName); + //END_REGION + + //REGION Movement Popup + + // See also _GLOBAL_TutorialMessages_MovementDetection + IF + DB_GLO_Tutorial_StartMovementTutorial(1) + THEN + TimerLaunch("TUT_Movement",5000); + + IF + TimerFinished("TUT_Movement") + AND + DB_IsPlayer(_Player) + AND + NOT DB_GLOBAL_TutorialMessages_MovementTutFinishedFor(_Player) + THEN + PROC_CheckPlayTut(_Player,"TUT_Movement"); + + IF + TimerFinished("TUT_Camera") + THEN + PROC_CheckPlayTut("TUT_Camera"); + DB_InitialTutorialsShown(1); + //END_REGION + + //REGION Inventory Popup + IF + ItemTemplateAddedToCharacter(_,_,_Player) + AND + _Player.DB_IsPlayer() + AND + DB_InitialTutorialsShown(1) + THEN + ProcCheckInventoryTutorial(_Player); + + PROC + ProcCheckInventoryTutorial((GUIDSTRING)_Player) + AND + IsSpeakerReserved(_Player,0) + THEN + PROC_CheckPlayTut((CHARACTERGUID)_Player,"TUT_Inventory"); + + PROC + ProcCheckInventoryTutorial((GUIDSTRING)_Player) + AND + IsSpeakerReserved(_Player,1) + THEN + DB_InventoryMessageQueued(_Player); + + IF + DialogEnded(_,_ID) + AND + DB_DialogPlayers(_ID,_Player,_) + AND + DB_InventoryMessageQueued(_Player) + THEN + NOT DB_InventoryMessageQueued(_Player); + //force a frame delay to give Osiris a chance to run other logic, in case this dialog causes combat or another dialog + SetStoryEvent(_Player,"GLO_CheckInventoryTutorial"); + + IF + StoryEvent(_Player,"GLO_CheckInventoryTutorial") + AND + QRY_SpeakerIsAvailable(_Player) + THEN + PROC_CheckPlayTut((CHARACTERGUID)_Player,"TUT_Inventory"); + + + //END_REGION + + //REGION Combat Popup + IF + ObjectTurnStarted((CHARACTERGUID)_Player) + AND + _Player.DB_IsPlayer() + AND + CharacterGetAbility(_Player, "Loremaster", _Val) + AND + _Val > 0 + AND + DB_CharCountHelper(_Player,"CombatTurnCounter",_Turn) + AND + _Turn >= 6 + THEN + PROC_CheckPlayTut(_Player,"TUT_Combat_Examine"); + //END_REGION + + //REGION Tool Popups + IF + ItemTemplateAddedToCharacter(TOOL_LockPick_A_06d0eecb-4271-42a7-bd8c-4cbf24927197,_,_Player) + AND + _Player.DB_IsPlayer() + THEN + PROC_CheckPlayTut(_Player, "TUT_Lockpick"); + + IF + ItemTemplateAddedToCharacter(TOOL_Hammer_Repair_A_be7226da-7211-4250-be95-ca780bcdb3df,_,_Player) + AND + _Player.DB_IsPlayer() + THEN + PROC_CheckPlayTut(_Player, "TUT_Hammer"); + + IF + ItemTemplateAddedToCharacter(TOOL_IdentifyingGlass_A_32288ce4-3d8d-46b3-a655-598350a96201,_,_Player) + AND + _Player.DB_IsPlayer() + THEN + PROC_CheckPlayTut(_Player, "TUT_IdentifyingGlass"); + + IF + ItemTemplateAddedToCharacter(TOOL_Trap_DisarmToolkit_9fda335e-2220-4ae9-a4c2-2424d5ef5165,_,_Player) + AND + _Player.DB_IsPlayer() + THEN + PROC_CheckPlayTut(_Player, "TUT_Disarm"); + + IF + ItemTemplateAddedToCharacter(TOOL_Shovel_A_41486dd2-3fd5-464e-870e-844120cf0517,_,_Player) + AND + _Player.DB_IsPlayer() + THEN + PROC_CheckPlayTut(_Player,"TUT_Shovel"); + //END_REGION + + IF + ObjectTurnStarted((CHARACTERGUID)_Player) + AND + _Player.DB_IsPlayer() + AND + CharacterGetHitpointsPercentage(_Player,_Percentage) + AND + _Percentage < 25 + AND + _Percentage > 0 + AND + DB_PotionDone(1) + THEN + PROC_CheckPlayTut(_Player,"TUT_Flee"); + + IF + ObjectTurnStarted((CHARACTERGUID)_Player) + AND + _Player.DB_IsPlayer() + AND + CharacterGetHitpointsPercentage(_Player,_Percentage) + AND + _Percentage < 25 + AND + _Percentage > 0 + THEN + PROC_CheckPlayTut(_Player,"TUT_Potion"); + DB_PotionDone(1); + + IF + CharacterItemEvent(_, _Item, "WaypointDiscovered") + AND + DB_WaypointInfo((ITEMGUID)_Item,(TRIGGERGUID)_,(STRING)_) + THEN + DB_WaypointTuto(1); + + IF + CharacterDied(_Player) + AND + _Player.DB_IsPlayer() + THEN + PROC_CheckPlayTutWithDelay(_Player,"TUT_Died",2000); + + IF + StoryEvent((CHARACTERGUID)_Player,_Event) + AND + DB_Event2DisplayText(_Event,_Text) //See the goal TrapReactions for context about that DB + AND + _Player.DB_IsPlayer() + THEN + PROC_CheckPlayTutWithDelay(_Player,"TUT_Perception",2000); + + IF + CharacterUsedItem(_Player,_Mound) + AND + DB_ShovelArea(_,_,_Mound) + THEN + PROC_CheckPlayTut(_Player,"TUT_Mound"); + + IF + TextEventSet("axeltuto") + THEN + DB_StartTutMessages(1); + + IF + CharacterLeveledUp(_Player) + AND + _Player.DB_IsPlayer() + THEN + PROC_CheckPlayTut(_Player, "TUT_LevelUp"); + + IF + ItemAddedToCharacter(_Item, _Player) + AND + DB_InitialTutorialsShown(1) + AND + DB_IsPlayer(_Player) + AND + ItemHasOnUse(_Item,"skillbook",1) + THEN + PROC_CheckPlayTut(_Player,"TUT_Skill"); + + IF + ItemAddedToCharacter(_Item, _Player) + AND + DB_InitialTutorialsShown(1) + AND + DB_IsPlayer(_Player) + AND + ItemHasOnUse(_Item,"scrollarrow",1) + AND + IsTagged(_Item,"GRENADES",0) + THEN + PROC_CheckPlayTut(_Player, "TUT_Scroll"); + + IF + ItemAddedToCharacter(_Item, _Player) + AND + DB_InitialTutorialsShown(1) + AND + DB_IsPlayer(_Player) + AND + ItemHasOnUse(_Item,"book",1) + THEN + PROC_CheckPlayTut(_Player, "TUT_Book"); + + IF + DifficultyChanged(0) + THEN + DB_TUT_PlayOnEasy(1); + + IF + DifficultyChanged(_Int) + AND + _Int != 0 + THEN + NOT DB_TUT_PlayOnEasy(1); + + IF + SkillAdded(_Player,_,1) + AND + DB_IsPlayer(_Player) + AND + CharacterIsControlled(_Player,1) + AND + DB_InitialTutorialsShown(1) + AND + NOT DB_TutorialMessages_SkillUnlockDone(_Player) + THEN + ProcObjectTimer(_Player,"TUT_SkillUnlock",500); + + PROC + ProcObjectTimerFinished(_Player,"TUT_SkillUnlock") + THEN + PROC_TutorialMessage_SkillUnlockAfterDialog((CHARACTERGUID)_Player); + + PROC + PROC_TutorialMessage_SkillUnlockAfterDialog((CHARACTERGUID)_Player) + AND + NOT DB_DialogPlayers(_,_Player,_) + THEN + DB_TutorialMessages_SkillUnlockDone(_Player); + PROC_CheckPlayTut(_Player, "TUT_SkillUnlock"); + + PROC + PROC_TutorialMessage_SkillUnlockAfterDialog((CHARACTERGUID)_Player) + AND + NOT DB_TutorialMessages_SkillUnlockDone(_Player) + AND + DB_DialogPlayers(_Inst,_Player,_) + AND + NOT DB_AutomatedDialog(_Inst) + THEN + DB_TutorialMessage_CheckEndDialog_SkillTut(_Inst,_Player); + + PROC + PROC_TutorialMessage_SkillUnlockAfterDialog((CHARACTERGUID)_Player) + AND + NOT DB_TutorialMessages_SkillUnlockDone(_Player) + AND + DB_DialogPlayers(_Inst,_Player,_) + AND + DB_AutomatedDialog(_Inst) + THEN + DB_TutorialMessage_CheckEndAutomatedDialog_SkillTut(_Inst,_Player); + + IF + DialogEnded(_,_Inst) + AND + DB_TutorialMessage_CheckEndDialog_SkillTut(_Inst,_Player) + THEN + NOT DB_TutorialMessage_CheckEndDialog_SkillTut(_Inst,_Player); + DB_TutorialMessages_SkillUnlockDone(_Player); + PROC_CheckPlayTut(_Player, "TUT_SkillUnlock"); + + IF + AutomatedDialogEnded(_,_Inst) + AND + DB_TutorialMessage_CheckEndAutomatedDialog_SkillTut(_Inst,_Player) + THEN + NOT DB_TutorialMessage_CheckEndAutomatedDialog_SkillTut(_Inst,_Player); + DB_TutorialMessages_SkillUnlockDone(_Player); + PROC_CheckPlayTut(_Player, "TUT_SkillUnlock"); + + IF + ItemTemplateOpening(_,_Item, _Player) + AND + DB_IsPlayer(_Player) + AND + ItemIsContainer(_Item, 1) + AND + DB_ItemOpened(_Nb) + AND + _Nb == 10 + THEN + PROC_CheckPlayTut(_Player, "TUT_DragIcon"); + + IF + ItemTemplateOpening(_,_Item, _Player) + AND + DB_IsPlayer(_Player) + AND + ItemIsContainer(_Item, 1) + AND + DB_ItemOpened(_Nb) + AND + IntegerSum(_Nb,1,_NewNb) + THEN + NOT DB_ItemOpened(_Nb); + DB_ItemOpened(_NewNb); + + IF + DialogStarted(_,_Instance) + AND + DB_DialogNPCs(_Instance,_Animal,1) + AND + DB_DialogPlayers(_Instance,_Player,1) + AND + CharacterHasTalent((CHARACTERGUID)_Player,"AnimalEmpathy",0) + AND + IsTagged(_Animal,"ANIMAL",1) + THEN + PROC_CheckPlayTut(_Player,"TUT_NoPetPal"); + + IF + DialogStarted(_,_Instance) + AND + DB_DialogNPCs(_Instance,_Animal,1) + AND + DB_DialogPlayers(_Instance,_Player,1) + AND + CharacterHasTalent((CHARACTERGUID)_Player,"AnimalEmpathy",1) + AND + IsTagged(_Animal,"ANIMAL",1) + THEN + PROC_CheckPlayTut(_Player,"TUT_PetPal"); + + IF + ItemAddedToCharacter(_Item,_Player) + AND + DB_IsPlayer(_Player) + AND + DB_CharCountHelper(_Player,"CombatTurnCounter",_Turn) + AND + _Turn >= 3 + AND + ItemHasOnUse(_Item,"scrollarrow",1) + AND + ItemIsEquipable(_Item,0) + THEN + PROC_CheckPlayTut(_Player,"TUT_HotbarAssignment"); + + IF + ItemAddedToCharacter(_Item,_Player) + AND + DB_InitialTutorialsShown(1) + AND + DB_IsPlayer(_Player) + AND + ItemIsEquipable(_Item,1) + THEN + PROC_CheckPlayTut(_Player,"TUT_Equipment"); + + IF + CharacterUsedItem(_Player,_Item) + AND + DB_IsPlayer(_Player) + AND + ObjectIsItem(_Item,1) + AND + ItemIsLocked(_Item,1) + THEN + PROC_CheckPlayTut(_Player,"TUT_Locked"); + DB_TUT_ShowedTutLocked(1); + + IF + CharacterItemEvent(_Character, _,"WaypointDiscovered") + THEN + PROC_CheckPlayTut(_Character, "TUT_Waypoint"); + + } + EXIT + { + + } +} +Goal(32).Title("_GLOBAL_TutorialMessages_MovementDetection"); +Goal(32) +{ + INIT + { + // Don't show the movement tutorial for players that move before the tutorial is shown + + // Trigger the camera tutorial 10 seconds after all players have moved, or + // 10 seconds after the first player closed the movement tutorial box + + } + KB + { + // Triggers to detect that someone moved from their initial position + IF + DB_Tutorial_PlayerMovementDetectionTrigger((TRIGGERGUID)_Trigger) + THEN + TriggerRegisterForPlayers(_Trigger); + + // Player entered detection trigger -> won't get movement tutorial + // + check whether everyone has moved (if so, complete this goal + // and schedule the camera tutorial) + IF + CharacterEnteredTrigger(_Player,_Trigger) + AND + DB_Tutorial_PlayerMovementDetectionTrigger(_Trigger) + THEN + PROC_GLOBAL_TutorialMessages_PlayerMoved(_Player); + + // Player got the movement tutorial -> record so we know + // we should only schedule the camera tutorial after a + // a movement tutorial box has been closed + PROC + PROC_CheckPlayTut(_Char,"TUT_Movement") + THEN + DB_GlobalTutorialMessages_TUT_Movement_Shown(1); + PROC_GLOBAL_TutorialMessages_PlayerMoved(_Char); + + //REGION Handle a player moving or getting the movement tutorial + // This player has moved or got the tutorial + PROC + PROC_GLOBAL_TutorialMessages_PlayerMoved((CHARACTERGUID)_Player) + THEN + DB_GLOBAL_TutorialMessages_MovementTutFinishedFor(_Player); + + // Anyone left that didn't move or didn't get the tutorial? + PROC + PROC_GLOBAL_TutorialMessages_PlayerMoved((CHARACTERGUID)_Player) + AND + DB_IsPlayer(_AnyPlayer) + AND + NOT DB_GLOBAL_TutorialMessages_MovementTutFinishedFor(_AnyPlayer) + THEN + DB_GLOBAL_TutorialMessages_MovementTutUnfinished(1); + + // No one left -> unregister movement detection triggers + PROC + PROC_GLOBAL_TutorialMessages_PlayerMoved((CHARACTERGUID)_Player) + AND + NOT DB_GLOBAL_TutorialMessages_MovementTutUnfinished(1) + AND + DB_Tutorial_PlayerMovementDetectionTrigger(_Trigger) + THEN + ProcTriggerUnregisterForPlayers(_Trigger); + NOT DB_Tutorial_PlayerMovementDetectionTrigger(_Trigger); + + // No one left -> schedule camera tutorial right away if everyone + // skipped the movement tutorial and complete goal + PROC + PROC_GLOBAL_TutorialMessages_PlayerMoved((CHARACTERGUID)_Player) + AND + NOT DB_GLOBAL_TutorialMessages_MovementTutUnfinished(1) + AND + NOT DB_GlobalTutorialMessages_TUT_Movement_Shown(1) + THEN + TimerLaunch("TUT_Camera",10000); + GoalCompleted; + + // Clean up + PROC + PROC_GLOBAL_TutorialMessages_PlayerMoved((CHARACTERGUID)_Player) + THEN + NOT DB_GLOBAL_TutorialMessages_MovementTutUnfinished(1); + //END_REGION + + // At least one player got the movement tutorial and dismissed it + // -> schedule the camera tutorial + IF + TutorialBoxClosed(_,_Message) + AND + StringContains(_Message,"TUT_Movement",1) + AND + NOT DB_Tutorial_PlayerCameraTutorialTimerLaunched(1) + THEN + // Only launch once, multiple players could have gotten this dialog + DB_Tutorial_PlayerCameraTutorialTimerLaunched(1); + TimerLaunch("TUT_Camera",10000); + GoalCompleted; + + } + EXIT + { + NOT DB_GlobalTutorialMessages_TUT_Movement_Shown(1); + NOT DB_Tutorial_PlayerCameraTutorialTimerLaunched(1); + + } +} +Goal(33).Title("_Greevers_Little_Helpers"); +Goal(33) +{ + INIT + { + DB_Singleton("InitData",0); + + DB_GLO_AttributeCheck_Var(1,"Name","Attribute_Check_1_Name_080aa25a-eb82-4060-a20f-93f3f0a3ec85"); + DB_GLO_AttributeCheck_Var(1,"Difficulty","Attribute_Check_1_Difficulty_6645b5b8-4c17-4678-98b2-d5d718c36829"); + DB_GLO_AttributeCheck_Var(1,"LevelOverride","Attribute_Check_1_LevelOverride_08b97107-2d16-4ff5-a2e5-a01ed50e7135"); + DB_GLO_AttributeCheck_Var(1,"Source","Attribute_Check_1_Source_dd323f37-f472-4a67-9a44-5ca9f15646bb"); + DB_GLO_AttributeCheck_Var(1,"Target","Attribute_Check_1_Target_c46387f7-f39b-46c5-b6ec-baeccc34291a"); + + DB_GLO_AttributeCheck_Var(2,"Difficulty","Attribute_Check_2_Difficulty_eab60140-a9e3-4579-b5fa-3cb5515c2282"); + DB_GLO_AttributeCheck_Var(2,"LevelOverride","Attribute_Check_2_LevelOverride_1dc16030-ece6-4677-ae14-62e7e9d1ac0e"); + DB_GLO_AttributeCheck_Var(2,"Name","Attribute_Check_2_Name_151dfe0f-6575-4ce2-bedc-6f157abdb36e"); + DB_GLO_AttributeCheck_Var(2,"Source","Attribute_Check_2_Source_c40af148-2c37-444b-a462-4387a4c6cede"); + DB_GLO_AttributeCheck_Var(2,"Target","Attribute_Check_2_Target_b8df81e7-d122-452b-bfaa-f9bbe87fd387"); + + DB_GLO_AttributeCheck_Var(3,"Difficulty","Attribute_Check_3_Difficulty_ff545cea-32e3-4a9a-b005-f3a6e0f0cabd"); + DB_GLO_AttributeCheck_Var(3,"LevelOverride","Attribute_Check_3_LevelOverride_6e3f0bc5-636f-48ad-ad0a-642c3146302b"); + DB_GLO_AttributeCheck_Var(3,"Name","Attribute_Check_3_Name_8281a807-0ae5-4ada-8dc5-3b859310c1f5"); + DB_GLO_AttributeCheck_Var(3,"Source","Attribute_Check_3_Source_32f31478-7329-4902-bb66-a9043819102c"); + DB_GLO_AttributeCheck_Var(3,"Target","Attribute_Check_3_Target_65312317-50da-4cce-a6e9-725a85e7fd46"); + + } + KB + { + //REGION Stacks + + //Stacks are a useful tool to mimic a FILO stack structure. + //Add items using the Stack((STRING)_ID,_Value) command. + //At any time you can query for DB_TopOfStackCharacter((STRING)_ID,_Value), DB_TopOfStackItem((STRING)_ID,_Value) or DB_TopOfStackTrigger((STRING)_ID,_Value) + //If you wish to pop the top member of the stack, just call PopStack((STRING)_ID); + PROC + Stack_Internal((STRING)_ID,(STRING)_Value) + AND + NOT DB_StackInternalCounter(_,_ID) + THEN + DB_StackInternalCounter(0,_ID); + DB_TopOfStack(_ID,_Value); + + PROC + Stack_Internal((STRING)_ID,(STRING)_Value) + AND + DB_StackInternalCounter(_Amount,_ID) + AND + IntegerSum(_Amount,1,_NewAmount) + AND + DB_TopOfStack(_ID,_TopValue) + THEN + NOT DB_StackInternalCounter(_Amount,_ID); + DB_StackInternalCounter(_NewAmount,_ID); + DB_StackInternalMember(_NewAmount,_ID,_Value); + NOT DB_TopOfStack(_ID,_TopValue); + DB_TopOfStack(_ID,_Value); + + PROC + PopStack((STRING)_ID) + AND + DB_StackInternalCounter(1,_ID) + AND + DB_TopOfStack(_ID,_Value) + THEN + NOT DB_TopOfStack(_ID,_Value); + NOT DB_StackInternalCounter(1,_ID); + NOT DB_StackInternalMember(1,_ID,_Value); + ProcClearOldTopOfStack(_ID); + + PROC + PopStack((STRING)_ID) + AND + DB_StackInternalCounter(_Amount,_ID) + AND + IntegerSubtract(_Amount,1,_NewAmount) + AND + DB_StackInternalMember(_Amount,_ID,_OldTopValue) + AND + DB_StackInternalMember(_NewAmount,_ID,_NewTopValue) + THEN + NOT DB_StackInternalCounter(_Amount,_ID); + DB_StackInternalCounter(_NewAmount,_ID); + NOT DB_StackInternalMember(_Amount,_ID,_OldTopValue); + NOT DB_TopOfStack(_ID,_OldTopValue); + DB_TopOfStack(_ID,_NewTopValue); + + PROC + Stack((STRING)_ID,(GUIDSTRING)_Object) + AND + GetUUID(_Object,_UUID) + THEN + DB_Stack_UUID(_Object,_UUID); + Stack_Internal(_ID,_UUID); + + IF + DB_TopOfStack(_ID,_UUID) + AND + DB_Stack_UUID(_Value,_UUID) + THEN + ProcClearOldTopOfStack(_ID); + DB_TopOfStackObject(_ID,_Value); + + PROC + ProcClearOldTopOfStack((STRING)_ID) + AND + DB_TopOfStackObject(_ID,_Value) + THEN + NOT DB_TopOfStackObject(_ID,_Value); + + //END_REGION + + //REGION Default Parameters + PROC + CharacterLookAt((CHARACTERGUID)_Character1,(GUIDSTRING)_Target) + THEN + CharacterLookAt(_Character1,_Target,0); + + PROC + PlayAnimation((GUIDSTRING)_Object,(STRING)_String) + THEN + PlayAnimation(_Object,_String,""); + + PROC + TeleportTo((GUIDSTRING)_Object,(GUIDSTRING)_Target) + THEN + TeleportTo(_Object,_Target,"",1,0); + + PROC + TeleportTo((GUIDSTRING)_Object,(GUIDSTRING)_Target,(STRING)_Event,(INTEGER)_TeleportLinkedCharacters) + THEN + TeleportTo(_Object,_Target,_Event,_TeleportLinkedCharacters,0); + + PROC + TeleportToPosition((GUIDSTRING)_Object,(REAL)_X,(REAL)_Y,(REAL)_Z,(STRING)_Event,(INTEGER)_TeleportLinkedCharacters) + THEN + TeleportToPosition(_Object,_X,_Y,_Z,_Event,_TeleportLinkedCharacters,0); + + PROC + ApplyStatus((GUIDSTRING)_Target,(STRING)_Status,(REAL)_Duration) + THEN + ApplyStatus(_Target,_Status,_Duration,0); + + PROC + PartySetFlag((CHARACTERGUID)_Character,(STRING)_Event) + THEN + PartySetFlag(_Character,_Event,0); + + PROC + ObjectSetFlag((GUIDSTRING)_Object,(STRING)_Event) + THEN + ObjectSetFlag(_Object,_Event,0); + + PROC + ObjectClearFlag((GUIDSTRING)_Object,(STRING)_Event) + THEN + ObjectClearFlag(_Object,_Event,0); + + PROC + UserSetFlag((CHARACTERGUID)_Character,(STRING)_Event) + THEN + UserSetFlag(_Character,_Event,0); + + PROC + CharacterUseSkill((CHARACTERGUID)_Character,(STRING)_Skill,(GUIDSTRING)_Target) + THEN + CharacterUseSkill(_Character,_Skill,_Target,0,0,0); + + PROC + CharacterUseSkill((CHARACTERGUID)_Character,(STRING)_Skill,(GUIDSTRING)_Target,(INTEGER)_ForceResetCooldown) + THEN + CharacterUseSkill(_Character,_Skill,_Target,_ForceResetCooldown,0,0); + + PROC + CharacterUseSkill((CHARACTERGUID)_Character,(STRING)_Skill,(GUIDSTRING)_Target,(INTEGER)_ForceResetCooldown, (INTEGER)_IgnoreHasSkill) + THEN + CharacterUseSkill(_Character,_Skill,_Target,_ForceResetCooldown,_IgnoreHasSkill,0); + + PROC + CharacterAddSkill((CHARACTERGUID)_Character, (STRING)_Skill) + THEN + CharacterAddSkill((CHARACTERGUID)_Character, (STRING)_Skill, 1); + + PROC + CharacterMakePlayer((CHARACTERGUID)_Character) + THEN + CharacterMakePlayer((CHARACTERGUID)_Character,NULL_00000000-0000-0000-0000-000000000000); + //END_REGION + + //REGION Trigger Spotter + QRY + Query_IsPlayerHiding((CHARACTERGUID)_Char) + AND + HasActiveStatus(_Char,"SNEAKING",1) + THEN + DB_NOOP(1); + + QRY + Query_IsPlayerHiding((CHARACTERGUID)_Char) + AND + HasActiveStatus(_Char,"INVISIBLE",1) + THEN + DB_NOOP(1); + + IF + DB_OneShot_DialogTrigger_NewSystem((TRIGGERGUID)_Trigger,(STRING)_,(CHARACTERGUID)_Spotter) + THEN + SetVarInteger(_Spotter,"IgnoreDialogChecks",0); + DB_SneakTriggerSpotter(_Trigger,_Spotter); + + IF + DB_OneShot_DialogTrigger_NewSystem((TRIGGERGUID)_Trigger,(STRING)_,(CHARACTERGUID)_Spotter1,(CHARACTERGUID)_Spotter2) + THEN + SetVarInteger(_Spotter1,"IgnoreDialogChecks",0); + SetVarInteger(_Spotter2,"IgnoreDialogChecks",0); + DB_SneakTriggerSpotter(_Trigger,_Spotter1); + DB_SneakTriggerSpotter(_Trigger,_Spotter2); + + IF + DB_OneShot_DialogTrigger_NewSystem((TRIGGERGUID)_Trigger,(STRING)_,(CHARACTERGUID)_Spotter1,(CHARACTERGUID)_Spotter2,(CHARACTERGUID)_Spotter3) + THEN + SetVarInteger(_Spotter1,"IgnoreDialogChecks",0); + SetVarInteger(_Spotter2,"IgnoreDialogChecks",0); + SetVarInteger(_Spotter3,"IgnoreDialogChecks",0); + DB_SneakTriggerSpotter(_Trigger,_Spotter1); + DB_SneakTriggerSpotter(_Trigger,_Spotter2); + DB_SneakTriggerSpotter(_Trigger,_Spotter3); + + PROC + ProcCharInTriggerSpotted((CHARACTERGUID)_Player,(TRIGGERGUID)_Trigger) + AND + DB_OneShot_DialogTrigger_NewSystem(_Trigger,_Dialog,_Spotter) + THEN + Proc_StartDialog(0,_Dialog,_Spotter,_Player); + NOT DB_OneShot_DialogTrigger_NewSystem(_Trigger,_Dialog,_Spotter); + + PROC + ProcCharInTriggerSpotted((CHARACTERGUID)_Player,(TRIGGERGUID)_Trigger) + AND + DB_OneShot_DialogTrigger_NewSystem(_Trigger,_Dialog,_Spotter1,_Spotter2) + THEN + Proc_StartDialog(0,_Dialog,_Spotter1,_Spotter2,_Player); + NOT DB_OneShot_DialogTrigger_NewSystem(_Trigger,_Dialog,_Spotter1,_Spotter2); + + PROC + ProcCharInTriggerSpotted((CHARACTERGUID)_Player,(TRIGGERGUID)_Trigger) + AND + DB_OneShot_DialogTrigger_NewSystem(_Trigger,_Dialog,_Spotter1,_Spotter2,_Spotter3) + THEN + Proc_StartDialog(0,_Dialog,_Spotter1,_Spotter2,_Spotter3,_Player); + NOT DB_OneShot_DialogTrigger_NewSystem(_Trigger,_Dialog,_Spotter1,_Spotter2,_Spotter3); + + IF + CharacterStatusRemoved(_Player,"SNEAKING",_) + AND + DB_IsPlayer(_Player) + AND + NOT Query_IsPlayerHiding(_Player) + THEN + SetStoryEvent(_Player,"BecameVisible"); + + IF + CharacterStatusRemoved(_Player,"INVISIBLE",_) + AND + DB_IsPlayer(_Player) + AND + NOT Query_IsPlayerHiding(_Player) + THEN + SetStoryEvent(_Player,"BecameVisible"); + + IF + CharacterStatusApplied(_Player,"SNEAKING",_) + AND + DB_IsPlayer(_Player) + AND + Query_IsPlayerHiding(_Player) + THEN + SetStoryEvent(_Player,"BecameInvisible"); + + IF + CharacterStatusApplied(_Player,"INVISIBLE",_) + AND + DB_IsPlayer(_Player) + AND + NOT Query_IsPlayerHiding(_Player) + THEN + SetStoryEvent(_Player,"BecameInvisible"); + + IF + DB_TriggerSendsSpotEvents(_Trigger) + THEN + ProcTriggerRegisterForPlayers(_Trigger); + + IF + CharacterEnteredTrigger(_Player,_SpotTrigger) + AND + NOT Query_IsPlayerHiding(_Player) + AND + DB_TriggerSendsSpotEvents(_SpotTrigger) + THEN + DB_Event_CharacterAppearedInSpotTrigger(_Player,_SpotTrigger); + NOT DB_Event_CharacterAppearedInSpotTrigger(_Player,_SpotTrigger); + + IF + CharacterLeftTrigger(_Player,_SpotTrigger) + AND + NOT Query_IsPlayerHiding(_Player) + AND + DB_TriggerSendsSpotEvents(_SpotTrigger) + THEN + DB_Event_CharacterDisappearedInSpotTrigger(_Player,_SpotTrigger); + NOT DB_Event_CharacterDisappearedInSpotTrigger(_Player,_SpotTrigger); + + IF + StoryEvent((CHARACTERGUID)_Player,"BecameVisible") + AND + DB_InRegion(_Player,_SpotTrigger) + AND + DB_TriggerSendsSpotEvents(_SpotTrigger) + THEN + DB_Event_CharacterAppearedInSpotTrigger(_Player,_SpotTrigger); + NOT DB_Event_CharacterAppearedInSpotTrigger(_Player,_SpotTrigger); + + IF + StoryEvent((CHARACTERGUID)_Player,"BecameInvisible") + AND + DB_InRegion(_Player,_SpotTrigger) + AND + DB_TriggerSendsSpotEvents(_SpotTrigger) + THEN + DB_Event_CharacterDisappearedInSpotTrigger(_Player,_SpotTrigger); + NOT DB_Event_CharacterDisappearedInSpotTrigger(_Player,_SpotTrigger); + + //END_REGION + + //REGION Global Event Memory + IF + GlobalFlagSet(_String) + THEN + DB_GlobalFlag(_String); + + IF + GlobalFlagCleared(_String) + THEN + NOT DB_GlobalFlag(_String); + //END_REGION + + //REGION Poof / Foop + PROC + Poof((GUIDSTRING)_Character) + THEN + Poof((GUIDSTRING)_Character,"RS3_FX_GP_ScriptedEvent_Teleport_GenericSmoke_01"); + + PROC + Poof((GUIDSTRING)_Character,(STRING)_Effect) + AND + GetRegion(_Character,_Region) + AND + DB_CurrentLevel(_Region) + AND + GetPosition(_Character,_X,_Y,_Z) + THEN + PlayEffectAtPosition(_Effect,_X,_Y,_Z); + + PROC + Poof((GUIDSTRING)_Character,(STRING)_Effect) + THEN + SetOnStage(_Character,0); + + PROC + Foop((GUIDSTRING)_Character) + THEN + Foop(_Character,"RS3_FX_GP_ScriptedEvent_Teleport_GenericSmoke_01"); + + PROC + Foop((GUIDSTRING)_Character,(STRING)_Effect) + AND + GetRegion(_Character,_Region) + AND + DB_CurrentLevel(_Region) + AND + GetPosition(_Character,_X,_Y,_Z) + THEN + PlayEffectAtPosition(_Effect,_X,_Y,_Z); + + PROC + Foop((GUIDSTRING)_Character,(STRING)_Effect) + THEN + SetOnStage(_Character,1); + + IF + StoryEvent(_Character,"GEN_GoOffStage") + THEN + SetOnStage(_Character,0); + + PROC + Proc_PoofOnce((GUIDSTRING)_Character,(STRING)_OnlyOnce) + AND + NOT DB_OnlyOnce(_OnlyOnce) + THEN + Poof(_Character); + DB_OnlyOnce(_OnlyOnce); + //END_REGION + + //REGION Uniformly distributed Randoms (Fair random with memory) + //Fair rand requires a UUID and a maxamount. The function will return [0,Maxamount-1] + //When no previous data found, all possible numbers are added into an array. + //Every FairRand query returns one random number from the array and removes it. + //Only when the array is empty, is it reseeded. + //Output: DB_FairRand_ReturnVal(INTEGER) + + //Clear return value + QRY + QRY_GetFairRand((STRING)_UUID,(INTEGER)_Amount) + AND + DB_FairRand_ReturnVal((INTEGER)_Number) + THEN + NOT DB_FairRand_ReturnVal(_Number); + + //If first time or all numbers used: reseed. + QRY + QRY_GetFairRand((STRING)_UUID,(INTEGER)_Amount) + AND + NOT DB_FairRand_Available(_UUID,_Amount,_) + THEN + Proc_SeedFairRand(_UUID,_Amount,_Amount); + + //Pick a random number from the available array (Put in new array with indeces, pick random index, return number) + //Special case: Make sure that the first pick of a list is not the same as the last pick of the previous list + QRY + QRY_GetFairRand((STRING)_UUID,(INTEGER)_Amount) + THEN + DB_FairRandAmount(0); + + QRY + QRY_GetFairRand((STRING)_UUID,(INTEGER)_Amount) + AND + _Amount > 1 + AND + DB_FairRand_Last_Of_Previous_Seeding(_PreviousNumber) + AND + DB_FairRand_Available(_UUID,_Amount,_AvailableNumber) + AND + _PreviousNumber != _AvailableNumber + AND + DB_FairRandAmount(_Count) + AND + IntegerSum(_Count,1,_NewCount) + THEN + DB_FairRand_Array(_Count,_AvailableNumber); + NOT DB_FairRandAmount(_Count); + DB_FairRandAmount(_NewCount); + DB_FairAmount_ListFilled(1); + + QRY + QRY_GetFairRand((STRING)_UUID,(INTEGER)_Amount) + AND + DB_FairRand_Last_Of_Previous_Seeding(_Number) + THEN + NOT DB_FairRand_Last_Of_Previous_Seeding(_Number); + + QRY + QRY_GetFairRand((STRING)_UUID,(INTEGER)_Amount) + AND + NOT DB_FairAmount_ListFilled(1) + AND + DB_FairRand_Available(_UUID,_Amount,_AvailableNumber) + AND + DB_FairRandAmount(_Count) + AND + IntegerSum(_Count,1,_NewCount) + THEN + DB_FairRand_Array(_Count,_AvailableNumber); + NOT DB_FairRandAmount(_Count); + DB_FairRandAmount(_NewCount); + + QRY + QRY_GetFairRand((STRING)_UUID,(INTEGER)_Amount) + AND + DB_FairAmount_ListFilled(1) + THEN + NOT DB_FairAmount_ListFilled(1); + + QRY + QRY_GetFairRand((STRING)_UUID,(INTEGER)_Amount) + AND + DB_FairRandAmount(_Count) + AND + _Count > 0 + AND + Random(_Count,_Index) + AND + DB_FairRand_Array(_Index,_Number) + THEN + NOT DB_FairRand_Available(_UUID,_Amount,_Number); + DB_FairRand_ReturnVal(_Number); + + //If this was the last value in the available list, remember it, so we don't pick it first in the next list. + QRY + QRY_GetFairRand((STRING)_UUID,(INTEGER)_Amount) + AND + NOT DB_FairRand_Available(_UUID,_Amount,_) + AND + DB_FairRand_ReturnVal(_Number) + THEN + DB_FairRand_Last_Of_Previous_Seeding(_Number); + + //Clean up temp vars + QRY + QRY_GetFairRand((STRING)_UUID,(INTEGER)_MaxAmount) + AND + DB_FairRandAmount(_Amount) + THEN + NOT DB_FairRandAmount(_Amount); + + QRY + QRY_GetFairRand((STRING)_UUID,(INTEGER)_Amount) + AND + DB_FairRand_Array(_Index,_Number) + THEN + NOT DB_FairRand_Array(_Index,_Number); + + PROC + Proc_SeedFairRand((STRING)_UUID,(INTEGER)_MaxAmount,(INTEGER)_Amount) + AND + _Amount > 0 + AND + IntegerSubtract(_Amount,1,_NewAmount) + THEN + DB_FairRand_Available(_UUID,_MaxAmount,_NewAmount); + Proc_SeedFairRand(_UUID,_MaxAmount,_NewAmount); + //END_REGION + + //REGION Get Random position in coordinate-based area ignoring AI grid + // Pass in two objects. Returns point within cube defined by their coordinates + // Returns result in DB_Helper_GetAnyRandomPositionInArea_Result(_X,_Y,_Z) + // NOTE: only tested currently with all coordinates of FirstPoint <= coordinates of _SecondPoint + // I don't know whether our random can handle a negative module. + QRY + QRY_Helper_GetAnyRandomPositionInArea((GUIDSTRING)_FirstPoint,(GUIDSTRING)_SecondPoint) + AND + DB_Helper_GetAnyRandomPositionInArea_Result(_X,_Y,_Z) + THEN + NOT DB_Helper_GetAnyRandomPositionInArea_Result(_X,_Y,_Z); + + QRY + QRY_Helper_GetAnyRandomPositionInArea((GUIDSTRING)_BottomLeftBack,(GUIDSTRING)_TopRightFront) + AND + GetPosition(_BottomLeftBack,_X1,_Y1,_Z1) + AND + GetPosition(_TopRightFront,_X2,_Y2,_Z2) + AND + RealSubtract(_X2,_X1,_XDeltaReal) + AND + RealSubtract(_Y2,_Y1,_YDeltaReal) + AND + RealSubtract(_Z2,_Z1,_ZDeltaReal) + AND + RealProduct(_XDeltaReal,100.0,_XScaledDeltaReal) + AND + RealProduct(_YDeltaReal,100.0,_YScaledDeltaReal) + AND + RealProduct(_ZDeltaReal,100.0,_ZScaledDeltaReal) + AND + Integer(_XScaledDeltaReal,_XScaledDeltaInt) + AND + Integer(_YScaledDeltaReal,_YScaledDeltaInt) + AND + Integer(_ZScaledDeltaReal,_ZScaledDeltaInt) + AND + Random(_XScaledDeltaInt,_XRandScaledDeltaInt) + AND + Random(_YScaledDeltaInt,_YRandScaledDeltaInt) + AND + Random(_ZScaledDeltaInt,_ZRandScaledDeltaInt) + AND + Real(_XRandScaledDeltaInt,_XRandScaledDeltaReal) + AND + Real(_YRandScaledDeltaInt,_YRandScaledDeltaReal) + AND + Real(_ZRandScaledDeltaInt,_ZRandScaledDeltaReal) + AND + RealDivide(_XRandScaledDeltaReal,100.0,_XRandDeltaReal) + AND + RealDivide(_YRandScaledDeltaReal,100.0,_YRandDeltaReal) + AND + RealDivide(_ZRandScaledDeltaReal,100.0,_ZRandDeltaReal) + AND + RealSum(_X1,_XRandDeltaReal,_NewX) + AND + RealSum(_Y1,_YRandDeltaReal,_NewY) + AND + RealSum(_Z1,_ZRandDeltaReal,_NewZ) + THEN + DB_Helper_GetAnyRandomPositionInArea_Result(_NewX,_NewY,_NewZ); + //END_REGION + + //REGION Singleton Variables //A variable which can only hold one value. All old value get overwritten. + IF + DB_Singleton(_VariableNam,_NewValue) + AND + DB_Singleton(_VariableNam,_OldValue) + AND + _NewValue != _OldValue + THEN + NOT DB_Singleton(_VariableNam,_OldValue); + //END_REGION + + //REGION Only Once Rule Query + QRY + QueryOnlyOnce((STRING)_OnlyOnceUUID) + AND + NOT DB_OnlyOnce(_OnlyOnceUUID) + THEN + DB_OnlyOnce(_OnlyOnceUUID); + //END_REGION + + //REGION Only once per user, but also mark users of nearby players + // NOTE: this is intended as replacement for triggering ADs guarded by a + // QueryOnlyOnce(). It does not check for all corner cases, such as + // character reassignment to another user. It's just meant to increase + // the chances that every user will see an AD rather than only the first + // one. + + // Use for atmospheric ADs that don't influence progression, and which are + // nice for a player to see/hear at least once. It avoids playing the same + // AD for multiple users in case they already heard another user's player + // say it, and at the same time allows different users to have the same AD + // if they arrive at a spot/perform the same action at different times. + QRY + QRY_OncePerUserAndNearbyPlayers((CHARACTERGUID)_Player, (STRING)_Flag) + AND + QRY_OncePerUserAndNearbyPlayers(_Player, _Flag, 10.0) + THEN + DB_NOOP(1); + + QRY + QRY_OncePerUserAndNearbyPlayers((CHARACTERGUID)_Player, (STRING)_Flag, (REAL)_Range) + AND + UserGetFlag(_Player, _Flag, 0) + THEN + PROC_OncePerUserAndNearbyPlayers_SetFlag(_Player, _Flag, _Range); + + PROC + PROC_OncePerUserAndNearbyPlayers_SetFlag((CHARACTERGUID)_Player, (STRING)_Flag, (REAL)_Range) + AND + DB_IsPlayer(_OtherPlayer) + AND + GetDistanceTo(_Player, _OtherPlayer, _Dist) + AND + _Dist < _Range + THEN + // Will also catch _Player himself + UserSetFlag(_OtherPlayer, _Flag); + //END_REGION + + //REGION Only once per user, but also mark users of nearby players + // NOTE: this is intended as replacement for triggering ADs guarded by a + // QueryOnlyOnce(). It does not check for all corner cases, such as + // character reassignment to another user. It's just meant to increase + // the chances that every user will see an AD rather than only the first + // one. + + // Use for atmospheric ADs that don't influence progression, and which are + // nice for a player to see/hear at least once. It avoids playing the same + // AD for multiple users in case they already heard another user's player + // say it, and at the same time allows different users to have the same AD + // if they arrive at a spot/perform the same action at different times. + QRY + QRY_OncePerUserAndNearbyPlayers((CHARACTERGUID)_Player, (STRING)_Flag) + AND + QRY_OncePerUserAndNearbyPlayers(_Player, _Flag, 10.0) + THEN + DB_NOOP(1); + + QRY + QRY_OncePerUserAndNearbyPlayers((CHARACTERGUID)_Player, (STRING)_Flag, (REAL)_Range) + AND + UserGetFlag(_Player, _Flag, 0) + THEN + PROC_OncePerUserAndNearbyPlayers_SetFlag(_Player, _Flag, _Range); + + PROC + PROC_OncePerUserAndNearbyPlayers_SetFlag((CHARACTERGUID)_Player, (STRING)_Flag, (REAL)_Range) + AND + DB_IsPlayer(_OtherPlayer) + AND + GetDistanceTo(_Player, _OtherPlayer, _Dist) + AND + _Dist < _Range + THEN + // Will also catch _Player himself + UserSetFlag(_OtherPlayer, _Flag); + //END_REGION + + //REGION Small macros + PROC + PROC_RemoveDialogFromCharacter((CHARACTERGUID)_NPC) + THEN + DialogRequestStop(_NPC); + ProcRemoveAllDialogEntriesForSpeaker(_NPC); + SetHasDialog(_Npc,0); + //END_REGION + + //REGION Counter Helper + PROC + Proc_CountHelper((STRING)_ID) + AND + NOT DB_CountHelper(_ID,_) + THEN + DB_CountHelper(_ID,0); + + PROC + Proc_CountHelper((STRING)_ID) + AND + DB_CountHelper(_ID,_OldValue) + AND + IntegerSum(_OldValue,1,_Value) + THEN + NOT DB_CountHelper(_ID,_OldValue); + DB_CountHelper(_ID,_Value); + + PROC + Proc_CharCountHelper((CHARACTERGUID)_Char,(STRING)_ID) + THEN + Proc_CharCountHelper(_Char,_ID,1); + + PROC + Proc_CharCountHelper((CHARACTERGUID)_Char,(STRING)_ID,_) + AND + NOT DB_CharCountHelper(_Char,_ID,_) + THEN + DB_CharCountHelper(_Char,_ID,0); + + PROC + Proc_CharCountHelper((CHARACTERGUID)_Char,(STRING)_ID,(INTEGER)_Count) + AND + DB_CharCountHelper(_Char,_ID,_OldValue) + AND + IntegerSum(_OldValue,_Count,_Value) + THEN + NOT DB_CharCountHelper(_Char,_ID,_OldValue); + DB_CharCountHelper(_Char,_ID,_Value); + + PROC + Proc_CharCountHelperReset((CHARACTERGUID)_Char,(STRING)_ID) + AND + DB_CharCountHelper(_Char,_ID,_Counter) + THEN + NOT DB_CharCountHelper(_Char,_ID,_Counter); + //END_REGION + + //REGION Unregister a Trigger For a User + // Note: this routines errs on the side of keeping the trigger unregistered. + // E.g., if a character is assigned to a user without other characters, triggers + // will kept be kept unregistered for that character if they had been unregistered + // before. + PROC + ProcUnregisterForUser((TRIGGERGUID)_Trigger,(CHARACTERGUID)_Player) + AND + CharacterGetReservedUserID(_Player,_UserID) + AND + DB_IsPlayer(_OtherPlayer) + AND + CharacterGetReservedUserID(_OtherPlayer,_UserID) + THEN + DB_TriggerUnregisteredForUserChar(_Trigger,_OtherPlayer); + TriggerUnregisterForCharacter(_Trigger,_OtherPlayer); + + IF + CharacterReservedUserIDChanged(_Player,_UserID) + AND + DB_IsPlayer(_Player) + AND + DB_IsPlayer(_OtherPlayer) + AND + NOT DB_UnregisterForUserIDChanged(1) + AND + _OtherPlayer != _Player + AND + CharacterGetReservedUserID(_OtherPlayer,_UserID) + THEN + DB_UnregisterForUserIDChanged(1); + // Only have to call this for one character, because this logic keeps it the + // same for all of the characters assigned to a single user + ProcUnregisterForUserIDChanged(_OtherPlayer,_Player); + + PROC + ProcUnregisterForUserIDChanged((CHARACTERGUID)_ExistingPlayer,(CHARACTERGUID)_NewPlayer) + AND + DB_TriggerUnregisteredForUserChar(_Trigger,_ExistingPlayer) + AND + NOT DB_TriggerUnregisteredForUserChar(_Trigger,_NewPlayer) + THEN + DB_TriggerUnregisteredForUserChar(_Trigger,_NewPlayer); + TriggerUnregisterForCharacter(_Trigger,_NewPlayer); + + PROC + ProcUnregisterForUserIDChanged((CHARACTERGUID)_ExistingPlayer,(CHARACTERGUID)_NewPlayer) + AND + DB_TriggerUnregisteredForUserChar(_Trigger,_NewPlayer) + AND + NOT DB_TriggerUnregisteredForUserChar(_Trigger,_ExistingPlayer) + THEN + NOT DB_TriggerUnregisteredForUserChar(_Trigger,_NewPlayer); + TriggerRegisterForCharacter(_Trigger,_NewPlayer); + + IF + CharacterReservedUserIDChanged(_Player,_UserID) + THEN + NOT DB_UnregisterForUserIDChanged(1); + + //END_REGION + + //REGION Turn Skipping + //While in DB_CharacterSkipTurn(_Char), skip turn + // + IF + ObjectFlagSet("GEN_EndTurn",_Char,_) + THEN + EndTurn(_Char); + ObjectClearFlag(_Char,"GEN_EndTurn",0); + + IF + ObjectFlagSet("GEN_SkipToTurn",_Char,_) + AND + DB_CombatObjects(_Char,_) + THEN + DB_CharacterSkipTurn(_Char); + ObjectClearFlag(_Char,"GEN_SkipToTurn",0); + + IF + ObjectTurnStarted(_Char) + AND + DB_CharacterSkipTurn(_Char) + THEN + EndTurn(_Char); + + PROC + Proc_PurgeSkipTurnDB() + AND + DB_CharacterSkipTurn(_Char) + THEN + NOT DB_CharacterSkipTurn(_Char); + + IF + ObjectTurnStarted(_Char) + AND + DB_SkipToCharactersTurn(_CharsTurn) + AND + DB_CombatObjects(_Char,_ID) + AND + DB_CombatObjects(_CharsTurn,_ID) + AND + _Char != _CharsTurn + THEN + EndTurn(_Char); + + IF + ObjectTurnStarted(_Char) + AND + DB_SkipToCharactersTurn(_Char) + THEN + NOT DB_SkipToCharactersTurn(_Char); + + IF + ObjectFlagSet("GEN_DisappearOutOfSight",(CHARACTERGUID)_Char,_) + THEN + ProcCharacterDisappearOutOfSight(_Char,0,1,"",0); + ObjectClearFlag(_Char,"GEN_DisappearOutOfSight",0); + + IF + ObjectLeftCombat((CHARACTERGUID)_Char,_) + AND + ObjectGetFlag(_Char,"GEN_DisappearOutOfSightWhenAvailable",1) + AND + QRY_SpeakerIsAvailable(_Char) + THEN + ProcCharacterDisappearOutOfSight(_Char,0,1,"",0); + ObjectClearFlag(_Char,"GEN_DisappearOutOfSightWhenAvailable",0); + + IF + DialogEnded(_,_ID) + AND + DialogGetInvolvedNPC(_ID,1,(CHARACTERGUID)_Char) + AND + ObjectGetFlag(_Char,"GEN_DisappearOutOfSightWhenAvailable",1) + AND + QRY_SpeakerIsAvailable(_Char) + THEN + ProcCharacterDisappearOutOfSight(_Char,0,1,"",0); + ObjectClearFlag(_Char,"GEN_DisappearOutOfSightWhenAvailable",0); + + + //END_REGION + + //REGION Move an item locally + //Add to an items X, Y and Z values + // + PROC + ProcItemLocalMove((ITEMGUID)_Item,(REAL)_PlusX,(REAL)_PlusY,(REAL)_PlusZ,(REAL)_Speed,(REAL)_Acceleration) + AND + GetPosition(_Item,_X,_Y,_Z) + AND + RealSum(_X,_PlusX,_TargetX) + AND + RealSum(_Y,_PlusY,_TargetY) + AND + RealSum(_Z,_PlusZ,_TargetZ) + THEN + Proc_ItemMoveToPosition(_Item,_TargetX,_TargetY,_TargetZ,_Speed,_Acceleration); + + + //END_REGION + + //REGION Force End a Dialog + //for use in behaviour scripts + IF + StoryEvent((GUIDSTRING)_Object,"ForceStopDialog") + THEN + ProcForceStopDialog(_Object); + + PROC + ProcForceStopDialog((GUIDSTRING)_Object) + THEN + DialogRequestStop(_Object); + + //END_REGION + + //REGION Generic hostile after dialog + //NPC in the dialogue will go hostile to the Player that this event is set on at the end of the dialog + IF + ObjectFlagSet("FactionHostileToIndivPlayerAfterDialog",(CHARACTERGUID)_Player,_Id) + THEN + DB_HostileToIndivPlayerAfterDialog(_Player,_Id); + + IF + DialogEnded(_,_Id) + AND + DB_HostileToIndivPlayerAfterDialog(_Player,_Id) + AND + DB_DialogNPCs(_Id,_Char,_) + THEN + ProcSetHostileToIndivPlayer((CHARACTERGUID)_Char,_Player); + ObjectClearFlag(_Player,"FactionHostileToIndivPlayerAfterDialog",_Id); + NOT DB_HostileToIndivPlayerAfterDialog(_Player,_Id); + + + //Temp Hostility + IF + ObjectFlagSet("TemporaryHostilityAfterDialog",(CHARACTERGUID)_Player,_Id) + THEN + DB_TemporaryHostilityAfterDialog(_Player,_Id); + + IF + DialogEnded(_,_Id) + AND + DB_TemporaryHostilityAfterDialog(_Player,_Id) + AND + DB_DialogNPCs(_Id,_Char,_) + THEN + ProcMakeNPCHostile((CHARACTERGUID)_Char,_Player); + ObjectClearFlag(_Player,"TemporaryHostilityAfterDialog",_Id); + NOT DB_TemporaryHostilityAfterDialog(_Player,_Id); + + //Set Attitude to -100 after dialog + IF + ObjectFlagSet("HostileAttitudeAfterDialog",(CHARACTERGUID)_Player,_Id) + THEN + DB_HostileAttitudeAfterDialog(_Player,_Id); + + IF + DialogEnded(_,_Id) + AND + DB_HostileAttitudeAfterDialog(_Player,_Id) + AND + DB_DialogNPCs(_Id,_Char,_) + THEN + CharacterAddAttitudeTowardsPlayer((CHARACTERGUID)_Char,_Player,-100); + ObjectClearFlag(_Player,"HostileAttitudeAfterDialog",_Id); + NOT DB_HostileAttitudeAfterDialog(_Player,_Id); + + + //Set Evil NPC after Dialog + IF + ObjectFlagSet("HostileAlignmentAfterDialog",(CHARACTERGUID)_Player,_Id) + THEN + DB_HostileAlignmentAfterDialog(_Player,_Id); + + IF + DialogEnded(_,_Id) + AND + DB_HostileAlignmentAfterDialog(_Player,_Id) + AND + DialogGetInvolvedNPC(_Id,1,(CHARACTERGUID)_Char) + THEN + ProcSetRelationToPlayers(_Char,0); + ObjectClearFlag(_Player,"HostileAlignmentAfterDialog",_Id); + NOT DB_HostileAlignmentAfterDialog(_Player,_Id); + + + //END_REGION + + //REGION Character Dies After Dialog Ends + + IF + ObjectFlagSet("CharacterDieAfterDialog",(CHARACTERGUID)_Player,_Id) + THEN + DB_CharacterDieAfterDialog(_Player,_Id); + + IF + DialogEnded(_,_Id) + AND + DB_CharacterDieAfterDialog(_Player,_Id) + THEN + CharacterDie(_Player,0,"DoT"); + ObjectClearFlag(_Player,"CharacterDieAfterDialog",_Id); + NOT DB_CharacterDieAfterDialog(_Player,_Id); + + + + //END_REGION + + //REGION Res, Heal, and remove statuses from a player + + PROC + Proc_CharacterFullRestore((CHARACTERGUID)_Char) + THEN + CharacterResurrect(_Char); + RemoveHarmfulStatuses(_Char); + CharacterSetHitpointsPercentage(_Char,100); + CharacterSetArmorPercentage(_Char,100); + CharacterSetMagicArmorPercentage(_Char,100); + CharacterResetCooldowns(_Char); + + + //END_REGION + + //REGION Camera Shake around Player + + PROC + Proc_ShakeCameraForTime((CHARACTERGUID)_Char,(INTEGER)_Time) + AND + GetUUID(_Char,_UUID) + AND + StringConcatenate("CameraShake_",_UUID,_ID) + THEN + PROC_LoopEffect("RS3_FX_GP_ScriptedEvent_CameraShake_Loop_00-50",_Char,_ID,"__ANY__",""); + ProcObjectTimer(_Char,"Timer_LoopCameraShakeHelper",_Time); + + PROC + ProcObjectTimerFinished(_Char,"Timer_LoopCameraShakeHelper") + AND + GetUUID(_Char,_UUID) + AND + StringConcatenate("CameraShake_",_UUID,_ID) + THEN + PROC_StopLoopEffect(_Char,_ID); + + PROC + Proc_CameraShakeAroundCharacter((GUIDSTRING)_Object,(INTEGER)_Duration,(REAL)_Radius) + AND + DB_IsPlayer(_Player) + AND + GetDistanceTo(_Player,_Object,_Dist) + AND + _Dist <= _Radius + THEN + PlayEffect(_Player,"RS3_FX_GP_Combat_CameraShake_A"); + ProcObjectTimer(_Player,"ShakeCameraOn_",200); + DB_CameraShakeAroundCharacter(_Object,_Duration,_Radius); + + + PROC + ProcObjectTimerFinished((CHARACTERGUID)_Player,"ShakeCameraOn_") + AND + DB_CameraShakeAroundCharacter(_Object,_Duration,_Radius) + AND + IntegerSubtract(_Duration,200,_Dif) + AND + _Dif >= 0 + THEN + NOT DB_CameraShakeAroundCharacter(_Object,_Duration,_Radius); + Proc_CameraShakeAroundCharacter(_Object,_Dif,_Radius); + + PROC + ProcObjectTimerFinished((CHARACTERGUID)_Player,"ShakeCameraOn_") + AND + DB_CameraShakeAroundCharacter(_Object,_Duration,_Radius) + AND + IntegerSubtract(_Duration,200,_Dif) + AND + _Dif < 0.0 + THEN + NOT DB_CameraShakeAroundCharacter(_Object,_Duration,_Radius); + + //END_REGION + + //REGION Add/Remove Source Points + IF + ObjectFlagSet("RemoveSourcePoints",(CHARACTERGUID)_Char,_) + THEN + ObjectClearFlag(_Char,"RemoveSourcePoints",0); + Proc_RemoveSourcePoints(_Char); + + IF + ObjectFlagSet("RemoveOneSourcePoint",(CHARACTERGUID)_Char,_) + THEN + ObjectClearFlag(_Char,"RemoveOneSourcePoint",0); + CharacterAddSourcePoints(_Char, -1); + + + IF + ObjectFlagSet("AddSourcePoint",(CHARACTERGUID)_Char,_) + THEN + ObjectClearFlag(_Char,"AddSourcePoint",0); + Proc_AddSourcePoint(_Char); + + IF + ObjectFlagSet("AddTwoSourcePoints",(CHARACTERGUID)_Char,_) + THEN + ObjectClearFlag(_Char,"AddTwoSourcePoints",0); + Proc_AddSourcePoint(_Char); + Proc_AddSourcePoint(_Char); + + PROC + Proc_AddSourcePoint((CHARACTERGUID)_Char) + THEN + CharacterAddSourcePoints(_Char,1); + + PROC + Proc_RemoveSourcePoints((CHARACTERGUID)_Char) + THEN + CharacterAddSourcePoints(_Char,-100); + + //END_REGION + + QRY + QRY_GLO_IsVisibleUndead((CHARACTERGUID)_Char) + AND + IsTagged(_Char, "UNDEAD", 1) + AND + IsTagged(_Char, "VEILED_UNDEAD", 0) + AND + IsTagged(_Char, "MASKED_UNDEAD", 0) + THEN + DB_NOOP(0); + + IF + StoryEvent((CHARACTERGUID)_Char,"GEN_PurgeQueue") + THEN + CharacterPurgeQueue(_Char); + ProcClearMovingFacts(_Char); + + //REGION Do Knockdown fall / getup Animation + IF + StoryEvent((CHARACTERGUID)_Char,"GEN_FallAndGetUp") + THEN + PlayAnimation(_Char,"knockdown_fall","Play_Anim_knockdown_getup"); + + IF + StoryEvent((CHARACTERGUID)_Char,"Play_Anim_knockdown_getup") + THEN + PlayAnimation(_Char,"knockdown_getup"); + + //END_REGION + + //REGION Do Knockdown fall / knockdown loop Animation + IF + StoryEvent((CHARACTERGUID)_Char,"GEN_FallAndLie") + THEN + PlayAnimation(_Char,"knockdown_fall","Play_Anim_knockdown_loop"); + + IF + StoryEvent((CHARACTERGUID)_Char,"Play_Anim_knockdown_loop") + THEN + CharacterSetAnimationOverride(_Char,"knockdown_loop"); + + //END_REGION + + //REGION Is Available to + + QRY + QRY_IsAvailableTo((CHARACTERGUID)_Char,(CHARACTERGUID)_Target) + AND + QRY_SpeakerIsAvailable(_Char) + AND + QRY_SpeakerIsAvailable(_Target) + AND + CharacterCanSee(_Char,_Target,1) + THEN + DB_NOOP(1); + + //END_REGION + + //REGION Lookat in Dialog + + IF + ObjectFlagSet("LookAtPlayer",(CHARACTERGUID)_Char,_ID) + AND + DialogGetInvolvedPlayer(_ID,1,(CHARACTERGUID)_Player) + THEN + CharacterLookAt(_Char,_Player); + ObjectClearFlag(_Char,"LookAtPlayer",0); + + //END_REGION + + //REGION MoveTo in Dialog + + IF + ObjectFlagSet("Speaker1MoveTo",(CHARACTERGUID)_Char,_ID) + AND + DialogGetInvolvedNPC(_ID,1,(CHARACTERGUID)_Speaker1) + THEN + CharacterMoveTo(_Speaker1,_Char,0,"",0); + ObjectClearFlag(_Char,"Speaker1MoveTo",0); + + IF + ObjectFlagSet("Speaker2MoveTo",(CHARACTERGUID)_Char,_ID) + AND + DialogGetInvolvedNPC(_ID,2,(CHARACTERGUID)_Speaker2) + THEN + CharacterMoveTo(_Speaker2,_Char,0,"",0); + ObjectClearFlag(_Char,"Speaker2MoveTo",0); + + IF + ObjectFlagSet("Player1MoveTo",(CHARACTERGUID)_Char,_ID) + AND + DialogGetInvolvedPlayer(_ID,1,(CHARACTERGUID)_Player) + THEN + CharacterMoveTo(_Player,_Char,0,"",0); + ObjectClearFlag(_Char,"Player1MoveTo",0); + + IF + ObjectFlagSet("Player2MoveTo",(CHARACTERGUID)_Char,_ID) + AND + DialogGetInvolvedPlayer(_ID,2,(CHARACTERGUID)_Player) + THEN + CharacterMoveTo(_Player,_Char,0,"",0); + ObjectClearFlag(_Char,"Player2MoveTo",0); + + IF + ObjectFlagSet("Speaker1RunTo",(CHARACTERGUID)_Char,_ID) + AND + DialogGetInvolvedNPC(_ID,1,(CHARACTERGUID)_Speaker1) + THEN + CharacterMoveTo(_Speaker1,_Char,1,"",0); + ObjectClearFlag(_Char,"Speaker1MoveTo",0); + + IF + ObjectFlagSet("Speaker2RunTo",(CHARACTERGUID)_Char,_ID) + AND + DialogGetInvolvedNPC(_ID,2,(CHARACTERGUID)_Speaker2) + THEN + CharacterMoveTo(_Speaker2,_Char,1,"",0); + ObjectClearFlag(_Char,"Speaker2MoveTo",0); + + IF + ObjectFlagSet("Player1RunTo",(CHARACTERGUID)_Char,_ID) + AND + DialogGetInvolvedPlayer(_ID,1,(CHARACTERGUID)_Player) + THEN + CharacterMoveTo(_Player,_Char,1,"",0); + ObjectClearFlag(_Char,"Player1MoveTo",0); + + IF + ObjectFlagSet("Player2RunTo",(CHARACTERGUID)_Char,_ID) + AND + DialogGetInvolvedPlayer(_ID,2,(CHARACTERGUID)_Player) + THEN + CharacterMoveTo(_Player,_Char,1,"",0); + ObjectClearFlag(_Char,"Player2MoveTo",0); + + //END_REGION + + //REGION Animation testing + IF + TextEventSet("AnimTest") + AND + CharacterGetHostCharacter((CHARACTERGUID)_Player) + AND + GetTextEventParamInteger(1,0) + THEN + CharacterPurgeQueue(_Player); + + IF + TextEventSet("AnimTest") + AND + CharacterGetHostCharacter((CHARACTERGUID)_Player) + AND + GetTextEventParamInteger(1,0) + AND + DB_AnimTestLoopingAnimation(_Player,_AnimName) + THEN + NOT DB_AnimTestLoopingAnimation(_Player,_AnimName); + + // oe animtest animation_name + IF + TextEventSet("AnimTest") + AND + CharacterGetHostCharacter((CHARACTERGUID)_Player) + AND + GetTextEventParamString(1,_AnimName) + AND + NOT GetTextEventParamInteger(2,_) + THEN + PlayAnimation(_Player,_AnimName); + + IF + TextEventSet("AnimTest") + AND + CharacterGetHostCharacter((CHARACTERGUID)_Player) + AND + GetTextEventParamString(1,_AnimName) + AND + GetTextEventParamInteger(2,1) + THEN + PlayAnimation(_Player,_AnimName); + + + IF + TextEventSet("AnimTest") + AND + CharacterGetHostCharacter((CHARACTERGUID)_Player) + AND + GetTextEventParamString(1,_AnimName) + AND + GetTextEventParamInteger(2,-1) + THEN + PlayAnimation(_Player,_AnimName,"AnimTest_PlayLoopingAnim"); + DB_AnimTestLoopingAnimation(_Player,_AnimName); + + IF + StoryEvent((CHARACTERGUID)_Player,"AnimTest_PlayLoopingAnim") + AND + DB_AnimTestLoopingAnimation(_Player,_AnimName) + THEN + PlayAnimation(_Player,_AnimName,"AnimTest_PlayLoopingAnim"); + + + IF + TextEventSet("AnimTest") + AND + CharacterGetHostCharacter((CHARACTERGUID)_Player) + AND + GetTextEventParamString(1,_AnimName) + AND + GetTextEventParamInteger(2,0) + THEN + CharacterPurgeQueue(_Player); + + + + IF + TextEventSet("AnimTest") + AND + CharacterGetHostCharacter((CHARACTERGUID)_Player) + AND + GetTextEventParamString(1,_AnimName) + AND + GetTextEventParamInteger(2,0) + AND + DB_AnimTestLoopingAnimation(_Player,_AnimName) + THEN + NOT DB_AnimTestLoopingAnimation(_Player,_AnimName); + + + //END_REGION + + //REGION FX testing + + // oe animtest animation_name + //REGION OneShot + + IF + TextEventSet("FxTest") + AND + CharacterGetHostCharacter((CHARACTERGUID)_Player) + AND + GetTextEventParamString(1,_FxName) + AND + GetTextEventParamInteger(2,1) + THEN + PlayEffect(_Player,_FxName); + //END_REGION + + //REGION Looping FX + IF + TextEventSet("FxTest") + AND + CharacterGetHostCharacter((CHARACTERGUID)_Player) + AND + GetTextEventParamString(1,_FxName) + AND + GetTextEventParamInteger(2,-1) + THEN + PROC_LoopEffect(_FxName,_Player,"FxTest","__ANY__","Dummy_FX"); + + IF + TextEventSet("FxTest") + AND + CharacterGetHostCharacter((CHARACTERGUID)_Player) + AND + GetTextEventParamString(1,_FxName) + AND + GetTextEventParamString(2,_BoneName) + AND + GetTextEventParamInteger(3,-1) + THEN + PROC_LoopEffect(_FxName,_Player,"FxTest","__ANY__",_BoneName); + + /////// 0 or STOP to stop looping + + IF + TextEventSet("FxTest") + AND + CharacterGetHostCharacter((CHARACTERGUID)_Player) + AND + GetTextEventParamString(1,"Stop") + THEN + PROC_StopLoopEffect(_Player,"FxTest"); + + IF + TextEventSet("FxTestStop") + AND + CharacterGetHostCharacter((CHARACTERGUID)_Player) + THEN + PROC_StopLoopEffect(_Player,"FxTest"); + + //END_REGION + + //END_REGION + + //REGION AddTag OE + + IF + TextEventSet("AddTag") + AND + CharacterGetHostCharacter((CHARACTERGUID)_Player) + AND + GetTextEventParamString(1,_TagName) + THEN + SetTag(_Player,_TagName); + DebugText(_Player,_TagName); + + //END_REGION + + //REGION DrawWeaponforXTime + PROC + proc_DrawWeaponforXTime((CHARACTERGUID)_NPC,(INTEGER)_Time) + THEN + CharacterSetFightMode(_NPC,1,0); + ProcObjectTimer(_NPC,"GLO_DrawWeaponsForXTime",_Time); + + PROC + ProcObjectTimerFinished(_NPC,"GLO_DrawWeaponsForXTime") + AND + CharacterIsInCombat((CHARACTERGUID)_NPC,0) + THEN + CharacterSetFightMode(_NPC,0,0); + //END_REGION + + //REGION Conditional Object Flag Setter + PROC + Proc_SetConditionalObjectFlag((GUIDSTRING)_Object,(STRING)_Flag,(INTEGER)_Condition) + AND + _Condition == 0 + THEN + ObjectClearFlag(_Object,_Flag,0); + + PROC + Proc_SetConditionalObjectFlag((GUIDSTRING)_Object,(STRING)_Flag,(INTEGER)_Condition) + AND + _Condition != 0 + THEN + ObjectSetFlag(_Object,_Flag,0); + + QRY + QRY_IsEmptyDB((STRING)_DB,(INTEGER)_Size) + AND + SysCount(_DB,_Size,0) + THEN + DB_NOOP(1); + + QRY + QRY_IsInRange((GUIDSTRING)_Source,(GUIDSTRING)_Target,(REAL)_Range) + AND + GetDistanceTo(_Source,_Target,_Dist) + AND + _Dist <= _Range + THEN + DB_NOOP(1); + + QRY + Query_CharacterIsAliveAndNotInCombat((CHARACTERGUID)_Char) + AND + CharacterIsDead(_Char,0) + AND + CharacterIsInCombat(_Char,0) + THEN + DB_NOOP(1); + + //END_REGION + + //REGION Attribute checks + QRY + QRY_CheckPlayerStat((CHARACTERGUID)_Player,(STRING)_Stat,(INTEGER)_Value) + AND + CharacterGetAttribute(_Player,_Stat,_PlayerStat) + AND + _PlayerStat >= _Value + THEN + DB_NOOP(1); + + IF + DB_GLO_Attribute_Check_AgainstSpeaker((STRING)_Dialog,(INTEGER)_Index,(STRING)_Attribute,(STRING)_Difficulty,(INTEGER)_Source,(INTEGER)_Target) + THEN + PROC_Define_GLO_Attribute_Check_Internal(_Dialog,_Index,_Attribute,_Difficulty,_Source,_Target,0); + + IF + DB_GLO_Attribute_Check_AgainstLevel((STRING)_Dialog,(INTEGER)_Index,(STRING)_Attribute,(STRING)_Difficulty,(INTEGER)_Source,(INTEGER)_Level) + THEN + PROC_Define_GLO_Attribute_Check_Internal(_Dialog,_Index,_Attribute,_Difficulty,_Source,1,_Level); + + PROC + PROC_Define_GLO_Attribute_Check_Internal((STRING)_Dialog,(INTEGER)_Index,(STRING)_Attribute,(STRING)_Difficulty,(INTEGER)_Source,(INTEGER)_Target,(INTEGER)_Level) + AND + DB_GLO_Attribute_Check_Internal(_Dialog,_Index,_AnyAttribute,_AnyDifficulty,_AnySource,_AnyTarget,_AnyLevel) + THEN + NOT DB_GLO_Attribute_Check_Internal(_Dialog,_Index,_AnyAttribute,_AnyDifficulty,_AnySource,_AnyTarget,_AnyLevel); + + PROC + PROC_Define_GLO_Attribute_Check_Internal((STRING)_Dialog,(INTEGER)_Index,(STRING)_Attribute,(STRING)_Difficulty,(INTEGER)_Source,(INTEGER)_Target,(INTEGER)_Level) + THEN + DB_GLO_Attribute_Check_Internal(_Dialog,_Index,_Attribute,_Difficulty,_Source,_Target,_Level); + + PROC + Proc_DialogFlagSetup(_Dialog,_Speaker1) + AND + DB_GLO_Attribute_Check_Internal(_Dialog,_,_,_,_,_,_) + THEN + PROC_GLO_Attribute_Check_Setup(_Dialog); + + PROC + Proc_DialogFlagSetup(_Dialog,_Speaker1,_Speaker2) + AND + DB_GLO_Attribute_Check_Internal(_Dialog,_,_,_,_,_,_) + THEN + PROC_GLO_Attribute_Check_Setup(_Dialog); + + PROC + Proc_DialogFlagSetup(_Dialog,_Speaker1,_Speaker2,_Speaker3) + AND + DB_GLO_Attribute_Check_Internal(_Dialog,_,_,_,_,_,_) + THEN + PROC_GLO_Attribute_Check_Setup(_Dialog); + + PROC + Proc_DialogFlagSetup(_Dialog,_Speaker1,_Speaker2,_Speaker3,_Speaker4) + AND + DB_GLO_Attribute_Check_Internal(_Dialog,_,_,_,_,_,_) + THEN + PROC_GLO_Attribute_Check_Setup(_Dialog); + + PROC + Proc_DialogFlagSetup(_Dialog,_Speaker1,_Speaker2,_Speaker3,_Speaker4,_Speaker5) + AND + DB_GLO_Attribute_Check_Internal(_Dialog,_,_,_,_,_,_) + THEN + PROC_GLO_Attribute_Check_Setup(_Dialog); + + PROC + Proc_DialogFlagSetup(_Dialog,_Speaker1,_Speaker2,_Speaker3,_Speaker4,_Speaker5,_Speaker6) + AND + DB_GLO_Attribute_Check_Internal(_Dialog,_,_,_,_,_,_) + THEN + PROC_GLO_Attribute_Check_Setup(_Dialog); + + PROC + PROC_GLO_Attribute_Check_Setup((STRING)_Dialog) + AND + DB_GLO_Attribute_Check_Internal(_Dialog,_Index,_Attribute,_Difficulty,_Source,_Target,_Level) + AND + DB_GLO_AttributeCheck_Var(_Index,"Difficulty",_DifficultyVar) + AND + DB_GLO_AttributeCheck_Var(_Index,"LevelOverride",_LevelOverrideVar) + AND + DB_GLO_AttributeCheck_Var(_Index,"Name",_NameVar) + AND + DB_GLO_AttributeCheck_Var(_Index,"Source",_SourceVar) + AND + DB_GLO_AttributeCheck_Var(_Index,"Target",_TargetVar) + THEN + DialogSetVariableString(_Dialog,_DifficultyVar,_Difficulty); + DialogSetVariableInt(_Dialog,_LevelOverrideVar,_Level); + DialogSetVariableString(_Dialog,_NameVar,_Attribute); + DialogSetVariableInt(_Dialog,_SourceVar,_Source); + DialogSetVariableInt(_Dialog,_TargetVar,_Target); + + //END_REGION + + //REGION User Based Voice Bark + + IF + DB_OneShotUser_VoicebarkTrigger((TRIGGERGUID)_Trigger,_) + THEN + ProcTriggerRegisterForPlayers(_Trigger); + + IF + CharacterEnteredTrigger(_Player,_Trigger) + AND + DB_OneShotUser_VoicebarkTrigger((TRIGGERGUID)_Trigger,(STRING)_Bark) + AND + StringConcatenate("Commented_",_Bark,_Flag) + AND + UserGetFlag(_Player,_Flag,0) + THEN + UserSetFlag(_Player,_Flag); + StartVoiceBark(_Bark,_Player); + + //END_REGION + + PROC + Proc_TeleportSmoke((GUIDSTRING)_Object) + AND + GetPosition(_Object,_X,_Y,_Z) + THEN + PlayEffectAtPosition("RS3_FX_GP_ScriptedEvent_Teleport_GenericSmoke_01",_X,_Y,_Z); + + + //REGION + IF + DB_HideEquippedWeapon((CHARACTERGUID)_Char) + AND + CharacterGetEquippedItem(_Char,"Weapon",(ITEMGUID)_Weapon) + THEN + CharacterUnequipItem(_Char,_Weapon); + DB_HiddenEquippedWeapon(_Char,_Weapon); + + IF + DB_HideEquippedShield((CHARACTERGUID)_Char) + AND + CharacterGetEquippedItem(_Char,"Shield",(ITEMGUID)_Shield) + THEN + CharacterUnequipItem(_Char,_Shield); + DB_HideEquippedShield(_Char,_Shield); + + PROC + Proc_EquipHiddenWeapon((CHARACTERGUID)_Char) + AND + DB_HiddenEquippedWeapon(_Char,_Weapon) + THEN + CharacterEquipItem(_Char,_Weapon); + NOT DB_HiddenEquippedWeapon(_Char,_Weapon); + + PROC + Proc_EquipHiddenShield((CHARACTERGUID)_Char) + AND + DB_HideEquippedShield(_Char,_Shield) + THEN + CharacterEquipItem(_Char,_Shield); + NOT DB_HideEquippedShield(_Char,_Shield); + + //END_REGION + + //REGION Combat Cutscene - control whos turn it is + + IF + DB_CombatCutscene_PrioritizedObject(_PrioritiedObject) + THEN + JumpToTurn(_PrioritiedObject); + + PROC + Proc_CombatCutscene_PrioritizedObject_Purge() + AND + DB_CombatCutscene_PrioritizedObject(_PrioritiedObject) + THEN + NOT DB_CombatCutscene_PrioritizedObject(_PrioritiedObject); + + IF + DB_CombatCutscene_PrioritizedObject(_PrioritiedObject) + AND + DB_CombatCutscene_PrioritizedObject(_OtherObject) + AND + _OtherObject != _PrioritiedObject + THEN + NOT DB_CombatCutscene_PrioritizedObject(_OtherObject); + + IF + ObjectTurnStarted((CHARACTERGUID)_Combatant) + AND + DB_CombatCutscene_PrioritizedObject(_PrioritiedObject) + AND + ObjectIsCharacter(_PrioritiedObject,1) + AND + DB_CombatCharacters(_Combatant,_Id) + AND + _Combatant != _PrioritiedObject + AND + DB_CombatCharacters(_PrioritiedObject,_PId) + AND + _Id == _PId + THEN + JumpToTurn(_PrioritiedObject); + + IF + CharacterWentOnStage(_PrioritiedObject,0) + AND + ObjectIsCharacter(_PrioritiedObject,1) + AND + DB_CombatCutscene_PrioritizedObject(_PrioritiedObject) + THEN + NOT DB_CombatCutscene_PrioritizedObject(_PrioritiedObject); + + //END_REGION + + //REGION Remove Weapons + + PROC + Proc_RemoveWeapons((CHARACTERGUID)_Char) + AND + CharacterGetEquippedItem(_Char,"Weapon",(ITEMGUID)_Weapon) + THEN + ItemRemove(_Weapon); + + PROC + Proc_RemoveWeapons((CHARACTERGUID)_Char) + AND + CharacterGetEquippedItem(_Char,"Shield",(ITEMGUID)_Shield) + THEN + ItemRemove(_Shield); + + + + //END_REGION + + + //REGION + IF + CharacterKilledBy(_Defender,_AttackerOwner,_Attacker) + AND + DB_IsPlayer(_AttackerOwner) + AND + DB_KilledEvent(_Defender, (STRING)_Event) + THEN + UserSetFlag(_AttackerOwner,_Event,0); + + IF + CharacterDying(_Killed) + AND + DB_KilledEvent(_Killed, _Event) + AND + DB_CombatCharacters(_Killed, _ID) + AND + DB_IsPlayer(_Killer) + AND + DB_CombatCharacters(_Killer, _ID) + AND + QRY_GLO_KilledEvent_WasEnemy((CHARACTERGUID)_Killed,(CHARACTERGUID)_Killer, _ID) + THEN + UserSetFlag(_Killer, _Event, 0); + + QRY + QRY_GLO_KilledEvent_WasEnemy((CHARACTERGUID)_Killed, (CHARACTERGUID)_Killer, (INTEGER)_ID) + AND + DB_KilledEvent_HostileToTarget(_Killer, _Killed, _ID) + THEN + DB_NOOP(0); + + QRY + QRY_GLO_KilledEvent_WasEnemy((CHARACTERGUID)_Killed, (CHARACTERGUID)_Killer, (INTEGER)_ID) + AND + CharacterIsEnemy(_Killed, _Killer, 1) + THEN + DB_NOOP(0); + + IF + ObjectEnteredCombat((CHARACTERGUID)_NPC, _ID) + AND + DB_KilledEvent(_NPC, _) + AND + DB_IsPlayer(_Player) + AND + DB_CombatCharacters(_Player, _ID) + AND + CharacterIsEnemy(_Player, _NPC, 1) + THEN + DB_KilledEvent_HostileToTarget((CHARACTERGUID)_Player, (CHARACTERGUID)_NPC, _ID); + + IF + ObjectEnteredCombat((CHARACTERGUID)_Player, _ID) + AND + DB_IsPlayer(_Player) + AND + DB_KilledEvent(_NPC, _) + AND + DB_CombatCharacters(_Player, _ID) + AND + CharacterIsEnemy(_Player,(CHARACTERGUID)_NPC,1) + THEN + DB_KilledEvent_HostileToTarget((CHARACTERGUID)_Player, (CHARACTERGUID)_NPC, _ID); + + IF + CombatEnded(_ID) + AND + DB_KilledEvent_HostileToTarget(_Player, _NPC, _ID) + THEN + NOT DB_KilledEvent_HostileToTarget(_Player, _NPC, _ID); + //END_REGION + + + //REGION + //This will add characters within a trigger to the dialog + //first int, if 1, will also add character when the enter the trigger after the dialog started + + IF + DialogStarted(_Dialog,_Id) + AND + DB_AddCharactersInTriggerToDialog((STRING)_Dialog,(TRIGGERGUID)_Trigger,(INTEGER)_) + THEN + DB_AddCharactersInTriggerToDialog_ID(_Dialog,_Id); + + IF + DialogStarted(_Dialog,_Id) + AND + DB_AddCharactersInTriggerToDialog(_Dialog,_Trigger,_) + AND + DB_InRegion(_Char,_Trigger) + AND + DB_IsPlayer(_Char) + AND + QRY_SpeakerIsAvailable(_Char) + AND + DB_AddCharactersInTriggerToDialog_ID(_Dialog,_Id) + THEN + DialogAddCharacter(_Id,_Char); + + IF + CharacterEnteredTrigger(_Char,_Trigger) + AND + DB_AddCharactersInTriggerToDialog(_Dialog,_Trigger,1) + AND + DB_IsPlayer(_Char) + AND + DB_AddCharactersInTriggerToDialog_ID(_Dialog,_Id) + THEN + DialogAddCharacter(_Id,_Char); + + IF + DialogEnded(_Dialog,_Id) + AND + DB_AddCharactersInTriggerToDialog_ID(_Dialog,_Id) + THEN + NOT DB_AddCharactersInTriggerToDialog_ID(_Dialog,_Id); + //END_REGION + + //REGION Replace one-time use dialog flag into a proc call (for flags used to trigger actions) + PROC + Proc_OneTimeEventFlag((STRING)_Flag) + THEN + DB_NOOP(1); + + PROC + Proc_OneTimeEventFlag((GUIDSTRING)_Object,(STRING)_Flag) + THEN + DB_NOOP(1); + + PROC + Proc_OneTimeEventFlag((GUIDSTRING)_Object,(STRING)_Flag,(INTEGER)_DialogInst) + THEN + DB_NOOP(1); + + IF + ObjectFlagSet(_Flag,_Object,_DlgInst) + AND + DB_OneTimeEventFlag(_Flag) + THEN + ObjectClearFlag(_Object,_Flag); + Proc_OneTimeEventFlag(_Object,_Flag); + Proc_OneTimeEventFlag(_Object,_Flag,_DlgInst); + + IF + GlobalFlagSet(_Flag) + AND + DB_OneTimeEventFlag(_Flag) + THEN + GlobalClearFlag(_Flag); + Proc_OneTimeEventFlag(_Flag); + //END_REGION + + //REGION Generic Leave Through Portal (_Poof Variation) + + PROC + Proc_CreatePortalAndLeave((CHARACTERGUID)_Char,(STRING)_Event) + AND + GetPosition(_Char,_X,_Y,_Z) + AND + RealSum(_Y,1.25,_Y2) + AND + RealSum(_X,2.0,_X2) + AND + CreateItemTemplateAtPosition("Helper_Invisible_A_835c266c-2619-41a6-9591-50fc937bf97d",_X2,_Y2,_Z,(ITEMGUID)_Portal) + THEN + DB_GEN_CreatePortalAndLeave(_Char,_Portal,_Event); + CharacterLookAt(_Char,_Portal); + PlayAnimation(_Char,"cast_target_cast","GEN_CreatePortalAndLeave_CreatePortal"); + PROC_LoopEffect("RS3_FX_Skills_Voodoo_Cast_Hand_01",_Char,"GEN_CreatePortalAndLeave_Fx","__ANY__","Dummy_R_HandFX"); + PROC_LoopEffect("RS3_FX_GP_ScriptedEvent_DarkOrb_Summoning_01",_Char,"GEN_CreatePortalAndLeave_Fx","__ANY__",""); + ProcObjectTimer(_Char,"GEN_CreatePortalAndLeave_CreatePortal_Timer",1800); + + PROC + ProcObjectTimerFinished((CHARACTERGUID)_Char,"GEN_CreatePortalAndLeave_CreatePortal_Timer") + AND + GetRotation(_Char,_,_Yr,_) + AND + DB_GEN_CreatePortalAndLeave(_Char,(ITEMGUID)_Portal,_Event) + THEN + ItemRotateToAngleY(_Portal,_Yr,1000.0); + PROC_LoopEffect("RS3_FX_GP_ScriptedEvent_Portal_01",_Portal,"GEN_CreatePortalAndLeave","__ANY__",""); + + + IF + StoryEvent((CHARACTERGUID)_Char,"GEN_CreatePortalAndLeave_CreatePortal") + AND + DB_GEN_CreatePortalAndLeave(_Char,_Portal,_Event) + THEN + PROC_StopLoopEffect(_Char,"GEN_CreatePortalAndLeave_Fx"); + ProcObjectTimer(_Char,"GEN_CreatePortalAndLeave_MoveToDelay",800); + + PROC + ProcObjectTimerFinished((CHARACTERGUID)_Char,"GEN_CreatePortalAndLeave_MoveToDelay") + AND + DB_GEN_CreatePortalAndLeave(_Char,(ITEMGUID)_Portal,_Event) + THEN + CharacterMoveTo(_Char,_Portal,0,"GEN_CreatePortalAndLeave_Offstage",0); + + IF + StoryEvent((CHARACTERGUID)_Char,"GEN_CreatePortalAndLeave_Offstage") + AND + DB_GEN_CreatePortalAndLeave(_Char,(ITEMGUID)_Portal,_Event) + THEN + ProcObjectTimer(_Portal,"GEN_CreatePortalAndLeave_Offstage_PortalRemove",3500); + PROC_StopLoopEffect(_Portal,"GEN_CreatePortalAndLeave"); + SetStoryEvent(_Char,_Event); + NOT DB_GEN_CreatePortalAndLeave(_Char,_Portal,_Event); + + PROC + ProcObjectTimerFinished((ITEMGUID)_Portal,"GEN_CreatePortalAndLeave_Offstage_PortalRemove") + THEN + ItemRemove(_Portal); + + + IF + TextEventSet("GEN_CreatePortalAndLeave_Offstage") + AND + CharacterGetHostCharacter((CHARACTERGUID)_Player) + THEN + Proc_CreatePortalAndLeave(_Player,""); + + //END_REGION + + //REGION StartDialog With Visible Tagged Player + + PROC + PROC_StartDialog_WithVisibleTag((CHARACTERGUID)_NPC,(CHARACTERGUID)_Player,(STRING)_Dialog,(STRING)_MainTag,(STRING)_FallBackTag,(INTEGER)_AddOtherPlayersToDialog) + AND + NOT DB_PROC_StartDialogWithVisibleTag(1) + AND + IsTagged(_Player,_MainTag,1) + AND + QRY_SpeakerIsAvailableAndInDialogRange(_NPC,_Player) + THEN + Proc_StartDialog(0,_Dialog,_NPC,_Player); + DB_PROC_StartDialogWithVisibleTag(1); + + PROC + PROC_StartDialog_WithVisibleTag((CHARACTERGUID)_NPC,(CHARACTERGUID)_Player,(STRING)_Dialog,(STRING)_MainTag,(STRING)_FallBackTag,(INTEGER)_AddOtherPlayersToDialog) + AND + NOT DB_PROC_StartDialogWithVisibleTag(1) + AND + IsTagged(_Player,_MainTag,0) + AND + DB_IsPlayer(_OtherPlayers) + AND + IsTagged(_OtherPlayers,_MainTag,1) + AND + QRY_SpeakerIsAvailableAndInDialogRange(_NPC,_OtherPlayers) + THEN + Proc_StartDialog(0,_Dialog,_NPC,_OtherPlayers); + DB_PROC_StartDialogWithVisibleTag(1); + + PROC + PROC_StartDialog_WithVisibleTag((CHARACTERGUID)_NPC,(CHARACTERGUID)_Player,(STRING)_Dialog,(STRING)_MainTag,(STRING)_FallBackTag,(INTEGER)_AddOtherPlayersToDialog) + AND + NOT DB_PROC_StartDialogWithVisibleTag(1) + AND + IsTagged(_Player,_FallBackTag,1) + AND + QRY_SpeakerIsAvailableAndInDialogRange(_NPC,_Player) + THEN + Proc_StartDialog(0,_Dialog,_NPC,_Player); + DB_PROC_StartDialogWithVisibleTag(1); + + PROC + PROC_StartDialog_WithVisibleTag((CHARACTERGUID)_NPC,(CHARACTERGUID)_Player,(STRING)_Dialog,(STRING)_MainTag,(STRING)_FallBackTag,(INTEGER)_AddOtherPlayersToDialog) + AND + NOT DB_PROC_StartDialogWithVisibleTag(1) + AND + IsTagged(_Player,_FallBackTag,0) + AND + DB_IsPlayer(_OtherPlayers) + AND + IsTagged(_OtherPlayers,_FallBackTag,1) + AND + QRY_SpeakerIsAvailableAndInDialogRange(_NPC,_OtherPlayers) + THEN + Proc_StartDialog(0,_Dialog,_NPC,_OtherPlayers); + DB_PROC_StartDialogWithVisibleTag(1); + + PROC + PROC_StartDialog_WithVisibleTag((CHARACTERGUID)_NPC,(CHARACTERGUID)_Player,(STRING)_Dialog,(STRING)_MainTag,(STRING)_FallBackTag,(INTEGER)_AddOtherPlayersToDialog) + AND + NOT DB_PROC_StartDialogWithVisibleTag(1) + AND + QRY_SpeakerIsAvailableAndInDialogRange(_NPC,_Player) + THEN + Proc_StartDialog(0,_Dialog,_NPC,_Player); + DB_PROC_StartDialogWithVisibleTag(1); + + PROC + PROC_StartDialog_WithVisibleTag((CHARACTERGUID)_NPC,(CHARACTERGUID)_Player,(STRING)_Dialog,(STRING)_MainTag,(STRING)_FallBackTag,1) + AND + DB_PROC_StartDialogWithVisibleTag(1) + THEN + DB_PROC_StartDialog_WithVisibleTag_AddPlayers(_NPC,_Player,_Dialog); + + PROC + PROC_StartDialog_WithVisibleTag((CHARACTERGUID)_NPC,(CHARACTERGUID)_Player,(STRING)_Dialog,(STRING)_MainTag,(STRING)_FallBackTag,(INTEGER)_AddOtherPlayersToDialog) + AND + DB_PROC_StartDialogWithVisibleTag(1) + THEN + NOT DB_PROC_StartDialogWithVisibleTag(1); + + IF + DialogStarted(_Dialog,_ID) + AND + DB_PROC_StartDialog_WithVisibleTag_AddPlayers(_NPC,_Player,_Dialog) + AND + DB_IsPlayer(_Players) + AND + NOT DB_DialogPlayers(_ID,_Players,_) + AND + CharacterCanSee(_NPC,_Players,1) + AND + QRY_SpeakerIsAvailable(_Players) + THEN + DialogAddCharacter(_ID,_Players); + + + IF + DialogStarted(_Dialog,_ID) + AND + DB_PROC_StartDialog_WithVisibleTag_AddPlayers(_NPC,_Player,_Dialog) + THEN + NOT DB_PROC_StartDialog_WithVisibleTag_AddPlayers(_NPC,_Player,_Dialog); + + //END_REGION + + //REGION Repair / Loremaster traders + + IF + DB_LoremasterTrader((CHARACTERGUID)_Trader,(INTEGER)_Level) + THEN + CharacterAddAbility(_Trader,"Loremaster",_Level); + + IF + DB_RepairTrader((CHARACTERGUID)_Trader,(INTEGER)_Level) + THEN + CharacterAddAbility(_Trader,"Repair",_Level); + + //END_REGION + + //REGION Safe teleport for characters: teleport and flush/stop existing commands + PROC + PROC_Helper_SafeTeleportTo((CHARACTERGUID)_Char,(GUIDSTRING)_Dest) + THEN + PROC_Helper_SafeTeleportTo(_Char,_Dest,"",1); + + PROC + PROC_Helper_SafeTeleportTo((CHARACTERGUID)_Char,(GUIDSTRING)_Dest,(STRING)_Event,(INTEGER)_TeleportLinkedCharacters) + THEN + TeleportTo(_Char,_Dest,_Event,_TeleportLinkedCharacters); + CharacterFlushQueue(_Char); + LeaveCombat(_Char); + SetStoryEvent(_Char,"ClearPeaceReturn"); + // Clear ongoing animations + PlayAnimation(_Char,""); + + //END_REGION + + //REGION Teleport In/Out script helpers + + IF + StoryEvent((CHARACTERGUID)_Char,"GEN_Teleport_In") + THEN + SetOnStage(_Char,1); + PlayAnimation(_Char,"Teleport_In_01"); + + + IF + StoryEvent((CHARACTERGUID)_Char,"GEN_Teleport_Out") + THEN + PlayAnimation(_Char,"Teleport_Out_01","GEN_GoOffStage"); + + + //END_REGION + + + + //REGION Sign function + QRY + QRY_IntegerSign((INTEGER)_Value) + AND + DB_IntegerSign(_Result) + THEN + NOT DB_IntegerSign(_Result); + + QRY + QRY_IntegerSign((INTEGER)_Value) + AND + _Value < 0 + THEN + DB_IntegerSign(-1); + + QRY + QRY_IntegerSign((INTEGER)_Value) + AND + _Value == 0 + THEN + DB_IntegerSign(0); + + QRY + QRY_IntegerSign((INTEGER)_Value) + AND + _Value > 0 + THEN + DB_IntegerSign(0); + //END_REGION + + //REGION Waypoint + PROC + OpenWaypointUI((CHARACTERGUID)_Player,(STRING)_WaypointCurrent,(ITEMGUID)_Item) + THEN + OpenWaypointUI((CHARACTERGUID)_Player,_WaypointCurrent,_Item,0); + + PROC + OpenWaypointUIForFlee((CHARACTERGUID)_Player,(STRING)_WaypointCurrent,(ITEMGUID)_Item) + THEN + OpenWaypointUI((CHARACTERGUID)_Player,_WaypointCurrent,_Item,1); + //END_REGION Waypoint + + } + EXIT + { + + } +} +Goal(34).Title("_InitGoal"); +Goal(34) +{ + INIT + { + + } + KB + { + PROC + InitStory() + THEN + DB_StoryStarted(1); + GoalCompleted; + + } + EXIT + { + + } +} +Goal(35).Title("_NPC_Stats"); +Goal(35) +{ + INIT + { + + } + KB + { + + } + EXIT + { + + } +} +Goal(36).Title("_PlayerAlignments"); +Goal(36) +{ + INIT + { + + } + KB + { + PROC + ProcFixPlayerAlignments() + AND + DB_IsPlayer(_Player) + AND + NOT DB_PlayerAlignments_BlockFix(_Player) + AND + IsInArena(_Player,0) + AND + CharacterIsPlayer(_Player,1) + AND + DB_IsPlayer(_OtherPlayer) + AND + NOT DB_PlayerAlignments_BlockFix(_OtherPlayer) + AND + _Player!=_OtherPlayer + AND + IsInArena(_OtherPlayer,0) + AND + CharacterIsPlayer(_OtherPlayer,1) + AND + CharacterGetReservedUserID(_Player,_User) + AND + CharacterGetReservedUserID(_OtherPlayer,_OtherUser) + AND + GetUserProfileID(_User,_UserProfileID) + AND + GetUserProfileID(_OtherUser,_OtherUserProfileID) + THEN + ProcSetPlayerAlignment(_Player,_OtherPlayer,_UserProfileID,_OtherUserProfileID); + + PROC + ProcFixPlayerAlignments() + AND + DB_IsPlayer((CHARACTERGUID)_Player) + AND + NOT DB_PlayerAlignments_BlockFix(_Player) + AND + IsInArena(_Player,0) + AND + DB_IsPlayer((CHARACTERGUID)_OtherPlayer) + AND + NOT DB_PlayerAlignments_BlockFix(_OtherPlayer) + AND + _Player != _OtherPlayer + AND + IsInArena(_OtherPlayer,0) + AND + CharacterGetRelationToCharacter(_Player,_OtherPlayer,_Relation) + THEN + ProcLeavePartyIfRelationLow(_Player,_OtherPlayer,_Relation); + + PROC + ProcSetPlayerAlignment((CHARACTERGUID)_Player,(CHARACTERGUID)_OtherPlayer,(STRING)_User,(STRING)_OtherUser) + AND + DB_UserAlign(_User,_OtherUser,_Result) + THEN + CharacterSetRelationIndivFactionToIndivFaction(_Player,_OtherPlayer,_Result); + + PROC + ProcSetPlayerAlignment((CHARACTERGUID)_Player,(CHARACTERGUID)_OtherPlayer,(STRING)_User,(STRING)_OtherUser) + AND + NOT DB_UserAlign(_User,_OtherUser,_) + AND + CharacterIsInPartyWith(_Player,_OtherPlayer,1) + THEN + CharacterSetRelationIndivFactionToIndivFaction(_Player,_OtherPlayer,100); + + PROC + ProcSetPlayerAlignment((CHARACTERGUID)_Player,(CHARACTERGUID)_OtherPlayer,(STRING)_User,(STRING)_OtherUser) + AND + NOT DB_UserAlign(_User,_OtherUser,_) + AND + CharacterIsInPartyWith(_Player,_OtherPlayer,0) + THEN + CharacterSetRelationIndivFactionToIndivFaction(_Player,_OtherPlayer,50); + + PROC + ProcLeavePartyIfRelationLow((CHARACTERGUID)_Player,(CHARACTERGUID)_OtherPlayer,(INTEGER)_Relation) + AND + _Relation < 25 + AND + CharacterIsInPartyWith(_Player,_OtherPlayer,1) + AND + CharacterGetReservedUserID(_Player,_UserID) + AND + CharacterGetReservedUserID(_OtherPlayer,_OtherUserID) + AND + _UserID != _OtherUserID + THEN + LeaveParty(_OtherUserID); + + /*************************************************************************************** + Fix alignments when swapping characters between users or creating new players. + ****************************************************************************************/ + + IF + CharacterReservedUserIDChanged(_,_) + THEN + ProcFixPlayerAlignments(); + + IF + DB_IsPlayer(_) + THEN + ProcFixPlayerAlignments(); + + IF + GlobalFlagSet("SetupUserAlignments") + THEN + ProcFixPlayerAlignments(); + GlobalClearFlag("SetupUserAlignments"); + + IF + CharacterJoinedParty(_) + THEN + ProcFixPlayerAlignments(); + + /*************************************************************************************** + DB_UserAlign logic + ****************************************************************************************/ + + IF + GameStarted(_,_) + THEN + GlobalSetFlag("SetupUserAlignments"); + + IF + UserDisconnected(_User,_,_UserProfileID) + AND + DB_UserAlign(_UserProfileID,_OtherUser,_Result) + THEN + NOT DB_UserAlign(_UserProfileID,_OtherUser,_Result); + + IF + UserDisconnected(_User,_,_UserProfileID) + AND + DB_UserAlign(_OtherUser,_UserProfileID,_Result) + THEN + NOT DB_UserAlign(_OtherUser,_UserProfileID,_Result); + + IF + UserMakeWar(_Source,_Target,1) + AND + GetUserProfileID(_Source,_SourceProfileID) + AND + GetUserProfileID(_Target,_TargetProfileID) + THEN + ProcSetUserAlign(_SourceProfileID,_TargetProfileID,0); + ProcSetUserAlign(_TargetProfileID,_SourceProfileID,0); + ProcFixPlayerAlignments(); + + IF + UserMakeWar(_Source,_Target,0) + AND + GetUserProfileID(_Source,_SourceProfileID) + AND + GetUserProfileID(_Target,_TargetProfileID) + THEN + NOT DB_UserAlign(_SourceProfileID,_TargetProfileID,0); + NOT DB_UserAlign(_TargetProfileID,_SourceProfileID,0); + ProcFixPlayerAlignments(); + + PROC + ProcSetUserAlign((STRING)_SourceProfileID,(STRING)_TargetProfileID,(INTEGER)_NewResult) + AND + DB_UserAlign(_SourceProfileID,_TargetProfileID,_Result) + THEN + NOT DB_UserAlign(_SourceProfileID,_TargetProfileID,_Result); + + PROC + ProcSetUserAlign((STRING)_SourceProfileID,(STRING)_TargetProfileID,(INTEGER)_NewResult) + THEN + DB_UserAlign(_SourceProfileID,_TargetProfileID,_NewResult); + + /*************************************************************************************** + DB_UserAlign Debugging + ****************************************************************************************/ + + IF + TextEventSet("useralign") + AND + DB_UserAlign(_User,_OtherUser,_Result) + AND + DB_IsPlayer(_Player) + AND + StringConcatenate(_User," vs ",_Res) + AND + StringConcatenate(_Res,_OtherUser,_Res2) + AND + StringConcatenate(_Res2,": ",_Res3) + AND + IntegertoString(_Result,_IntStr) + AND + StringConcatenate(_Res3,_IntStr,_FinalResult) + THEN + DebugText(_Player,_FinalResult); + + } + EXIT + { + + } +} +Goal(37).Title("_PROC_AUDIO"); +Goal(37) +{ + INIT + { + //MoveToTrigger Audio Events + DB_MoveToTrigger_Materials("STONE"); + DB_MoveToTrigger_Materials("WOOD"); + DB_MoveToTrigger_Materials("METAL"); + + DB_CameraShakeAudioEventTrigger(800,5001); + + DB_DebugCameraShakerParser("500",500); + DB_DebugCameraShakerParser("1000",1000); + DB_DebugCameraShakerParser("3000",3000); + DB_DebugCameraShakerParser("5000",5000); + DB_DebugCameraShakerParser("8000",8000); + DB_DebugCameraShakerParser("13000",13000); + + + DB_DebugCameraShakerParser("2",2); + DB_DebugCameraShakerParser("5",5); + DB_DebugCameraShakerParser("1",1); + DB_DebugCameraShakerParser("25",25); + DB_DebugCameraShakerParser("60",60); + + } + KB + { + //REGION MoveToTrigger //Audio Play Sounds according to Material + + PROC + proc_ItemMoveToTrigger((ITEMGUID)_Item,(TRIGGERGUID)_Trigger,(REAL)_Speed,(REAL)_Acceleration,(INTEGER)_UseRotation) + THEN + ItemMoveToTrigger(_Item,_Trigger,_Speed,_Acceleration,_UseRotation); + + PROC + Proc_ItemMoveToPosition((ITEMGUID)_Item,(REAL)_X,(REAL)_Y,(REAL)_Z,(REAL)_Speed,(REAL)_Acceleration) + THEN + ItemMoveToPosition(_Item,_X,_Y,_Z,_Speed,_Acceleration); + + PROC + Proc_ItemMoveToPosition((ITEMGUID)_Item,(REAL)_PlusX,(REAL)_PlusY,(REAL)_PlusZ,(REAL)_Speed,(REAL)_Acceleration) + AND + NOT DB_MovingItem(_Item,_) + AND + DB_MoveToTrigger_Materials(_Tag) + AND + IsTagged(_Item,_Tag,1) + AND + StringConcatenate("Items_Puzzles_MovingObjects_",_Tag,_SoundEventPre) + AND + StringConcatenate(_SoundEventPre,"_Start",_SoundEvent) + THEN + DB_MovingItem(_Item,_Tag); + PlaySound(_Item,_SoundEvent); + DebugText(_Item,"PlayEvent_Move_Start"); + + + PROC + proc_ItemMoveToTrigger((ITEMGUID)_Item,(TRIGGERGUID)_Trigger,(REAL)_Speed,(REAL)_Acceleration,(INTEGER)_UseRotation) + AND + NOT DB_MovingItem(_Item,_) + AND + DB_MoveToTrigger_Materials(_Tag) + AND + IsTagged(_Item,_Tag,1) + AND + StringConcatenate("Items_Puzzles_MovingObjects_",_Tag,_SoundEventPre) + AND + StringConcatenate(_SoundEventPre,"_Start",_SoundEvent) + THEN + DB_MovingItem(_Item,_Tag); + PlaySound(_Item,_SoundEvent); + DebugText(_Item,"PlayEvent_Move_Start"); + + + IF + StoryEvent(_Item,"GEN_ItemMoved") + AND + NOT DB_MovingItem((ITEMGUID)_Item,_) + AND + DB_MoveToTrigger_Materials(_Tag) + AND + IsTagged(_Item,_Tag,1) + AND + StringConcatenate("Items_Puzzles_MovingObjects_",_Tag,_SoundEventPre) + AND + StringConcatenate(_SoundEventPre,"_Start",_SoundEvent) + THEN + DB_MovingItem(_Item,_Tag); + PlaySound(_Item,_SoundEvent); + DebugText(_Item,"PlayEvent_Move_Start"); + + + IF + ItemMoved(_Item) + AND + DB_MovingItem(_Item,_Tag) + AND + StringConcatenate("Items_Puzzles_MovingObjects_",_Tag,_SoundEventPre) + AND + StringConcatenate(_SoundEventPre,"_Stop",_SoundEvent) + THEN + NOT DB_MovingItem(_Item,_Tag); + PlaySound(_Item,_SoundEvent); + DebugText(_Item,"PlayEvent_Move_END"); + //END_REGION + + //REGION ScreenShake Audio Logic + + PROC + ProcObjectTimer(_Char,"Timer_LoopCameraShakeHelper",_Time) + AND + DB_CameraShakeAudioEventTrigger(_MinDuration,_) + AND + _Time > _MinDuration + THEN + PlaySound(_Char,"SE_GP_ScriptedEvent_CameraShake_Earth"); + + PROC + ProcObjectTimer(_Char,"Timer_LoopCameraShakeHelper",_Time) + AND + DB_CameraShakeAudioEventTrigger(_MinDuration,_MaxDurationToCallStop) + AND + _Time > _MinDuration + AND + _Time < _MaxDurationToCallStop + THEN + DB_Timer_LoopCameraShakeHelper_StopSound(_Char); + + PROC + ProcObjectTimerFinished(_Char,"Timer_LoopCameraShakeHelper") + AND + DB_Timer_LoopCameraShakeHelper_StopSound(_Char) + THEN + NOT DB_Timer_LoopCameraShakeHelper_StopSound(_Char); + PlaySound(_Char,"SE_GP_ScriptedEvent_CameraShake_Earth_Stop"); + + + IF + TextEventSet("TestCameraShakeAudio") + AND + GetTextEventParamString(1,_TimeString) + AND + DB_DebugCameraShakerParser(_TimeString,_TimerInt) + AND + DB_IsPlayer(_Player) + THEN + Proc_ShakeCameraForTime((CHARACTERGUID)_Player,_TimerInt); + + + IF + TextEventSet("TestCameraShake_AroundChar_Audio") + AND + GetTextEventParamString(1,_TimeString) + AND + DB_DebugCameraShakerParser(_TimeString,_TimerInt) + AND + DB_IsPlayer(_Player) + THEN + Proc_CameraShakeAroundCharacter(_Player,_TimerInt,6.0); + //END_REGION + + } + EXIT + { + + } +} +Goal(38).Title("_Sandbox"); +Goal(38) +{ + INIT + { + DB_Dialogs(CHARACTERGUID_Sandbox_Ship_Captain_dc36935c-4452-47af-8bab-f34c48754e13, CHARACTERGUID_Sandbox_Ship_Elisabeth_Fendrish_a7b94f10-865f-4ffd-8770-fce3973366ff, "Sandbox_Quest"); + + DB_QuestDef_State("SandboxQuest","Accept", 1); + DB_QuestDef_State("SandboxQuest","GoBack"); + DB_QuestDef_State("SandboxQuest","Finish", -1); + + ProcTriggerRegisterForPlayers(TRIGGERGUID_CaveQuestTrigger_fb308de8-145b-4749-8b92-db51ea2bc019); + + DB_QuestDef_QuestReward("SandboxQuest","Finish","QuestUpdate_SandboxQuest_Finish"); + + } + KB + { + IF + CharacterEnteredTrigger(_Player, TRIGGERGUID_CaveQuestTrigger_fb308de8-145b-4749-8b92-db51ea2bc019) + THEN + ObjectSetFlag(_Player, "QuestUpdate_SandboxQuest_GoBack"); + + } + EXIT + { + + } +} +Goal(39).Title("_Shroud"); +Goal(39) +{ + INIT + { + + } + KB + { + PROC + ProcDisableShroud() + AND + NOT DB_ShroudDisabled(1) + THEN + DB_ShroudDisabled(1); + ShroudRender(0); + + PROC + ProcEnableShroud() + AND + DB_ShroudDisabled(1) + THEN + NOT DB_ShroudDisabled(1); + ShroudRender(1); + + } + EXIT + { + + } +} +Goal(40).Title("_Story Npcs"); +Goal(40) +{ + INIT + { + + } + KB + { + PROC + SetStoryNpc((CHARACTERGUID)_Npc,(INTEGER)_State) + THEN + CharacterMakeStoryNpc(_Npc,_State); + + PROC + SetStoryNpc((CHARACTERGUID)_Npc,0) + THEN + NOT DB_IsStoryNpc(_Npc); + + PROC + SetStoryNpc((CHARACTERGUID)_Npc,1) + THEN + DB_IsStoryNpc(_Npc); + + PROC + MakeAttackable((CHARACTERGUID)_Player,(CHARACTERGUID)_Npc) + THEN + SetStoryNpc(_Npc,0); + CharacterSetTemporaryHostileRelation(_Npc,_Player); + CharacterAddAttitudeTowardsPlayer(_Npc,_Player,-100); + + // Default arg: + PROC + SetStoryNpc((CHARACTERGUID)_Npc) + THEN + SetStoryNpc(_Npc,1); + + PROC + SetStoryNpcStatus((CHARACTERGUID)_Npc) + AND + DB_IsStoryNpc(_Npc) + THEN + CharacterMakeStoryNpc(_Npc,1); + + PROC + SetStoryNpcStatus((CHARACTERGUID)_Npc) + AND + NOT DB_IsStoryNpc(_Npc) + THEN + CharacterMakeStoryNpc(_Npc,0); + + } + EXIT + { + + } +} +Goal(41).Title("_Trade"); +Goal(41) +{ + INIT + { + DB_DoubleAttitudePrice(1, 2); + DB_DoubleAttitudePrice(2, 2); + DB_DoubleAttitudePrice(3, 3); + DB_DoubleAttitudePrice(4, 3); + DB_DoubleAttitudePrice(5, 4); + DB_DoubleAttitudePrice(6, 5); + DB_DoubleAttitudePrice(7, 6); + DB_DoubleAttitudePrice(8, 7); + DB_DoubleAttitudePrice(9, 12); + DB_DoubleAttitudePrice(10, 15); + DB_DoubleAttitudePrice(11, 20); + DB_DoubleAttitudePrice(12, 25); + DB_DoubleAttitudePrice(13, 30); + DB_DoubleAttitudePrice(14, 35); + DB_DoubleAttitudePrice(15, 40); + DB_DoubleAttitudePrice(16, 60); + DB_DoubleAttitudePrice(17, 65); + DB_DoubleAttitudePrice(18, 75); + DB_DoubleAttitudePrice(19, 85); + DB_DoubleAttitudePrice(20, 95); + DB_DoubleAttitudePriceOverTwenty(100); + + } + KB + { + //REGION Creating and resetting TraderTreasure + PROC + GenTradeItems((CHARACTERGUID)_Player,(CHARACTERGUID)_Trader) + AND + NOT DB_TraderGeneratedTreasureForLevel(_Trader,_) + THEN + DB_TraderGeneratedTreasureForLevel(_Trader,0); + + PROC + GenTradeItems((CHARACTERGUID)_Player,(CHARACTERGUID)_Trader) + AND + DB_TraderGeneratedTreasureForLevel(_Trader,_OldLevel) + AND + CharacterGetLevel(_Player,_NewLevel) + AND + _NewLevel > _OldLevel + THEN + NOT DB_TraderGeneratedTreasureForLevel(_Trader,_OldLevel); + DB_TraderGeneratedTreasureForLevel(_Trader,_NewLevel); + DoGenTradeItems(_Player,_Trader); + + PROC + ProcClearTradeFacts((CHARACTERGUID)_Trader) + AND + DB_TraderGeneratedTreasureForLevel(_Trader,_OldLevel) + THEN + NOT DB_TraderGeneratedTreasureForLevel(_Trader,_OldLevel); + + PROC + GenTradeItems((CHARACTERGUID)_Player,(CHARACTERGUID)_Trader) + AND + DB_LastTradeItemGeneration(_Trader,(INTEGER)_THLastGen) + AND + _THLastGen != 0 + AND // check if last generation was long enough ago: + DB_Time(_,_,_TH) + AND + IntegerSubtract(_TH,_THLastGen,_Delta) + AND + _Delta >= 12 + THEN + DoGenTradeItems(_Player,_Trader); + + // If not yet an inventory generated for this npc, do it: + PROC + GenTradeItems((CHARACTERGUID)_Player,(CHARACTERGUID)_Npc) + AND + DB_LastTradeItemGeneration(_Npc,0) // not yet generated + THEN + DoGenTradeItems(_Player,_Npc); + + // + PROC + DoGenTradeItems((CHARACTERGUID)_Player,(CHARACTERGUID)_Npc) + AND + DB_LastTradeItemGeneration(_Npc,_THLastGen) + THEN + NOT DB_LastTradeItemGeneration(_Npc,_THLastGen); + + // + PROC + DoGenTradeItems((CHARACTERGUID)_Player,(CHARACTERGUID)_Npc) + AND + DB_Time(_,_,_TH) + THEN + ProcGenerateTradeTreasure(_Player,_Npc); + DB_LastTradeItemGeneration(_Npc,_TH); + + PROC + ProcClearGeneratedItems((CHARACTERGUID)_Npc) + AND + NOT DB_ItemsCleared(_Npc) + THEN + DB_ItemsCleared(_Npc); + CharacterClearTradeGeneratedItems(_Npc); + + IF + DB_CustomTradeTreasure(_Npc,_Treasure) + THEN + ProcClearLastTradeTime(_Npc); + + PROC + ProcClearLastTradeTime((CHARACTERGUID)_Trader) + AND + DB_LastTradeItemGeneration(_Trader,(INTEGER)_THLastGen) + THEN + NOT DB_LastTradeItemGeneration(_Trader,_THLastGen); + DB_LastTradeItemGeneration(_Trader,0); + + PROC + ProcGenerateTradeTreasure((CHARACTERGUID)_Player,(CHARACTERGUID)_Npc) + AND + DB_CustomTradeTreasure((CHARACTERGUID)_Npc,(STRING)_Treasure) + THEN + ProcClearGeneratedItems(_Npc); + DB_CharacterGenerateCustomTradeTreasure(_Player,_Npc,_Treasure); + DB_TreasureGenerated(1); + + PROC + ProcGenerateTradeTreasure((CHARACTERGUID)_Player,(CHARACTERGUID)_Npc) + AND + NOT DB_TreasureGenerated(1) + THEN + GenerateItems(_Player,_Npc); + + PROC + ProcGenerateTradeTreasure((CHARACTERGUID)_Player,(CHARACTERGUID)_Npc) + THEN + NOT DB_TreasureGenerated(1); + NOT DB_ItemsCleared(_Npc); + //END_REGION + + //REGION Start Trade + // + // StartTrade: set all facts and startup the trade window: + // + PROC + StartTrade((CHARACTERGUID)_Player,(CHARACTERGUID)_Npc) + THEN + GenTradeItems(_Player,_Npc); // regenerate items (clear if _Npc.DB_IsHostile()) + ResetInsults(_Player,_Npc); + + PROC + StartTrade((CHARACTERGUID)_Player,(CHARACTERGUID)_Npc) + THEN + DoStartTrade_1(_Player,_Npc); + + PROC + DoStartTrade_1((CHARACTERGUID)_Player,(CHARACTERGUID)_Npc) + AND + DB_NoRepair(_Npc) + AND + DB_NoIdentify(_Npc) + THEN + ActivateTrade(_Player,_Npc,0,0,1); + + PROC + DoStartTrade_1((CHARACTERGUID)_Player,(CHARACTERGUID)_Npc) + AND + NOT DB_NoRepair(_Npc) + AND + DB_NoIdentify(_Npc) + THEN + ActivateTrade(_Player,_Npc,1,0,1); + + PROC + DoStartTrade_1((CHARACTERGUID)_Player,(CHARACTERGUID)_Npc) + AND + DB_NoRepair(_Npc) + AND + NOT DB_NoIdentify(_Npc) + THEN + ActivateTrade(_Player,_Npc,0,1,1); + + PROC + DoStartTrade_1((CHARACTERGUID)_Player,(CHARACTERGUID)_Npc) + AND + NOT DB_NoRepair(_Npc) + AND + NOT DB_NoIdentify(_Npc) + THEN + ActivateTrade(_Player,_Npc,1,1,1); + + IF + RequestTrade(_Player,_Npc) + THEN + StartTrade(_Player,_Npc); + + PROC + ResetInsults((CHARACTERGUID)_Player,(CHARACTERGUID)_Npc) + AND + DB_InsultCounter(_Player, _Npc, _Counter) + THEN + NOT DB_InsultCounter(_Player, _Npc, _Counter); + DB_InsultCounter(_Player, _Npc, 0); + + PROC + ResetInsults((CHARACTERGUID)_Player,(CHARACTERGUID)_Npc) + AND + NOT DB_InsultCounter(_Player, _Npc, _) + THEN + DB_InsultCounter(_Player, _Npc, 0); + + //END_REGION + + //REGION Finish trade and adjust attitude if necessary + IF + HappyWithDeal(_Player,_Npc,_ValuePlayer,_ValueNpc) + AND + QRY_GetAttitudeChangeForTrade(_Player,_Npc,_ValuePlayer,_ValueNpc) + AND + DB_AttitudeAdjustMent(_Player,_Npc,_Att) + AND + _Att != 0 + THEN + CharacterAddAttitudeTowardsPlayer(_Npc,_Player,_Att); + + IF + HappyWithDeal(_Player,_Npc,_ValuePlayer,_ValueNpc) + AND + _ValuePlayer >= _ValueNpc + AND + DB_AttitudeAdjustMent(_Player,_Npc,_Att) + THEN + ExecuteDeal(_Player,1,_Att); + + IF + HappyWithDeal(_Player,_Npc,_ValuePlayer,_ValueNpc) + AND + _ValuePlayer < _ValueNpc + AND + DB_AttitudeAdjustMent(_Player,_Npc,_Att) + AND + DB_InsultCounter(_Player,_Npc,_Counter) + AND + IntegerSum(_Counter,1,_NewCounter) + THEN + ExecuteDeal(_Player,0,_Att); + NOT DB_InsultCounter(_Player,_Npc,_Counter); + DB_InsultCounter(_Player,_Npc,_NewCounter); + + QRY + QRY_GetAttitudeChangeForTrade((CHARACTERGUID)_Player,(CHARACTERGUID)_Npc,(INTEGER)_ValuePlayer,(INTEGER)_ValueNpc) + AND + NOT DB_TempTradeBalance(_Player,_Npc,_) + THEN + DB_TempTradeBalance(_Player,_Npc,0); + + QRY + QRY_GetAttitudeChangeForTrade((CHARACTERGUID)_Player,(CHARACTERGUID)_Npc,(INTEGER)_ValuePlayer,(INTEGER)_ValueNpc) + AND + DB_AttitudeAdjustMent(_Player,_Npc,_Att) + THEN + NOT DB_AttitudeAdjustMent(_Player,_Npc,_Att); + + QRY + QRY_GetAttitudeChangeForTrade((CHARACTERGUID)_Player,(CHARACTERGUID)_Npc,(INTEGER)_ValuePlayer,(INTEGER)_ValueNpc) + AND + IntegerSubtract(_ValuePlayer,_ValueNPC,_Delta) + AND + DB_TempTradeBalance(_Player,_Npc,_OldBalance) + AND + IntegerSum(_Delta,_OldBalance,_NewBalance) + AND + CharacterGetLevel(_Npc,_NpcLevel) + AND + _NpcLevel <= 20 + AND + DB_DoubleAttitudePrice(_NpcLevel,_DoublePrice)// Working with doubles to allow us to work with halves in integer envirnoment + AND + IntegerSum(_NewBalance,_NewBalance,_DoubleBalance) + AND + IntegerDivide(_DoubleBalance,_DoublePrice,_Q) // 1 Attitude point costs (L+1)/2 + AND + IntegerProduct(_Q,_DoublePrice,_DoubleSubtracted) + AND + IntegerSum(_DoubleSubtracted,1,_DoubleSubtractedIncremented) + AND + IntegerDivide(_DoubleSubtractedIncremented,2,_TotalSubtracted) + AND + IntegerSubtract(_NewBalance,_TotalSubtracted,_Remainder) + THEN + NOT DB_TempTradeBalance(_Player,_Npc,_OldBalance); + DB_TempTradeBalance(_Player,_Npc,_Remainder); + DB_AttitudeAdjustMent(_Player,_Npc,_Q); + + QRY + QRY_GetAttitudeChangeForTrade((CHARACTERGUID)_Player,(CHARACTERGUID)_Npc,(INTEGER)_ValuePlayer,(INTEGER)_ValueNpc) + AND + IntegerSubtract(_ValuePlayer,_ValueNPC,_Delta) + AND + DB_TempTradeBalance(_Player,_Npc,_OldBalance) + AND + IntegerSum(_Delta,_OldBalance,_NewBalance) + AND + CharacterGetLevel(_Npc,_NpcLevel) + AND + _NpcLevel > 20 + AND + DB_DoubleAttitudePriceOverTwenty(_DoublePrice)// Working with doubles to allow us to work with halves in integer envirnoment + AND + IntegerSum(_NewBalance,_NewBalance,_DoubleBalance) + AND + IntegerDivide(_DoubleBalance,_DoublePrice,_Q) // 1 Attitude point costs (L+1)/2 + AND + IntegerProduct(_Q,_DoublePrice,_DoubleSubtracted) + AND + IntegerSum(_DoubleSubtracted,1,_DoubleSubtractedIncremented) + AND + IntegerDivide(_DoubleSubtractedIncremented,2,_TotalSubtracted) + AND + IntegerSubtract(_NewBalance,_TotalSubtracted,_Remainder) + THEN + NOT DB_TempTradeBalance(_Player,_Npc,_OldBalance); + DB_TempTradeBalance(_Player,_Npc,_Remainder); + DB_AttitudeAdjustMent(_Player,_Npc,_Q); + + + IF + DB_AttitudeAdjustMent(_Player,_Npc,_Q) + AND + _Q < 0 + AND + DB_InsultCounter(_Player, _Npc, _InsultCount) + AND + _InsultCount < 2 // Player can insult trader with the offer 2 times before Attitude reduction kicks in + AND + IntegerSum(_InsultCount, 1, _NewInsultCount) + THEN + NOT DB_AttitudeAdjustMent(_Player,_Npc,_Q); + DB_AttitudeAdjustMent(_Player,_Npc,0); + + IF + DB_AttitudeAdjustMent(_Player,_Npc,_Q) + AND + _Q < 0 + AND + DB_InsultCounter(_Player, _Npc, _InsultCount) + AND + _InsultCount >= 2 + AND + _Q != -15 + AND + CharacterGetAttitudeTowardsPlayer(_Npc,_Player,_OldAtt) + AND + IntegerSum(_OldAtt,_Q,_NewAtt) + AND + _NewAtt != -45 + THEN + NOT DB_AttitudeAdjustMent(_Player,_Npc,_Q); + DB_AttitudeAdjustMent(_Player,_Npc,-15); + + QRY + QRY_GetAttitudeChangeForTrade((CHARACTERGUID)_Player,(CHARACTERGUID)_Npc,(INTEGER)_ValuePlayer,(INTEGER)_ValueNpc) + AND + CharacterGetAttitudeTowardsPlayer(_Npc,_Player,_OldAtt) + AND + DB_AttitudeAdjustMent(_Player,_Npc,_OldDelta) + AND + IntegerSum(_OldAtt,_OldDelta,_NewAtt) + AND + _NewAtt < -45 + AND + IntegerSubtract(-45,_OldAtt,_NewDelta) + THEN + NOT DB_AttitudeAdjustMent(_Player,_Npc,_OldDelta); + DB_AttitudeAdjustMent(_Player,_Npc,_NewDelta); + //END_REGION + + //Start Trade via Event (instead of button click in dialog) + IF + ObjectFlagSet("StartTrade",(CHARACTERGUID)_Player,_Instance) + AND + DB_DialogNPCs(_Instance,_Npc,1) + THEN + StartTrade((CHARACTERGUID)_Player,(CHARACTERGUID)_Npc); + ObjectClearFlag(_Player,"StartTrade",_Instance); + + //REGION Manual Trade Toggling Per Player + + PROC + Proc_DialogFlagSetup(_,(GUIDSTRING)_NPC,(GUIDSTRING)_Player) + AND + DB_ManualTradeNPC(_NPC) + AND + DB_PreventTradeBetween(_NPC,_Player) + THEN + CharacterSetCanTrade((CHARACTERGUID)_NPC,0); + + PROC + Proc_DialogFlagSetup(_,(GUIDSTRING)_NPC,(GUIDSTRING)_Player) + AND + IsTagged(_NPC,"GHOST",1) + THEN + CharacterSetCanTrade((CHARACTERGUID)_NPC,0); + + IF + ObjectFlagSet("PreventTradeWithPlayer",(GUIDSTRING)_NPC,_ID) + AND + DialogGetInvolvedPlayer(_ID,1,(CHARACTERGUID)_Player) + THEN + DB_ManualTradeNPC(_NPC); + DB_PreventTradeBetween(_NPC,_Player); + ObjectClearFlag(_NPC,"PreventTradeWithPlayer",_ID); + + PROC + Proc_DialogFlagSetup(_,(GUIDSTRING)_NPC,(GUIDSTRING)_Player) + AND + DB_ManualTradeNPC(_NPC) + AND + NOT DB_PreventTradeBetween(_NPC,_Player) + THEN + CharacterSetCanTrade((CHARACTERGUID)_NPC,1); + //END_REGION + + } + EXIT + { + + } +} +Goal(42).Title("_TrapReactions"); +Goal(42) +{ + INIT + { + DB_Event2DisplayText("HiddenPerceptionReveal","GEN_AD_HiddenPerceptionReveal"); + DB_Event2DisplayText("HiddenTrapReveal","GEN_AD_HiddenTrapReveal"); + DB_Event2DisplayText("FakePerceptionReveal","GEN_AD_FakePerceptionReveal"); + DB_Event2DisplayText("AmbushReveal","GLO_AD_AmbushDetected"); + + } + KB + { + IF + StoryEvent((CHARACTERGUID)_Player,_Event) + AND + NOT DB_BlockTextSpam(_Player) + AND + _Player.DB_IsPlayer() + AND + DB_CustomEvent2DisplayText((CHARACTERGUID)_Player,(STRING)_Event,(STRING)_Text) + AND + QRY_SpeakerIsAvailable(_Player) + THEN + Proc_StartDialog(1,_Text, _Player); + DB_BlockTextSpam(_Player); + ProcObjectTimer(_Player,"BlockTextSpam",5000); + + IF + StoryEvent((CHARACTERGUID)_Player,_Event) + AND + NOT DB_BlockTextSpam(_Player) + AND + _Player.DB_IsPlayer() + AND + DB_Event2DisplayText(_Event,_Text) + AND + QRY_SpeakerIsAvailable(_Player) + THEN + Proc_StartDialog(1,_Text, _Player); + DB_BlockTextSpam(_Player); + ProcObjectTimer(_Player,"BlockTextSpam",5000); + + PROC + ProcObjectTimerFinished(_Player,"BlockTextSpam") + THEN + NOT DB_BlockTextSpam((CHARACTERGUID)_Player); + + + + } + EXIT + { + + } +} +Goal(43).Title("_Waypoints"); +Goal(43) +{ + INIT + { + + } + KB + { + IF + CharacterTeleportToWaypoint(_Char,_Trigger) + AND + GetPosition(_Trigger,_X,_Y,_Z) + THEN + TeleportToPosition(_Char,_X,_Y,_Z,"",1,1); + + IF + CharacterTeleportToFleeWaypoint(_Char,_Trigger) + AND + GetPosition(_Trigger,_X,_Y,_Z) + THEN + TeleportToPosition(_Char,_X,_Y,_Z,"",0,1); + + IF + DB_WaypointInfo(_Item,_Trigger,_CurrentWP) + THEN + RegisterWaypoint(_CurrentWP,_Item); + + IF + CharacterItemEvent(_Player,(ITEMGUID)_Item,"WaypointDiscovered") + AND + DB_WaypointInfo(_Item,_,_) + THEN + ProcDoWaypointUnlock(_Item, _Player); + + IF + CharacterItemEvent(_, (ITEMGUID)_Item,"WaypointDiscovered") + THEN + ShowNotification("GLO_WaypointDiscovered"); + + IF + CharacterUsedItem(_Player,_Item) + AND + DB_WaypointInfo(_Item,_Trigger,_CurrentWP) + AND + NOT DB_BlockWaypointUsage(_Player) + THEN + ProcDoWaypointUnlock(_Item,_Player); + + //Check for UI blocker + IF + CharacterUsedItem(_Player,_Item) + AND + DB_WaypointInfo(_Item,_Trigger,_CurrentWP) + AND + NOT DB_BlockWaypointUsage(_Player) + AND + NOT DB_WaypointBlockUI(_Item) + AND + NOT DB_Dialogs(_Item,_) + AND + NOT DB_IsSign(_Item,(STRING)_,(STRING)_) + THEN + OpenWaypointUI(_Player,_CurrentWP,_Item); + + IF + TextEventSet("wp") + THEN + PROC_UnlockAllWP(); + ProcTestOpenWaypointUI(); + + PROC + ProcTestOpenWaypointUI() + AND + _Player.DB_IsPlayer() + AND + NOT DB_OnlyOnce("openedWPUI") + THEN + DB_OnlyOnce("openedWPUI"); + OpenWaypointUI(_Player,"",NULL_00000000-0000-0000-0000-000000000000); + + + PROC + ProcTestOpenWaypointUI() + THEN + NOT DB_OnlyOnce("openedWPUI"); + + PROC + PROC_UnlockAllWP() + AND + DB_WaypointInfo(_,_Trigger,_CurrentWP) + THEN + UnlockWaypoint(_CurrentWP,_Trigger,NULL_00000000-0000-0000-0000-000000000000); + + PROC + ProcDoWaypointUnlock((ITEMGUID)_Item,(CHARACTERGUID)_Player) + AND + DB_WaypointInfo(_Item,_Trigger,_CurrentWP) + THEN + UnlockWaypoint(_CurrentWP,_Trigger,_Player); + + PROC + ProcDoWaypointUnlock(_,_) + AND + DB_IsPlayer(_OtherPlayer) + AND + NOT DB_NumUnlockedWaypoints(_OtherPlayer,_) + THEN + DB_NumUnlockedWaypoints(_OtherPlayer,0); + + PROC + ProcDoWaypointUnlock((ITEMGUID)_Item,(CHARACTERGUID)_Player) + AND + DB_IsPlayer(_OtherPlayer) + AND + NOT DB_UnlockedWaypoint(_Item,_OtherPlayer) + AND + CharacterIsInPartyWith(_OtherPlayer,_Player,1) + AND + DB_NumUnlockedWaypoints(_OtherPlayer,_OldCount) + AND + IntegerSum(_OldCount,1,_NewCount) + THEN + DB_UnlockedWaypoint(_Item,_OtherPlayer); + NOT DB_NumUnlockedWaypoints(_OtherPlayer,_OldCount); + DB_NumUnlockedWaypoints(_OtherPlayer,_NewCount); + + + IF + ObjectFlagSet("OpenWaypointUI",_Player,_ID) + AND + DB_DialogNPCs(_ID,_Item,1) + AND + DB_WaypointInfo((ITEMGUID)_Item,_Trigger,_CurrentWP) + THEN + OpenWaypointUI((CHARACTERGUID)_Player,_CurrentWP,_Item); + ObjectClearFlag(_Player,"OpenWaypointUI",_ID); + PROC_CheckPlayTut(_Player,"TUT_Waypoint"); + + } + EXIT + { + + } +} +Goal(44).Title("GLO_BidirShovelTunnel"); +Goal(44) +{ + INIT + { + // Support for a bidirectional tunnel that can be opened up from either side by shovelling + // Usage: + // DB_BidirShovelPileTunnel(IDString,Side1ShovelTrigger,Side1ShovelPileObject,Side1TunnelEntranceObject,Side2ShovelTrigger,Side2ShovelPileObject,Side2TunnelEntranceObject); + // + // As soon as one side is opened up, so is the other side. Can't easily use simply two shovel + // piles with the same ID, because then the dirt pile on the other side won't be removed. Also + // avoids conflicts in case people shovel on both sides at the same time. + // + // When the tunnel is opened on side 1/2, ProcBidirShovelTunnelOpenedOnSide(1/2) is called + // (only one of these will be called per tunnel) + + } + KB + { + //REGION Convert tunnel into two shovel areas + IF + DB_BidirShovelPileTunnel((STRING)_IDString,(TRIGGERGUID)_Side1ShovelTrigger,(ITEMGUID)_Side1ShovelPileObject,(ITEMGUID)_Side1TunnelEntranceObject,(TRIGGERGUID)_Side2ShovelTrigger,(ITEMGUID)_Side2ShovelPileObject,(ITEMGUID)_Side2TunnelEntranceObject) + AND + StringConcatenate(_IDString,"___side1",_Side1IDString) + AND + StringConcatenate(_IDString,"___side2",_Side2IDString) + THEN + DB_BidirShovelTunnelExit(_IDString,_Side1IDString); + DB_BidirShovelTunnelExit(_IDString,_Side2IDString); + DB_ShovelArea((TRIGGERGUID)_Side1ShovelTrigger,(STRING)_Side1IDString,(ITEMGUID)_Side1ShovelPileObject); + DB_ShovelArea((TRIGGERGUID)_Side2ShovelTrigger,(STRING)_Side2IDString,(ITEMGUID)_Side2ShovelPileObject); + DB_ShovelRewardItemSpawn((STRING)_Side1IDString,(ITEMGUID)_Side1TunnelEntranceObject); + DB_ShovelRewardItemSpawn((STRING)_Side2IDString,(ITEMGUID)_Side2TunnelEntranceObject); + //END_REGION + + //REGION User hooks + PROC + ProcBidirShovelTunnelOpenedOnSide((CHARACTERGUID)_,(STRING)_,(INTEGER)_) + THEN + DB_NOOP(1); + + PROC + ProcShovelRewards(_Player,_ThisSideIDString) + AND + DB_BidirShovelTunnelExit(_IDString,_ThisSideIDString) + AND + StringContains(_ThisSideIDString,"__side1",1) + THEN + ProcBidirShovelTunnelOpenedOnSide(_Player,_IDString,1); + + PROC + ProcShovelRewards(_Player,_ThisSideIDString) + AND + DB_BidirShovelTunnelExit(_IDString,_ThisSideIDString) + AND + StringContains(_ThisSideIDString,"__side2",1) + THEN + ProcBidirShovelTunnelOpenedOnSide(_Player,_IDString,2); + //END_REGION + + //REGION If one side is opened up, open the other one too + PROC + PROC_ShovelTunnelOpenUpOtherSide((CHARACTERGUID)_Player, (STRING)_IDString) + AND + DB_BidirShovelTunnelExit(_IDString,_OtherSideIDString) + AND + DB_ShovelArea(_OtherTrigger,_OtherSideIDString,_OtherPileObject) + AND + // avoid issues if someone is digging up the other side at the same time + NOT DB_Shovelling_Mound(_,_OtherPileObject) + THEN + NOT DB_BidirShovelTunnelExit(_IDString,_OtherSideIDString); + SetOnStage(_OtherPileObject,0); + NOT DB_ShovelArea(_OtherTrigger,_OtherSideIDString,_OtherPileObject); + // Won't trigger code below again, because all DB_BidirShovelTunnelExit() for this _IDString have been cleared + ProcShovelRewards(_Player,_OtherSideIDString); + + PROC + ProcShovelRewards(_Player,_ThisSideIDString) + AND + DB_BidirShovelTunnelExit(_IDString,_ThisSideIDString) + AND + DB_BidirShovelPileTunnel(_IDString,_Side1ShovelTrigger,_Side1ShovelPileObject,_Side1TunnelEntranceObject,_Side2ShovelTrigger,_Side2ShovelPileObject,_Side2TunnelEntranceObject) + THEN + NOT DB_BidirShovelTunnelExit(_IDString,_ThisSideIDString); + // Only DB_BidirShovelTunnelExit(_IDString,_) definition left now is for the other side + PROC_ShovelTunnelOpenUpOtherSide(_Player,_IDString); + NOT DB_BidirShovelPileTunnel(_IDString,_Side1ShovelTrigger,_Side1ShovelPileObject,_Side1TunnelEntranceObject,_Side2ShovelTrigger,_Side2ShovelPileObject,_Side2TunnelEntranceObject); + + //END_REGION + + } + EXIT + { + + } +} +Goal(45).Title("GLO_Resting"); +Goal(45) +{ + INIT + { + DB_RestTemplates("FUR_Humans_Camping_Sleepingbag_B_4d7216c9-c21e-4ab0-b98e-97d744798912","STORY_PartyRest",10.0,17.0); + + } + KB + { + IF + CharacterUsedItemTemplate(_Character,_Temp,_) + AND + DB_RestTemplates(_Temp,_Consume,_PartyRadius,_SafeRadius) + THEN + UserRest(_Character,_Consume,_PartyRadius,_SafeRadius); + + } + EXIT + { + + } +} +Goal(46).Title("GLO_SavegamePatchHelpers"); +Goal(46) +{ + INIT + { + + } + KB + { + QRY + QRY_VersionIsOlderThan((INTEGER)_Major,(INTEGER)_Minor,(INTEGER)_Rev,(INTEGER)_Build,(INTEGER)_CompMajor,(INTEGER)_CompMinor,(INTEGER)_CompRev,(INTEGER)_CompBuild) + AND + _Major < _CompMajor + THEN + DB_Noop(1); + + QRY + QRY_VersionIsOlderThan((INTEGER)_Major,(INTEGER)_Minor,(INTEGER)_Rev,(INTEGER)_Build,(INTEGER)_CompMajor,(INTEGER)_CompMinor,(INTEGER)_CompRev,(INTEGER)_CompBuild) + AND + _Major == _CompMajor + AND + _Minor < _CompMinor + THEN + DB_Noop(1); + + QRY + QRY_VersionIsOlderThan((INTEGER)_Major,(INTEGER)_Minor,(INTEGER)_Rev,(INTEGER)_Build,(INTEGER)_CompMajor,(INTEGER)_CompMinor,(INTEGER)_CompRev,(INTEGER)_CompBuild) + AND + _Major == _CompMajor + AND + _Minor == _CompMinor + AND + _Rev < _CompRev + THEN + DB_Noop(1); + + QRY + QRY_VersionIsOlderThan((INTEGER)_Major,(INTEGER)_Minor,(INTEGER)_Rev,(INTEGER)_Build,(INTEGER)_CompMajor,(INTEGER)_CompMinor,(INTEGER)_CompRev,(INTEGER)_CompBuild) + AND + _Major == _CompMajor + AND + _Minor == _CompMinor + AND + _Rev == _CompRev + AND + _Build < _CompBuild + THEN + DB_Noop(1); + + } + EXIT + { + + } +} +Goal(47).Title("QRY_Characters"); +Goal(47) +{ + INIT + { + + } + KB + { + QRY + QRY_SpeakerBlockedBycombat((GUIDSTRING)_Char,(INTEGER)_Ignore) + AND + ObjectIsCharacter(_Char,1) + AND + _Ignore == 0 + AND + CharacterIsInCombat((CHARACTERGUID)_Char,1) + THEN + DB_NOOP(1); + + QRY + QRY_SpeakerIsAvailable((GUIDSTRING)_Item) + AND + ObjectExists(_Item,1) + AND + ObjectIsItem(_Item,1) + AND + QRY_SpeakerIsAvailable(_Item,0) + THEN + DB_NOOP(1); + + QRY + QRY_SpeakerIsAvailable((GUIDSTRING)_Char) + AND + ObjectIsCharacter(_Char,1) + AND + QRY_SpeakerIsAvailable((GUIDSTRING)_Char, 0) + THEN + DB_NOOP(1); + + QRY + QRY_SpeakerIsDead((GUIDSTRING)_Char) + AND + ObjectIsCharacter(_Char,1) + AND + CharacterIsDeadOrFeign((CHARACTERGUID)_Char,1) + THEN + DB_NOOP(1); + + QRY + QRY_SpeakerIsDead((GUIDSTRING)_Item) + AND + ObjectIsItem(_Item,1) + AND + ItemIsDestroyed((ITEMGUID)_Item,1) + THEN + DB_NOOP(1); + + QRY + QRY_HasInteractionDisabled((GUIDSTRING)_Char) + AND + ObjectIsCharacter(_Char,1) + AND + CharacterIsPolymorphInteractionDisabled((CHARACTERGUID)_Char,1) + THEN + DB_NOOP(1); + + QRY + QRY_SpeakerIsAvailable((GUIDSTRING)_Char, (INTEGER)_IgnoreCombat) + AND + NOT QRY_SpeakerIsDead(_Char) + AND + IsSpeakerReserved(_Char,0) + AND + NOT QRY_HasInteractionDisabled(_Char) + AND + NOT QRY_SpeakerBlockedBycombat(_Char,_IgnoreCombat) + THEN + DB_NOOP(1); + + QRY + QRY_SpeakerIsAvailable(NULL_00000000-0000-0000-0000-000000000000) + THEN + DB_NOOP(1); + + QRY + QRY_CharacterIsNotDisabled((CHARACTERGUID)_Character) + AND + HasActiveStatus(_Character,"FROZEN",0) + AND + HasActiveStatus(_Character,"STUNNED",0) + AND + HasActiveStatus(_Character,"FEAR",0) + AND + HasActiveStatus(_Character,"PETRIFIED",0) + AND + HasActiveStatus(_Character,"KNOCKED_DOWN",0) + THEN + DB_NOOP(1); + + QRY + QRY_SpeakerIsAvailableForCombat((CHARACTERGUID)_Char) + AND + CharacterIsDeadOrFeign(_Char,0) + AND + IsSpeakerReserved(_Char,0) + THEN + DB_NOOP(1); + + + QRY + QRY_SpeakerIsAvailableAndInDialogRange((CHARACTERGUID)_Char,(CHARACTERGUID)_SourceCharacter) + AND + CharacterIsDeadOrFeign(_Char,0) + AND + IsSpeakerReserved(_Char,0) + AND + CharacterIsInCombat(_Char,0) + AND + GetDistanceTo(_Char,_SourceCharacter,_Distance) + AND + _Distance < 10.0 + THEN + DB_NOOP(1); + + QRY + Query_IsPlayerHiding((CHARACTERGUID)_Char) + AND + HasActiveStatus(_Char,"SNEAKING",1) + THEN + DB_NOOP(1); + + QRY + Query_IsPlayerHiding((CHARACTERGUID)_Char) + AND + HasActiveStatus(_Char,"INVISIBLE",1) + THEN + DB_NOOP(1); + + QRY + QRY_CharacterIsNull((CHARACTERGUID)_Char) + AND + _Char == NULL_00000000-0000-0000-0000-000000000000 + THEN + DB_NOOP(1); + + } + EXIT + { + + } +} +Goal(48).Title("Sandbox"); +Goal(48) +{ + INIT + { + DB_CheckLevelStart("_TMPL_Sandbox"); + + } + KB + { + IF + RegionStarted("_TMPL_Sandbox") + THEN + GoalCompleted; + + IF + DB_CheckLevelStart("_TMPL_Sandbox") + AND + DB_CurrentLevel("_TMPL_Sandbox") + THEN + GoalCompleted; + + } + EXIT + { + NOT DB_CheckLevelStart("_TMPL_Sandbox"); + + } +} +Goal(49).Title("ZZZ_LastGoal"); +Goal(49) +{ + INIT + { + + } + KB + { + PROC + ProcClearAutomatedDialog((INTEGER)_Inst) + THEN + ProcClearDialogPlayers(_Inst); + ProcClearDialogNPCs(_Inst); + ProcClearDialogCounts(_Inst); + NOT DB_AutomatedDialog(_Inst); + + IF + VoiceBarkEnded(_,_Inst) + THEN + NOT DB_AutomatedDialogIsVB(_Inst); + ProcClearAutomatedDialog(_Inst); + + IF + AutomatedDialogEnded(_,_Inst) + AND + NOT DB_AutomatedDialogIsVB(_Inst) + THEN + ProcClearAutomatedDialog(_Inst); + + IF + AutomatedDialogEnded(_Dialog,_Inst) + THEN + NOT DB_DialogName(_Dialog,_Inst); + + IF + DialogEnded(_Dialog,_Inst) + THEN + ProcClearDialogPlayers(_Inst); + ProcClearDialogNPCs(_Inst); + ProcClearDialogCounts(_Inst); + NOT DB_DialogName(_Dialog,_Inst); + NOT DB_MarkedForDelete(_Inst); + + IF + DialogActorLeft(_Dialog,_Inst,_Actor) + AND + DB_DialogPlayers(_Inst,_Actor,_Index) + THEN + NOT DB_DialogPlayers(_Inst,_Actor,_Index); + ProcClearPlayerIfNotInOtherDialog(_Inst,_Actor); + + IF + DialogActorLeft(_Dialog,_Inst,_Actor) + AND + DB_DialogNPCs(_Inst,_Actor,_Index) + THEN + NOT DB_DialogNPCs(_Inst,_Actor,_Index); + ProcClearNPCIfNotInOtherDialog(_Inst,_Actor); + + IF + DialogActorLeft(_Dialog,_Inst,_Actor) + THEN + ProcSetNumberOfInvolvedActors(_Inst); + + PROC + ProcClearDialogPlayers((INTEGER)_Inst) + AND + DB_DialogPlayers(_Inst,_Player,_Index) + THEN + NOT DB_DialogPlayers(_Inst,_Player,_Index); + + PROC + ProcClearDialogNPCs((INTEGER)_Inst) + AND + DB_DialogNPCs(_Inst,_Player,_Index) + THEN + NOT DB_DialogNPCs(_Inst,_Player,_Index); + + PROC + ProcClearDialogCounts((INTEGER)_Inst) + AND + DB_DialogNumPlayers(_Inst,_NumPlayers) + AND + DB_DialogNumNPCs(_Inst,_NumNPCs) + THEN + NOT DB_DialogNumPlayers(_Inst,_NumPlayers); + NOT DB_DialogNumNPCs(_Inst,_NumNPCs); + + IF + CharacterCreationFinished(_) + THEN + NOT DB_InCharacterCreation(1); + NotifyCharacterCreationFinished(); + + } + EXIT + { + + } +} +Goal(10).SubGoal(2); +Goal(10).SubGoal(4); +Goal(10).SubGoal(8); +Goal(10).SubGoal(9); +Goal(10).SubGoal(14); +Goal(10).SubGoal(15); +Goal(10).SubGoal(19); +Goal(10).SubGoal(29); +Goal(10).SubGoal(30); +Goal(10).SubGoal(37); +Goal(10).SubGoal(39); +Goal(10).SubGoal(43); +Goal(10).SubGoal(48); +Goal(2).SubGoals(AND); +Goal(4).SubGoals(AND); +Goal(8).SubGoals(AND); +Goal(9).SubGoals(AND); +Goal(14).SubGoals(AND); +Goal(15).SubGoals(AND); +Goal(19).SubGoals(AND); +Goal(29).SubGoals(AND); +Goal(30).SubGoals(AND); +Goal(37).SubGoals(AND); +Goal(39).SubGoals(AND); +Goal(43).SubGoals(AND); +Goal(48).SubGoals(AND); +Goal(48).SubGoal(38); +Goal(38).SubGoals(AND); + diff --git a/test/fixtures/object.lsf b/test/fixtures/object.lsf new file mode 100644 index 0000000..f6cbede Binary files /dev/null and b/test/fixtures/object.lsf differ diff --git a/test/fixtures/simple.txt b/test/fixtures/simple.txt new file mode 100644 index 0000000..87e6826 --- /dev/null +++ b/test/fixtures/simple.txt @@ -0,0 +1,40 @@ +Version 1 +SubGoalCombiner SGC_AND +INITSECTION + DB_MY_Test((TRIGGERGUID)TRIGGERGUID_S_MY_TriggerName_000_f2b9bdc9-0369-4cc3-81ed-ad85e2c423b2, "Test"); +KBSECTION + IF + RegionStarted("MyRegion") + THEN + GoalCompleted; + + PROC + Proc_My_TestProc((INTEGER)_Value) + AND + DB_MY_TestValue((INTEGER)_OldValue) + AND + _OldValue < _Value + THEN + NOT DB_MY_TestValue(_OldValue); + + PROC + Proc_My_TestProc((INTEGER)_Value) + THEN + DB_MY_TestValue(_Value); + + QRY + Qry_My_HasTestValue() + AND + DB_MY_TestValue((INTEGER)_Value) + THEN + DB_NOOP(1); + + QRY + Qry_My_IsTestValue((INTEGER)_Value) + AND + DB_MY_TestValue(_Value) + THEN + DB_NOOP(1); +EXITSECTION +ENDEXITSECTION +ParentTargetEdge "__Start" diff --git a/test/lsf-parser.js b/test/lsf-parser.js new file mode 100644 index 0000000..51db59c --- /dev/null +++ b/test/lsf-parser.js @@ -0,0 +1,12 @@ +const assert = require("assert"); +const fs = require("fs"); + +const { default: LSFReader } = require("../lib/server/parsers/lsf/Parser"); + +describe("LSF parser", function() { + it("reads a lsf file", function() { + const buffer = fs.readFileSync("test/fixtures/object.lsf"); + const parser = new LSFReader(); + parser.read(buffer); + }); +}); diff --git a/test/story-parser.js b/test/story-parser.js new file mode 100644 index 0000000..a073dd6 --- /dev/null +++ b/test/story-parser.js @@ -0,0 +1,38 @@ +const assert = require("assert"); +const fs = require("fs"); + +const { + default: HeaderParser +} = require("../lib/server/parsers/story/HeaderParser"); + +const { + default: GoalParser +} = require("../lib/server/parsers/story/GoalParser"); + +function load(filename) { + return fs.readFileSync("test/fixtures/" + filename, "utf-8"); +} + +function saveJson(filename, data) { + fs.writeFileSync("test/fixtures/" + filename, JSON.stringify(data, null, 2), { + encoding: "utf-8" + }); +} + +describe("Story parser", function() { + it("creates tokens from script files", function() { + const parser = new GoalParser(load("simple.txt")); + const nodes = parser.tokenize(); + // saveJson("simple-tokens.json", nodes); + }); + it("parses simple sory script", function() { + const parser = new GoalParser(load("simple.txt")); + const nodes = parser.parse(); + // saveJson("simple-nodes.json", nodes); + }); + it("parses stroy div", function() { + const parser = new HeaderParser(load("headers.div")); + const nodes = parser.parse(); + // saveJson("headers.json", nodes); + }); +}); diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..688584c --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,13 @@ +{ + "compilerOptions": { + "lib": ["es6"], + "module": "commonjs", + "outDir": "lib", + "rootDir": "src", + "sourceMap": true, + "strict": true, + "target": "es6" + }, + "include": ["src"], + "exclude": ["node_modules"] +} diff --git a/tsconfig.publish.json b/tsconfig.publish.json new file mode 100644 index 0000000..55590b1 --- /dev/null +++ b/tsconfig.publish.json @@ -0,0 +1,6 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "sourceMap": false + } +}