diff --git a/project/jni/application/gemrb/AndroidAppSettings.cfg b/project/jni/application/gemrb/AndroidAppSettings.cfg new file mode 100644 index 000000000..f65c94933 --- /dev/null +++ b/project/jni/application/gemrb/AndroidAppSettings.cfg @@ -0,0 +1,33 @@ +# The application settings for Android libSDL port +AppSettingVersion=15 +LibSdlVersion=1.2 +AppName="GemRB" +AppFullName=net.sourceforge.gemrb +ScreenOrientation=h +InhibitSuspend=n +AppDataDownloadUrl="GemRB data(local)|data.zip" +SdlVideoResize=y +SdlVideoResizeKeepAspect=y +NeedDepthBuffer=n +AppUsesMouse=y +AppNeedsTwoButtonMouse=y +AppNeedsArrowKeys=n +AppNeedsTextInput=y +AppUsesJoystick=y +AppHandlesJoystickSensitivity=n +AppUsesMultitouch=n +NonBlockingSwapBuffers=n +RedefinedKeys="LCTRL c p o e" +AppTouchscreenKeyboardKeysAmount=0 +AppTouchscreenKeyboardKeysAmountAutoFire=0 +MultiABI=y +AppVersionCode=063 +AppVersionName="0.6.3" +CompiledLibraries="sdl_mixer ogg vorbis openal png python" +CustomBuildScript=n +AppCflags='-fexceptions -finline-functions -O2 -DSTATIC_LINK=Yes' +AppLdflags='' +AppSubdirsBuild='' +AppUseCrystaXToolchain=y +AppCmdline='GemRB' +ReadmeText='^You may press "Home" now - the data will be downloaded in background' diff --git a/project/jni/application/gemrb/AndroidData/data.zip b/project/jni/application/gemrb/AndroidData/data.zip new file mode 100644 index 000000000..8b60a6777 Binary files /dev/null and b/project/jni/application/gemrb/AndroidData/data.zip differ diff --git a/project/jni/application/gemrb/icon.png b/project/jni/application/gemrb/icon.png new file mode 100644 index 000000000..8ef0eba2d Binary files /dev/null and b/project/jni/application/gemrb/icon.png differ diff --git a/project/jni/application/gemrb/src/AUTHORS b/project/jni/application/gemrb/src/AUTHORS new file mode 100644 index 000000000..28a7ad991 --- /dev/null +++ b/project/jni/application/gemrb/src/AUTHORS @@ -0,0 +1,17 @@ +GemRB authors listed in Alphabetical Order + +Alyssa Milburn +Avenger +Balrog994 +Brian Tanedo +Dark-Star +Divide +Edheldil +GuidoJ +Jaka Kranjc +Lotana +Marshall Mattingly III +Thuy Nguyen +Tom Prince +Willem Jan Palenstijn +Zefklop diff --git a/project/jni/application/gemrb/src/COPYING b/project/jni/application/gemrb/src/COPYING new file mode 100644 index 000000000..5b6e7c66c --- /dev/null +++ b/project/jni/application/gemrb/src/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/project/jni/application/gemrb/src/GemRB.cpp b/project/jni/application/gemrb/src/GemRB.cpp new file mode 100644 index 000000000..fecabd6e2 --- /dev/null +++ b/project/jni/application/gemrb/src/GemRB.cpp @@ -0,0 +1,71 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * + */ + +// GemRB.cpp : Defines the entry point for the application. + +#include "win32def.h" // logging + +#include + +#include "Interface.h" + + +//this supposed to convince SDL to work on OS/X +//WARNING: commenting this out will cause SDL 1.2.x to crash +#ifdef __APPLE_CC__ // we need startup SDL here +#include +#endif + +#ifdef ANDROID +#include +#include "audio.h" + +// pause audio playing if app goes in background +static void appPutToBackground() +{ + core->GetAudioDrv()->Pause(); +} +// resume audio playing if app return to foreground +static void appPutToForeground() +{ + core->GetAudioDrv()->Resume(); +} + +#endif + +int main(int argc, char* argv[]) +{ +#ifdef ANDROID + SDL_ANDROID_SetApplicationPutToBackgroundCallback(&appPutToBackground, &appPutToForeground); +#endif + Interface::SanityCheck(VERSION_GEMRB); + core = new Interface( argc, argv ); + if (core->Init() == GEM_ERROR) { + delete( core ); + printf("Press enter to continue..."); + textcolor(DEFAULT); + getc(stdin); + return -1; + } + core->Main(); + delete( core ); + textcolor(DEFAULT); + return 0; +} diff --git a/project/jni/application/gemrb/src/NEWS b/project/jni/application/gemrb/src/NEWS new file mode 100644 index 000000000..961e2a864 --- /dev/null +++ b/project/jni/application/gemrb/src/NEWS @@ -0,0 +1,381 @@ +GemRB git (2bd6d0e): + New features: + - + + Improved features: + - + - bugfixes + + Applied patches: + +GemRB V0.6.3 git (2010-11-21) + New features: + - IWD:HoW is now completable! + - casting sounds and footsteps + - autodetection of secret doors, detect illusions + - basic bardsong support and selective magic resistance (bg2-style) + - proper store economics, ergonomics and dragging + - custom blood color (creature-dependant) + - new actions, iwd effects and triggers + - Importing a SoA game into ToB + + Improved features: + - actor selection and action bar (for summons and illusions too!) + - door bashing and traps + - loading screens, ambushes, worldmap + - sparkles, panic and other effects + - actions, dialogs, object matching + - personal items support (swap/equip/remove) + - bugfixes + + Applied patches: + iwd regression fix from Eggert Jón Magnússon + +GemRB V0.6.2 (2010-08-21): + New features: + - a basic SDL_mixer plugin for faster, but lower-quality audio + - dualclassing for bg1 and iwd + - new triggers, actions, infravision + - feet circle flickering on portrait hover, coloration in dialog + - wisdom xp bonus (pst) + + Improved features: + - actions, triggers, object matching + - item loading and ability selection, inventory + - projectiles, effects, subtitles, verbal constants + - the core and guiscript design was cleaned up in many places + - bugfixes + + Applied patches: + backslash check patch from anthiste + bg1 character generation patch from Maighstir + a crosscompiling fix from F.Fischer + +GemRB V0.6.1 (2010-06-16): + New features: + - a minimal dataset + - reputation penalties on death or injury + - casting level bonus/malus (wild mages, clerics) + - tinting for different times of the day and weather effects + - a BI(n)K player plugin for the IWD2 movies + - new actions, turn undead + + Improved features: + - the internal design was cleaned up in many places + - game saving, modal actions, combat, effects, spawns + - magic missiles are now drawn properly + - various guiscripts (no more flickering!) + - bugfixes + + Applied patches: + two patches from Brendan Molloy + +GemRB V0.6.0 (2009-11-03): + New features: + - BG1 and IWD are roughly completable! + - levelup support for bg1 and iwd, dream cutscenes in ToB + - more hardcoded projectiles and avatar animations + - evasion, backstabbing and basic hide in shadows + - compatibility with the widescreen mod (unreleased) allows for multiple + custom resolutions + - contingency and sequencer spells, beginnings of wild magic support + + Improved features: + - combat, travelling and feedback + - better spellcasting timing + - actions, effects and triggers + - various guiscripts + - bugfixes + + Applied patches: + a few patches from nugrud for how/totl support + +GemRB V0.5.1 (2009-08-27): + New features: + - BG2:SoA is roughly completable! + - almost all missing IE's hardcoded projectiles, spell hit projectiles, + projectile trails, projectile failure (spell), projectile effectlists + - auto-reloading of projectile weapons in case the ammo stack runs out + - damage resistance + - sorcerer style spellbooks, reading of iwd2 spellbooks + - target following to other areas + - the null sound plugin is now always loaded last by default; for old + installs see the provided configuration example (DelayPlugin) + - intelligence and wisdom dictated lore bonus + - a GUIEnhancements config option (on by default) that enables a few + extra controls (for convenience and larger mods) + - PST death counters (don't anger the Lady) + - initial support for targetting by portrait + + Improved features: + - actions, effects and triggers + - pathfinding, feet circles, fog of war and worldmap travel + - combat and spellcasting (especially summoning) + - projectiles + - config and default table value parsing is smarter about spaces + - various guiscripts + - bugfixes + + Applied patches: + various patches from nugrud for bg2 gui enhancements + fix compilation (with cmake) on OS X, by hanicka + +GemRB V0.5.0 (2009-06-25): + New features: + - SoA, ToB and PST are roughly playable beyond their first levels + - combat: dual-wielding, APR, proficiency and style boni, dexterity + bonus, initiatitive and speed factor, individual combat rounds + - many IE's hardcoded projectiles and support for projectile sounds + - IWD2 GUI now works after chargen too + - bg2 chargen now levels to the correct level + - summoned and charmed creatures can be ordered around + - actor tooltips (name and injury status) + - running, initial variable values and portal animations in PST + - hardcoded monk bonuses + + Improved features: + - dialog, actions and triggers + - combat mechanics, animation, feedback, ranged combat + - matters of time and matter + - levelup, dual classing, multiclass handling + - focus: scrolling while paused is now possible + - animations (projectile, creature) + - pathfinding + - area music restarts when there's no music playing + - disarm trap checks skills + - various guiscripts + - bugfixes + + Applied patches: + #2802190 jbmetz (improve the rpm spec handling) + #2802437 danamin (patch bomb sanitizing bg1 chargen + bg2 code share) + +GemRB V0.4.0 (2009-05-25): + New features: + - level up support in bg2 + - basic party reordering + - bashing of containers and doors + - persistent area effects (cloudkill, stinking cloud, web, etc.) + - item amount window for stack splitting (shift+click or doubleclick) + - depletion of item charges + - opcodes: disable spellcasting, cutscene2 (pocketplane travel), knock, + clear air, polymorph, disable button + - dynamic scrollbar creation (display of more than 10 kits, 24 spells) + - portrait effect icons + - item ability selection + - character customization + + Improved features: + - fog of war + - party reformation + - iwd and how guiscripts have been merged + - traps + - pst dialogs + - regeneration, hp bonuses, healing + - animations and projectiles + - rewritten MVE player + - ranged combat + - various guiscripts + - bugfixes + + Applied patches: + #2770564 Whiteclone (pst options window bug) + numerous patches from mattinm finishing the level up support + a few patches from ape fixing and extending iwd + #2579743 jbmetz added RPM spec files + +GemRB V0.3.2 (2009-02-16): + New features: + - default cancel button, bound to the escape key + - tooltip animations and a shortcut (tab) + - wrapper python classes that simplified the GUIScripts + - trap detection, removal, triggering, xp, feedback, autopause + - modal effects + - proper xp award for dual- and multiclass actors + - double click (used in the map window) + - click-and-hold incrementing/decrementing + - accumulate kill statistics + - characters can move while the map is open + - sound on item equip + - arbitrary feat prerequisites in iwd2 + - hard pause for all games (originally a ToB feature); triggered with 'h' + - extended night areas (originally a bg2 feature) + + Improved features: + - walking animation timing + - formations (arbitrary sizes, rotation, cursor) + - ppc support (no more crashes) + - container/door/infopoint cursor and highlight handling + - various guiscripts + - cmake build system (now really works on *nix) + - magic item exclusion + - stores and bags + - fixed attack loop when target dies + - bugfixes + + Applied patches: + #2159734 Zefklop (Mouse activity during movies) + #2243323 Zefklop (correct Openal cleanup) + #2263333 Whiteclone (bg1 guiinv) + #2380891 Amikrop (iwd1 guicommonwindows) + +GemRB V0.3.1 (2008-09-25): + New features: + - mouse scroll support + - starting tob inventory + - character import in iwd and how + - spritecover for area animations + - proper XP bonus for thieving and learning spells + + Improved features: + - gcc 4.3 compatibility + - PST bestiary + - bg2 and tob game modes have been merged + - bg2 and iwd2 character generation was simplified and improved + - stricter dualclassing prerequisites + - the cmake build system is available for other platforms too + - pathfinding + - starting time is now at day 0 + - less memory leaks + - bugfixes + +GemRB V0.3.0 (2008-02-17): + New features: + - TLK override handling (custom biographies and map notes) + - weapon immunities + - party AI + - expansion playmode + - more actions, triggers and effects + - loading of projectile explosion animations + - kit information window + - optional CMake build system (windows only) + + Improved features: + - sound (now perfect!) + - character generation + - opcodes + - character record window + - pathfinding + - tooltip delay + - bugfixes + +GemRB V0.2.9 (2007-07-06): + New features: + - thieving + - tracking + - graphical feedback (color pulse, blur, mirror image, vvc overlays etc) + - projectiles + - spell casting + - item use + - challenge rating calculation + + Improved features: + - more opcodes + - bugfixes + - shop/inventory gui + +GemRB V0.2.8 (2006-12-24): + New features: + - equipment is rendered both on paperdoll and avatar + - weather (snow/rain) is now rendered + + Improved features: + - action menus + - game scripting (actions/triggers) + +GemRB V0.2.7 (2006-08-30): + New features: + - large animations + - worldmap travel + - dialogue portraits + - translucent shadows option + - personal space of actors + - combat + - many new effects + - overlay animation + + Improved features: + - Script fixes + - Action menus + - TextScreen + - doors + - animated overlays + - new actions + +GemRB V0.2.6 (2005-12-06): + New features: + - Effects are in a different plugin + - DoxyGen docs + - Wallgroup covers + - Door triggers + - Action menus (talk/attack) + - party/protagonist death handled + + Improved features: + - Textscreen graphic fixed + - script workflow + - compilation and running on different systems (MacOSX, PPC Linux) + - various leaks/instabilities fixed + - Saving games + - inventory screens in many games + +GemRB V0.2.5 (2005-08-22): + New features: + - Save game + - Effects are now loaded + - Equipping effects in items + - Spawn points in areas + - Textscreen (scrolled text between chapters) + + Improved features: + - GameScript is now much more reliable: Action override works, triggers fire once and then get cleared + - fully working Store screen + - fixed padding of message window rows (in dialogs) + +GemRB V0.2.4 (2005-05-29): + New features: + - Store dialogs (Temple, Inn, Container, Tavern, Store) + - Fog of war with line of sight + - Doors block path and line of sight + - Window frames at higher resolutions + - Animated buttons (PST portraits, Donation window) + - Store opens when appropriate + - Containers + + Improved features: + - Fixed dialogs + - new GUIScript functions with documentation + - Fog of war/door/store related gamescript actions + - fixed object distance and area variable handling in gamescript + - other new gamescript actions/triggers + - Implemented PCs fidget animations + + Documentation: + - Introduction to writing GUIScript scripts + +GemRB v0.2.3 (2005-02-13): + New features: + - GUI for most of the games, especially interactive Inventory and Spellbook + - Map and WorldMap + - Load screen interstitials with progress bar + - Spell and item cache to speed up object management + - Added gamescript actions/triggers + - Selection of spells during character generation + - First attempt on effects code + - First attempt on Fog-Of-War + - Tooltips + - Overhead text + - Ambient sounds + - Volume control + - Manual page gemrb(1) + - Documentation for GemRB Python API and our custom override files + + Improved features: + - Character generation + - GUI + - Build infrastructure on Linux and Un*x systems + - Progress towards portability to 64 bit and big endian machines + - Many bugfixes and new bugs as well ;-) + - Shortened version numbers + - Simplified user configuration, game specific settings are now + in gemrb/override dir diff --git a/project/jni/application/gemrb/src/README b/project/jni/application/gemrb/src/README new file mode 100644 index 000000000..8d3b71955 --- /dev/null +++ b/project/jni/application/gemrb/src/README @@ -0,0 +1,77 @@ +Introduction +------------ +GemRB (Game Engine Made with preRendered Background) is a "port" +(actually a new implementation) of the original Infinity Engine (the one +of Baldur's Gate, Icewind Dale, Planescape: Torment, ...) to +Linux/Unix, MacOS X and Windows with some enhancements. Would you like to +create a game like Baldur's Gate? + +It means that you either need some of the original game's data +somewhere on your harddisk, or you can try to use the data from the +Dragonlance Total Conversion project - see the link below. + +The original game data has to be installed on a windows +partition and mounted to your Linux/Unix filesystem, installed on +windows and then copied to your filesystem, installed with WINE or +extracted manually from the CDs using the tool `unshield'. + +What little documentation exists is mostly in gemrb/docs/en/ and +subdirectories, the gemrb.6 man page and this file. + +Supported platforms +------------------- +Supported (i.e. we got reports about successfully running GemRB) systems: +Linux x86, x86-64, ppc +FreeBSD x86 +MS Windows (98, XP or Vista) +various Macintosh systems (even pre x86) also should work ... +some smart phones (Symbian, Android) +some consoles (OpenPandora, Dingoo) +some exotic OSes (ReactOS, SyllableOS, Haiku) + +Requirements +------------ +See the INSTALL file. + +Contacts +-------- +Our homepage: +http://gemrb.sourceforge.net + +Our project at sourceforge.net: +http://sourceforge.net/projects/gemrb + +New GemRB forum (users): +http://forums.gibberlings3.net/index.php?showforum=91 + +IRC channel: +The best way to talk with us is by joining the #GemRB channel +on the FreeNode IRC network. There's somebody to talk with most of +the time. + + +Useful links +------------ +IESDP, documentation for the Infinity Engine file formats and more: +http://iesdp.gibberlings3.net/ + +Near Infinity, Java viewer and editor for data files of the original games: +http://www.idi.ntnu.no/~joh/ni/index.html + +DLTCEP, MS Windows viewer and editor for data files of the original games: +http://forums.gibberlings3.net/index.php?showforum=137 + +Unshield, extractor for .CAB files created by InstallShield +http://synce.sourceforge.net/synce/unshield.php + +Valgrind, a powerful developer tool to fix programmer errors (leaks, buffer overflows and all the like that happen) +http://valgrind.org/ + +SDL, Simple Directmedia Layer, the graphical library used for GemRB +http://www.libsdl.org/index.php + +OpenAL, Cross-Platform 3D audio libraries, the sound library used for GemRB +http://openal.org/ + +WINE, Open Source implementation of the Windows API, useful for installing the games +http://www.winehq.org diff --git a/project/jni/application/gemrb/src/TODO b/project/jni/application/gemrb/src/TODO new file mode 100644 index 000000000..2f1af016a --- /dev/null +++ b/project/jni/application/gemrb/src/TODO @@ -0,0 +1,101 @@ +Scripts: +1. (DONE?) ToB specific actions/triggers, like pocketplane (so ToB will work) +2. (PARTLY) Properly detect the play mode (sp/mp, normal/extended) + +Strings: +1. fix (finish implementation of) talk table override +2. implement feature: "" (or even more ambitious stat specific strref tokens) + +Combat: +1. (PARTLY) fix combat round timings +2. implement customisable combat calculation, it should be general enough to +simulate all games, without any hardcoded parts + +Items: +1. (DONE-almost) Break items, count charges +2. (PARTLY) Implement switching weapon abilities +3. (DONE-almost) Implement item usage (similar to spells) + +Effects: +1. (DONE-almost) Implement common effects +2. (PARTLY) Implement IWD2 effects +3. (DONE-almost) Implement TOB effects +4. (PARTLY) Implement PST effects +5. (DONE-almost) Implement area/non-living affecting effects + +Area: +1. (PARTLY) Don't load scripts for pile items? (research when a script is unused) +2. (DONE) Create real streaming ambients (do not preload them, just use them when needed) +3. (DONE) in pathfinder, calculate with the actor's feet circle size (npc blocking still needed) +4. (DONE-almost) fix overlaid tiles - bug #1623839 + +Store: +1. Store caching (especially bags) + +Animation: +1. (PARTLY) fix char animation sequences +2. stanceID is still fuzzy. Fix it. (FIXED?) +3. (PARTLY) Implement projectile animations (area, cone, fragments, hardcoded features) + +Actor: +1. (PARTLY) Use the character sheet (Actor.cpp) itself to store attributes of the character + during character generation. + Benefits: data is already stored in the destination, data constraints and relations + are easily implemented. + How to: replace GemRB.SetVar with GemRB.SetPlayerStat. Don't forget to create + the character first. - see bg1 chargen for a complete solution +2. Move position of actor (and ground circle) to the center of a searchmap cell +3. (PARTLY) Actually handle the iwd2 spelllists. Exporter is still needed. + +Game GUI: +1. (PARTLY) implement class based (but customisable) action button bar. Generally port + the IWD2 system to all engines +2. implement grabbing mouse pointer by a control to fix dragging of PST Float menu window +3. (PARTLY) Fix drop capitals (initials) Calculate text height/width correctly, display + it correctly too. +4. (PARTLY) Fix unwanted screen shake (especially when on bottom of area) +5. (DONE-almost) Level up code, this should be written mostly in GuiScript!!! +6. (PARTLY) Contingency, spell trigger setup windows +7. (PARTLY) Customize windows (part of character record window) + +General: +1. The Cache and Variables classes could be rewritten to incorporate the release + function more smoothly (use templates?) +2. various directories (GemRB override, game override ...) should be resolved + right after loading config files and remain static afterwards. Maybe define + some PATH variable describing all the directories searched for files +3. valgrind reports a big heap of unreleased python objects +4. Implement at least all the options accessible from the GUI options setup, + rewrite baldur|torment|icewind.ini + +Graphics: +1. use scaling in Video::SpriteScaleDown() instead of in Video::GetPreview() + and in BMPImporter +2. move SDLVideoDriver sprite functions to their own file, rename them to + SpriteIsPixelTransparent etc. +3. (PARTLY) Add PNG support? (still image done) +4. Fog of war: fully visible squares with one corner neighbour invisible need alpha of the adjacent corner to the invisible square tuned down (uh, i hope it is clear what to do, look for artifacts in the fog of war edge) + +Sound: +1. valgrind reports invalid memory access due to Unqueueing buffers and using + them in another thread (openal weirdness?) +2. (PARTLY) sounds get sometimes distorted, might be connected to problem #1 +3. (PARTLY) Separate OpenAL interface from ACM loader and MVE player +4. implement sound handles so looping, moving sound, stopping sound is possible +5. fix sound settings (currently the volumes get reset on area change, for example) +6. implement and use as much from EAX (echo, damping, etc) as possible + +Release: +2. Get a sample game with some free license which could be distributed + with GemRB. + + +Documentation: +1. (PARTLY) make tool to scan source files for those with non-standard + copyright notices +2. Add Doxygen doc comments to more objects +3. Write GemRB overview, structure and high-level flow docs + + +Community: +2. Move this todo to bug/task tracker at Sourceforge :-) diff --git a/project/jni/application/gemrb/src/core/ActorMgr.cpp b/project/jni/application/gemrb/src/core/ActorMgr.cpp new file mode 100644 index 000000000..ebcee7124 --- /dev/null +++ b/project/jni/application/gemrb/src/core/ActorMgr.cpp @@ -0,0 +1,29 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * + */ + +#include "ActorMgr.h" + +ActorMgr::ActorMgr(void) +{ +} + +ActorMgr::~ActorMgr(void) +{ +} diff --git a/project/jni/application/gemrb/src/core/ActorMgr.h b/project/jni/application/gemrb/src/core/ActorMgr.h new file mode 100644 index 000000000..7afcfbaf8 --- /dev/null +++ b/project/jni/application/gemrb/src/core/ActorMgr.h @@ -0,0 +1,41 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * + */ + +#ifndef ACTORMGR_H +#define ACTORMGR_H + +#include "Plugin.h" +#include "Scriptable/Actor.h" +#include "System/DataStream.h" + +class GEM_EXPORT ActorMgr : public Plugin { +public: + ActorMgr(void); + virtual ~ActorMgr(void); + virtual bool Open(DataStream* stream, bool autoFree = true) = 0; + virtual Actor* GetActor(unsigned char is_in_party) = 0; + + //returns saved size, updates internal offsets before save + virtual int GetStoredFileSize(Actor *ac) = 0; + //saves file + virtual int PutActor(DataStream *stream, Actor *actor, bool chr=false) = 0; +}; + +#endif diff --git a/project/jni/application/gemrb/src/core/Ambient.cpp b/project/jni/application/gemrb/src/core/Ambient.cpp new file mode 100644 index 000000000..db543f474 --- /dev/null +++ b/project/jni/application/gemrb/src/core/Ambient.cpp @@ -0,0 +1,36 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2004 The GemRB Project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * + */ + +#include "Ambient.h" + +Ambient::Ambient() +{ +} + +Ambient::~Ambient() +{ + unsigned int i=sounds.size(); + while(i--) { + free(sounds[i]); + } +} + +void Ambient::setActive() { flags |= IE_AMBI_ENABLED; } +void Ambient::setInactive() { flags &= ~IE_AMBI_ENABLED; } diff --git a/project/jni/application/gemrb/src/core/Ambient.h b/project/jni/application/gemrb/src/core/Ambient.h new file mode 100644 index 000000000..ec4f9be22 --- /dev/null +++ b/project/jni/application/gemrb/src/core/Ambient.h @@ -0,0 +1,77 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2004 The GemRB Project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * + */ + +#ifndef AMBIENT_H +#define AMBIENT_H + +#include "exports.h" +#include "globals.h" +#include "ie_types.h" + +#include +#include +#include + +#define IE_AMBI_ENABLED 1 +#define IE_AMBI_POINT 2 +#define IE_AMBI_MAIN 4 +#define IE_AMBI_AREA 8 + +class GEM_EXPORT Ambient { +public: + Ambient(); + ~Ambient(); + + /* there is a good reason to have these in the header: + * they are automatically inlined, so we have + * no roundtrips and no overhead for accessors --Divide */ + const char *getName() const { return name; } + const Point &getOrigin() const { return origin; } + ieWord getRadius() const { return radius; } + ieWord getHeight() const { return height; } + ieWord getGain() const { return gain; } + char *getSound(ieDword i) const + { + if(i sounds; + ieDword interval; // no pauses if zero + ieDword perset; + ieDword appearance; + ieDword flags; + +}; + +#endif diff --git a/project/jni/application/gemrb/src/core/AmbientMgr.cpp b/project/jni/application/gemrb/src/core/AmbientMgr.cpp new file mode 100644 index 000000000..a998eb86a --- /dev/null +++ b/project/jni/application/gemrb/src/core/AmbientMgr.cpp @@ -0,0 +1,62 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2004 The GemRB Project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * + */ + +#include "AmbientMgr.h" + +#include "Ambient.h" + +AmbientMgr::AmbientMgr() +{ +} + +AmbientMgr::~AmbientMgr() +{ + reset(); +} + +void AmbientMgr::activate(const std::string &name) +{ + for (std::vector::iterator it = ambients.begin(); it != ambients.end(); ++it) { + if ((*it) -> getName() == name) { + (*it) -> setActive(); + break; + } + } +} + +void AmbientMgr::deactivate(const std::string &name) +{ + for (std::vector::iterator it = ambients.begin(); it != ambients.end(); ++it) { + if ((*it) -> getName() == name) { + (*it) -> setInactive(); + break; + } + } +} + +bool AmbientMgr::isActive(const std::string &name) const +{ + for (std::vector::const_iterator it = ambients.begin(); it != ambients.end(); ++it) { + if ((*it) -> getName() == name) { + return (*it) -> getFlags() & IE_AMBI_ENABLED; + } + } + return false; +} diff --git a/project/jni/application/gemrb/src/core/AmbientMgr.h b/project/jni/application/gemrb/src/core/AmbientMgr.h new file mode 100644 index 000000000..0b6daf122 --- /dev/null +++ b/project/jni/application/gemrb/src/core/AmbientMgr.h @@ -0,0 +1,48 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2004 The GemRB Project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * + */ + +#ifndef AMBIENTMGR_H +#define AMBIENTMGR_H + +#include "exports.h" +#include "win32def.h" + +#include +#include + +class Ambient; + +class GEM_EXPORT AmbientMgr { +public: + AmbientMgr(); + virtual ~AmbientMgr(); + virtual void reset() { ambients = std::vector (); } + virtual void setAmbients(const std::vector &a) { reset(); ambients = a; activate(); } + virtual void activate(const std::string &name); + virtual void activate() { active = true; } // hard play ;-) + virtual void deactivate(const std::string &name); + virtual void deactivate() { active = false; } // hard stop + virtual bool isActive(const std::string &name) const; +protected: + std::vector ambients; + bool active; +}; + +#endif diff --git a/project/jni/application/gemrb/src/core/AnimStructures.h b/project/jni/application/gemrb/src/core/AnimStructures.h new file mode 100644 index 000000000..9692cf947 --- /dev/null +++ b/project/jni/application/gemrb/src/core/AnimStructures.h @@ -0,0 +1,29 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * + */ + +#ifndef ANIMSTRUCTURES_H +#define ANIMSTRUCTURES_H + +struct CycleEntry { + ieWord FramesCount; + ieWord FirstFrame; +}; + +#endif diff --git a/project/jni/application/gemrb/src/core/Animation.cpp b/project/jni/application/gemrb/src/core/Animation.cpp new file mode 100644 index 000000000..6ec1caa1f --- /dev/null +++ b/project/jni/application/gemrb/src/core/Animation.cpp @@ -0,0 +1,261 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * + */ + +#include "Animation.h" + +#include "win32def.h" + +#include "Game.h" +#include "Interface.h" +#include "Map.h" +#include "Video.h" + +Animation::Animation(int count) +{ + frames = (Sprite2D **) calloc(count, sizeof(Sprite2D *)); + indicesCount = count; + if (count) { + pos = rand() % count; + } + else { + pos = 0; + } + starttime = 0; + x = 0; + y = 0; + Flags = A_ANI_ACTIVE; + fps = 15; + endReached = false; + //behaviour flags + playReversed = false; + gameAnimation = false; +} + +Animation::~Animation(void) +{ + Video *video = core->GetVideoDriver(); + + for (unsigned int i = 0; i < indicesCount; i++) { + video->FreeSprite( frames[i] ); + } + free(frames); +} + +void Animation::SetPos(unsigned int index) +{ + if (index=indicesCount) { + printf("You tried to write past a buffer in animation, BAD!\n"); + abort(); + } + core->GetVideoDriver()->FreeSprite(frames[index]); + frames[index]=frame; + + int x = -frame->XPos; + int y = -frame->YPos; + int w = frame->Width; + int h = frame->Height; + if (x < animArea.x) { + animArea.w += (animArea.x - x); + animArea.x = x; + } + if (y < animArea.y) { + animArea.h += (animArea.y - y); + animArea.y = y; + } + if (x+w > animArea.x+animArea.w) { + animArea.w = x+w-animArea.x; + } + if (y+h > animArea.y+animArea.h) { + animArea.h = y+h-animArea.y; + } +} + +unsigned int Animation::GetCurrentFrame() const +{ + if (playReversed) + return indicesCount-pos-1; + return pos; +} + +Sprite2D* Animation::LastFrame(void) +{ + if (!Flags&A_ANI_ACTIVE) { + printf("Frame fetched while animation is inactive!\n"); + return NULL; + } + if (gameAnimation) { + starttime = core->GetGame()->Ticks; + } else { + GetTime( starttime ); + } + Sprite2D* ret; + if (playReversed) + ret = frames[indicesCount-pos-1]; + else + ret = frames[pos]; + return ret; +} + +Sprite2D* Animation::NextFrame(void) +{ + if (!Flags&A_ANI_ACTIVE) { + printf("Frame fetched while animation is inactive!\n"); + return NULL; + } + if (starttime == 0) { + if (gameAnimation) { + starttime = core->GetGame()->Ticks; + } else { + GetTime( starttime ); + } + } + Sprite2D* ret; + if (playReversed) + ret = frames[indicesCount-pos-1]; + else + ret = frames[pos]; + + if (endReached && (Flags&A_ANI_PLAYONCE) ) + return ret; + + unsigned long time; + if (gameAnimation) { + time = core->GetGame()->Ticks; + } else { + GetTime(time); + } + + //it could be that we skip more than one frame in case of slow rendering + //large, composite animations (dragons, multi-part area anims) require synchronisation + if (( time - starttime ) >= ( unsigned long ) ( 1000 / fps )) { + int inc = (time-starttime)*fps/1000; + pos += inc; + starttime += inc*1000/fps; + } + if (pos >= indicesCount ) { + if (indicesCount) { + if (Flags&A_ANI_PLAYONCE) { + pos = indicesCount-1; + endReached = true; + } else { + pos = pos%indicesCount; + endReached = false; //looping, there is no end + } + } else { + pos = 0; + endReached = true; + } + starttime = 0; + } + return ret; +} + +Sprite2D* Animation::GetSyncedNextFrame(Animation* master) +{ + if (!Flags&A_ANI_ACTIVE) { + printf("Frame fetched while animation is inactive!\n"); + return NULL; + } + Sprite2D* ret; + if (playReversed) + ret = frames[indicesCount-pos-1]; + else + ret = frames[pos]; + + starttime = master->starttime; + pos = master->pos; + endReached = master->endReached; + + return ret; +} + + +void Animation::release(void) +{ + delete this; +} +/** Gets the i-th frame */ +Sprite2D* Animation::GetFrame(unsigned int i) +{ + if (i >= indicesCount) { + return NULL; + } + return frames[i]; +} + +void Animation::MirrorAnimation() +{ + Video *video = core->GetVideoDriver(); + + for (size_t i = 0; i < indicesCount; i++) { + Sprite2D * tmp = frames[i]; + frames[i] = video->MirrorSpriteHorizontal( tmp, true ); + video->FreeSprite(tmp); + } + + // flip animArea horizontally as well + animArea.x = -animArea.w - animArea.x; +} + +void Animation::MirrorAnimationVert() +{ + Video *video = core->GetVideoDriver(); + + for (size_t i = 0; i < indicesCount; i++) { + Sprite2D * tmp = frames[i]; + frames[i] = video->MirrorSpriteVertical( tmp, true ); + video->FreeSprite(tmp); + } + + // flip animArea vertically as well +// animArea.y = -animArea.h - animArea.y; +} + +void Animation::AddAnimArea(Animation* slave) +{ + int x = slave->animArea.x; + int y = slave->animArea.y; + int w = slave->animArea.w; + int h = slave->animArea.h; + if (x < animArea.x) { + animArea.w += (animArea.x - x); + animArea.x = x; + } + if (y < animArea.y) { + animArea.h += (animArea.y - y); + animArea.y = y; + } + if (x+w > animArea.x+animArea.w) { + animArea.w = x+w-animArea.x; + } + if (y+h > animArea.y+animArea.h) { + animArea.h = y+h-animArea.y; + } +} diff --git a/project/jni/application/gemrb/src/core/Animation.h b/project/jni/application/gemrb/src/core/Animation.h new file mode 100644 index 000000000..fcf9a7d13 --- /dev/null +++ b/project/jni/application/gemrb/src/core/Animation.h @@ -0,0 +1,72 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * + */ + +#ifndef ANIMATION_H +#define ANIMATION_H + +#include "RGBAColor.h" +#include "exports.h" +#include "globals.h" + +#include "Region.h" +#include "Sprite2D.h" + +#include + +class GEM_EXPORT Animation { +private: + Sprite2D **frames; + unsigned int indicesCount; + unsigned long starttime; +public: + bool endReached; + unsigned int pos; + int x, y; + unsigned char fps; + bool playReversed; + bool gameAnimation; + Region animArea; + ieDword Flags; + Animation(int count); + ~Animation(void); + void AddFrame(Sprite2D* frame, unsigned int index); + Sprite2D* LastFrame(void); + Sprite2D* NextFrame(void); + Sprite2D* GetSyncedNextFrame(Animation* master); + void release(void); + /** Gets the i-th frame */ + Sprite2D* GetFrame(unsigned int i); + /** Mirrors all the frames vertically */ + void MirrorAnimationVert(); + /** Mirrors all the frames horizontally */ + void MirrorAnimation(); + /** sets frame index */ + void SetPos(unsigned int index); + /** Sets ScriptName for area animation */ + void SetScriptName(const char *name); + /** returns the frame count */ + unsigned int GetFrameCount() const { return indicesCount; } + /** returns the current frame's index */ + unsigned int GetCurrentFrame() const; + /** add other animation's animarea to self */ + void AddAnimArea(Animation* slave); +}; + +#endif diff --git a/project/jni/application/gemrb/src/core/AnimationFactory.cpp b/project/jni/application/gemrb/src/core/AnimationFactory.cpp new file mode 100644 index 000000000..e6637394c --- /dev/null +++ b/project/jni/application/gemrb/src/core/AnimationFactory.cpp @@ -0,0 +1,168 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * + */ + +#include "AnimationFactory.h" + +#include "win32def.h" + +#include "Interface.h" +#include "Video.h" + +AnimationFactory::AnimationFactory(const char* ResRef) + : FactoryObject( ResRef, IE_BAM_CLASS_ID ) +{ + FLTable = NULL; + FrameData = NULL; + datarefcount = 0; +} + +AnimationFactory::~AnimationFactory(void) +{ + for (unsigned int i = 0; i < frames.size(); i++) { + core->GetVideoDriver()->FreeSprite( frames[i] ); + } + if (FLTable) + free( FLTable); + + // FIXME: track down where sprites are being leaked + if (datarefcount) { + fprintf(stderr, "AnimationFactory %s has refcount %d\n", ResRef, datarefcount); + //assert(datarefcount == 0); + } + if (FrameData) + free( FrameData); +} + +void AnimationFactory::AddFrame(Sprite2D* frame) +{ + frames.push_back( frame ); +} + +void AnimationFactory::AddCycle(CycleEntry cycle) +{ + cycles.push_back( cycle ); +} + +void AnimationFactory::LoadFLT(unsigned short* buffer, int count) +{ + if (FLTable) { + free( FLTable ); + } + //FLTable = new unsigned short[count]; + FLTable = (unsigned short *) malloc(count * sizeof( unsigned short ) ); + memcpy( FLTable, buffer, count * sizeof( unsigned short ) ); +} + +void AnimationFactory::SetFrameData(unsigned char* FrameData) +{ + this->FrameData = FrameData; +} + + +Animation* AnimationFactory::GetCycle(unsigned char cycle) +{ + if (cycle >= cycles.size()) { + return NULL; + } + int ff = cycles[cycle].FirstFrame; + int lf = ff + cycles[cycle].FramesCount; + Animation* anim = new Animation( cycles[cycle].FramesCount ); + int c = 0; + for (int i = ff; i < lf; i++) { + frames[FLTable[i]]->acquire(); + anim->AddFrame( frames[FLTable[i]], c++ ); + } + return anim; +} + +/* returns the required frame of the named cycle, cycle defaults to 0 */ +Sprite2D* AnimationFactory::GetFrame(unsigned short index, unsigned char cycle) const +{ + if (cycle >= cycles.size()) { + return NULL; + } + int ff = cycles[cycle]. FirstFrame, fc = cycles[cycle].FramesCount; + if(index >= fc) { + return NULL; + } + Sprite2D* spr = frames[FLTable[ff+index]]; + spr->acquire(); + return spr; +} + +Sprite2D* AnimationFactory::GetFrameWithoutCycle(unsigned short index) const +{ + if(index >= frames.size()) { + return NULL; + } + Sprite2D* spr = frames[index]; + spr->acquire(); + return spr; +} + +Sprite2D* AnimationFactory::GetPaperdollImage(ieDword *Colors, + Sprite2D *&Picture2, unsigned int type) const +{ + if (frames.size()<2) { + return NULL; + } + + Video* video = core->GetVideoDriver(); + Picture2 = video->DuplicateSprite(frames[1]); + if (!Picture2) { + return NULL; + } + if (Colors) { + Palette* palette = Picture2->GetPalette(); + palette->SetupPaperdollColours(Colors, type); + Picture2->SetPalette(palette); + palette->Release(); + } + + Picture2->XPos = (short)frames[1]->XPos; + Picture2->YPos = (short)frames[1]->YPos - 80; + + + Sprite2D* spr = core->GetVideoDriver()->DuplicateSprite(frames[0]); + if (Colors) { + Palette* palette = spr->GetPalette(); + palette->SetupPaperdollColours(Colors, type); + spr->SetPalette(palette); + palette->Release(); + } + + spr->XPos = (short)frames[0]->XPos; + spr->YPos = (short)frames[0]->YPos; + + //don't free pixels, createsprite stores it in spr + + return spr; +} + +void AnimationFactory::IncDataRefCount() +{ + ++datarefcount; +} + +void AnimationFactory::DecDataRefCount() +{ + assert(datarefcount > 0); + --datarefcount; +} diff --git a/project/jni/application/gemrb/src/core/AnimationFactory.h b/project/jni/application/gemrb/src/core/AnimationFactory.h new file mode 100644 index 000000000..b385362eb --- /dev/null +++ b/project/jni/application/gemrb/src/core/AnimationFactory.h @@ -0,0 +1,58 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * + */ + +#ifndef ANIMATIONFACTORY_H +#define ANIMATIONFACTORY_H + +#include "exports.h" +#include "globals.h" + +#include "Animation.h" +#include "FactoryObject.h" + +class GEM_EXPORT AnimationFactory : public FactoryObject { +private: + std::vector< Sprite2D*> frames; + std::vector< CycleEntry> cycles; + unsigned short* FLTable; // Frame Lookup Table + unsigned char* FrameData; + int datarefcount; +public: + AnimationFactory(const char* ResRef); + ~AnimationFactory(void); + void AddFrame(Sprite2D* frame); + void AddCycle(CycleEntry cycle); + void LoadFLT(unsigned short* buffer, int count); + void SetFrameData(unsigned char* FrameData); + Animation* GetCycle(unsigned char cycle); + /** No descriptions */ + Sprite2D* GetFrame(unsigned short index, unsigned char cycle=0) const; + Sprite2D* GetFrameWithoutCycle(unsigned short index) const; + size_t GetCycleCount() const { return cycles.size(); } + size_t GetFrameCount() const { return frames.size(); } + int GetCycleSize(int idx) const { return cycles[idx].FramesCount; } + Sprite2D* GetPaperdollImage(ieDword *Colors, Sprite2D *&Picture2, + unsigned int type) const; + + void IncDataRefCount(); + void DecDataRefCount(); +}; + +#endif diff --git a/project/jni/application/gemrb/src/core/AnimationMgr.cpp b/project/jni/application/gemrb/src/core/AnimationMgr.cpp new file mode 100644 index 000000000..7ddece946 --- /dev/null +++ b/project/jni/application/gemrb/src/core/AnimationMgr.cpp @@ -0,0 +1,29 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * + */ + +#include "AnimationMgr.h" + +AnimationMgr::AnimationMgr(void) +{ +} + +AnimationMgr::~AnimationMgr(void) +{ +} diff --git a/project/jni/application/gemrb/src/core/AnimationMgr.h b/project/jni/application/gemrb/src/core/AnimationMgr.h new file mode 100644 index 000000000..d161aff4b --- /dev/null +++ b/project/jni/application/gemrb/src/core/AnimationMgr.h @@ -0,0 +1,47 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * + */ + +#ifndef ANIMATIONMGR_H +#define ANIMATIONMGR_H + +#include "globals.h" + +#include "Animation.h" +#include "AnimationFactory.h" +#include "Font.h" +#include "Plugin.h" + +class GEM_EXPORT AnimationMgr : public Plugin { +public: + AnimationMgr(void); + virtual ~AnimationMgr(void); + virtual bool Open(DataStream* stream, bool autoFree = true) = 0; + virtual int GetCycleSize(unsigned char Cycle) = 0; + virtual AnimationFactory* GetAnimationFactory(const char* ResRef, + unsigned char mode = IE_NORMAL) = 0; + /** This function will load the Animation as a Font */ + virtual Font* GetFont() = 0; + /** Debug Function: Returns the Global Animation Palette as a Sprite2D Object. + If the Global Animation Palette is NULL, returns NULL. */ + virtual Sprite2D* GetPalette() = 0; + virtual int GetCycleCount() = 0; +}; + +#endif diff --git a/project/jni/application/gemrb/src/core/ArchiveImporter.cpp b/project/jni/application/gemrb/src/core/ArchiveImporter.cpp new file mode 100644 index 000000000..2c8524ff0 --- /dev/null +++ b/project/jni/application/gemrb/src/core/ArchiveImporter.cpp @@ -0,0 +1,29 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * + */ + +#include "ArchiveImporter.h" + +ArchiveImporter::ArchiveImporter(void) +{ +} + +ArchiveImporter::~ArchiveImporter(void) +{ +} diff --git a/project/jni/application/gemrb/src/core/ArchiveImporter.h b/project/jni/application/gemrb/src/core/ArchiveImporter.h new file mode 100644 index 000000000..076d6795c --- /dev/null +++ b/project/jni/application/gemrb/src/core/ArchiveImporter.h @@ -0,0 +1,40 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * + */ + +#ifndef ARCHIVEIMPORTER_H +#define ARCHIVEIMPORTER_H + +#include "globals.h" + +#include "Plugin.h" + +class GEM_EXPORT ArchiveImporter : public Plugin { +public: + ArchiveImporter(void); + virtual ~ArchiveImporter(void); + virtual int OpenArchive(const char* filename) = 0; + virtual int CreateArchive(DataStream *stream) = 0; + //decompressing a .sav file similar to CBF + virtual int DecompressSaveGame(DataStream *compressed) = 0; + virtual int AddToSaveGame(DataStream *str, DataStream *uncompressed) = 0; + virtual DataStream* GetStream(unsigned long Resource, unsigned long Type) = 0; +}; + +#endif diff --git a/project/jni/application/gemrb/src/core/Audio.cpp b/project/jni/application/gemrb/src/core/Audio.cpp new file mode 100644 index 000000000..fa114c4bd --- /dev/null +++ b/project/jni/application/gemrb/src/core/Audio.cpp @@ -0,0 +1,35 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003-2004 The GemRB Project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * + */ + +#include "Audio.h" + +const TypeID Audio::ID = { "Audio" }; + +Audio::Audio(void) +{ +} + +Audio::~Audio(void) +{ +} + +SoundHandle::~SoundHandle() +{ +} diff --git a/project/jni/application/gemrb/src/core/Audio.h b/project/jni/application/gemrb/src/core/Audio.h new file mode 100644 index 000000000..640083160 --- /dev/null +++ b/project/jni/application/gemrb/src/core/Audio.h @@ -0,0 +1,82 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003-2004 The GemRB Project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * + */ + +#ifndef AUDIO_H_INCLUDED +#define AUDIO_H_INCLUDED + +#include "globals.h" +#include "win32def.h" + +#include "Plugin.h" +#include "Holder.h" + +#define GEM_SND_RELATIVE 1 +#define GEM_SND_LOOPING 2 +#define GEM_SND_SPEECH IE_STR_SPEECH // 4 +#define GEM_SND_VOL_MUSIC 1 +#define GEM_SND_VOL_AMBIENTS 2 + +class AmbientMgr; +class SoundMgr; + +class GEM_EXPORT SoundHandle : public Held { +public: + virtual bool Playing() = 0; + virtual void SetPos(int XPos, int YPos) = 0; + virtual void Stop() = 0; + virtual void StopLooping() = 0; + virtual ~SoundHandle(); +}; + +class GEM_EXPORT Audio : public Plugin { +public: + static const TypeID ID; +public: + Audio(void); + virtual ~Audio(); + virtual bool Init(void) = 0; + virtual Holder Play(const char* ResRef, int XPos, int YPos, unsigned int flags = 0, unsigned int *length = 0) = 0; + virtual Holder Play(const char* ResRef, unsigned int *length = 0) { return Play(ResRef, 0, 0, GEM_SND_RELATIVE, length); } + virtual bool IsSpeaking() = 0; + virtual AmbientMgr* GetAmbientMgr() { return ambim; } + virtual void UpdateVolume(unsigned int flags = GEM_SND_VOL_MUSIC | GEM_SND_VOL_AMBIENTS) = 0; + virtual bool CanPlay() = 0; + virtual void ResetMusics() = 0; + virtual bool Play() = 0; + virtual bool Stop() = 0; + virtual bool Pause() = 0; + virtual bool Resume() = 0; + virtual int CreateStream(Holder) = 0; + virtual void UpdateListenerPos(int XPos, int YPos ) = 0; + virtual void GetListenerPos(int &XPos, int &YPos ) = 0; + virtual bool ReleaseStream(int stream, bool HardStop=false ) = 0; + virtual int SetupNewStream( ieWord x, ieWord y, ieWord z, + ieWord gain, bool point, bool Ambient) = 0; + virtual int QueueAmbient(int stream, const char* sound) = 0; + virtual void SetAmbientStreamVolume(int stream, int volume) = 0; + virtual void QueueBuffer(int stream, unsigned short bits, + int channels, short* memory, int size, int samplerate) = 0; + +protected: + AmbientMgr* ambim; + +}; + +#endif // AUDIO_H_INCLUDED diff --git a/project/jni/application/gemrb/src/core/Bitmap.cpp b/project/jni/application/gemrb/src/core/Bitmap.cpp new file mode 100644 index 000000000..2a55fa4cf --- /dev/null +++ b/project/jni/application/gemrb/src/core/Bitmap.cpp @@ -0,0 +1,29 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2007 The GemRB Project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "Bitmap.h" + +Bitmap::Bitmap(unsigned int w, unsigned int h) + : height(h), width(w), data(new unsigned char[height*width]) +{ +} + +Bitmap::~Bitmap() +{ + delete[] data; +} diff --git a/project/jni/application/gemrb/src/core/Bitmap.h b/project/jni/application/gemrb/src/core/Bitmap.h new file mode 100644 index 000000000..2c37bdd87 --- /dev/null +++ b/project/jni/application/gemrb/src/core/Bitmap.h @@ -0,0 +1,55 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2007 The GemRB Project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef BITMAP_H +#define BITMAP_H + +#include "exports.h" + +class GEM_EXPORT Bitmap { +public: + Bitmap(unsigned int height, unsigned int width); + ~Bitmap(); + unsigned char GetAt(unsigned int x, unsigned int y) const + { + if (x >= width || y >= height) + return 0; + return data[width*y+x]; + + } + void SetAt(unsigned int x, unsigned int y, unsigned char idx) + { + if (x >= width || y >= height) + return; + data[width*y+x] = idx; + + } + unsigned int GetHeight() const + { + return height; + } + unsigned int GetWidth() const + { + return width; + } +private: + unsigned int height, width; + unsigned char *data; +}; + +#endif diff --git a/project/jni/application/gemrb/src/core/CMakeLists.txt b/project/jni/application/gemrb/src/core/CMakeLists.txt new file mode 100644 index 000000000..aafa67645 --- /dev/null +++ b/project/jni/application/gemrb/src/core/CMakeLists.txt @@ -0,0 +1,49 @@ +ADD_DEFINITIONS(-DGEM_BUILD_DLL) + +FILE(GLOB gemrb_core_LIB_SRCS "*.cpp" + GameScript/Actions.cpp + GameScript/GSUtils.cpp + GameScript/GameScript.cpp + GameScript/Matching.cpp + GameScript/Objects.cpp + GameScript/Triggers.cpp + GUI/Button.cpp + GUI/Console.cpp + GUI/Control.cpp + GUI/EventMgr.cpp + GUI/GameControl.cpp + GUI/Label.cpp + GUI/MapControl.cpp + GUI/Progressbar.cpp + GUI/ScrollBar.cpp + GUI/Slider.cpp + GUI/TextArea.cpp + GUI/TextEdit.cpp + GUI/Window.cpp + GUI/WorldMapControl.cpp + Scriptable/Actor.cpp + Scriptable/ActorBlock.cpp + Scriptable/PCStatStruct.cpp + System/CachedFileStream.cpp + System/DataStream.cpp + System/FileStream.cpp + System/MemoryStream.cpp + System/VFS.cpp + System/snprintf.cpp + ) + +if (STATIC_LINK) + ADD_LIBRARY(gemrb_core STATIC ${gemrb_core_LIB_SRCS}) +else (STATIC_LINK) + ADD_LIBRARY(gemrb_core SHARED ${gemrb_core_LIB_SRCS}) + IF(WIN32) + INSTALL(TARGETS gemrb_core RUNTIME DESTINATION ${LIB_DIR}) + ELSE(WIN32) + INSTALL(TARGETS gemrb_core LIBRARY DESTINATION ${LIB_DIR}) + ENDIF(WIN32) +endif (STATIC_LINK) + +SET_TARGET_PROPERTIES(gemrb_core PROPERTIES + COMPILE_DEFINITIONS + "PLUGINDIR=\"${PLUGIN_DIR}\";DATADIR=\"${DATA_DIR}\";SYSCONFDIR=\"${SYSCONF_DIR}\"" + ) diff --git a/project/jni/application/gemrb/src/core/Cache.cpp b/project/jni/application/gemrb/src/core/Cache.cpp new file mode 100644 index 000000000..647b0cb93 --- /dev/null +++ b/project/jni/application/gemrb/src/core/Cache.cpp @@ -0,0 +1,311 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * + */ + +#include "Cache.h" + +#include + +// private inlines +inline unsigned int Cache::MyHashKey(const char* key) const +{ + int nHash = tolower(key[0]); + for (int i=1;(i 0 ); + assert( nHashTableSize > 16 ); + + m_pHashTable = NULL; + m_nHashTableSize = nHashTableSize; // default size + m_nCount = 0; + m_pFreeList = NULL; + m_pBlocks = NULL; + m_nBlockSize = nBlockSize; +} + +void Cache::InitHashTable(unsigned int nHashSize, bool bAllocNow) + //Used to force allocation of a hash table or to override the default + //hash table size of (which is fairly small) +{ + assert( m_nCount == 0 ); + assert( nHashSize > 16 ); + + if (m_pHashTable != NULL) { + // free hash table + free( m_pHashTable); + m_pHashTable = NULL; + } + + if (bAllocNow) { + m_pHashTable = (Cache::MyAssoc **) malloc( sizeof( Cache::MyAssoc * ) * nHashSize ); + memset( m_pHashTable, 0, sizeof( Cache::MyAssoc * ) * nHashSize ); + } + m_nHashTableSize = nHashSize; +} + +void Cache::RemoveAll(ReleaseFun fun) +{ + if (m_pHashTable) { + for (unsigned int nHash = 0; nHash < m_nHashTableSize; nHash++) + { + MyAssoc* pAssoc; + for (pAssoc = m_pHashTable[nHash]; pAssoc != NULL; + pAssoc = pAssoc->pNext) + { + if (fun) + fun(pAssoc->data); + pAssoc->MyAssoc::~MyAssoc(); + } + } + // free hash table + free( m_pHashTable ); + m_pHashTable = NULL; + } + + m_nCount = 0; + m_pFreeList = NULL; + + // free memory blocks + MemBlock* p = m_pBlocks; + while (p != NULL) { + MemBlock* pNext = p->pNext; + free( p ); + p = pNext; + } + + m_pBlocks = NULL; +} + +Cache::~Cache() +{ + RemoveAll(NULL); +} + +Cache::MyAssoc* Cache::NewAssoc() +{ + if (m_pFreeList == NULL) { + // add another block + Cache::MemBlock* newBlock = ( Cache::MemBlock* ) malloc(m_nBlockSize * sizeof( Cache::MyAssoc ) + sizeof( Cache::MemBlock )); + assert( newBlock != NULL ); // we must have something + + newBlock->pNext = m_pBlocks; + m_pBlocks = newBlock; + + // chain them into free list + Cache::MyAssoc* pAssoc = ( Cache::MyAssoc* ) + ( newBlock + 1 ); + for (int i = 0; i < m_nBlockSize; i++) { + pAssoc->pNext = m_pFreeList; + m_pFreeList = pAssoc++; + } + } + + Cache::MyAssoc* pAssoc = m_pFreeList; + m_pFreeList = m_pFreeList->pNext; + m_nCount++; + assert( m_nCount > 0 ); // make sure we don't overflow +#ifdef _DEBUG + pAssoc->key[0] = 0; + pAssoc->data = 0; +#endif + pAssoc->nRefCount=1; + return pAssoc; +} + +void Cache::FreeAssoc(Cache::MyAssoc* pAssoc) +{ + if(pAssoc->pNext) { + pAssoc->pNext->pPrev=pAssoc->pPrev; + } + *pAssoc->pPrev = pAssoc->pNext; + pAssoc->pNext = m_pFreeList; + m_pFreeList = pAssoc; + m_nCount--; + assert( m_nCount >= 0 ); // make sure we don't underflow + + // if no more elements, cleanup completely + if (m_nCount == 0) { + RemoveAll(NULL); + } +} + +Cache::MyAssoc *Cache::GetNextAssoc(Cache::MyAssoc *Position) const +{ + if (m_pHashTable == NULL || m_nCount==0) { + return NULL; + } + + Cache::MyAssoc* pAssocRet = (Cache::MyAssoc*)Position; + + if (pAssocRet == NULL) + { + // find the first association + for (unsigned int nBucket = 0; nBucket < m_nHashTableSize; nBucket++) + if ((pAssocRet = m_pHashTable[nBucket]) != NULL) + break; + return pAssocRet; + } + Cache::MyAssoc* pAssocNext = pAssocRet->pNext; + if (pAssocNext == NULL) + { + // go to next bucket + for (unsigned int nBucket = MyHashKey(pAssocRet->key) + 1; + nBucket < m_nHashTableSize; nBucket++) + if ((pAssocNext = m_pHashTable[nBucket]) != NULL) + break; + } + + return pAssocNext; +} + +Cache::MyAssoc* Cache::GetAssocAt(const ieResRef key) const + // find association (or return NULL) +{ + if (m_pHashTable == NULL) { + return NULL; + } + + unsigned int nHash = MyHashKey( key ); + + // see if it exists + Cache::MyAssoc* pAssoc; + for (pAssoc = m_pHashTable[nHash]; + pAssoc != NULL; + pAssoc = pAssoc->pNext) { + if (!strnicmp( pAssoc->key, key, KEYSIZE )) { + return pAssoc; + } + } + return NULL; +} + +void *Cache::GetResource(const ieResRef key) const +{ + Cache::MyAssoc* pAssoc = GetAssocAt( key ); + if (pAssoc == NULL) { + return NULL; + } // not in map + + pAssoc->nRefCount++; + return pAssoc->data; +} + +//returns true if it was successful +bool Cache::SetAt(const ieResRef key, void *rValue) +{ + int i; + + if (m_pHashTable == NULL) { + InitHashTable( m_nHashTableSize ); + } + + Cache::MyAssoc* pAssoc=GetAssocAt( key ); + + if (pAssoc) { + //already exists, but we return true if it is the same + return (pAssoc->data==rValue); + } + + // it doesn't exist, add a new Association + pAssoc = NewAssoc(); + for (i=0;ikey[i]=tolower(key[i]); + } + for (;ikey[i]=0; + } + pAssoc->data=rValue; + // put into hash table + unsigned int nHash = MyHashKey(pAssoc->key); + pAssoc->pNext = m_pHashTable[nHash]; + pAssoc->pPrev = &m_pHashTable[nHash]; + if (pAssoc->pNext) { + pAssoc->pNext->pPrev = &pAssoc->pNext; + } + m_pHashTable[nHash] = pAssoc; + return true; +} + +int Cache::RefCount(const ieResRef key) const +{ + Cache::MyAssoc* pAssoc=GetAssocAt( key ); + if (pAssoc) { + return pAssoc->nRefCount; + } + return -1; +} + +int Cache::DecRef(void *data, const ieResRef key, bool remove) +{ + Cache::MyAssoc* pAssoc; + + if (key) { + pAssoc=GetAssocAt( key ); + if (pAssoc && (pAssoc->data==data) ) { + if (!pAssoc->nRefCount) { + return -1; + } + --pAssoc->nRefCount; + if (remove && !pAssoc->nRefCount) { + FreeAssoc(pAssoc); + return 0; + } + return pAssoc->nRefCount; + } + return -1; + } + + pAssoc=(Cache::MyAssoc *) GetNextAssoc(NULL); + + while (pAssoc) { + if (pAssoc->data == data) { + if (!pAssoc->nRefCount) { + return -1; + } + --pAssoc->nRefCount; + if (remove && !pAssoc->nRefCount) { + FreeAssoc(pAssoc); + return 0; + } + return pAssoc->nRefCount; + } + pAssoc=GetNextAssoc(pAssoc); + } + return -1; +} + +void Cache::Cleanup() +{ + Cache::MyAssoc* pAssoc=(Cache::MyAssoc *) GetNextAssoc(NULL); + + while (pAssoc) + { + Cache::MyAssoc* nextAssoc = GetNextAssoc(pAssoc); + if (pAssoc->nRefCount == 0) { + FreeAssoc(pAssoc); + } + pAssoc=nextAssoc; + } +} diff --git a/project/jni/application/gemrb/src/core/Cache.h b/project/jni/application/gemrb/src/core/Cache.h new file mode 100644 index 000000000..b83e57641 --- /dev/null +++ b/project/jni/application/gemrb/src/core/Cache.h @@ -0,0 +1,93 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 |Avenger| + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * + */ + +#ifndef CACHE_H +#define CACHE_H + +#include "globals.h" +#include "win32def.h" + +#define KEYSIZE 8 + +#ifndef ReleaseFun +typedef void (*ReleaseFun)(void *); +#endif + +class Cache +{ +protected: + // Association + struct MyAssoc { + MyAssoc* pNext; + MyAssoc** pPrev; + char key[KEYSIZE]; //not ieResRef! + ieDword nRefCount; + void* data; + }; + struct MemBlock { + MemBlock* pNext; + }; + +public: + // Construction + Cache(int nBlockSize = 10, int nHashTableSize = 129); + + // Attributes + // number of elements + inline int GetCount() const + { + return m_nCount; + } + inline bool IsEmpty() const + { + return m_nCount==0; + } + // Lookup + void *GetResource(const ieResRef key) const; + // Operations + bool SetAt(const ieResRef key, void *rValue); + // decreases refcount or drops data + //if name is supplied it is faster, it will use rValue to validate the request + int DecRef(void *rValue, const ieResRef name, bool free); + int RefCount(const ieResRef key) const; + void RemoveAll(ReleaseFun fun);//removes all refcounts + void Cleanup(); //removes only zero refcounts + void InitHashTable(unsigned int hashSize, bool bAllocNow = true); + + // Implementation +protected: + MyAssoc** m_pHashTable; + unsigned int m_nHashTableSize; + int m_nCount; + MyAssoc* m_pFreeList; + MemBlock* m_pBlocks; + int m_nBlockSize; + + Cache::MyAssoc* NewAssoc(); + void FreeAssoc(Cache::MyAssoc*); + Cache::MyAssoc* GetAssocAt(const ieResRef) const; + Cache::MyAssoc *GetNextAssoc(Cache::MyAssoc * rNextPosition) const; + unsigned int MyHashKey(const ieResRef) const; + +public: + ~Cache(); +}; + +#endif //CACHE_H diff --git a/project/jni/application/gemrb/src/core/Calendar.cpp b/project/jni/application/gemrb/src/core/Calendar.cpp new file mode 100644 index 000000000..673bdb040 --- /dev/null +++ b/project/jni/application/gemrb/src/core/Calendar.cpp @@ -0,0 +1,96 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2009 The GemRB Project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * + */ + +#include "Calendar.h" + +#include "win32def.h" + +#include "Interface.h" +#include "TableMgr.h" +#include "Variables.h" + +Calendar::Calendar(void) +{ + int i; + AutoTable tab("months"); + if (!tab) { + monthnamecount=-1; + monthnames = NULL; + days = NULL; + return; + } + monthnamecount = tab->GetRowCount(); + monthnames = (int *) malloc(sizeof(int) * monthnamecount); + days = (int *) malloc(sizeof(int) * monthnamecount); + daysinyear=0; + for(i=0;iQueryField(i,0)); + daysinyear+=days[i]; + monthnames[i]=atoi(tab->QueryField(i,1)); + } +} + +Calendar::~Calendar(void) +{ + if (monthnames) free(monthnames); + if (days) free(days); +} + +void Calendar::GetMonthName(int dayandmonth) const +{ + int month=1; + + for(int i=0;iGetTokenDictionary()->SetAtCopy("DAY", dayandmonth+1); + + tmp = core->GetString( monthnames[i] ); + core->GetTokenDictionary()->SetAt("MONTHNAME",tmp); + //must not free tmp, SetAt doesn't copy the pointer! + + core->GetTokenDictionary()->SetAtCopy("MONTH", month); + return; + } + dayandmonth-=days[i]; + //ignoring single days (they are not months) + if (days[i]!=1) month++; + } +} + +int Calendar::GetCalendarDay(int date) const +{ + int dayandmonth; + int month=1; + + if (!daysinyear) return 0; + dayandmonth = date%daysinyear; + for(int i=0;i { +public: + virtual ~Callback(); + virtual bool call (); + virtual bool call (int); +}; + +typedef Holder EventHandler; + +#endif diff --git a/project/jni/application/gemrb/src/core/CharAnimations.cpp b/project/jni/application/gemrb/src/core/CharAnimations.cpp new file mode 100644 index 000000000..979094834 --- /dev/null +++ b/project/jni/application/gemrb/src/core/CharAnimations.cpp @@ -0,0 +1,2359 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * + */ + +#include "CharAnimations.h" + +#include "win32def.h" + +#include "Game.h" +#include "GameData.h" +#include "ImageMgr.h" +#include "Interface.h" +#include "Map.h" +#include "Palette.h" +#include "Video.h" + +static int AvatarsCount = 0; +static AvatarStruct *AvatarTable = NULL; +static const ieByte SixteenToNine[16]={0,1,2,3,4,5,6,7,8,7,6,5,4,3,2,1}; +static const ieByte SixteenToFive[16]={0,0,1,1,2,2,3,3,4,4,3,3,2,2,1,1}; + +static const int zOrder_Mirror16[16][4] = { + { 0, 3, 2, 1 }, + { 0, 3, 2, 1 }, + { 0, 3, 1, 2 }, + { 0, 3, 1, 2 }, + { 1, 0, 3, 2 }, + { 1, 0, 3, 2 }, + { 1, 0, 3, 2 }, + { 1, 0, 3, 2 }, + { 1, 0, 3, 2 }, + { 1, 0, 3, 2 }, + { 1, 0, 3, 2 }, + { 1, 0, 3, 2 }, + { 1, 0, 3, 2 }, + { 0, 3, 1, 2 }, + { 0, 3, 1, 2 }, + { 0, 3, 2, 1 } +}; + +static const int zOrder_8[8][4] = { + { 0, 3, 2, 1 }, + { 0, 3, 1, 2 }, + { 1, 0, 3, 2 }, + { 1, 0, 3, 2 }, + { 1, 0, 3, 2 }, + { 2, 0, 3, 1 }, + { 2, 0, 3, 1 }, + { 2, 0, 3, 1 } +}; + +struct EquipResRefData { + char Suffix[9]; + unsigned char Cycle; +}; + + +void CharAnimations::ReleaseMemory() +{ + if (AvatarTable) { + free(AvatarTable); + AvatarTable=NULL; + } +} + +int CharAnimations::GetAvatarsCount() +{ + return AvatarsCount; +} + +AvatarStruct *CharAnimations::GetAvatarStruct(int RowNum) +{ + return AvatarTable+RowNum; +} + +unsigned int CharAnimations::GetAnimationID() const +{ + if (AvatarsRowNum==~0u) return 0; + return AvatarTable[AvatarsRowNum].AnimID; +} + +int CharAnimations::GetCircleSize() const +{ + if (AvatarsRowNum==~0u) return -1; + return AvatarTable[AvatarsRowNum].CircleSize; +} +int CharAnimations::NoPalette() const +{ + if (AvatarsRowNum==~0u) return -1; + return AvatarTable[AvatarsRowNum].PaletteType; +} + +int CharAnimations::GetAnimType() const +{ + if (AvatarsRowNum==~0u) return -1; + return AvatarTable[AvatarsRowNum].AnimationType; +} + +int CharAnimations::GetSize() const +{ + if (AvatarsRowNum==~0u) return 0; + return AvatarTable[AvatarsRowNum].Size; +} + +int CharAnimations::GetBloodColor() const +{ + if(AvatarsRowNum==~0u) return 0; + return AvatarTable[AvatarsRowNum].BloodColor; +} + +static ieResRef EmptySound={0}; + +const ieResRef &CharAnimations::GetWalkSound() const +{ + if(AvatarsRowNum==~0u) return EmptySound; + return AvatarTable[AvatarsRowNum].WalkSound; +} + +int CharAnimations::GetWalkSoundCount() const +{ + if(AvatarsRowNum==~0u) return 0; + return AvatarTable[AvatarsRowNum].WalkSoundCount; +} + +int CharAnimations::GetActorPartCount() const +{ + if (AvatarsRowNum==~0u) return -1; + switch (AvatarTable[AvatarsRowNum].AnimationType) { + case IE_ANI_NINE_FRAMES: //dragon animations + return 9; + case IE_ANI_FOUR_FRAMES: //wyvern animations + return 4; + case IE_ANI_PST_GHOST: //special pst anims + if (AvatarTable[AvatarsRowNum].Prefixes[1][0]=='*') { + return 1; + } + if (AvatarTable[AvatarsRowNum].Prefixes[2][0]=='*') { + return 2; + } + if (AvatarTable[AvatarsRowNum].Prefixes[3][0]=='*') { + return 3; + } + return 4; + default: + return 1; + } +} + +int CharAnimations::GetTotalPartCount() const +{ + if (AvatarsRowNum==~0u) return -1; + switch (AvatarTable[AvatarsRowNum].AnimationType) { + case IE_ANI_FOUR_FILES: + case IE_ANI_FOUR_FILES_2: + return GetActorPartCount() + 1; // only weapon + case IE_ANI_CODE_MIRROR: + return GetActorPartCount() + 3; // equipment + case IE_ANI_TWENTYTWO: + return GetActorPartCount() + 3; // equipment + default: + return GetActorPartCount(); + } +} + +void CharAnimations::SetArmourLevel(int ArmourLevel) +{ + if (AvatarsRowNum==~0u) return; + //ignore ArmourLevel for the static pst anims (all sprites are displayed) + if (AvatarTable[AvatarsRowNum].AnimationType == IE_ANI_PST_GHOST) { + ArmourLevel = 0; + } + strncpy( ResRef, AvatarTable[AvatarsRowNum].Prefixes[ArmourLevel], 8 ); + ResRef[8]=0; + DropAnims(); +} + +//RangedType could be weird, reducing its value to 0,1,2 +void CharAnimations::SetRangedType(int rt) +{ + if ((unsigned int) rt<2) { + RangedType=(ieByte) rt; + } else { + RangedType=2; + } +} + +void CharAnimations::SetWeaponType(int wt) +{ + if (wt != WeaponType) { + WeaponType = wt; + DropAnims(); + } +} + +void CharAnimations::SetHelmetRef(const char* ref) +{ + HelmetRef[0] = ref[0]; + HelmetRef[1] = ref[1]; + + // TODO: Only drop helmet anims? + // Note: this doesn't happen "often", so this isn't a performance + // bottleneck. (wjp) + DropAnims(); + gamedata->FreePalette(palette[PAL_HELMET], 0); + gamedata->FreePalette(modifiedPalette[PAL_HELMET], 0); +} + +void CharAnimations::SetWeaponRef(const char* ref) +{ + WeaponRef[0] = ref[0]; + WeaponRef[1] = ref[1]; + + // TODO: Only drop weapon anims? + DropAnims(); + gamedata->FreePalette(palette[PAL_WEAPON], 0); + gamedata->FreePalette(modifiedPalette[PAL_WEAPON], 0); +} + +void CharAnimations::SetOffhandRef(const char* ref) +{ + OffhandRef[0] = ref[0]; + OffhandRef[1] = ref[1]; + + // TODO: Only drop shield/offhand anims? + DropAnims(); + gamedata->FreePalette(palette[PAL_OFFHAND], 0); + gamedata->FreePalette(modifiedPalette[PAL_OFFHAND], 0); +} + +void CharAnimations::LockPalette(const ieDword *gradients) +{ + if (lockPalette) return; + //cannot lock colors for PST animations + if (GetAnimType() >= IE_ANI_PST_ANIMATION_1) + { + return; + } + //force initialisation of animation + SetColors( gradients ); + GetAnimation(0,0); + if (palette[PAL_MAIN]) { + lockPalette=true; + } +} + +// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 +static const char *StancePrefix[]={"3","2","5","5","4","4","2","2","5","4","1","3","3","3","4","1","4","4","4"}; +static const char *CyclePrefix[]= {"0","0","1","1","1","1","0","0","1","1","0","1","1","1","1","1","1","1","1"}; +static const unsigned int CycleOffset[] = {0, 0, 0, 0, 0, 9, 0, 0, 0, 18, 0, 0, 9, 18, 0, 0, 0, 0, 0}; + +void CharAnimations::SetColors(const ieDword *arg) +{ + Colors = arg; + SetupColors(PAL_MAIN); + SetupColors(PAL_WEAPON); + SetupColors(PAL_OFFHAND); + SetupColors(PAL_HELMET); +} + +void CharAnimations::SetupColors(PaletteType type) +{ + Palette* pal = palette[(int)type]; + + if (!pal) { + return; + } + + if (!Colors) { + return; + } + + if (GetAnimType() >= IE_ANI_PST_ANIMATION_1) { + // Only do main palette + if (type != PAL_MAIN) { + return; + } + // TODO: handle equipment colour glows + + // Colors[6] is the COLORCOUNT stat in PST. + // It tells how many customisable color slots we have. + // The color slots start from the end of the palette and go + // backwards. There are 6 available slots with a size of 32 each. + // Actually, the slots seem to be written in the cre file + // but we ignore them, i'm not sure this is correct + int colorcount = Colors[6]; + int size = 32; + //the color count shouldn't be more than 6! + if (colorcount>6) colorcount=6; + int dest = 256-colorcount*size; + bool needmod = false; + if (GlobalColorMod.type != RGBModifier::NONE) { + needmod = true; + } + if ((colorcount == 0) && (needmod==false) ) { + gamedata->FreePalette(palette[PAL_MAIN], PaletteResRef); + PaletteResRef[0]=0; + return; + } + for (int i = 0; i < colorcount; i++) { + core->GetPalette( Colors[i]&255, size, + &palette[PAL_MAIN]->col[dest] ); + dest +=size; + } + + if (needmod) { + if (!modifiedPalette[PAL_MAIN]) + modifiedPalette[PAL_MAIN] = new Palette(); + modifiedPalette[PAL_MAIN]->SetupGlobalRGBModification(palette[PAL_MAIN], GlobalColorMod); + } else { + gamedata->FreePalette(modifiedPalette[PAL_MAIN], 0); + } + return; + } + + int PType = NoPalette(); + if ( PType && (type == PAL_MAIN) ) { + bool needmod = false; + if (GlobalColorMod.type != RGBModifier::NONE) { + needmod = true; + } + if (!needmod && PaletteResRef[0]) { + gamedata->FreePalette(palette[PAL_MAIN], PaletteResRef); + } + PaletteResRef[0]=0; + //handling special palettes like MBER_BL (black bear) + if (PType!=1) { + if (GetAnimType()==IE_ANI_NINE_FRAMES) { + snprintf(PaletteResRef,9,"%.4s_%-.2s%s",ResRef, (char *) &PType, StancePrefix[StanceID]); + } else { + snprintf(PaletteResRef,9,"%.4s_%-.2s",ResRef, (char *) &PType); + } + strlwr(PaletteResRef); + Palette *tmppal = gamedata->GetPalette(PaletteResRef); + if (tmppal) { + palette[PAL_MAIN] = tmppal; + } else { + PaletteResRef[0]=0; + } + } + if (needmod) { + if (!modifiedPalette[PAL_MAIN]) + modifiedPalette[PAL_MAIN] = new Palette(); + modifiedPalette[PAL_MAIN]->SetupGlobalRGBModification(palette[PAL_MAIN], GlobalColorMod); + } else { + gamedata->FreePalette(modifiedPalette[PAL_MAIN], 0); + } + return; + } + + pal->SetupPaperdollColours(Colors, (int)type); + if (lockPalette) { + return; + } + + int i; + bool needmod = false; + if (GlobalColorMod.type != RGBModifier::NONE) { + needmod = true; + } else { + for (i = 0; i < 7; ++i) { + if (ColorMods[i+8*((int)type)].type != RGBModifier::NONE) + needmod = true; + } + } + + + if (needmod) { + if (!modifiedPalette[(int)type]) + modifiedPalette[(int)type] = new Palette(); + + if (GlobalColorMod.type != RGBModifier::NONE) { + modifiedPalette[(int)type]->SetupGlobalRGBModification(palette[(int)type], GlobalColorMod); + } else { + modifiedPalette[(int)type]->SetupRGBModification(palette[(int)type],ColorMods, (int)type); + } + } else { + gamedata->FreePalette(modifiedPalette[(int)type], 0); + } + +} + +Palette* CharAnimations::GetPartPalette(int part) +{ + int actorPartCount = GetActorPartCount(); + PaletteType type = PAL_MAIN; + + if (part == actorPartCount) type = PAL_WEAPON; + if (part == actorPartCount+1) type = PAL_OFFHAND; + if (part == actorPartCount+2) type = PAL_HELMET; + + if (modifiedPalette[(int)type]) + return modifiedPalette[(int)type]; + + return palette[(int)type]; +} + +static int compare_avatars(const void *a, const void *b) +{ + unsigned int aa = ((AvatarStruct *)a)->AnimID; + unsigned int bb = ((AvatarStruct *)b)->AnimID; + return (int) (aa-bb); +} + +void CharAnimations::InitAvatarsTable() +{ + AutoTable Avatars("avatars"); + if (!Avatars) { + printMessage("CharAnimations", "A critical animation file is missing!\n", LIGHT_RED); + abort(); + } + AvatarTable = (AvatarStruct *) calloc ( AvatarsCount = Avatars->GetRowCount(), sizeof(AvatarStruct) ); + int i=AvatarsCount; + DataFileMgr *resdata = core->GetResDataINI(); + while(i--) { + AvatarTable[i].AnimID=(unsigned int) strtol(Avatars->GetRowName(i),NULL,0 ); + strnlwrcpy(AvatarTable[i].Prefixes[0],Avatars->QueryField(i,AV_PREFIX1),8); + strnlwrcpy(AvatarTable[i].Prefixes[1],Avatars->QueryField(i,AV_PREFIX2),8); + strnlwrcpy(AvatarTable[i].Prefixes[2],Avatars->QueryField(i,AV_PREFIX3),8); + strnlwrcpy(AvatarTable[i].Prefixes[3],Avatars->QueryField(i,AV_PREFIX4),8); + AvatarTable[i].AnimationType=(ieByte) atoi(Avatars->QueryField(i,AV_ANIMTYPE) ); + AvatarTable[i].CircleSize=(ieByte) atoi(Avatars->QueryField(i,AV_CIRCLESIZE) ); + const char *tmp = Avatars->QueryField(i,AV_USE_PALETTE); + //QueryField will always return a zero terminated string + //so tmp[0] must exist + if ( isalpha (tmp[0]) ) { + //this is a hack, we store 2 letters on an integer + //it was allocated with calloc, so don't bother erasing it + strncpy( (char *) &AvatarTable[i].PaletteType, tmp, 3); + } + else { + AvatarTable[i].PaletteType=atoi(Avatars->QueryField(i,AV_USE_PALETTE) ); + } + char size = Avatars->QueryField(i,AV_SIZE)[0]; + if (size == '*') { + size = 0; + } + AvatarTable[i].Size = size; + + AvatarTable[i].WalkScale = 0; + AvatarTable[i].RunScale = 0; + AvatarTable[i].Bestiary = -1; + + if (resdata) { + char section[12]; + snprintf(section,10,"%d", i); + + if (!resdata->GetKeysCount(section)) continue; + + float walkscale = resdata->GetKeyAsFloat(section, "walkscale", 0.0f); + if (walkscale != 0.0f) AvatarTable[i].WalkScale = (int)(1000.0f / walkscale); + float runscale = resdata->GetKeyAsFloat(section, "runscale", 0.0f); + if (runscale != 0.0f) AvatarTable[i].RunScale = (int)(1000.0f / runscale); + AvatarTable[i].Bestiary = resdata->GetKeyAsInt(section, "bestiary", -1); + } + } + qsort(AvatarTable, AvatarsCount, sizeof(AvatarStruct), compare_avatars); + + + AutoTable blood("bloodclr"); + if (blood) { + int rows = blood->GetRowCount(); + for(int i=0;iQueryField(i,0), (long &)value); + valid_number(blood->QueryField(i,1), (long &)rmin); + valid_number(blood->QueryField(i,2), (long &)rmax); + if (value>255 || rmin>0xffff || rmax>0xffff) { + printMessage("CharAnimations", "bloodclr entry:", LIGHT_RED); + printf("%02x %04x-%04x ", (unsigned int) value, (unsigned int) rmin, (unsigned int) rmax); + printStatus("Invalid value!", LIGHT_RED); + continue; + } + for(int j=0;jAvatarTable[j].AnimID) continue; + AvatarTable[j].BloodColor = value; + } + } + } + + AutoTable walk("walksnd"); + if (walk) { + int rows = walk->GetRowCount(); + for(int i=0;iQueryField(i,0), 8); + valid_number(walk->QueryField(i,1), (long &)rmin); + valid_number(walk->QueryField(i,2), (long &)rmax); + valid_number(walk->QueryField(i,3), (long &)range); + if (value[0]=='*') { + value[0]=0; + range = 0; + } + for(int j=0;jAvatarTable[j].AnimID) continue; + memcpy(AvatarTable[j].WalkSound, value, sizeof(ieResRef) ); + AvatarTable[j].WalkSoundCount = range; + } + } + } +} + +CharAnimations::CharAnimations(unsigned int AnimID, ieDword ArmourLevel) +{ + Colors = NULL; + int i,j; + for (i = 0; i < 4; ++i) { + modifiedPalette[i] = NULL; + palette[i] = NULL; + } + nextStanceID = 0; + StanceID = 0; + autoSwitchOnEnd = false; + lockPalette = false; + if (!AvatarsCount) { + InitAvatarsTable(); + } + + for (i = 0; i < MAX_ANIMS; i++) { + for (j = 0; j < MAX_ORIENT; j++) { + Anims[i][j] = NULL; + } + } + ArmorType = 0; + RangedType = 0; + WeaponType = 0; + PaletteResRef[0] = 0; + WeaponRef[0] = 0; + HelmetRef[0] = 0; + OffhandRef[0] = 0; + for (i = 0; i < 32; ++i) { + ColorMods[i].type = RGBModifier::NONE; + ColorMods[i].speed = 0; + // make initial phase depend on location to make the pulse appear + // less even + ColorMods[i].phase = 5*i; + } + GlobalColorMod.type = RGBModifier::NONE; + GlobalColorMod.speed = 0; + GlobalColorMod.phase = 0; + lastModUpdate = 0; + + AvatarsRowNum=AvatarsCount; + if (core->HasFeature(GF_ONE_BYTE_ANIMID) ) { + ieDword tmp = AnimID&0xf000; + if (tmp==0x6000 || tmp==0xe000) { + AnimID&=0xff; + } + } + + while (AvatarsRowNum--) { + if (AvatarTable[AvatarsRowNum].AnimID<=AnimID) { + SetArmourLevel( ArmourLevel ); + return; + } + } + ResRef[0]=0; + printMessage("CharAnimations", " ", LIGHT_RED); + printf("Invalid or nonexistent avatar entry:%04X\n", AnimID); +} + +//we have to drop them when armourlevel changes +void CharAnimations::DropAnims() +{ + Animation** tmppoi; + int partCount = GetTotalPartCount(); + for (int StanceID = 0; StanceID < MAX_ANIMS; StanceID++) { + for (int i = 0; i < MAX_ORIENT; i++) { + if (Anims[StanceID][i]) { + tmppoi = Anims[StanceID][i]; + for (int j = 0; j < partCount; j++) + delete Anims[StanceID][i][j]; + delete[] tmppoi; + + // anims can only be duplicated at the Animation** level + for (int IDb=StanceID;IDb < MAX_ANIMS; IDb++) { + for (int i2 = 0; i2FreePalette(palette[PAL_MAIN], PaletteResRef); + int i; + for (i = 1; i < 4; ++i) + gamedata->FreePalette(palette[i], 0); + for (i = 0; i < 4; ++i) + gamedata->FreePalette(modifiedPalette[i], 0); +} +/* +This is a simple Idea of how the animation are coded + +There are the following animation types: + +IE_ANI_CODE_MIRROR: The code automatically mirrors the needed frames + (as in the example above) + + These Animations are stores using the following template: + [NAME][ARMORTYPE][ACTIONCODE] + + Each BAM File contains only 9 Orientations, the missing 7 Animations + are created by Horizontally Mirroring the 1-7 Orientations. + +IE_ANI_CODE_MIRROR_2: another mirroring type with more animations + [NAME]g[1,11-15,2,21-26] + +IE_ANI_CODE_MIRROR_3: Almost identical to IE_ANI_CODE_MIRROR_2, but with fewer cycles in g26 + +IE_ANI_ONE_FILE: The whole animation is in one file, no mirroring needed. + Each animation group is 16 Cycles. + +IE_ANI_TWO_FILES: The whole animation is in 2 files. The East and West part are in 2 BAM Files. + Example: + ACHKg1 + ACHKg1E + + Each BAM File contains many animation groups, each animation group + stores 5 Orientations, the missing 3 are stored in East BAM Files. + + +IE_ANI_FOUR_FILES: The Animation is coded in Four Files. Probably it is an old Two File animation with + additional frames added in a second time. + +IE_ANI_FOUR_FILES_2: Like IE_ANI_FOUR_FILES but with only 16 cycles per frame. + +IE_ANI_TWENTYTWO: This Animation Type stores the Animation in the following format + [NAME][ACTIONCODE][/E] + ACTIONCODE=A1-6, CA, SX, SA (sling is A1) + The g1 file contains several animation states. See MHR + Probably it could use A7-9 files too, bringing the file numbers to 28. + This is the original bg1 format. + +IE_ANI_SIX_FILES: The layout for these files is: + [NAME][g1-3][/E] + Each state contains 16 Orientations, but the last 6 are stored in the East file. + g1 contains only the walking animation. + G2 contains stand, ready, get hit, die and twitch. + g3 contains 3 attacks. + +IE_ANI_SIX_FILES_2: Similar to SIX_FILES, but the orientation numbers are reduced like in FOUR_FILES. Only one animation uses it: MOGR + +IE_ANI_TWO_FILES_2: Animations using this type are stored using the following template: + [NAME]g1[/E] + Each state contains 8 Orientations, but the second 4 are stored in the East file. + From the standard animations, only AHRS and ACOW belong to this type. + +IE_ANI_TWO_FILES_3: Animations using this type are stored using the following template: + [NAME][ACTIONTYPE][/E] + + Example: + MBFI* + MBFI*E + + Each BAM File contains one animation group, each animation group + stores 5 Orientations though the files contain all 8 Cycles, the missing 3 are stored in East BAM Files in Cycle: Stance*8+ (5,6,7). + This is the standard IWD animation, but BG2 also has it. + See MMR + +IE_ANI_TWO_FILES_3B: Animations using this type are stored using the following template: + [NAME][ACTIONTYPE][/E] + + Example: + MBFI* + MBFI*E + + This is a cut down version of IE_ANI_TWO_FILES_3. A2, CA and SP suffixes are missing. + This is the standard IWD animation, but BG2 also has it. + See MOR2 + +IE_ANI_FOUR_FRAMES: These animations are large, four bams make a frame. + + +IE_ANI_NINE_FRAMES: These animations are huge, nine bams make a frame. + + +IE_ANI_FRAGMENT: These animations are used for projectile graphics. + A single file contains 5 cycles (code mirror for east animation) + +IE_ANI_PST_ANIMATION_1: +IE_ANI_PST_ANIMATION_2: +IE_ANI_PST_ANIMATION_3: + Planescape: Torment Animations are stored in a different + way than the other games. This format uses the following template: + [C/D][ACTIONTYPE][NAME][B] + + Example: + CAT1MRTB + + Each Animation stores 5 Orientations, which are automatically mirrored + to form an 8 Orientation Animation. PST Animations have a different Palette + format. This Animation Type handles the PST Palette format too. + + NOTE: Walking/Running animations store 9 Orientations. + The second variation is missing the resting stance (STD) and the transitions. + These creatures are always in combat stance (don't rest). + Animation_3 is without STC (combat stance), they are always standing + +IE_ANI_PST_STAND: This is a modified PST animation, it contains only a + Standing image for every orientations, it follows the + [C/D]STD[NAME][B] standard. + +IE_ANI_PST_GHOST: This is a special static animation with no standard + All armourlevels are drawn simultaneously. There is no orientation or stance. + + + WEST PART | EAST PART + | + NW NNW N NNE NE + NW 006 007 008 009 010 NE +WNW 005 | 011 ENE + W 004 xxx 012 E +WSW 003 | 013 ESE + SW 002 001 000 015 014 SE + SW SSW S SSE SE + | + | + +*/ + +Animation** CharAnimations::GetAnimation(unsigned char Stance, unsigned char Orient) +{ + if (StanceID>=MAX_ANIMS) { + printf("Illegal stance ID\n"); + abort(); + } + + //for paletted dragon animations, we need the stance id + StanceID = nextStanceID = Stance; + int AnimType = GetAnimType(); + + //alter stance here if it is missing and you know a substitute + //probably we should feed this result back to the actor? + switch (AnimType) { + case -1: //invalid animation + return NULL; + + case IE_ANI_PST_STAND: + StanceID=IE_ANI_AWAKE; + break; + case IE_ANI_PST_GHOST: + StanceID=IE_ANI_AWAKE; + Orient=0; + break; + case IE_ANI_PST_ANIMATION_3: //stc->std + if (StanceID==IE_ANI_READY) { + StanceID=IE_ANI_AWAKE; + } + break; + case IE_ANI_PST_ANIMATION_2: //std->stc + if (StanceID==IE_ANI_AWAKE) { + StanceID=IE_ANI_READY; + } + break; + } + //pst animations don't have separate animation for sleep/die + if (AnimType >= IE_ANI_PST_ANIMATION_1) { + if (StanceID==IE_ANI_DIE) { + StanceID=IE_ANI_TWITCH; + } + } + + //TODO: Implement Auto Resource Loading + //setting up the sequencing of animation cycles + autoSwitchOnEnd = false; + switch (StanceID) { + case IE_ANI_DAMAGE: + nextStanceID = IE_ANI_READY; + autoSwitchOnEnd = true; + break; + case IE_ANI_SLEEP: //going to sleep + nextStanceID = IE_ANI_TWITCH; + autoSwitchOnEnd = true; + break; + case IE_ANI_TWITCH: //dead, sleeping + autoSwitchOnEnd = false; + break; + case IE_ANI_DIE: //going to die + nextStanceID = IE_ANI_TWITCH; + autoSwitchOnEnd = true; + break; + case IE_ANI_WALK: + case IE_ANI_RUN: + case IE_ANI_CAST: // looping + case IE_ANI_READY: + break; + case IE_ANI_AWAKE: + break; + case IE_ANI_EMERGE: + case IE_ANI_GET_UP: + case IE_ANI_HEAD_TURN: + case IE_ANI_PST_START: + nextStanceID = IE_ANI_AWAKE; + autoSwitchOnEnd = true; + break; + case IE_ANI_CONJURE: //ending + case IE_ANI_SHOOT: + case IE_ANI_ATTACK: + case IE_ANI_ATTACK_JAB: + case IE_ANI_ATTACK_SLASH: + case IE_ANI_ATTACK_BACKSLASH: + nextStanceID = IE_ANI_READY; + autoSwitchOnEnd = true; + break; + default: + printf ("Invalid Stance: %d\n", StanceID); + break; + } + Animation** anims = Anims[StanceID][Orient]; + + if (anims) { + return anims; + } + + int partCount = GetTotalPartCount(); + int actorPartCount = GetActorPartCount(); + if (partCount < 0) return 0; + anims = new Animation*[partCount]; + + EquipResRefData* equipdat = 0; + for (int part = 0; part < partCount; ++part) + { + anims[part] = 0; + + //newresref is based on the prefix (ResRef) and various + // other things. + //this is longer than expected so it won't overflow + char NewResRef[12]; + unsigned char Cycle = 0; + if (part < actorPartCount) { + // Character animation parts + + if (equipdat) delete equipdat; + + //we need this long for special anims + strncpy( NewResRef, ResRef, 8 ); + GetAnimResRef( StanceID, Orient, NewResRef, Cycle, part, equipdat); + } else { + // Equipment animation parts + + anims[part] = 0; + if (GetSize() == 0) continue; + + if (part == actorPartCount) { + if (WeaponRef[0] == 0) continue; + // weapon + GetEquipmentResRef(WeaponRef,false,NewResRef,Cycle,equipdat); + } else if (part == actorPartCount+1) { + if (OffhandRef[0] == 0) continue; + if (WeaponType == IE_ANI_WEAPON_2H) continue; + // off-hand + if (WeaponType == IE_ANI_WEAPON_1H) { + GetEquipmentResRef(OffhandRef,false,NewResRef,Cycle, + equipdat); + } else { // IE_ANI_WEAPON_2W + GetEquipmentResRef(OffhandRef,true,NewResRef,Cycle, + equipdat); + } + } else if (part == actorPartCount+2) { + if (HelmetRef[0] == 0) continue; + // helmet + GetEquipmentResRef(HelmetRef,false,NewResRef,Cycle,equipdat); + } + } + NewResRef[8]=0; //cutting right to size + + AnimationFactory* af = ( AnimationFactory* ) + gamedata->GetFactoryResource( NewResRef, + IE_BAM_CLASS_ID, IE_NORMAL ); + + if (!af) { + if (part < actorPartCount) { + char warnbuf[200]; + snprintf(warnbuf, 200, + "Couldn't create animationfactory: %s (%04x)\n", NewResRef, GetAnimationID()); + printMessage("CharAnimations",warnbuf,LIGHT_RED); + for (int i = 0; i < part; ++i) + delete anims[i]; + delete[] anims; + delete equipdat; + return 0; + } else { + // not fatal if animation for equipment is missing + continue; + } + } + + Animation* a = af->GetCycle( Cycle ); + anims[part] = a; + + if (!a) { + if (part < actorPartCount) { + char warnbuf[200]; + snprintf(warnbuf, 200, + "Couldn't load animation: %s, cycle %d\n", + NewResRef, Cycle); + printMessage("CharAnimations",warnbuf,LIGHT_RED); + for (int i = 0; i < part; ++i) + delete anims[i]; + delete[] anims; + delete equipdat; + return 0; + } else { + // not fatal if animation for equipment is missing + continue; + } + } + + if (part < actorPartCount) { + //if you need to revert this change, consider true paletted + //animations which need a GlobalColorMod (mgir for example) + + //if (!palette[PAL_MAIN] && ((GlobalColorMod.type!=RGBModifier::NONE) || (NoPalette()!=1)) ) { + if(!palette[PAL_MAIN]) { + // This is the first time we're loading an Animation. + // We copy the palette of its first frame into our own palette + palette[PAL_MAIN] = a->GetFrame(0)->GetPalette()->Copy(); + // ...and setup the colours properly + SetupColors(PAL_MAIN); + } + } else if (part == actorPartCount) { + if (!palette[PAL_WEAPON]) { + palette[PAL_WEAPON] = a->GetFrame(0)->GetPalette()->Copy(); + SetupColors(PAL_WEAPON); + } + } else if (part == actorPartCount+1) { + if (!palette[PAL_OFFHAND]) { + palette[PAL_OFFHAND] = a->GetFrame(0)->GetPalette()->Copy(); + SetupColors(PAL_OFFHAND); + } + } else if (part == actorPartCount+2) { + if (!palette[PAL_HELMET]) { + palette[PAL_HELMET] = a->GetFrame(0)->GetPalette()->Copy(); + SetupColors(PAL_HELMET); + } + } + + //animation is affected by game flags + a->gameAnimation = true; + a->SetPos( 0 ); + + //setting up the sequencing of animation cycles + switch (StanceID) { + case IE_ANI_DAMAGE: + case IE_ANI_SLEEP: + case IE_ANI_TWITCH: + case IE_ANI_DIE: + case IE_ANI_PST_START: + case IE_ANI_HEAD_TURN: + case IE_ANI_CONJURE: + case IE_ANI_SHOOT: + case IE_ANI_ATTACK: + case IE_ANI_ATTACK_JAB: + case IE_ANI_ATTACK_SLASH: + case IE_ANI_ATTACK_BACKSLASH: + a->Flags |= A_ANI_PLAYONCE; + break; + case IE_ANI_EMERGE: + case IE_ANI_GET_UP: + a->playReversed = true; + a->Flags |= A_ANI_PLAYONCE; + break; + } + switch (GetAnimType()) { + case IE_ANI_NINE_FRAMES: //dragon animations + case IE_ANI_FOUR_FRAMES: //wyvern animations + case IE_ANI_BIRD: + case IE_ANI_CODE_MIRROR: + case IE_ANI_CODE_MIRROR_2: //9 orientations + case IE_ANI_CODE_MIRROR_3: + case IE_ANI_PST_ANIMATION_3: //no stc just std + case IE_ANI_PST_ANIMATION_2: //no std just stc + case IE_ANI_PST_ANIMATION_1: + case IE_ANI_FRAGMENT: + if (Orient > 8) { + a->MirrorAnimation( ); + } + break; + default: + break; + } + + // make animarea of part 0 encompass the animarea of the other parts + if (part > 0) + anims[0]->AddAnimArea(a); + + } + + switch (GetAnimType()) { + case IE_ANI_NINE_FRAMES: //dragon animations + case IE_ANI_FOUR_FRAMES: //wyvern animations + case IE_ANI_BIRD: + case IE_ANI_CODE_MIRROR: + case IE_ANI_SIX_FILES: //16 anims some are stored elsewhere + case IE_ANI_ONE_FILE: //16 orientations + case IE_ANI_CODE_MIRROR_2: //9 orientations + case IE_ANI_CODE_MIRROR_3: + Anims[StanceID][Orient] = anims; + break; + case IE_ANI_TWO_FILES: + case IE_ANI_TWENTYTWO: + case IE_ANI_TWO_FILES_2: + case IE_ANI_TWO_FILES_3: + case IE_ANI_TWO_FILES_3B: + case IE_ANI_FOUR_FILES: + case IE_ANI_FOUR_FILES_2: + case IE_ANI_SIX_FILES_2: + case IE_ANI_FRAGMENT: + Orient&=~1; + Anims[StanceID][Orient] = anims; + Anims[StanceID][Orient + 1] = anims; + break; + + case IE_ANI_PST_ANIMATION_3: //no stc just std + case IE_ANI_PST_ANIMATION_2: //no std just stc + case IE_ANI_PST_ANIMATION_1: + switch (StanceID) { + case IE_ANI_WALK: + case IE_ANI_RUN: + case IE_ANI_PST_START: + Anims[StanceID][Orient] = anims; + break; + default: + Orient &=~1; + Anims[StanceID][Orient] = anims; + Anims[StanceID][Orient + 1] = anims; + break; + } + break; + + case IE_ANI_PST_STAND: + Orient &=~1; + Anims[StanceID][Orient] = anims; + Anims[StanceID][Orient+1] = anims; + break; + case IE_ANI_PST_GHOST: + Orient = 0; + StanceID = IE_ANI_AWAKE; + Anims[StanceID][0] = anims; + break; + default: + printMessage("CharAnimations","Unknown animation type\n",LIGHT_RED); + abort(); + } + delete equipdat; + + return Anims[StanceID][Orient]; +} + +static const int one_file[19]={2, 1, 0, 0, 2, 3, 0, 1, 0, 4, 1, 0, 0, 0, 3, 1, 4, 4, 4}; + +void CharAnimations::GetAnimResRef(unsigned char StanceID, + unsigned char Orient, + char* NewResRef, unsigned char& Cycle, + int Part, EquipResRefData*& EquipData) +{ + char tmp[256]; + EquipData = 0; + Orient &= 15; + switch (GetAnimType()) { + case IE_ANI_FOUR_FRAMES: + AddFFSuffix( NewResRef, StanceID, Cycle, Orient, Part ); + break; + + case IE_ANI_NINE_FRAMES: + AddNFSuffix( NewResRef, StanceID, Cycle, Orient, Part ); + break; + + case IE_ANI_CODE_MIRROR: + AddVHRSuffix( NewResRef, StanceID, Cycle, Orient, EquipData ); + break; + + case IE_ANI_BIRD: + Cycle = (ieByte) ((StanceID&1) * 9 + SixteenToNine[Orient]); + break; + + case IE_ANI_FRAGMENT: + Cycle = SixteenToFive[Orient]; + break; + + case IE_ANI_ONE_FILE: + Cycle = (ieByte) (one_file[StanceID] * 16 + Orient); + break; + + case IE_ANI_SIX_FILES: + AddSixSuffix( NewResRef, StanceID, Cycle, Orient ); + break; + + case IE_ANI_TWENTYTWO: //5+3 animations + AddMHRSuffix( NewResRef, StanceID, Cycle, Orient, EquipData ); + break; + + case IE_ANI_TWO_FILES_2: //4+4 animations + AddLR2Suffix( NewResRef, StanceID, Cycle, Orient ); + break; + + case IE_ANI_TWO_FILES_3: //IWD style anims + AddMMRSuffix( NewResRef, StanceID, Cycle, Orient ); + break; + + case IE_ANI_TWO_FILES_3B: //IWD style anims + AddMMR2Suffix( NewResRef, StanceID, Cycle, Orient ); + break; + + case IE_ANI_TWO_FILES: + AddTwoFileSuffix(NewResRef, StanceID, Cycle, Orient ); + break; + + case IE_ANI_FOUR_FILES: + AddLRSuffix( NewResRef, StanceID, Cycle, Orient, EquipData ); + break; + + case IE_ANI_FOUR_FILES_2: + AddLRSuffix2( NewResRef, StanceID, Cycle, Orient, EquipData ); + break; + + case IE_ANI_SIX_FILES_2: //MOGR (variant of FOUR_FILES) + AddLR3Suffix( NewResRef, StanceID, Cycle, Orient ); + break; + + case IE_ANI_CODE_MIRROR_2: //9 orientations + AddVHR2Suffix( NewResRef, StanceID, Cycle, Orient ); + break; + + case IE_ANI_CODE_MIRROR_3: // like IE_ANI_CODE_MIRROR_2 but with fewer cycles in g26 + AddVHR3Suffix( NewResRef, StanceID, Cycle, Orient ); + break; + + case IE_ANI_PST_ANIMATION_1: + case IE_ANI_PST_ANIMATION_2: + case IE_ANI_PST_ANIMATION_3: + AddPSTSuffix( NewResRef, StanceID, Cycle, Orient ); + break; + + case IE_ANI_PST_STAND: + sprintf(NewResRef,"%cSTD%4s",ResRef[0], ResRef+1); + Cycle = (ieByte) SixteenToFive[Orient]; + break; + case IE_ANI_PST_GHOST: // pst static animations + //still doesn't handle the second cycle of the golem anim + Cycle = 0; + strnlwrcpy(NewResRef, AvatarTable[AvatarsRowNum].Prefixes[Part], 8); + break; + default: + sprintf (tmp,"Unknown animation type in avatars.2da row: %d\n", AvatarsRowNum); + printMessage ("CharAnimations",tmp, LIGHT_RED); + abort(); + } +} + +void CharAnimations::GetEquipmentResRef(const char* equipRef, bool offhand, + char* ResRef, unsigned char& Cycle, EquipResRefData* equip) +{ + switch (GetAnimType()) { + case IE_ANI_FOUR_FILES: + case IE_ANI_FOUR_FILES_2: + GetLREquipmentRef( ResRef, Cycle, equipRef, offhand, equip ); + break; + case IE_ANI_CODE_MIRROR: + GetVHREquipmentRef( ResRef, Cycle, equipRef, offhand, equip ); + break; + case IE_ANI_TWENTYTWO: + GetMHREquipmentRef( ResRef, Cycle, equipRef, offhand, equip ); + break; + default: + printMessage ("CharAnimations", "Unsupported animation type for equipment animation.\n", LIGHT_RED); + abort(); + break; + } +} + +const int* CharAnimations::GetZOrder(unsigned char Orient) +{ + switch (GetAnimType()) { + case IE_ANI_CODE_MIRROR: + return zOrder_Mirror16[Orient]; + case IE_ANI_TWENTYTWO: + return zOrder_8[Orient/2]; + case IE_ANI_FOUR_FILES: + return 0; // FIXME + default: + return 0; + } +} + + +void CharAnimations::AddPSTSuffix(char* ResRef, unsigned char StanceID, + unsigned char& Cycle, unsigned char Orient) +{ + const char *Prefix; + + switch (StanceID) { + case IE_ANI_ATTACK: + case IE_ANI_ATTACK_SLASH: + case IE_ANI_ATTACK_JAB: + case IE_ANI_ATTACK_BACKSLASH: + Cycle=SixteenToFive[Orient]; + Prefix="at1"; break; + case IE_ANI_DAMAGE: + Cycle=SixteenToFive[Orient]; + Prefix="hit"; break; + case IE_ANI_GET_UP: + case IE_ANI_EMERGE: + Cycle=SixteenToFive[Orient]; + Prefix="gup"; break; + case IE_ANI_AWAKE: + Cycle=SixteenToFive[Orient]; + Prefix="std"; break; + case IE_ANI_READY: + Cycle=SixteenToFive[Orient]; + Prefix="stc"; break; + case IE_ANI_DIE: + case IE_ANI_SLEEP: + case IE_ANI_TWITCH: + Cycle=SixteenToFive[Orient]; + Prefix="dfb"; break; + case IE_ANI_RUN: + Cycle=SixteenToNine[Orient]; + Prefix="run"; break; + case IE_ANI_WALK: + Cycle=SixteenToNine[Orient]; + Prefix="wlk"; break; + case IE_ANI_HEAD_TURN: + Cycle=SixteenToFive[Orient]; + if (rand()&1) { + Prefix="sf2"; + sprintf(ResRef,"%c%3s%4s",this->ResRef[0], Prefix, this->ResRef+1); + if (gamedata->Exists(ResRef, IE_BAM_CLASS_ID) ) { + return; + } + } + Prefix="sf1"; + sprintf(ResRef,"%c%3s%4s",this->ResRef[0], Prefix, this->ResRef+1); + if (gamedata->Exists(ResRef, IE_BAM_CLASS_ID) ) { + return; + } + Prefix = "stc"; + break; + case IE_ANI_PST_START: + Cycle=0; + Prefix="ms1"; break; + default: //just in case + Cycle=SixteenToFive[Orient]; + Prefix="stc"; break; + } + sprintf(ResRef,"%c%3s%4s",this->ResRef[0], Prefix, this->ResRef+1); +} + +void CharAnimations::AddVHR2Suffix(char* ResRef, unsigned char StanceID, + unsigned char& Cycle, unsigned char Orient) +{ + Cycle=SixteenToNine[Orient]; + + switch (StanceID) { + case IE_ANI_ATTACK: //temporarily + case IE_ANI_ATTACK_BACKSLASH: + strcat( ResRef, "g21" ); + break; + + case IE_ANI_ATTACK_SLASH: + strcat( ResRef, "g2" ); + break; + + case IE_ANI_ATTACK_JAB: + strcat( ResRef, "g26" ); + Cycle+=45; + break; + + case IE_ANI_CAST: //looping + strcat( ResRef, "g25" ); + Cycle+=45; + break; + + case IE_ANI_CONJURE://ending + strcat( ResRef, "g26" ); + Cycle+=54; + break; + + case IE_ANI_SHOOT: + strcat( ResRef, "g24" ); + Cycle+=27; + break; + + case IE_ANI_HEAD_TURN: + case IE_ANI_AWAKE: + strcat( ResRef, "g12" ); + Cycle+=18; + break; + + case IE_ANI_SLEEP: + strcat( ResRef, "g15" ); + Cycle+=45; + break; + + case IE_ANI_TWITCH: + strcat( ResRef, "g14" ); + Cycle+=45; + break; + + case IE_ANI_DIE: + case IE_ANI_EMERGE: + case IE_ANI_GET_UP: + case IE_ANI_PST_START: + strcat( ResRef, "g14" ); + Cycle+=36; + break; + + case IE_ANI_DAMAGE: + strcat( ResRef, "g13" ); + Cycle+=27; + break; + + case IE_ANI_READY: + strcat( ResRef, "g1" ); + Cycle+=9; + break; + + case IE_ANI_WALK: + strcat( ResRef, "g11" ); + break; + default: + printf("VHR2 Animation: unhandled stance: %s %d\n", ResRef, StanceID); + abort(); + break; + } +} + +void CharAnimations::AddVHR3Suffix(char* ResRef, unsigned char StanceID, + unsigned char& Cycle, unsigned char Orient) +{ + Cycle=SixteenToNine[Orient]; + + switch (StanceID) { + case IE_ANI_ATTACK: //temporarily + case IE_ANI_ATTACK_BACKSLASH: + strcat( ResRef, "g21" ); + break; + + case IE_ANI_ATTACK_SLASH: + strcat( ResRef, "g2" ); + break; + + case IE_ANI_ATTACK_JAB: + strcat( ResRef, "g26" ); + Cycle+=18; + break; + + case IE_ANI_CAST: //looping + strcat( ResRef, "g25" ); + Cycle+=45; + break; + + case IE_ANI_CONJURE://ending + strcat( ResRef, "g26" ); + Cycle+=36; + break; + + case IE_ANI_SHOOT: + strcat( ResRef, "g24" ); + Cycle+=27; + break; + + case IE_ANI_HEAD_TURN: + case IE_ANI_AWAKE: + strcat( ResRef, "g12" ); + Cycle+=18; + break; + + case IE_ANI_SLEEP: + strcat( ResRef, "g15" ); + Cycle+=45; + break; + + case IE_ANI_TWITCH: + strcat( ResRef, "g14" ); + Cycle+=45; + break; + + case IE_ANI_DIE: + case IE_ANI_EMERGE: + case IE_ANI_GET_UP: + case IE_ANI_PST_START: + strcat( ResRef, "g14" ); + Cycle+=36; + break; + + case IE_ANI_DAMAGE: + strcat( ResRef, "g13" ); + Cycle+=27; + break; + + case IE_ANI_READY: + strcat( ResRef, "g1" ); + Cycle+=9; + break; + + case IE_ANI_WALK: + strcat( ResRef, "g11" ); + break; + default: + printf("VHR3 Animation: unhandled stance: %s %d\n", ResRef, StanceID); + abort(); + break; + } +} + +// Note: almost like SixSuffix +void CharAnimations::AddFFSuffix(char* ResRef, unsigned char StanceID, + unsigned char& Cycle, unsigned char Orient, int Part) +{ + Cycle=SixteenToNine[Orient]; + switch (StanceID) { + case IE_ANI_WALK: + strcat( ResRef, "g1" ); + break; + + case IE_ANI_ATTACK: + case IE_ANI_ATTACK_SLASH: + strcat( ResRef, "g3" ); + break; + + case IE_ANI_ATTACK_BACKSLASH: + strcat( ResRef, "g3" ); + Cycle += 16; + break; + + case IE_ANI_ATTACK_JAB: + case IE_ANI_CAST: + case IE_ANI_CONJURE: + strcat( ResRef, "g3" ); + Cycle += 32; + break; + + case IE_ANI_HEAD_TURN: //could be wrong + case IE_ANI_AWAKE: + strcat( ResRef, "g2" ); + break; + + case IE_ANI_READY: + strcat( ResRef, "g2" ); + Cycle += 16; + break; + + case IE_ANI_DAMAGE: + strcat( ResRef, "g2" ); + Cycle += 32; + break; + + case IE_ANI_DIE: + case IE_ANI_GET_UP: + case IE_ANI_EMERGE: + case IE_ANI_PST_START: + strcat( ResRef, "g2" ); + Cycle += 48; + break; + + case IE_ANI_TWITCH: + strcat( ResRef, "g2" ); + Cycle += 64; + break; + + default: + printf("Four frames Animation: unhandled stance: %s %d\n", ResRef, StanceID); + abort(); + break; + + } + ResRef[6]=(char) (Part+'1'); + ResRef[7]=0; +} + +void CharAnimations::AddNFSuffix(char* ResRef, unsigned char StanceID, + unsigned char& Cycle, unsigned char Orient, int Part) +{ + char prefix[10]; + + Cycle = SixteenToNine[Orient]; + snprintf(prefix, 9, "%s%s%d%s%d", ResRef, StancePrefix[StanceID], Part+1, + CyclePrefix[StanceID], Cycle); + strnlwrcpy(ResRef,prefix,8); + Cycle=(ieByte) (Cycle+CycleOffset[StanceID]); +} + +//Attack +//h1, h2, w2 +//static const char *SlashPrefix[]={"a1","a4","a7"}; +//static const char *BackPrefix[]={"a2","a5","a8"}; +//static const char *JabPrefix[]={"a3","a6","a9"}; +static const char *SlashPrefix[]={"a1","a2","a7"}; +static const char *BackPrefix[]={"a3","a4","a8"}; +static const char *JabPrefix[]={"a5","a6","a9"}; +static const char *RangedPrefix[]={"sa","sx","ss"}; +static const char *RangedPrefixOld[]={"sa","sx","a1"}; + +void CharAnimations::AddVHRSuffix(char* ResRef, unsigned char StanceID, + unsigned char& Cycle, unsigned char Orient, EquipResRefData*& EquipData) +{ + Cycle = SixteenToNine[Orient]; + EquipData = new EquipResRefData; + EquipData->Suffix[0] = 0; + switch (StanceID) { + case IE_ANI_ATTACK: + case IE_ANI_ATTACK_SLASH: + strcat( ResRef, SlashPrefix[WeaponType] ); + strcpy( EquipData->Suffix, SlashPrefix[WeaponType] ); + break; + + case IE_ANI_ATTACK_BACKSLASH: + strcat( ResRef, BackPrefix[WeaponType] ); + strcpy( EquipData->Suffix, BackPrefix[WeaponType] ); + break; + + case IE_ANI_ATTACK_JAB: + strcat( ResRef, JabPrefix[WeaponType] ); + strcpy( EquipData->Suffix, JabPrefix[WeaponType] ); + break; + + case IE_ANI_AWAKE: + strcat( ResRef, "g17" ); + strcpy( EquipData->Suffix, "g1" ); + Cycle += 63; + break; + + case IE_ANI_CAST: //looping + strcat( ResRef, "ca" ); + strcpy( EquipData->Suffix, "ca" ); + break; + + case IE_ANI_CONJURE: //ending + strcat( ResRef, "ca" ); + strcpy( EquipData->Suffix, "ca" ); + Cycle += 9; + break; + + case IE_ANI_DAMAGE: + strcat( ResRef, "g14" ); + strcpy( EquipData->Suffix, "g1" ); + Cycle += 36; + break; + + case IE_ANI_DIE: + strcat( ResRef, "g15" ); + strcpy( EquipData->Suffix, "g1" ); + Cycle += 45; + break; + //I cannot find an emerge animation... + //Maybe is Die reversed + case IE_ANI_GET_UP: + case IE_ANI_EMERGE: + case IE_ANI_PST_START: + strcat( ResRef, "g19" ); + strcpy( EquipData->Suffix, "g1" ); + Cycle += 81; + break; + + case IE_ANI_HEAD_TURN: + if (rand()&1) { + strcat( ResRef, "g12" ); + Cycle += 18; + } else { + strcat( ResRef, "g18" ); + Cycle += 72; + } + strcpy( EquipData->Suffix, "g1" ); + break; + + //Unknown... maybe only a transparency effect apply + case IE_ANI_HIDE: + break; + + case IE_ANI_READY: + if ( WeaponType == IE_ANI_WEAPON_2H ) { + strcat( ResRef, "g13" ); + Cycle += 27; + } else { + strcat( ResRef, "g1" ); + Cycle += 9; + } + strcpy( EquipData->Suffix, "g1" ); + break; + //This depends on the ranged weapon equipped + case IE_ANI_SHOOT: + strcat( ResRef, RangedPrefix[RangedType] ); + strcpy( EquipData->Suffix, RangedPrefix[RangedType] ); + break; + + case IE_ANI_SLEEP: + strcat( ResRef, "g16" ); + strcpy( EquipData->Suffix, "g1" ); + Cycle += 54; + break; + + case IE_ANI_TWITCH: + strcat( ResRef, "g16" ); + strcpy( EquipData->Suffix, "g1" ); + Cycle += 54; + break; + + case IE_ANI_WALK: + strcat( ResRef, "g11" ); + strcpy( EquipData->Suffix, "g1" ); + break; + + default: + printf("VHR Animation: unhandled stance: %s %d\n", ResRef, StanceID); + abort(); + break; + } + EquipData->Cycle = Cycle; +} + +void CharAnimations::GetVHREquipmentRef(char* ResRef, unsigned char& Cycle, + const char* equipRef, bool offhand, + EquipResRefData* equip) +{ + Cycle = equip->Cycle; + if (offhand) { + sprintf( ResRef, "wq%c%c%co%s", GetSize(), equipRef[0], equipRef[1], equip->Suffix ); + } else { + sprintf( ResRef, "wq%c%c%c%s", GetSize(), equipRef[0], equipRef[1], equip->Suffix ); + } +} + +void CharAnimations::AddSixSuffix(char* ResRef, unsigned char StanceID, + unsigned char& Cycle, unsigned char Orient) +{ + switch (StanceID) { + case IE_ANI_WALK: + strcat( ResRef, "g1" ); + Cycle = Orient; + break; + + case IE_ANI_ATTACK: + case IE_ANI_ATTACK_SLASH: + strcat( ResRef, "g3" ); + Cycle = Orient; + break; + + case IE_ANI_ATTACK_BACKSLASH: + strcat( ResRef, "g3" ); + Cycle = 16 + Orient; + break; + + case IE_ANI_ATTACK_JAB: + strcat( ResRef, "g3" ); + Cycle = 32 + Orient; + break; + + case IE_ANI_HEAD_TURN: //could be wrong + case IE_ANI_AWAKE: + strcat( ResRef, "g2" ); + Cycle = 0 + Orient; + break; + + case IE_ANI_READY: + strcat( ResRef, "g2" ); + Cycle = 16 + Orient; + break; + + case IE_ANI_DAMAGE: + strcat( ResRef, "g2" ); + Cycle = 32 + Orient; + break; + + case IE_ANI_DIE: + case IE_ANI_GET_UP: + case IE_ANI_EMERGE: + case IE_ANI_PST_START: + strcat( ResRef, "g2" ); + Cycle = 48 + Orient; + break; + + case IE_ANI_TWITCH: + strcat( ResRef, "g2" ); + Cycle = 64 + Orient; + break; + + default: + printf("Six Animation: unhandled stance: %s %d\n", ResRef, StanceID); + abort(); + break; + + } + if (Orient>9) { + strcat( ResRef, "e" ); + } +} + +void CharAnimations::AddLR2Suffix(char* ResRef, unsigned char StanceID, + unsigned char& Cycle, unsigned char Orient) +{ + Orient /= 2; + + switch (StanceID) { + case IE_ANI_READY: + case IE_ANI_CAST: //looping + case IE_ANI_CONJURE://ending + case IE_ANI_HIDE: + case IE_ANI_WALK: + case IE_ANI_AWAKE: + Cycle = 0 + Orient; + break; + + case IE_ANI_SHOOT: + case IE_ANI_ATTACK: + case IE_ANI_ATTACK_SLASH: + case IE_ANI_ATTACK_BACKSLASH: + case IE_ANI_ATTACK_JAB: + case IE_ANI_HEAD_TURN: + Cycle = 8 + Orient; + break; + + case IE_ANI_DIE: + case IE_ANI_GET_UP: + case IE_ANI_EMERGE: + case IE_ANI_PST_START: + Cycle = 24 + Orient; + break; + + case IE_ANI_DAMAGE: + Cycle = 16 + Orient; + break; + + case IE_ANI_SLEEP: + case IE_ANI_TWITCH: + Cycle = 32 + Orient; + break; + default: + printf("LR2 Animation: unhandled stance: %s %d\n", ResRef, StanceID); + abort(); + break; + } + if (Orient>=4) { + strcat( ResRef, "g1e" ); + } else { + strcat( ResRef, "g1" ); + } +} + +void CharAnimations::AddMHRSuffix(char* ResRef, unsigned char StanceID, + unsigned char& Cycle, unsigned char Orient, EquipResRefData*& EquipData) +{ + Orient /= 2; + EquipData = new EquipResRefData; + EquipData->Suffix[0] = 0; + + switch (StanceID) { + case IE_ANI_ATTACK: + case IE_ANI_ATTACK_SLASH: + strcat (ResRef, SlashPrefix[WeaponType]); + strcpy( EquipData->Suffix, SlashPrefix[WeaponType] ); + Cycle = Orient; + break; + + case IE_ANI_ATTACK_BACKSLASH: + strcat (ResRef, BackPrefix[WeaponType]); + strcpy( EquipData->Suffix, BackPrefix[WeaponType] ); + Cycle = Orient; + break; + + case IE_ANI_ATTACK_JAB: + strcat (ResRef, JabPrefix[WeaponType]); + strcpy( EquipData->Suffix, JabPrefix[WeaponType] ); + Cycle = Orient; + break; + + case IE_ANI_READY: + strcat( ResRef, "g1" ); + strcpy( EquipData->Suffix, "g1" ); + if ( WeaponType == IE_ANI_WEAPON_2W ) { + Cycle = 24 + Orient; + } else { + Cycle = 8 + Orient; + } + break; + + case IE_ANI_CAST://looping + strcat( ResRef, "ca" ); + strcpy( EquipData->Suffix, "ca" ); + Cycle = 8 + Orient; + break; + + case IE_ANI_CONJURE://ending + strcat( ResRef, "ca" ); + strcpy( EquipData->Suffix, "ca" ); + Cycle = Orient; + break; + + case IE_ANI_DAMAGE: + strcat( ResRef, "g1" ); + strcpy( EquipData->Suffix, "g1" ); + Cycle = 40 + Orient; + break; + + case IE_ANI_DIE: + case IE_ANI_GET_UP: + case IE_ANI_PST_START: + strcat( ResRef, "g1" ); + strcpy( EquipData->Suffix, "g1" ); + Cycle = 48 + Orient; + break; + + //I cannot find an emerge animation... + //Maybe is Die reversed + case IE_ANI_EMERGE: + strcat( ResRef, "g1" ); + strcpy( EquipData->Suffix, "g1" ); + Cycle = 48 + Orient; + break; + + case IE_ANI_HEAD_TURN: + strcat( ResRef, "g1" ); + strcpy( EquipData->Suffix, "g1" ); + Cycle = 32 + Orient; + break; + + //Unknown... maybe only a transparency effect apply + case IE_ANI_HIDE: + break; + + case IE_ANI_AWAKE: + strcat( ResRef, "g1" ); + strcpy( EquipData->Suffix, "g1" ); + Cycle = 16 + Orient; + break; + + //This depends on the ranged weapon equipped + case IE_ANI_SHOOT: + strcat (ResRef, RangedPrefixOld[RangedType]); + strcpy( EquipData->Suffix, RangedPrefixOld[RangedType] ); + Cycle = Orient; + break; + + case IE_ANI_SLEEP: + strcat( ResRef, "g1" ); + strcpy( EquipData->Suffix, "g1" ); + Cycle = 64 + Orient; + break; + + case IE_ANI_TWITCH: + strcat( ResRef, "g1" ); + strcpy( EquipData->Suffix, "g1" ); + Cycle = 56 + Orient; + break; + + case IE_ANI_WALK: + strcat( ResRef, "g1" ); + strcpy( EquipData->Suffix, "g1" ); + Cycle = Orient; + break; + default: + printf("MHR Animation: unhandled stance: %s %d\n", ResRef, StanceID); + abort(); + break; + } + if (Orient>=5) { + strcat( ResRef, "e" ); + strcat( EquipData->Suffix, "e" ); + } + EquipData->Cycle = Cycle; +} + +void CharAnimations::GetMHREquipmentRef(char* ResRef, unsigned char& Cycle, + const char* equipRef, bool offhand, + EquipResRefData* equip) +{ + Cycle = equip->Cycle; + if (offhand) { + //i think there is no offhand stuff for bg1, lets use the bg2 equivalent here? + sprintf( ResRef, "wq%c%c%co%s", GetSize(), equipRef[0], equipRef[1], equip->Suffix ); + } else { + sprintf( ResRef, "wp%c%c%c%s", GetSize(), equipRef[0], equipRef[1], equip->Suffix ); + } +} + +void CharAnimations::AddTwoFileSuffix( char* ResRef, unsigned char StanceID, + unsigned char& Cycle, unsigned char Orient) +{ + switch(StanceID) { + case IE_ANI_HEAD_TURN: + Cycle = 16 + Orient / 2; + break; + case IE_ANI_DAMAGE: + Cycle = 24 + Orient / 2; + break; + case IE_ANI_SLEEP: + case IE_ANI_TWITCH: + Cycle = 40 + Orient / 2; + break; + case IE_ANI_GET_UP: + case IE_ANI_EMERGE: + case IE_ANI_DIE: + case IE_ANI_PST_START: + Cycle = 32 + Orient / 2; + break; + case IE_ANI_WALK: + Cycle = Orient / 2; + break; + default: + Cycle = 8 + Orient / 2; + break; + } + strcat( ResRef, "g1" ); + if (Orient > 9) { + strcat( ResRef, "e" ); + } +} + +void CharAnimations::AddLRSuffix2( char* ResRef, unsigned char StanceID, + unsigned char& Cycle, unsigned char Orient, EquipResRefData *&EquipData) +{ + EquipData = new EquipResRefData; + EquipData->Suffix[0] = 0; + switch (StanceID) { + case IE_ANI_ATTACK: + case IE_ANI_ATTACK_BACKSLASH: + case IE_ANI_ATTACK_SLASH: + case IE_ANI_ATTACK_JAB: + strcat( ResRef, "g2" ); + strcpy( EquipData->Suffix, "g2" ); + Cycle = Orient / 2; + break; + case IE_ANI_CAST: + case IE_ANI_CONJURE: + case IE_ANI_SHOOT: + strcat( ResRef, "g2" ); + strcpy( EquipData->Suffix, "g2" ); + Cycle = 8 + Orient / 2; + break; + case IE_ANI_WALK: + strcat( ResRef, "g1" ); + strcpy( EquipData->Suffix, "g1" ); + Cycle = Orient / 2; + break; + case IE_ANI_READY: + strcat( ResRef, "g1" ); + strcpy( EquipData->Suffix, "g1" ); + Cycle = 8 + Orient / 2; + break; + case IE_ANI_HEAD_TURN: //could be wrong + case IE_ANI_AWAKE: + strcat( ResRef, "g1" ); + strcpy( EquipData->Suffix, "g1" ); + Cycle = 16 + Orient / 2; + break; + case IE_ANI_DAMAGE: + strcat( ResRef, "g1" ); + strcpy( EquipData->Suffix, "g1" ); + Cycle = 24 + Orient / 2; + break; + case IE_ANI_GET_UP: + case IE_ANI_EMERGE: + case IE_ANI_PST_START: + case IE_ANI_DIE: + strcat( ResRef, "g1" ); + strcpy( EquipData->Suffix, "g1" ); + Cycle = 32 + Orient / 2; + break; + case IE_ANI_SLEEP: + case IE_ANI_TWITCH: + strcat( ResRef, "g1" ); + strcpy( EquipData->Suffix, "g1" ); + Cycle = 40 + Orient / 2; + break; + default: + printf("LRSuffix2 Animation: unhandled stance: %s %d\n", ResRef, StanceID); + abort(); + break; + } + if (Orient > 9) { + strcat( ResRef, "e" ); + strcat( EquipData->Suffix, "e"); + } + EquipData->Cycle = Cycle; +} + +void CharAnimations::AddLRSuffix( char* ResRef, unsigned char StanceID, + unsigned char& Cycle, unsigned char Orient, EquipResRefData *&EquipData) +{ + EquipData = new EquipResRefData; + EquipData->Suffix[0] = 0; + switch (StanceID) { + case IE_ANI_ATTACK: + case IE_ANI_ATTACK_BACKSLASH: + strcat( ResRef, "g2" ); + strcpy( EquipData->Suffix, "g2" ); + Cycle = Orient / 2; + break; + case IE_ANI_ATTACK_SLASH: + strcat( ResRef, "g2" ); + strcpy( EquipData->Suffix, "g2" ); + Cycle = 8 + Orient / 2; + break; + case IE_ANI_ATTACK_JAB: + strcat( ResRef, "g2" ); + strcpy( EquipData->Suffix, "g2" ); + Cycle = 16 + Orient / 2; + break; + case IE_ANI_CAST: + case IE_ANI_CONJURE: + case IE_ANI_SHOOT: + //these animations are missing + strcat( ResRef, "g2" ); + strcpy( EquipData->Suffix, "g2" ); + Cycle = Orient / 2; + break; + case IE_ANI_WALK: + strcat( ResRef, "g1" ); + strcpy( EquipData->Suffix, "g1" ); + Cycle = Orient / 2; + break; + case IE_ANI_READY: + strcat( ResRef, "g1" ); + strcpy( EquipData->Suffix, "g1" ); + Cycle = 8 + Orient / 2; + break; + case IE_ANI_HEAD_TURN: //could be wrong + case IE_ANI_AWAKE: + strcat( ResRef, "g1" ); + strcpy( EquipData->Suffix, "g1" ); + Cycle = 16 + Orient / 2; + break; + case IE_ANI_DAMAGE: + strcat( ResRef, "g1" ); + strcpy( EquipData->Suffix, "g1" ); + Cycle = 24 + Orient / 2; + break; + case IE_ANI_GET_UP: + case IE_ANI_EMERGE: + case IE_ANI_PST_START: + case IE_ANI_DIE: + strcat( ResRef, "g1" ); + strcpy( EquipData->Suffix, "g1" ); + Cycle = 32 + Orient / 2; + break; + case IE_ANI_TWITCH: + case IE_ANI_SLEEP: + strcat( ResRef, "g1" ); + strcpy( EquipData->Suffix, "g1" ); + Cycle = 40 + Orient / 2; + break; + default: + printf("LR Animation: unhandled stance: %s %d\n", ResRef, StanceID); + abort(); + break; + } + if (Orient > 9) { + strcat( ResRef, "e" ); + strcat( EquipData->Suffix, "e"); + } + EquipData->Cycle = Cycle; +} + +void CharAnimations::GetLREquipmentRef(char* ResRef, unsigned char& Cycle, + const char* equipRef, bool /*offhand*/, + EquipResRefData* equip) +{ + Cycle = equip->Cycle; + //hackhackhack + sprintf( ResRef, "%4s%c%s", this->ResRef, equipRef[0], equip->Suffix ); +} + +//Only for the ogre animation (MOGR) +void CharAnimations::AddLR3Suffix( char* ResRef, unsigned char StanceID, + unsigned char& Cycle, unsigned char Orient) +{ + switch (StanceID) { + case IE_ANI_ATTACK: + case IE_ANI_ATTACK_BACKSLASH: + strcat( ResRef, "g2" ); + Cycle = Orient / 2; + break; + case IE_ANI_ATTACK_SLASH: + strcat( ResRef, "g2" ); + Cycle = 8 + Orient / 2; + break; + case IE_ANI_ATTACK_JAB: + strcat( ResRef, "g2" ); + Cycle = 8 + Orient / 2; //there is no third attack animation + break; + case IE_ANI_CAST: + case IE_ANI_CONJURE: + case IE_ANI_SHOOT: + strcat( ResRef, "g3" ); + Cycle = Orient / 2; + break; + case IE_ANI_WALK: + strcat( ResRef, "g1" ); + Cycle = 16 + Orient / 2; + break; + case IE_ANI_READY: + strcat( ResRef, "g1" ); + Cycle = 8 + Orient / 2; + break; + case IE_ANI_HEAD_TURN: //could be wrong + case IE_ANI_AWAKE: + strcat( ResRef, "g1" ); + Cycle = Orient / 2; + break; + case IE_ANI_DAMAGE: + strcat( ResRef, "g3" ); + Cycle = 8 + Orient / 2; + break; + case IE_ANI_DIE: + case IE_ANI_GET_UP: + case IE_ANI_EMERGE: + case IE_ANI_PST_START: + case IE_ANI_SLEEP: + strcat( ResRef, "g3" ); + Cycle = 16 + Orient / 2; + break; + case IE_ANI_TWITCH: + strcat( ResRef, "g3" ); + Cycle = 24 + Orient / 2; + break; + default: + printf("LR3 Animation: unhandled stance: %s %d\n", ResRef, StanceID); + abort(); + break; + } + if (Orient > 9) { + strcat( ResRef, "e" ); + } +} + +void CharAnimations::AddMMR2Suffix(char* ResRef, unsigned char StanceID, + unsigned char& Cycle, unsigned char Orient) +{ + switch (StanceID) { + case IE_ANI_ATTACK: + case IE_ANI_ATTACK_SLASH: + case IE_ANI_ATTACK_BACKSLASH: + case IE_ANI_ATTACK_JAB: + case IE_ANI_CONJURE: + case IE_ANI_CAST: + strcat( ResRef, "a1" ); + Cycle = ( Orient / 2 ); + break; + + case IE_ANI_SHOOT: + strcat( ResRef, "a4" ); + Cycle = ( Orient / 2 ); + break; + + case IE_ANI_AWAKE: + case IE_ANI_READY: + strcat( ResRef, "sd" ); + Cycle = ( Orient / 2 ); + break; + + case IE_ANI_HEAD_TURN: + strcat( ResRef, "sc" ); + Cycle = ( Orient / 2 ); + break; + + case IE_ANI_DAMAGE: + strcat( ResRef, "gh" ); + Cycle = ( Orient / 2 ); + break; + + case IE_ANI_DIE: + strcat( ResRef, "de" ); + Cycle = ( Orient / 2 ); + break; + + case IE_ANI_GET_UP: + case IE_ANI_EMERGE: + case IE_ANI_PST_START: + strcat( ResRef, "gu" ); + Cycle = ( Orient / 2 ); + break; + + //Unknown... maybe only a transparency effect apply + case IE_ANI_HIDE: + break; + + case IE_ANI_SLEEP: + strcat( ResRef, "sl" ); + Cycle = ( Orient / 2 ); + break; + + case IE_ANI_TWITCH: + strcat( ResRef, "tw" ); + Cycle = ( Orient / 2 ); + break; + + case IE_ANI_WALK: + strcat( ResRef, "wk" ); + Cycle = ( Orient / 2 ); + break; + default: + printf("MMR Animation: unhandled stance: %s %d\n", ResRef, StanceID); + abort(); + break; + } + if (Orient > 9) { + strcat( ResRef, "e" ); + } +} + +void CharAnimations::AddMMRSuffix(char* ResRef, unsigned char StanceID, + unsigned char& Cycle, unsigned char Orient) +{ + switch (StanceID) { + case IE_ANI_ATTACK: + case IE_ANI_ATTACK_SLASH: + case IE_ANI_ATTACK_BACKSLASH: + strcat( ResRef, "a1" ); + Cycle = ( Orient / 2 ); + break; + + case IE_ANI_SHOOT: + strcat( ResRef, "a4" ); + Cycle = ( Orient / 2 ); + break; + + case IE_ANI_ATTACK_JAB: + strcat( ResRef, "a2" ); + Cycle = ( Orient / 2 ); + break; + + case IE_ANI_AWAKE: + case IE_ANI_READY: + strcat( ResRef, "sd" ); + Cycle = ( Orient / 2 ); + break; + + case IE_ANI_CONJURE: + strcat( ResRef, "ca" ); + Cycle = ( Orient / 2 ); + break; + + case IE_ANI_CAST: + strcat( ResRef, "sp" ); + Cycle = ( Orient / 2 ); + break; + + case IE_ANI_HEAD_TURN: + strcat( ResRef, "sc" ); + Cycle = ( Orient / 2 ); + break; + + case IE_ANI_DAMAGE: + strcat( ResRef, "gh" ); + Cycle = ( Orient / 2 ); + break; + + case IE_ANI_DIE: + strcat( ResRef, "de" ); + Cycle = ( Orient / 2 ); + break; + + case IE_ANI_GET_UP: + case IE_ANI_EMERGE: + case IE_ANI_PST_START: + strcat( ResRef, "gu" ); + Cycle = ( Orient / 2 ); + break; + + //Unknown... maybe only a transparency effect apply + case IE_ANI_HIDE: + break; + + case IE_ANI_SLEEP: + strcat( ResRef, "sl" ); + Cycle = ( Orient / 2 ); + break; + + case IE_ANI_TWITCH: + strcat( ResRef, "tw" ); + Cycle = ( Orient / 2 ); + break; + + case IE_ANI_WALK: + strcat( ResRef, "wk" ); + Cycle = ( Orient / 2 ); + break; + default: + printf("MMR Animation: unhandled stance: %s %d\n", ResRef, StanceID); + abort(); + break; + } + if (Orient > 9) { + strcat( ResRef, "e" ); + } +} + +void CharAnimations::PulseRGBModifiers() +{ + unsigned long time = core->GetGame()->Ticks; + + if (time - lastModUpdate <= 40) + return; + + if (time - lastModUpdate > 400) lastModUpdate = time - 40; + + int inc = (time - lastModUpdate)/40; + bool change[4] = { false, false, false, false }; + if (GlobalColorMod.type != RGBModifier::NONE && + GlobalColorMod.speed > 0) + { + GlobalColorMod.phase += inc; + change[0] = change[1] = change[2] = change[3] = true; + + // reset if done + if (GlobalColorMod.phase > 2*GlobalColorMod.speed) { + GlobalColorMod.type = RGBModifier::NONE; + GlobalColorMod.phase = 0; + GlobalColorMod.speed = 0; + } + } + + for (int i = 0; i < 32; ++i) { + if (ColorMods[i].type != RGBModifier::NONE && + ColorMods[i].speed > 0) + { + ColorMods[i].phase += inc; + change[i>>3] = true; + } + } + + if (change[0]) SetupColors(PAL_MAIN); + if (change[1]) SetupColors(PAL_WEAPON); + if (change[2]) SetupColors(PAL_OFFHAND); + if (change[3]) SetupColors(PAL_HELMET); + + lastModUpdate += inc*40; +} diff --git a/project/jni/application/gemrb/src/core/CharAnimations.h b/project/jni/application/gemrb/src/core/CharAnimations.h new file mode 100644 index 000000000..c963663a9 --- /dev/null +++ b/project/jni/application/gemrb/src/core/CharAnimations.h @@ -0,0 +1,231 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * + */ + +#ifndef CHARANIMATIONS_H +#define CHARANIMATIONS_H + +#include "RGBAColor.h" +#include "exports.h" + +#include "Animation.h" +#include "Palette.h" +#include "TableMgr.h" + +#include + +#define AV_PREFIX1 0 +#define AV_PREFIX2 1 +#define AV_PREFIX3 2 +#define AV_PREFIX4 3 +#define AV_ANIMTYPE 4 +#define AV_CIRCLESIZE 5 +#define AV_USE_PALETTE 6 +#define AV_SIZE 7 + +#define MAX_ANIMS 19 + +#define IE_ANI_ATTACK 0 +#define IE_ANI_AWAKE 1 +#define IE_ANI_CAST 2 +#define IE_ANI_CONJURE 3 +#define IE_ANI_DAMAGE 4 +#define IE_ANI_DIE 5 +#define IE_ANI_HEAD_TURN 6 +#define IE_ANI_READY 7 +#define IE_ANI_SHOOT 8 +#define IE_ANI_TWITCH 9 +#define IE_ANI_WALK 10 +#define IE_ANI_ATTACK_SLASH 11 +#define IE_ANI_ATTACK_BACKSLASH 12 +#define IE_ANI_ATTACK_JAB 13 +#define IE_ANI_EMERGE 14 +#define IE_ANI_HIDE 15 +#define IE_ANI_RUN 15 //pst has no hide, i hope +#define IE_ANI_SLEEP 16 +#define IE_ANI_GET_UP 17 +#define IE_ANI_PST_START 18 + +//BG2, IWD animation types +#define IE_ANI_CODE_MIRROR 0 +#define IE_ANI_ONE_FILE 1 +#define IE_ANI_FOUR_FILES 2 +#define IE_ANI_TWO_FILES 3 +#define IE_ANI_CODE_MIRROR_2 4 +#define IE_ANI_SIX_FILES_2 5 //MOGR +#define IE_ANI_TWENTYTWO 6 +#define IE_ANI_BIRD 7 +#define IE_ANI_SIX_FILES 8 //MCAR/MWYV +#define IE_ANI_TWO_FILES_3 9 //iwd animations +#define IE_ANI_TWO_FILES_2 10 //low res bg1 anim +#define IE_ANI_FOUR_FRAMES 11 //wyvern anims +#define IE_ANI_NINE_FRAMES 12 //dragon anims +#define IE_ANI_FRAGMENT 13 //fragment animation +#define IE_ANI_FOUR_FILES_2 14 //METT +#define IE_ANI_CODE_MIRROR_3 15 //MSPS +#define IE_ANI_TWO_FILES_3B 16 //iwd animations (eg. MBBM) + +//PST animation types +#define IE_ANI_PST_ANIMATION_1 56 //full animation +#define IE_ANI_PST_GHOST 57 //no orientations +#define IE_ANI_PST_STAND 58 //has orientations +#define IE_ANI_PST_ANIMATION_2 59 //full animation std-->stc +#define IE_ANI_PST_ANIMATION_3 60 //full animation stc-->std + +//armour levels +#define IE_ANI_NO_ARMOR 0 +#define IE_ANI_LIGHT_ARMOR 1 +#define IE_ANI_MEDIUM_ARMOR 2 +#define IE_ANI_HEAVY_ARMOR 3 + +#define IE_ANI_WEAPON_1H 0 +#define IE_ANI_WEAPON_2H 1 +#define IE_ANI_WEAPON_2W 2 + +#define IE_ANI_RANGED_BOW 0 +#define IE_ANI_RANGED_XBOW 1 +#define IE_ANI_RANGED_THROW 2 + +struct AvatarStruct { + /* entries from avatars.2da */ + unsigned int AnimID; + unsigned int PaletteType; + ieResRef Prefixes[4]; + unsigned char AnimationType; + unsigned char CircleSize; + char Size; + + /* comes from bloodclr.2da */ + char BloodColor; + + /* resdata.ini entries */ + unsigned int WalkScale; /* 1000 / walkscale */ + unsigned int RunScale; /* 1000 / runscale */ + int Bestiary; + + /* comes from walksnd.2da */ + ieResRef WalkSound; + ieByte WalkSoundCount; +}; + +struct EquipResRefData; + +class GEM_EXPORT CharAnimations { +private: + Animation** Anims[MAX_ANIMS][MAX_ORIENT]; + char HelmetRef[2]; + char WeaponRef[2]; + char OffhandRef[2]; +public: + const ieDword *Colors; //these are the custom color indices + RGBModifier ColorMods[32]; // color modification effects + unsigned long lastModUpdate; + RGBModifier GlobalColorMod; // global color modification effect + + Palette* palette[4]; + Palette* modifiedPalette[4]; + unsigned int AvatarsRowNum; + unsigned char ArmorType, WeaponType, RangedType; + ieResRef ResRef; + ieResRef PaletteResRef; + unsigned char nextStanceID, StanceID; + bool autoSwitchOnEnd; + bool lockPalette; +public: + CharAnimations(unsigned int AnimID, ieDword ArmourLevel); + ~CharAnimations(void); + static void ReleaseMemory(); + void SetArmourLevel(int ArmourLevel); + void SetRangedType(int Ranged); + void SetWeaponType(int WeaponType); + void SetHelmetRef(const char* ref); + void SetWeaponRef(const char* ref); + void SetOffhandRef(const char* ref); + void SetupColors(PaletteType type); + void SetColors(const ieDword *Colors); + void LockPalette(const ieDword *Colors); + + // returns an array of animations of size GetTotalPartCount() + Animation** GetAnimation(unsigned char Stance, unsigned char Orient); + int GetTotalPartCount() const; + const int* GetZOrder(unsigned char Orient); + + // returns Palette for a given part (unlocked) + Palette* GetPartPalette(int part); // TODO: clean this up + +public: //attribute functions + static int GetAvatarsCount(); + static AvatarStruct *GetAvatarStruct(int RowNum); + unsigned int GetAnimationID() const; + int GetCircleSize() const; + int NoPalette() const; + int GetAnimType() const; + int GetSize() const; + int GetBloodColor() const; + const ieResRef &GetWalkSound() const; + int GetWalkSoundCount() const; + void PulseRGBModifiers(); + +private: + void DropAnims(); + void InitAvatarsTable(); + int GetActorPartCount() const; + void AddPSTSuffix(char* ResRef, unsigned char AnimID, + unsigned char& Cycle, unsigned char Orient); + void AddFFSuffix(char* ResRef, unsigned char AnimID, + unsigned char& Cycle, unsigned char Orient, int Part); + void AddNFSuffix(char* ResRef, unsigned char AnimID, + unsigned char& Cycle, unsigned char Orient, int Part); + void AddVHR2Suffix(char* ResRef, unsigned char AnimID, + unsigned char& Cycle, unsigned char Orient); + void AddVHRSuffix(char* ResRef, unsigned char AnimID, + unsigned char& Cycle, unsigned char Orient, EquipResRefData*& equip); + void AddVHR3Suffix(char* ResRef, unsigned char AnimID, + unsigned char& Cycle, unsigned char Orient); + void GetVHREquipmentRef(char* ResRef, unsigned char& Cycle, + const char* equipRef, bool offhand, EquipResRefData* equip); + void AddSixSuffix(char* ResRef, unsigned char AnimID, + unsigned char& Cycle, unsigned char Orient); + void AddMHRSuffix(char* ResRef, unsigned char AnimID, + unsigned char& Cycle, unsigned char Orient, EquipResRefData*& equip); + void GetMHREquipmentRef(char* ResRef, unsigned char& Cycle, + const char* equipRef, bool offhand, EquipResRefData* equip); + void AddMMRSuffix(char* ResRef, unsigned char AnimID, + unsigned char& Cycle, unsigned char Orient); + void AddMMR2Suffix(char* ResRef, unsigned char AnimID, + unsigned char& Cycle, unsigned char Orient); + void AddTwoFileSuffix(char* ResRef, unsigned char AnimID, + unsigned char& Cycle, unsigned char Orient); + void AddLRSuffix(char* ResRef, unsigned char AnimID, + unsigned char& Cycle, unsigned char Orient, EquipResRefData*& equip); + void AddLRSuffix2( char* ResRef, unsigned char StanceID, + unsigned char& Cycle, unsigned char Orient, EquipResRefData *&EquipData); + void GetLREquipmentRef(char* ResRef, unsigned char& Cycle, + const char* equipRef, bool offhand, EquipResRefData* equip); + void AddLR2Suffix(char* ResRef, unsigned char AnimID, + unsigned char& Cycle, unsigned char Orient); + void AddLR3Suffix(char* ResRef, unsigned char AnimID, + unsigned char& Cycle, unsigned char Orient); + void GetAnimResRef(unsigned char AnimID, unsigned char Orient, + char* ResRef, unsigned char& Cycle, int Part, EquipResRefData*& equip); + void GetEquipmentResRef(const char* equipRef, bool offhand, + char* ResRef, unsigned char& Cycle, EquipResRefData* equip); +}; + +#endif diff --git a/project/jni/application/gemrb/src/core/Compressor.cpp b/project/jni/application/gemrb/src/core/Compressor.cpp new file mode 100644 index 000000000..e7a675be3 --- /dev/null +++ b/project/jni/application/gemrb/src/core/Compressor.cpp @@ -0,0 +1,37 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * + */ + +#include "Compressor.h" + +#include "globals.h" + +Compressor::Compressor(void) +{ +} + +Compressor::~Compressor(void) +{ +} + +// Initialization Function. Returns FALSE if there was an error during initialization, else returns TRUE. +int Compressor::Init(void) +{ + return GEM_OK; +} diff --git a/project/jni/application/gemrb/src/core/Compressor.h b/project/jni/application/gemrb/src/core/Compressor.h new file mode 100644 index 000000000..3409f8019 --- /dev/null +++ b/project/jni/application/gemrb/src/core/Compressor.h @@ -0,0 +1,41 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * + */ + +#ifndef COMPRESSOR_H +#define COMPRESSOR_H + +#include "Plugin.h" +#include "System/DataStream.h" + +#include + +class GEM_EXPORT Compressor : public Plugin { +public: + Compressor(void); + virtual ~Compressor(void); + /** Initialization Function. Returns FALSE if there was an error during initialization, else returns TRUE. */ + virtual int Init(void); + /** decompresses a datastream (memory or file) to a FILE * stream */ + virtual int Decompress(FILE* dest, DataStream* source, unsigned int size_guess = 0) const = 0; + /** compresses a datastream (memory or file) to another DataStream */ + virtual int Compress(DataStream *dest, DataStream* source) const = 0; +}; + +#endif diff --git a/project/jni/application/gemrb/src/core/ControlAnimation.cpp b/project/jni/application/gemrb/src/core/ControlAnimation.cpp new file mode 100644 index 000000000..6e618844a --- /dev/null +++ b/project/jni/application/gemrb/src/core/ControlAnimation.cpp @@ -0,0 +1,138 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * + */ + +#include "ControlAnimation.h" + +#include "win32def.h" + +#include "GameData.h" +#include "Interface.h" +#include "Palette.h" /* needed only for paperdoll palettes */ +#include "Video.h" /* needed only for paperdoll palettes */ +#include "GUI/Button.h" + +ControlAnimation::ControlAnimation(Control* ctl, const ieResRef ResRef, int Cycle) +{ + control = NULL; + bam = NULL; + cycle = Cycle; + frame = 0; + anim_phase = 0; + + bam = ( AnimationFactory* ) gamedata->GetFactoryResource( ResRef, + IE_BAM_CLASS_ID, IE_NORMAL ); + + if (! bam) + return; + + control = ctl; + control->animation = this; + has_palette = false; +} + +//freeing the bitmaps only once, but using an intelligent algorithm +ControlAnimation::~ControlAnimation(void) +{ + //removing from timer first + core->timer->RemoveAnimation( this ); + + bam = NULL; +} + +bool ControlAnimation::SameResource(const ieResRef ResRef, int Cycle) +{ + if (!control ) return false; + if (!bam) return false; + if (strnicmp(ResRef, bam->ResRef, sizeof(ieResRef) )) return false; + int c = cycle; + if (control->Flags&IE_GUI_BUTTON_PLAYRANDOM) { + c&=~1; + } + if (Cycle!=c) return false; + return true; +} + +void ControlAnimation::UpdateAnimation(void) +{ + unsigned long time; + int Cycle = cycle; + + if (control->Flags & IE_GUI_BUTTON_PLAYRANDOM) { + // simple Finite-State Machine + if (anim_phase == 0) { + frame = 0; + anim_phase = 1; + time = 500 + 500 * (rand() % 20); + cycle&=~1; + Cycle=cycle; + } else if (anim_phase == 1) { + if (rand() % 30 == 0) { + cycle|=1; + Cycle=cycle; + } + anim_phase = 2; + time = 100; + } else { + frame++; + time = 100; + } + } else { + frame ++; + if (has_palette) { + time = 100; //hack for slower movement + } else { + time = 15; + } + } + + Sprite2D* pic = bam->GetFrame( (unsigned short) frame, (unsigned char) Cycle ); + + if (pic == NULL) { + //stopping at end frame + if (control->Flags & IE_GUI_BUTTON_PLAYONCE) { + core->timer->RemoveAnimation( this ); + return; + } + anim_phase = 0; + frame = 0; + pic = bam->GetFrame( 0, (unsigned char) Cycle ); + } + + if (pic == NULL) { + return; + } + + if (has_palette) { + Palette* palette = pic->GetPalette(); + palette->SetupPaperdollColours(colors, 0); + pic->SetPalette(palette); + palette->Release(); + } + + control->SetAnimPicture( pic ); + core->timer->AddAnimation( this, time ); +} + +void ControlAnimation::SetPaletteGradients(ieDword *col) +{ + memcpy(colors, col, 8*sizeof(ieDword)); + has_palette = true; +} + diff --git a/project/jni/application/gemrb/src/core/ControlAnimation.h b/project/jni/application/gemrb/src/core/ControlAnimation.h new file mode 100644 index 000000000..c85870f67 --- /dev/null +++ b/project/jni/application/gemrb/src/core/ControlAnimation.h @@ -0,0 +1,51 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * + */ + +#ifndef CONTROLANIMATIONS_H +#define CONTROLANIMATIONS_H + +#include "RGBAColor.h" +#include "exports.h" + +#include "AnimationFactory.h" +#include "Sprite2D.h" +#include "GUI/Control.h" + +#include + +class GEM_EXPORT ControlAnimation { +private: + AnimationFactory* bam; + Control* control; + unsigned int cycle; + unsigned int frame; + unsigned int anim_phase; + bool has_palette; + ieDword colors[8]; +public: + ControlAnimation(Control* ctl, const ieResRef ResRef, int Cycle = 0); + ~ControlAnimation(void); + void UpdateAnimation(); + //report if the current resource is the same as descripted by the params + bool SameResource(const ieResRef ResRef, int Cycle); + void SetPaletteGradients(ieDword *col); +}; + +#endif diff --git a/project/jni/application/gemrb/src/core/Core.cpp b/project/jni/application/gemrb/src/core/Core.cpp new file mode 100644 index 000000000..f0c075366 --- /dev/null +++ b/project/jni/application/gemrb/src/core/Core.cpp @@ -0,0 +1,323 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * + */ + +/** + * @file Core.cpp + * Some compatibility and utility functions + * @author The GemRB Project + */ + +#include "globals.h" +#include "exports.h" + +#include "Interface.h" +#include "Scriptable/Actor.h" + +#include +#include +#ifdef WIN32 +#include "win32def.h" +#ifdef _DEBUG +#include +#include +#endif + +BOOL WINAPI DllEntryPoint(HINSTANCE /*hinstDLL*/, DWORD /*fdwReason*/, + LPVOID /*lpvReserved*/) +{ + return true; +} +#endif + +//// Globally used functions + +ieByte pl_uppercase[256]; +ieByte pl_lowercase[256]; + +// these 3 functions will copy a string to a zero terminated string with a maximum length +void strnlwrcpy(char *dest, const char *source, int count) +{ + while(count--) { + *dest++ = pl_lowercase[(ieByte) *source]; + if(!*source++) { + while(count--) *dest++=0; + break; + } + } + *dest=0; +} + +void strnuprcpy(char* dest, const char *source, int count) +{ + while(count--) { + *dest++ = pl_uppercase[(ieByte) *source]; + if(!*source++) { + while(count--) *dest++=0; + break; + } + } + *dest=0; +} + +// this one also filters spaces +void strnspccpy(char* dest, const char *source, int count) +{ + memset(dest,0,count); + while(count--) { + char c = pl_lowercase[(ieByte) *source]; + if (c!=' ') { + *dest++=c; + } + if(!*source++) { + return; + } + } +} + +#ifndef HAVE_STRNLEN +int strnlen(const char* string, int maxlen) +{ + if (!string) { + return -1; + } + int i = 0; + while (maxlen-- > 0) { + if (!string[i]) + break; + i++; + } + return i; +} +#endif // ! HAVE_STRNLEN + +static const unsigned char orientations[25]={ +6,7,8,9,10, +5,6,8,10,11, +4,4,0,12,12, +3,2,0,14,13, +2,1,0,15,14 +}; + +/** Calculates the orientation of a character (or projectile) facing a point */ +unsigned char GetOrient(const Point &s, const Point &d) +{ + int deltaX = s.x - d.x; + int deltaY = s.y - d.y; + int div = Distance(s,d); + if(!div) return 0; //default + if(div>3) div/=2; + int aX=deltaX/div; + int aY=deltaY/div; + return orientations[(aY+2)*5+aX+2]; +} + +/** Calculates distance between 2 points */ +unsigned int Distance(Point p, Point q) +{ + long x = ( p.x - q.x ); + long y = ( p.y - q.y ); + return (unsigned int) sqrt( ( double ) ( x* x + y* y ) ); +} + +/** Calculates distance squared from a point to a scriptable */ +unsigned int SquaredMapDistance(Point p, Scriptable *b) +{ + long x = ( p.x/16 - b->Pos.x/16 ); + long y = ( p.y/12 - b->Pos.y/12 ); + return (unsigned int)(x*x + y*y); +} + +/** Calculates distance between 2 points */ +unsigned int Distance(Point p, Scriptable *b) +{ + long x = ( p.x - b->Pos.x ); + long y = ( p.y - b->Pos.y ); + return (unsigned int) sqrt( ( double ) ( x* x + y* y ) ); +} + +unsigned int PersonalDistance(Point p, Scriptable *b) +{ + long x = ( p.x - b->Pos.x ); + long y = ( p.y - b->Pos.y ); + int ret = (int) sqrt( ( double ) ( x* x + y* y ) ); + if (b->Type==ST_ACTOR) { + ret-=((Actor *)b)->size*10; + } + if (ret<0) return (unsigned int) 0; + return (unsigned int) ret; +} + +unsigned int SquaredPersonalDistance(Point p, Scriptable *b) +{ + long x = ( p.x - b->Pos.x ); + long y = ( p.y - b->Pos.y ); + int ret = x*x + y*y; + if (b->Type==ST_ACTOR) { + ret-=((Actor *)b)->size*100; + } + if (ret<0) return (unsigned int) 0; + return (unsigned int) ret; +} + +/** Calculates map distance between 2 scriptables */ +unsigned int SquaredMapDistance(Scriptable *a, Scriptable *b) +{ + long x = (a->Pos.x/16 - b->Pos.x/16 ); + long y = (a->Pos.y/12 - b->Pos.y/12 ); + return (unsigned int)(x*x + y*y); +} + +/** Calculates distance between 2 scriptables */ +unsigned int Distance(Scriptable *a, Scriptable *b) +{ + long x = ( a->Pos.x - b->Pos.x ); + long y = ( a->Pos.y - b->Pos.y ); + return (unsigned int) sqrt( ( double ) ( x* x + y* y ) ); +} + +/** Calculates distance squared between 2 scriptables */ +unsigned int SquaredDistance(Scriptable *a, Scriptable *b) +{ + long x = ( a->Pos.x - b->Pos.x ); + long y = ( a->Pos.y - b->Pos.y ); + return (unsigned int) ( x* x + y* y ); +} + +/** Calculates distance between 2 scriptables, including feet circle if applicable */ +unsigned int PersonalDistance(Scriptable *a, Scriptable *b) +{ + long x = ( a->Pos.x - b->Pos.x ); + long y = ( a->Pos.y - b->Pos.y ); + int ret = (int) sqrt( ( double ) ( x* x + y* y ) ); + if (a->Type==ST_ACTOR) { + ret-=((Actor *)a)->size*10; + } + if (b->Type==ST_ACTOR) { + ret-=((Actor *)b)->size*10; + } + if (ret<0) return (unsigned int) 0; + return (unsigned int) ret; +} + +unsigned int SquaredPersonalDistance(Scriptable *a, Scriptable *b) +{ + long x = ( a->Pos.x - b->Pos.x ); + long y = ( a->Pos.y - b->Pos.y ); + int ret = x*x + y*y; + if (a->Type==ST_ACTOR) { + ret-=((Actor *)a)->size*100; + } + if (b->Type==ST_ACTOR) { + ret-=((Actor *)b)->size*100; + } + if (ret<0) return (unsigned int) 0; + return (unsigned int) ret; +} + +// returns EA relation between two scriptables (non actors are always enemies) +// it is used for protectile targeting/iwd ids targeting too! +int EARelation(Scriptable* Owner, Actor* target) +{ + ieDword eao = EA_ENEMY; + + if (Owner && Owner->Type==ST_ACTOR) { + eao = ((Actor *) Owner)->GetStat(IE_EA); + } + + ieDword eat = target->GetStat(IE_EA); + + if (eao<=EA_GOODCUTOFF) { + + if (eat<=EA_GOODCUTOFF) { + return EAR_FRIEND; + } + if (eat>=EA_EVILCUTOFF) { + return EAR_HOSTILE; + } + + return EAR_NEUTRAL; + } + + if (eao>=EA_EVILCUTOFF) { + + if (eat<=EA_GOODCUTOFF) { + return EAR_HOSTILE; + } + if (eat>=EA_EVILCUTOFF) { + return EAR_FRIEND; + } + + return EAR_NEUTRAL; + } + + return EAR_NEUTRAL; +} + +/** Returns the length of string (up to a delimiter) */ +GEM_EXPORT int strlench(const char* string, char ch) +{ + int i; + for (i = 0; string[i] && string[i] != ch; i++) + ; + return i; +} + +//// Compatibility functions +#ifndef HAVE_STRNDUP +GEM_EXPORT char* strndup(const char* s, size_t l) +{ + size_t len = strlen( s ); + if (len < l) { + l = len; + } + char* string = ( char* ) malloc( l + 1 ); + strncpy( string, s, l ); + string[l] = 0; + return string; +} +#endif + +#ifdef WIN32 + +#else + +char* strupr(char* string) +{ + char* s; + if (string) { + for (s = string; *s; ++s) + *s = toupper( *s ); + } + return string; +} + +char* strlwr(char* string) +{ + char* s; + if (string) { + for (s = string; *s; ++s) + *s = tolower( *s ); + } + return string; +} + + +#endif // ! WIN32 + diff --git a/project/jni/application/gemrb/src/core/DataFileMgr.cpp b/project/jni/application/gemrb/src/core/DataFileMgr.cpp new file mode 100644 index 000000000..2c95d6787 --- /dev/null +++ b/project/jni/application/gemrb/src/core/DataFileMgr.cpp @@ -0,0 +1,29 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * + */ + +#include "DataFileMgr.h" + +DataFileMgr::DataFileMgr(void) +{ +} + +DataFileMgr::~DataFileMgr(void) +{ +} diff --git a/project/jni/application/gemrb/src/core/DataFileMgr.h b/project/jni/application/gemrb/src/core/DataFileMgr.h new file mode 100644 index 000000000..8773767b7 --- /dev/null +++ b/project/jni/application/gemrb/src/core/DataFileMgr.h @@ -0,0 +1,58 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * + */ + +/** + * @file DataFileMgr.h + * Declares DataFileMgr class, abstract loader for .INI files + * @author The GemRB Project + */ + + +#ifndef DATAFILEMGR_H +#define DATAFILEMGR_H + +#include "Plugin.h" +#include "System/DataStream.h" + +/** + * @class DataFileMgr + * Abstract loader for .INI files + */ + +class GEM_EXPORT DataFileMgr : public Plugin { +public: + DataFileMgr(void); + virtual ~DataFileMgr(void); + virtual bool Open(DataStream* stream, bool autoFree = false) = 0; + virtual int GetTagsCount() const = 0; + virtual const char* GetTagNameByIndex(int index) const = 0; + virtual int GetKeysCount(const char* Tag) const = 0; + virtual const char* GetKeyNameByIndex(const char* Tag, int index) const = 0; + virtual const char* GetKeyAsString(const char* Tag, const char* Key, + const char* Default) const = 0; + virtual int GetKeyAsInt(const char* Tag, const char* Key, + const int Default) const = 0; + virtual float GetKeyAsFloat(const char* Tag, const char* Key, + const float Default) const = 0; + virtual bool GetKeyAsBool(const char* Tag, const char* Key, + const bool Default) const = 0; +}; + +#endif diff --git a/project/jni/application/gemrb/src/core/Dialog.cpp b/project/jni/application/gemrb/src/core/Dialog.cpp new file mode 100644 index 000000000..a8399ee59 --- /dev/null +++ b/project/jni/application/gemrb/src/core/Dialog.cpp @@ -0,0 +1,100 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * + */ + +#include "Dialog.h" + +#include "win32def.h" + +#include "GameScript/GameScript.h" + +Dialog::Dialog(void) +{ + TopLevelCount = 0; +} + +Dialog::~Dialog(void) +{ + if (initialStates) { + for (unsigned int i = 0; i < TopLevelCount; i++) { + if (initialStates[i]) { + FreeDialogState( initialStates[i] ); + } + } + free(initialStates); + } + if (Order) free(Order); +} + +DialogState* Dialog::GetState(unsigned int index) +{ + if (index >= TopLevelCount) { + return NULL; + } + return initialStates[index]; +} + +void Dialog::FreeDialogState(DialogState* ds) +{ + for (unsigned int i = 0; i < ds->transitionsCount; i++) { + DialogTransition *trans = ds->transitions[i]; + for (size_t j = 0; j < trans->actions.size(); ++j) + trans->actions[j]->Release(); + if (trans->condition) + delete trans->condition; + delete( trans ); + } + free( ds->transitions ); + if (ds->condition) { + delete ds->condition; + } + delete( ds ); +} + +int Dialog::FindFirstState(Scriptable* target) +{ + for (unsigned int i = 0; i < TopLevelCount; i++) { + Condition *cond = GetState( Order[i] )->condition; + if (cond && cond->Evaluate(target)) { + return Order[i]; + } + } + return -1; +} + +int Dialog::FindRandomState(Scriptable* target) +{ + unsigned int i; + unsigned int max = TopLevelCount; + if (!max) return -1; + unsigned int pick = rand()%max; + for (i=pick; i < max; i++) { + Condition *cond = GetState(i)->condition; + if (cond && cond->Evaluate(target)) { + return i; + } + } + for (i=0; i < pick; i++) { + Condition *cond = GetState(i)->condition; + if (cond && cond->Evaluate(target)) { + return i; + } + } + return -1; +} diff --git a/project/jni/application/gemrb/src/core/Dialog.h b/project/jni/application/gemrb/src/core/Dialog.h new file mode 100644 index 000000000..4dc473b6a --- /dev/null +++ b/project/jni/application/gemrb/src/core/Dialog.h @@ -0,0 +1,82 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * + */ + +#ifndef DIALOG_H +#define DIALOG_H + +#include "exports.h" +#include "globals.h" + +#include "GameScript/GameScript.h" + +#include + +#define IE_DLG_TR_TEXT 0x01 +#define IE_DLG_TR_TRIGGER 0x02 +#define IE_DLG_TR_ACTION 0x04 +#define IE_DLG_TR_FINAL 0x08 +#define IE_DLG_TR_JOURNAL 0x10 +#define IE_DLG_UNSOLVED 0x40 +#define IE_DLG_SOLVED 0x100 +#define IE_DLG_QUEST_GROUP 0x4000 // this is a GemRB extension + +struct DialogTransition { + ieDword Flags; + ieStrRef textStrRef; + ieStrRef journalStrRef; + Condition* condition; + std::vector actions; + ieResRef Dialog; + ieDword stateIndex; +}; + +struct DialogState { + ieStrRef StrRef; + DialogTransition** transitions; + unsigned int transitionsCount; + Condition* condition; + unsigned int weight; +}; + +class GEM_EXPORT Dialog { +public: + Dialog(void); + ~Dialog(void); +private: + void FreeDialogState(DialogState* ds); +public: + void AddState(DialogState* ds); + DialogState* GetState(unsigned int index); + int FindFirstState(Scriptable* target); + int FindRandomState(Scriptable* target); + + void Release() + { + delete this; + } +public: + ieResRef ResRef; + ieDword Flags; //freeze flags (bg2) + unsigned int TopLevelCount; + ieDword* Order; + DialogState** initialStates; +}; + +#endif diff --git a/project/jni/application/gemrb/src/core/DialogHandler.cpp b/project/jni/application/gemrb/src/core/DialogHandler.cpp new file mode 100644 index 000000000..5653a8096 --- /dev/null +++ b/project/jni/application/gemrb/src/core/DialogHandler.cpp @@ -0,0 +1,473 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "DialogHandler.h" + +#include "strrefs.h" + +#include "DialogMgr.h" +#include "DisplayMessage.h" +#include "Game.h" +#include "GameData.h" +#include "Video.h" +#include "GUI/GameControl.h" + +//translate section values (journal, solved, unsolved, user) +static int sectionMap[4]={4,1,2,0}; +static const int bg2Sections[4]={4,1,2,0}; +static const int noSections[4]={0,0,0,0}; + +DialogHandler::DialogHandler(void) +{ + dlg = NULL; + targetID = 0; + originalTargetID = 0; + speakerID = 0; + if (core->HasFeature(GF_JOURNAL_HAS_SECTIONS) ) { + memcpy(sectionMap, bg2Sections, sizeof(sectionMap) ); + } else { + memcpy(sectionMap, noSections, sizeof(sectionMap) ); + } +} + +DialogHandler::~DialogHandler(void) +{ + if (dlg) { + delete dlg; + } +} + +//Try to start dialogue between two actors (one of them could be inanimate) +int DialogHandler::InitDialog(Scriptable* spk, Scriptable* tgt, const char* dlgref) +{ + if (dlg) { + delete dlg; + dlg = NULL; + } + + PluginHolder dm(IE_DLG_CLASS_ID); + dm->Open( gamedata->GetResource( dlgref, IE_DLG_CLASS_ID ), true ); + dlg = dm->GetDialog(); + + if (!dlg) { + printMessage("GameControl", " ", LIGHT_RED); + printf( "Cannot start dialog: %s\n", dlgref ); + return -1; + } + + strnlwrcpy(dlg->ResRef, dlgref, 8); //this isn't handled by GetDialog??? + + //target is here because it could be changed when a dialog runs onto + //and external link, we need to find the new target (whose dialog was + //linked to) + + Actor *oldTarget = GetActorByGlobalID(targetID); + speakerID = spk->GetGlobalID(); + targetID = tgt->GetGlobalID(); + if (!originalTargetID) originalTargetID = tgt->GetGlobalID(); + if (tgt->Type==ST_ACTOR) { + Actor *tar = (Actor *) tgt; + spk->LastTalkedTo=targetID; + tar->LastTalkedTo=speakerID; + tar->SetCircleSize(); + } + if (oldTarget) oldTarget->SetCircleSize(); + + //check if we are already in dialog + if (core->GetGameControl()->GetDialogueFlags()&DF_IN_DIALOG) { + return 0; + } + + int si = dlg->FindFirstState( tgt ); + if (si < 0) { + return -1; + } + + //we need GUI for dialogs + core->GetGameControl()->UnhideGUI(); + + //no exploring while in dialogue + core->GetGameControl()->SetScreenFlags(SF_GUIENABLED|SF_DISABLEMOUSE|SF_LOCKSCROLL, BM_OR); + core->GetGameControl()->SetDialogueFlags(DF_IN_DIALOG, BM_OR); + + if (tgt->Type==ST_ACTOR) { + Actor *tar = (Actor *) tgt; + tar->DialogInterrupt(); + } + + //allow mouse selection from dialog (even though screen is locked) + Video *video = core->GetVideoDriver(); + Region vp = video->GetViewport(); + video->SetMouseEnabled(true); + core->timer->SetMoveViewPort( tgt->Pos.x, tgt->Pos.y, 0, true ); + video->MoveViewportTo( tgt->Pos.x-vp.w/2, tgt->Pos.y-vp.h/2 ); + //there are 3 bits, if they are all unset, the dialog freezes scripts + if (!(dlg->Flags&7) ) { + core->GetGameControl()->SetDialogueFlags(DF_FREEZE_SCRIPTS, BM_OR); + } + //opening control size to maximum, enabling dialog window + core->GetGame()->SetControlStatus(CS_HIDEGUI, BM_NAND); + core->GetGame()->SetControlStatus(CS_DIALOG, BM_OR); + core->SetEventFlag(EF_PORTRAIT); + return 0; +} + +/*try to break will only try to break it, false means unconditional stop*/ +void DialogHandler::EndDialog(bool try_to_break) +{ + if (try_to_break && (core->GetGameControl()->GetDialogueFlags()&DF_UNBREAKABLE) ) { + return; + } + + Actor *tmp = GetSpeaker(); + if (tmp) { + tmp->LeaveDialog(); + } + speakerID = 0; + Scriptable *tmp2 = GetTarget(); + if (tmp2 && tmp2->Type == ST_ACTOR) { + tmp = (Actor *)tmp2; + } else { + tmp = NULL; + } + if (tmp) { + tmp->LeaveDialog(); + } + targetID = 0; + if (tmp) tmp->SetCircleSize(); + originalTargetID = 0; + ds = NULL; + if (dlg) { + delete dlg; + dlg = NULL; + } + //restoring original size + core->GetGame()->SetControlStatus(CS_DIALOG, BM_NAND); + core->GetGameControl()->SetScreenFlags(SF_DISABLEMOUSE|SF_LOCKSCROLL, BM_NAND); + core->GetGameControl()->SetDialogueFlags(0, BM_SET); + core->SetEventFlag(EF_PORTRAIT); +} + + +void DialogHandler::DialogChoose(unsigned int choose) +{ + TextArea* ta = core->GetMessageTextArea(); + if (!ta) { + printMessage("GameControl","Dialog aborted???",LIGHT_RED); + EndDialog(); + return; + } + + Actor *speaker = GetSpeaker(); + if (!speaker) { + printMessage("GameControl","Speaker gone???",LIGHT_RED); + EndDialog(); + return; + } + + Scriptable *target = GetTarget(); + if (!target) { + printMessage("GameControl","Target gone???",LIGHT_RED); + EndDialog(); + return; + } + Actor *tgt = NULL; + if (target->Type == ST_ACTOR) { + tgt = (Actor *)target; + } + + Video *video = core->GetVideoDriver(); + Region vp = video->GetViewport(); + video->SetMouseEnabled(true); + core->timer->SetMoveViewPort( target->Pos.x, target->Pos.y, 0, true ); + video->MoveViewportTo( target->Pos.x-vp.w/2, target->Pos.y-vp.h/2 ); + + if (choose == (unsigned int) -1) { + //increasing talkcount after top level condition was determined + + int si = dlg->FindFirstState( tgt ); + if (si<0) { + EndDialog(); + return; + } + + if (tgt) { + if (core->GetGameControl()->GetDialogueFlags()&DF_TALKCOUNT) { + core->GetGameControl()->SetDialogueFlags(DF_TALKCOUNT, BM_NAND); + tgt->TalkCount++; + } else if (core->GetGameControl()->GetDialogueFlags()&DF_INTERACT) { + core->GetGameControl()->SetDialogueFlags(DF_INTERACT, BM_NAND); + tgt->InteractCount++; + } + } + ds = dlg->GetState( si ); + } else { + if (ds->transitionsCount <= choose) { + return; + } + + DialogTransition* tr = ds->transitions[choose]; + + ta->PopMinRow(); + + if (tr->Flags&IE_DLG_TR_JOURNAL) { + int Section = 0; + if (tr->Flags&IE_DLG_UNSOLVED) { + Section |= 1; + } + if (tr->Flags&IE_DLG_SOLVED) { + Section |= 2; + } + if (core->GetGame()->AddJournalEntry(tr->journalStrRef, sectionMap[Section], tr->Flags>>16) ) { + displaymsg->DisplayConstantString(STR_JOURNALCHANGE,0xffff00); + char *string = core->GetString( tr->journalStrRef ); + //cutting off the strings at the first crlf + char *poi = strchr(string,'\n'); + if (poi) { + *poi='\0'; + } + displaymsg->DisplayString( string ); + free( string ); + } + } + + if (tr->textStrRef != 0xffffffff) { + //allow_zero is for PST (deionarra's text) + displaymsg->DisplayStringName( (int) (tr->textStrRef), 0x8080FF, speaker, IE_STR_SOUND|IE_STR_SPEECH|IE_STR_ALLOW_ZERO); + if (core->HasFeature( GF_DIALOGUE_SCROLLS )) { + ta->AppendText( "", -1 ); + } + } + + if (tr->actions.size()) { + // does this belong here? we must clear actions somewhere before + // we start executing them (otherwise queued actions interfere) + // executing actions directly does not work, because dialog + // needs to end before final actions are executed due to + // actions making new dialogs! + if (target->Type == ST_ACTOR) ((Movable *)target)->ClearPath(); // fuzzie added this + target->ClearActions(); + + for (unsigned int i = 0; i < tr->actions.size(); i++) { + target->AddAction(tr->actions[i]); + //GameScript::ExecuteAction( target, action ); + } + } + + int final_dialog = tr->Flags & IE_DLG_TR_FINAL; + + if (final_dialog) { + ta->SetMinRow( false ); + EndDialog(); + } + + // *** the commented-out line here should no longer be required, with instant handling *** + // all dialog actions must be executed immediately + //target->ProcessActions(true); + // (do not clear actions - final actions can involve waiting/moving) + + if (final_dialog) { + return; + } + + // avoid problems when dhjollde.dlg tries starting a cutscene in the middle of a dialog + // (it seems harmless doing it in non-HoW too, since other versions would just break in such a situation) + core->SetCutSceneMode( false ); + + //displaying dialog for selected option + int si = tr->stateIndex; + //follow external linkage, if required + if (tr->Dialog[0] && strnicmp( tr->Dialog, dlg->ResRef, 8 )) { + //target should be recalculated! + tgt = NULL; + if (originalTargetID) { + // always try original target first (sometimes there are multiple + // actors with the same dialog in an area, we want to pick the one + // we were talking to) + tgt = GetActorByGlobalID(originalTargetID); + if (tgt && strnicmp( tgt->GetDialog(GD_NORMAL), tr->Dialog, 8 ) != 0) { + tgt = NULL; + } + } + if (!tgt) { + // then just search the current area for an actor with the dialog + tgt = target->GetCurrentArea()->GetActorByDialog(tr->Dialog); + } + if (!tgt) { + // try searching for banter dialogue: the original engine seems to + // happily let you randomly switch between normal and banter dialogs + + // TODO: work out if this should go somewhere more central (such + // as GetActorByDialog), or if there's a less awful way to do this + // (we could cache the entries, for example) + // TODO: fix for ToB (see also the Interact action) + AutoTable pdtable("interdia"); + if (pdtable) { + int row = pdtable->FindTableValue( pdtable->GetColumnIndex("FILE"), tr->Dialog ); + tgt = target->GetCurrentArea()->GetActorByScriptName(pdtable->GetRowName(row)); + } + } + target = tgt; + if (!target) { + printMessage("Dialog","Can't redirect dialog\n",YELLOW); + ta->SetMinRow( false ); + EndDialog(); + return; + } + Actor *oldTarget = GetActorByGlobalID(targetID); + targetID = tgt->GetGlobalID(); + tgt->SetCircleSize(); + if (oldTarget) oldTarget->SetCircleSize(); + // we have to make a backup, tr->Dialog is freed + ieResRef tmpresref; + strnlwrcpy(tmpresref,tr->Dialog, 8); + if (target->GetInternalFlag()&IF_NOINT) { + // this whole check moved out of InitDialog by fuzzie, see comments + // for the IF_NOINT check in BeginDialog + displaymsg->DisplayConstantString(STR_TARGETBUSY,0xff0000); + ta->SetMinRow( false ); + EndDialog(); + return; + } + int ret = InitDialog( speaker, target, tmpresref); + if (ret<0) { + // error was displayed by InitDialog + ta->SetMinRow( false ); + EndDialog(); + return; + } + } + ds = dlg->GetState( si ); + if (!ds) { + printMessage("Dialog","Can't find next dialog\n",YELLOW); + ta->SetMinRow( false ); + EndDialog(); + return; + } + } + //displaying npc text + displaymsg->DisplayStringName( ds->StrRef, 0x70FF70, target, IE_STR_SOUND|IE_STR_SPEECH); + //adding a gap between options and npc text + ta->AppendText("",-1); + int i; + int idx = 0; + ta->SetMinRow( true ); + //first looking for a 'continue' opportunity, the order is descending (a la IE) + unsigned int x = ds->transitionsCount; + while(x--) { + if (ds->transitions[x]->Flags & IE_DLG_TR_FINAL) { + continue; + } + if (ds->transitions[x]->textStrRef != 0xffffffff) { + continue; + } + if (ds->transitions[x]->Flags & IE_DLG_TR_TRIGGER) { + if (ds->transitions[x]->condition && + !ds->transitions[x]->condition->Evaluate(target)) { + continue; + } + } + core->GetDictionary()->SetAt("DialogOption",x); + core->GetGameControl()->SetDialogueFlags(DF_OPENCONTINUEWINDOW, BM_OR); + goto end_of_choose; + } + for (x = 0; x < ds->transitionsCount; x++) { + if (ds->transitions[x]->Flags & IE_DLG_TR_TRIGGER) { + if (ds->transitions[x]->condition && + !ds->transitions[x]->condition->Evaluate(target)) { + continue; + } + } + idx++; + if (ds->transitions[x]->textStrRef == 0xffffffff) { + //dialogchoose should be set to x + //it isn't important which END option was chosen, as it ends + core->GetDictionary()->SetAt("DialogOption",x); + core->GetGameControl()->SetDialogueFlags(DF_OPENENDWINDOW, BM_OR); + } else { + char *string = ( char * ) malloc( 40 ); + sprintf( string, "[s=%d,ffffff,ff0000]%d - [p]", x, idx ); + i = ta->AppendText( string, -1 ); + free( string ); + string = core->GetString( ds->transitions[x]->textStrRef ); + ta->AppendText( string, i ); + free( string ); + ta->AppendText( "[/p][/s]", i ); + } + } + // this happens if a trigger isn't implemented or the dialog is wrong + if (!idx) { + printMessage("Dialog", "There were no valid dialog options!\n", YELLOW); + core->GetGameControl()->SetDialogueFlags(DF_OPENENDWINDOW, BM_OR); + } +end_of_choose: + //padding the rows so our text will be at the top + if (core->HasFeature( GF_DIALOGUE_SCROLLS )) { + ta->AppendText( "", -1 ); + } + else { + ta->PadMinRow(); + } +} + +// TODO: duplicate of the one in GameControl +Actor *DialogHandler::GetActorByGlobalID(ieDword ID) +{ + if (!ID) + return NULL; + Game* game = core->GetGame(); + if (!game) + return NULL; + + Map* area = game->GetCurrentArea( ); + if (!area) + return NULL; + return area->GetActorByGlobalID(ID); +} + +Scriptable *DialogHandler::GetTarget() +{ + // TODO: area GetScriptableByGlobalID? + + if (!targetID) return NULL; + + Game *game = core->GetGame(); + if (!game) return NULL; + + Map *area = game->GetCurrentArea(); + if (!area) return NULL; + + Actor *actor = area->GetActorByGlobalID(targetID); + if (actor) return actor; + + Door *door = area->GetDoorByGlobalID(targetID); + if (door) return door; + Container *container = area->GetContainerByGlobalID(targetID); + if (container) return container; + InfoPoint *ip = area->GetInfoPointByGlobalID(targetID); + if (ip) return ip; + + return NULL; +} + +Actor *DialogHandler::GetSpeaker() +{ + return GetActorByGlobalID(speakerID); +} + diff --git a/project/jni/application/gemrb/src/core/DialogHandler.h b/project/jni/application/gemrb/src/core/DialogHandler.h new file mode 100644 index 000000000..499fb0dea --- /dev/null +++ b/project/jni/application/gemrb/src/core/DialogHandler.h @@ -0,0 +1,51 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * + */ + +#ifndef DIALOGHANDLER_H +#define DIALOGHANDLER_H + +#include "exports.h" + +#include "Dialog.h" + +class GEM_EXPORT DialogHandler { +public: + DialogHandler(); + ~DialogHandler(); +private: + /** this function safely retrieves an Actor by ID */ + Actor *GetActorByGlobalID(ieDword ID); +private: + DialogState* ds; + Dialog* dlg; +public: + ieDword speakerID; + ieDword targetID; + ieDword originalTargetID; +public: + Scriptable *GetTarget(); + Actor *GetSpeaker(); + + int InitDialog(Scriptable* speaker, Scriptable* target, const char* dlgref); + void EndDialog(bool try_to_break=false); + void DialogChoose(unsigned int choose); +}; + +#endif diff --git a/project/jni/application/gemrb/src/core/DialogMgr.cpp b/project/jni/application/gemrb/src/core/DialogMgr.cpp new file mode 100644 index 000000000..bc68aa544 --- /dev/null +++ b/project/jni/application/gemrb/src/core/DialogMgr.cpp @@ -0,0 +1,29 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * + */ + +#include "DialogMgr.h" + +DialogMgr::DialogMgr(void) +{ +} + +DialogMgr::~DialogMgr(void) +{ +} diff --git a/project/jni/application/gemrb/src/core/DialogMgr.h b/project/jni/application/gemrb/src/core/DialogMgr.h new file mode 100644 index 000000000..125ae52a0 --- /dev/null +++ b/project/jni/application/gemrb/src/core/DialogMgr.h @@ -0,0 +1,36 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * + */ + +#ifndef DIALOGMGR_H +#define DIALOGMGR_H + +#include "Dialog.h" +#include "Plugin.h" +#include "System/DataStream.h" + +class GEM_EXPORT DialogMgr : public Plugin { +public: + DialogMgr(void); + virtual ~DialogMgr(void); + virtual bool Open(DataStream* stream, bool autoFree = true) = 0; + virtual Dialog* GetDialog() const = 0; +}; + +#endif diff --git a/project/jni/application/gemrb/src/core/DisplayMessage.cpp b/project/jni/application/gemrb/src/core/DisplayMessage.cpp new file mode 100644 index 000000000..1954acc3f --- /dev/null +++ b/project/jni/application/gemrb/src/core/DisplayMessage.cpp @@ -0,0 +1,230 @@ +/* GemRB - Infinity Engine Emulator +* Copyright (C) 2003-2005 The GemRB Project +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* as published by the Free Software Foundation; either version 2 +* of the License, or (at your option) any later version. + +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. + +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +* +* +*/ + +#include "DisplayMessage.h" + +#include "strrefs.h" + +#include "Interface.h" +#include "TableMgr.h" +#include "GUI/Label.h" +#include "GUI/TextArea.h" + +GEM_EXPORT DisplayMessage * displaymsg; + +static int strref_table[STRREF_COUNT]; + +#define PALSIZE 8 +static Color ActorColor[PALSIZE]; +static const char* DisplayFormatName = "[color=%lX]%s - [/color][p][color=%lX]%s[/color][/p]"; +static const char* DisplayFormatAction = "[color=%lX]%s - [/color][p][color=%lX]%s %s[/color][/p]"; +static const char* DisplayFormat = "[/color][p][color=%lX]%s[/color][/p]"; +static const char* DisplayFormatValue = "[/color][p][color=%lX]%s: %d[/color][/p]"; +static const char* DisplayFormatNameString = "[color=%lX]%s - [/color][p][color=%lX]%s: %s[/color][/p]"; + +DisplayMessage::DisplayMessage(void) { + ReadStrrefs(); +} + +bool DisplayMessage::ReadStrrefs() +{ + int i; + memset(strref_table,-1,sizeof(strref_table) ); + AutoTable tab("strings"); + if (!tab) { + return false; + } + for(i=0;iQueryField(i,0)); + } + return true; +} + +void DisplayMessage::DisplayString(const char* Text, Scriptable *target) const +{ + Label *l = core->GetMessageLabel(); + if (l) { + l->SetText(Text, 0); + } + TextArea *ta = core->GetMessageTextArea(); + if (ta) { + ta->AppendText( Text, -1 ); + } else { + if(target) { + char *tmp = strdup(Text); + + target->DisplayHeadText(tmp); + } + } +} + +ieStrRef DisplayMessage::GetStringReference(int stridx) const +{ + return strref_table[stridx]; +} + +bool DisplayMessage::HasStringReference(int stridx) const +{ + return strref_table[stridx] != -1; +} + +unsigned int DisplayMessage::GetSpeakerColor(const char *&name, const Scriptable *&speaker) const +{ + unsigned int speaker_color; + + if(!speaker) return 0; + switch (speaker->Type) { + case ST_ACTOR: + name = speaker->GetName(-1); + core->GetPalette( ((Actor *) speaker)->GetStat(IE_MAJOR_COLOR) & 0xFF, PALSIZE, ActorColor ); + speaker_color = (ActorColor[4].r<<16) | (ActorColor[4].g<<8) | ActorColor[4].b; + break; + case ST_TRIGGER: case ST_PROXIMITY: case ST_TRAVEL: + name = core->GetString( ((InfoPoint *) speaker)->DialogName ); + speaker_color = 0xc0c0c0; + break; + default: + name = ""; + speaker_color = 0x800000; + break; + } + return speaker_color; +} + + +//simply displaying a constant string +void DisplayMessage::DisplayConstantString(int stridx, unsigned int color, Scriptable *target) const +{ + if (stridx<0) return; + const char* text = core->GetString( strref_table[stridx], IE_STR_SOUND ); + DisplayString(text, color, target); +} + +void DisplayMessage::DisplayString(int stridx, unsigned int color, ieDword flags) const +{ + if (stridx<0) return; + const char* text = core->GetString( stridx, flags); + DisplayString(text, color, NULL); +} + +void DisplayMessage::DisplayString(const char *text, unsigned int color, Scriptable *target) const +{ + if (!text) return; + int newlen = (int)(strlen( DisplayFormat) + strlen( text ) + 12); + char* newstr = ( char* ) malloc( newlen ); + snprintf( newstr, newlen, DisplayFormat, color, text ); + DisplayString( newstr, target ); + free( newstr ); +} + +// String format is +// blah : whatever +void DisplayMessage::DisplayConstantStringValue(int stridx, unsigned int color, ieDword value) const +{ + if (stridx<0) return; + char* text = core->GetString( strref_table[stridx], IE_STR_SOUND ); + int newlen = (int)(strlen( DisplayFormat ) + strlen( text ) + 28); + char* newstr = ( char* ) malloc( newlen ); + snprintf( newstr, newlen, DisplayFormatValue, color, text, (int) value ); + core->FreeString( text ); + DisplayString( newstr ); + free( newstr ); +} + +// String format is +// - blah blah : whatever +void DisplayMessage::DisplayConstantStringNameString(int stridx, unsigned int color, int stridx2, const Scriptable *actor) const +{ + unsigned int actor_color; + const char *name = 0; + + if (stridx<0) return; + actor_color = GetSpeakerColor(name, actor); + char* text = core->GetString( strref_table[stridx], IE_STR_SOUND ); + char* text2 = core->GetString( strref_table[stridx2], IE_STR_SOUND ); + int newlen = (int)(strlen( DisplayFormat ) + strlen(name) + strlen( text ) + strlen(text2) + 18); + char* newstr = ( char* ) malloc( newlen ); + if (strlen(text2)) { + snprintf( newstr, newlen, DisplayFormatNameString, actor_color, name, color, text, text2 ); + } else { + snprintf( newstr, newlen, DisplayFormatName, color, name, color, text ); + } + core->FreeString( text ); + core->FreeString( text2 ); + DisplayString( newstr ); + free( newstr ); +} + +// String format is +// - blah blah +void DisplayMessage::DisplayConstantStringName(int stridx, unsigned int color, const Scriptable *speaker) const +{ + if (stridx<0) return; + if(!speaker) return; + + const char* text = core->GetString( strref_table[stridx], IE_STR_SOUND|IE_STR_SPEECH ); + DisplayStringName(text, color, speaker); +} + +void DisplayMessage::DisplayConstantStringAction(int stridx, unsigned int color, const Scriptable *attacker, const Scriptable *target) const +{ + unsigned int attacker_color; + const char *name1 = 0; + const char *name2 = 0; + + if (stridx<0) return; + + GetSpeakerColor(name2, target); + attacker_color = GetSpeakerColor(name1, attacker); + + char* text = core->GetString( strref_table[stridx], IE_STR_SOUND|IE_STR_SPEECH ); + int newlen = (int)(strlen( DisplayFormatAction ) + strlen( name1 ) + + + strlen( name2 ) + strlen( text ) + 18); + char* newstr = ( char* ) malloc( newlen ); + snprintf( newstr, newlen, DisplayFormatAction, attacker_color, name1, color, + text, name2); + core->FreeString( text ); + DisplayString( newstr ); + free( newstr ); +} + +void DisplayMessage::DisplayStringName(int stridx, unsigned int color, const Scriptable *speaker, ieDword flags) const +{ + if (stridx<0) return; + + const char* text = core->GetString( stridx, flags); + DisplayStringName(text, color, speaker); +} + +void DisplayMessage::DisplayStringName(const char *text, unsigned int color, const Scriptable *speaker) const +{ + unsigned int speaker_color; + const char *name = 0; + + if (!text) return; + speaker_color = GetSpeakerColor(name, speaker); + + int newlen = (int)(strlen( DisplayFormatName ) + strlen( name ) + + + strlen( text ) + 18); + char* newstr = ( char* ) malloc( newlen ); + snprintf( newstr, newlen, DisplayFormatName, speaker_color, name, color, text ); + DisplayString( newstr ); + free( newstr ); +} diff --git a/project/jni/application/gemrb/src/core/DisplayMessage.h b/project/jni/application/gemrb/src/core/DisplayMessage.h new file mode 100644 index 000000000..9fc781b5d --- /dev/null +++ b/project/jni/application/gemrb/src/core/DisplayMessage.h @@ -0,0 +1,70 @@ +/* GemRB - Infinity Engine Emulator +* Copyright (C) 2003-2005 The GemRB Project +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* as published by the Free Software Foundation; either version 2 +* of the License, or (at your option) any later version. + +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. + +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +* +* +*/ + +/** + * @file DisplayMessage.h + * Declaration of the DisplayMessage class used for displaying messages in + * game message window + */ + +#ifndef DISPLAYMESSAGE_H +#define DISPLAYMESSAGE_H + +#include "exports.h" + +#include "ActorMgr.h" + +class GEM_EXPORT DisplayMessage +{ +private: + bool ReadStrrefs(); + +public: + DisplayMessage(void); + + /** returns a string reference from a string reference index constant */ + ieStrRef GetStringReference(int stridx) const; + /** returns true if a string reference for a string reference index constant exists */ + bool HasStringReference(int stridx) const; + /** returns the speaker's color and name */ + unsigned int GetSpeakerColor(const char *&name, const Scriptable *&speaker) const; + /** displays any string in the textarea */ + void DisplayString(const char *txt, Scriptable *speaker=NULL) const; + /** displays a string constant in the textarea */ + void DisplayConstantString(int stridx, unsigned int color, Scriptable *speaker=NULL) const; + /** displays actor name - action : parameter */ + void DisplayConstantStringNameString(int stridx, unsigned int color, int stridx2, const Scriptable *actor) const; + /** displays a string constant followed by a number in the textarea */ + void DisplayConstantStringValue(int stridx, unsigned int color, ieDword value) const; + /** displays a string constant in the textarea, starting with speaker's name */ + void DisplayConstantStringName(int stridx, unsigned int color, const Scriptable *speaker) const; + /** displays a string constant in the textarea, starting with actor, and ending with target */ + void DisplayConstantStringAction(int stridx, unsigned int color, const Scriptable *actor, const Scriptable *target) const; + /** displays a string in the textarea */ + void DisplayString(int stridx, unsigned int color, ieDword flags) const; + void DisplayString(const char *text, unsigned int color, Scriptable *target) const; + /** displays a string in the textarea, starting with speaker's name */ + void DisplayStringName(int stridx, unsigned int color, const Scriptable *speaker, ieDword flags) const; + void DisplayStringName(const char *text, unsigned int color, const Scriptable *speaker) const; +}; + +extern GEM_EXPORT DisplayMessage * displaymsg; + +#endif diff --git a/project/jni/application/gemrb/src/core/Effect.h b/project/jni/application/gemrb/src/core/Effect.h new file mode 100644 index 000000000..400c6bcbd --- /dev/null +++ b/project/jni/application/gemrb/src/core/Effect.h @@ -0,0 +1,139 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * + */ + +/** + * @file Effect.h + * Declares Effect class implementing spell and spell-like effects + * and related defines + */ + +#ifndef EFFECT_H +#define EFFECT_H + +#include "ie_types.h" + +#include "Region.h" + +class Actor; + +//local variables in creatures are stored in fake opcodes +#define FAKE_VARIABLE_OPCODE 187 +#define FAKE_VARIABLE_MARKER 1 + +// Effect target types +#define FX_TARGET_UNKNOWN 0 +#define FX_TARGET_SELF 1 +#define FX_TARGET_PRESET 2 +#define FX_TARGET_PARTY 3 +#define FX_TARGET_ALL 4 +#define FX_TARGET_ALL_BUT_PARTY 5 +#define FX_TARGET_OWN_SIDE 6 +#define FX_TARGET_OTHER_SIDE 7 +#define FX_TARGET_ALL_BUT_SELF 8 +#define FX_TARGET_ORIGINAL 9 + +// Effect duration/timing types +#define FX_DURATION_INSTANT_LIMITED 0 +#define FX_DURATION_INSTANT_PERMANENT 1 +#define FX_DURATION_INSTANT_WHILE_EQUIPPED 2 +#define FX_DURATION_DELAY_LIMITED 3 //this contains a relative onset time (delay) also used as duration, transforms to 6 when applied +#define FX_DURATION_DELAY_PERMANENT 4 //this transforms to 9 (i guess) +#define FX_DURATION_DELAY_UNSAVED 5 //this transforms to 8 +#define FX_DURATION_DELAY_LIMITED_PENDING 6 //this contains an absolute onset time and a duration +#define FX_DURATION_AFTER_EXPIRES 7 //this is a delayed non permanent effect (resolves to JUST_EXPIRED) +#define FX_DURATION_PERMANENT_UNSAVED 8 +#define FX_DURATION_INSTANT_PERMANENT_AFTER_BONUSES 9//this is a special permanent +#define FX_DURATION_JUST_EXPIRED 10 +#define MAX_TIMING_MODE 11 +#define FX_DURATION_ABSOLUTE 0x1000 + +// Effect resistance types +#define FX_NO_RESIST_NO_DISPEL 0 +#define FX_CAN_RESIST_CAN_DISPEL 1 +//#define FX_CAN_RESIST_NO_DISPEL 2 //same as 0 (not resistable, not dispellable) +#define FX_NO_RESIST_CAN_DISPEL 3 +#define FX_CAN_DISPEL 1 +#define FX_CAN_RESIST 3 + +/** + * @class Effect + * Structure holding information about single spell or spell-like effect. + */ + +// the same as ITMFeature and SPLFeature +struct Effect { + ieDword Opcode; + ieDword Target; + ieDword Power; + ieDword Parameter1; + ieDword Parameter2; + ieWord TimingMode; //0x1000 -- no need of conversion + ieWord unknown2; + ieDword Resistance; + ieDword Duration; + ieWord Probability1; + ieWord Probability2; + //keep these four in one bunch, VariableName will + //spread across them + ieResRef Resource; + ieResRef Resource2; //vvc in a lot of effects + ieResRef Resource3; + ieResRef Resource4; + ieDword DiceThrown; + ieDword DiceSides; + ieDword SavingThrowType; + ieDword SavingThrowBonus; + ieWord IsVariable; + ieWord IsSaveForHalfDamage; + + // EFF V2.0 fields: + ieDword PrimaryType; //school + ieDword MinAffectedLevel; + ieDword MaxAffectedLevel; + ieDword Parameter3; + ieDword Parameter4; + ieDword PosX, PosY; + ieDword SourceType; //1-item, 2-spell + ieResRef Source; + ieDword SourceFlags; + ieDword Projectile; //9c + ieDwordSigned InventorySlot; //a0 + //Original engine had a VariableName here, but it is stored in the resource fields + ieDword CasterLevel; //c4 in both + ieDword FirstApply; //c8 in bg2, cc in iwd2 + ieDword SecondaryType; + ieDword SecondaryDelay; //still not sure about this + ieDword CasterID; //10c in bg2 (not saved?) + // These are not in the IE files, but are our precomputed values + ieDword random_value; +public: + //don't modify position in case it was already set + void SetPosition(const Point &p) { + if(PosX==0xffffffff && PosY==0xffffffff) { + PosX=p.x; + PosY=p.y; + } + } +}; + +// FIXME: what about area spells? They can have map & coordinates as target +//void AddEffect(Effect* fx, Actor* self, Actor* pretarget); + +#endif // ! EFFECT_H diff --git a/project/jni/application/gemrb/src/core/EffectMgr.cpp b/project/jni/application/gemrb/src/core/EffectMgr.cpp new file mode 100644 index 000000000..7ca50f42e --- /dev/null +++ b/project/jni/application/gemrb/src/core/EffectMgr.cpp @@ -0,0 +1,29 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * + */ + +#include "EffectMgr.h" + +EffectMgr::EffectMgr(void) +{ +} + +EffectMgr::~EffectMgr(void) +{ +} diff --git a/project/jni/application/gemrb/src/core/EffectMgr.h b/project/jni/application/gemrb/src/core/EffectMgr.h new file mode 100644 index 000000000..689651c55 --- /dev/null +++ b/project/jni/application/gemrb/src/core/EffectMgr.h @@ -0,0 +1,56 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * + */ + +/** + * @file EffectMgr.h + * Declares EffectMgr class, loader for Effect objects + * @author The GemRB Project + */ + + +#ifndef EFFECTMGR_H +#define EFFECTMGR_H + +#include "Effect.h" +#include "Plugin.h" +#include "System/DataStream.h" + +/** + * @class EffectMgr + * Abstract loader for Effect objects + */ + +class GEM_EXPORT EffectMgr : public Plugin { +public: + EffectMgr(void); + virtual ~EffectMgr(void); + virtual bool Open(DataStream* stream, bool autoFree = true) = 0; + + /** Fills fx with Effect data loaded from the stream */ + virtual Effect* GetEffect(Effect *fx) = 0; + /** Fills fx with Effect v1 data loaded from the stream*/ + virtual Effect* GetEffectV1(Effect *fx) = 0; + /** Fills fx with Effect v2.0 data loaded from the stream*/ + virtual Effect* GetEffectV20(Effect *fx) = 0; + /** Fills the stream with Effect v2 data loaded from the effect*/ + virtual void PutEffectV2(DataStream *stream, const Effect *fx) = 0; +}; + +#endif diff --git a/project/jni/application/gemrb/src/core/EffectQueue.cpp b/project/jni/application/gemrb/src/core/EffectQueue.cpp new file mode 100644 index 000000000..3828c85ad --- /dev/null +++ b/project/jni/application/gemrb/src/core/EffectQueue.cpp @@ -0,0 +1,1980 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * + */ + +#include "EffectQueue.h" + +#include "DisplayMessage.h" +#include "Effect.h" +#include "Game.h" +#include "Interface.h" +#include "Map.h" +#include "SymbolMgr.h" +#include "Scriptable/Actor.h" +#include "Spell.h" //needs for the source flags bitfield + +#include + +static struct { + const char* Name; + EffectFunction Function; + int Strref; +} Opcodes[MAX_EFFECTS]; + +static int initialized = 0; +static EffectRef *effectnames = NULL; +static int effectnames_count = 0; +static int pstflags = false; + +bool EffectQueue::match_ids(Actor *target, int table, ieDword value) +{ + if( value == 0) { + return true; + } + + int a, stat; + + switch (table) { + case 2: //EA + stat = IE_EA; break; + case 3: //GENERAL + stat = IE_GENERAL; break; + case 4: //RACE + stat = IE_RACE; break; + case 5: //CLASS + stat = IE_CLASS; break; + case 6: //SPECIFIC + stat = IE_SPECIFIC; break; + case 7: //GENDER + stat = IE_SEX; break; + case 8: //ALIGNMENT + stat = target->GetStat(IE_ALIGNMENT); + a = value&15; + if( a) { + if( a != ( stat & 15 )) { + return false; + } + } + a = value & 0xf0; + if( a) { + if( a != ( stat & 0xf0 )) { + return false; + } + } + return true; + default: + return false; + } + if( target->GetStat(stat)==value) { + return true; + } + return false; +} + +static const bool fx_instant[MAX_TIMING_MODE]={true,true,true,false,false,false,false,false,true,true,true}; + +inline bool IsInstant(ieByte timingmode) +{ + if( timingmode>=MAX_TIMING_MODE) return false; + return fx_instant[timingmode]; +} + +static const bool fx_equipped[MAX_TIMING_MODE]={false,false,true,false,false,true,false,false,true,false,false}; + +inline bool IsEquipped(ieByte timingmode) +{ + if( timingmode>=MAX_TIMING_MODE) return false; + return fx_equipped[timingmode]; +} + +// 0 1 2 3 4 5 6 7 8 9 10 +static const bool fx_relative[MAX_TIMING_MODE]={true,false,false,true,true,true,false,false,false,false,false}; + +inline bool NeedPrepare(ieWord timingmode) +{ + if( timingmode>=MAX_TIMING_MODE) return false; + return fx_relative[timingmode]; +} + +#define INVALID -1 +#define PERMANENT 0 +#define DELAYED 1 +#define DURATION 2 + +static const int fx_prepared[MAX_TIMING_MODE]={DURATION,PERMANENT,PERMANENT,DELAYED, //0-3 +DELAYED,DELAYED,DELAYED,DELAYED,PERMANENT,PERMANENT,PERMANENT}; //4-7 + +inline int DelayType(ieByte timingmode) +{ + if( timingmode>=MAX_TIMING_MODE) return INVALID; + return fx_prepared[timingmode]; +} + +//which effects are removable +static const bool fx_removable[MAX_TIMING_MODE]={true,true,false,true,true,false,true,true,false,false,true}; + +inline int IsRemovable(ieByte timingmode) +{ + if( timingmode>=MAX_TIMING_MODE) return INVALID; + return fx_removable[timingmode]; +} + +//change the timing method after the effect triggered +static const ieByte fx_triggered[MAX_TIMING_MODE]={FX_DURATION_JUST_EXPIRED,FX_DURATION_INSTANT_PERMANENT,//0,1 +FX_DURATION_INSTANT_WHILE_EQUIPPED,FX_DURATION_DELAY_LIMITED_PENDING,//2,3 +FX_DURATION_AFTER_EXPIRES,FX_DURATION_PERMANENT_UNSAVED, //4,5 +FX_DURATION_INSTANT_LIMITED,FX_DURATION_JUST_EXPIRED,FX_DURATION_PERMANENT_UNSAVED,//6,8 +FX_DURATION_INSTANT_PERMANENT_AFTER_BONUSES,FX_DURATION_JUST_EXPIRED};//9,10 + +//change the timing method for effect that should trigger after this effect expired +static const ieDword fx_to_delayed[]={FX_DURATION_JUST_EXPIRED,FX_DURATION_JUST_EXPIRED, +FX_DURATION_PERMANENT_UNSAVED,FX_DURATION_DELAY_LIMITED_PENDING, +FX_DURATION_AFTER_EXPIRES,FX_DURATION_PERMANENT_UNSAVED, //4,5 +FX_DURATION_JUST_EXPIRED,FX_DURATION_JUST_EXPIRED,FX_DURATION_JUST_EXPIRED,//6,8 +FX_DURATION_JUST_EXPIRED,FX_DURATION_JUST_EXPIRED};//9,10 + +inline ieByte TriggeredEffect(ieByte timingmode) +{ + if( timingmode>=MAX_TIMING_MODE) return false; + return fx_triggered[timingmode]; +} + +int compare_effects(const void *a, const void *b) +{ + return stricmp(((EffectRef *) a)->Name,((EffectRef *) b)->Name); +} + +int find_effect(const void *a, const void *b) +{ + return stricmp((const char *) a,((const EffectRef *) b)->Name); +} + +static EffectRef* FindEffect(const char* effectname) +{ + if( !effectname || !effectnames) { + return NULL; + } + void *tmp = bsearch(effectname, effectnames, effectnames_count, sizeof(EffectRef), find_effect); + if( !tmp) { + printMessage( "EffectQueue", "", YELLOW); + printf("Couldn't assign effect: %s\n", effectname ); + } + return (EffectRef *) tmp; +} + +static EffectRef fx_protection_from_display_string_ref={"Protection:String",NULL,-1}; + +//special effects without level check (but with damage dices precalculated) +static EffectRef diced_effects[] = { + //core effects + {"Damage",NULL,-1}, + {"CurrentHPModifier",NULL,-1}, + {"MaximumHPModifier",NULL,-1}, + //iwd effects + {"BurningBlood",NULL,-1}, //iwd + {"ColdDamage",NULL,-1}, + {"CrushingDamage",NULL,-1}, + {"VampiricTouch",NULL,-1}, + {"VitriolicSphere",NULL,-1}, + //pst effects + {"TransferHP",NULL,-1}, +{NULL,NULL,0} }; + +//special effects without level check (but with damage dices not precalculated) +static EffectRef diced_effects2[] = { + {"BurningBlood2",NULL,-1}, //how/iwd2 + {"StaticCharge",NULL,-1}, //how/iwd2 + {"SoulEater",NULL,-1}, //how/iwd2 + {"LichTouch",NULL,-1}, //how +{NULL,NULL,0} }; + +inline static void ResolveEffectRef(EffectRef &effect_reference) +{ + if( effect_reference.opcode==-1) { + EffectRef* ref = FindEffect(effect_reference.Name); + if( ref && ref->opcode>=0) { + effect_reference.opcode = ref->opcode; + return; + } + effect_reference.opcode = -2; + } +} + +bool Init_EffectQueue() +{ + int i; + + if( initialized) { + return true; + } + pstflags = !!core->HasFeature(GF_PST_STATE_FLAGS); + + memset( Opcodes, 0, sizeof( Opcodes ) ); + for(i=0;iLoadSymbol( "effects" ); + if( eT < 0) { + printMessage( "EffectQueue","A critical scripting file is missing!\n",LIGHT_RED ); + return false; + } + Holder effectsTable = core->GetSymbol( eT ); + if( !effectsTable) { + printMessage( "EffectQueue","A critical scripting file is damaged!\n",LIGHT_RED ); + return false; + } + + for (i = 0; i < MAX_EFFECTS; i++) { + const char* effectname = effectsTable->GetValue( i ); + if( efftextTable) { + int row = efftextTable->GetRowCount(); + while (row--) { + const char* ret = efftextTable->GetRowName( row ); + long val; + if( valid_number( ret, val ) && (i == val) ) { + Opcodes[i].Strref = atoi( efftextTable->QueryField( row, 1 ) ); + } + } + } + + EffectRef* poi = FindEffect( effectname ); + if( poi != NULL) { + Opcodes[i].Function = poi->Function; + Opcodes[i].Name = poi->Name; + //reverse linking opcode number + //using this unused field + if( (poi->opcode!=-1) && effectname[0]!='*') { + printf("Clashing Opcodes FN: %d vs. %d, %s\n", i, poi->opcode, effectname); + abort(); + } + poi->opcode = i; + } + //printf("-------- FN: %d, %s\n", i, effectname); + } + core->DelSymbol( eT ); + + //additional initialisations + for (i=0;diced_effects[i].Name;i++) { + ResolveEffectRef(diced_effects[i]); + } + for (i=0;diced_effects2[i].Name;i++) { + ResolveEffectRef(diced_effects2[i]); + } + + return true; +} + +void EffectQueue_ReleaseMemory() +{ + if( effectnames) { + free (effectnames); + } + effectnames_count = 0; + effectnames = NULL; +} + +void EffectQueue_RegisterOpcodes(int count, const EffectRef* opcodes) +{ + if( ! effectnames) { + effectnames = (EffectRef*) malloc( (count+1) * sizeof( EffectRef ) ); + } else { + effectnames = (EffectRef*) realloc( effectnames, (effectnames_count + count + 1) * sizeof( EffectRef ) ); + } + + memcpy( effectnames + effectnames_count, opcodes, count * sizeof( EffectRef )); + effectnames_count += count; + effectnames[effectnames_count].Name = NULL; + //if we merge two effect lists, then we need to sort their effect tables + //actually, we might always want to sort this list, so there is no + //need to do it manually (sorted table is needed if we use bsearch) + qsort(effectnames, effectnames_count, sizeof(EffectRef), compare_effects); +} + +EffectQueue::EffectQueue() +{ + Owner = NULL; +} + +EffectQueue::~EffectQueue() +{ + std::list< Effect* >::iterator f; + + for ( f = effects.begin(); f != effects.end(); f++ ) { + delete (*f); + } +} + +Effect *EffectQueue::CreateEffect(ieDword opcode, ieDword param1, ieDword param2, ieWord timing) +{ + if( opcode==0xffffffff) { + return NULL; + } + Effect *fx = new Effect(); + if( !fx) { + return NULL; + } + memset(fx,0,sizeof(Effect)); + fx->Target = FX_TARGET_SELF; + fx->Opcode = opcode; + //probability2 is the low number (by effectqueue 331) + fx->Probability1 = 100; + fx->Parameter1 = param1; + fx->Parameter2 = param2; + fx->TimingMode = timing; + fx->PosX = 0xffffffff; + fx->PosY = 0xffffffff; + return fx; +} + +//return the count of effects with matching parameters +//useful for effects where there is no separate stat to see +ieDword EffectQueue::CountEffects(EffectRef &effect_reference, ieDword param1, ieDword param2, const char *resource) const +{ + ResolveEffectRef(effect_reference); + if( effect_reference.opcode<0) { + return 0; + } + return CountEffects(effect_reference.opcode, param1, param2, resource); +} + +//Change the location of an existing effect +//this is used when some external code needs to adjust the effect's location +//used when the gui sets the effect's final target +void EffectQueue::ModifyEffectPoint(EffectRef &effect_reference, ieDword x, ieDword y) const +{ + ResolveEffectRef(effect_reference); + if( effect_reference.opcode<0) { + return; + } + ModifyEffectPoint(effect_reference.opcode, x, y); +} + +Effect *EffectQueue::CreateEffect(EffectRef &effect_reference, ieDword param1, ieDword param2, ieWord timing) +{ + ResolveEffectRef(effect_reference); + if( effect_reference.opcode<0) { + return NULL; + } + return CreateEffect(effect_reference.opcode, param1, param2, timing); +} + +//copies the whole effectqueue (area projectiles use it) +EffectQueue *EffectQueue::CopySelf() const +{ + EffectQueue *effects; + + effects = new EffectQueue(); + std::list< Effect* >::const_iterator fxit = GetFirstEffect(); + Effect *fx; + + while( (fx = GetNextEffect(fxit))) { + effects->AddEffect(fx, false); + } + effects->SetOwner(GetOwner()); + return effects; +} + +//create a new effect with most of the characteristics of the old effect +//only opcode and parameters are changed +//This is used mostly inside effects, when an effect needs to spawn +//other effects with the same coordinates, source, duration, etc. +Effect *EffectQueue::CreateEffectCopy(Effect *oldfx, ieDword opcode, ieDword param1, ieDword param2) +{ + if( opcode==0xffffffff) { + return NULL; + } + Effect *fx = new Effect(); + if( !fx) { + return NULL; + } + memcpy(fx,oldfx,sizeof(Effect) ); + fx->Opcode=opcode; + fx->Parameter1=param1; + fx->Parameter2=param2; + return fx; +} + +Effect *EffectQueue::CreateEffectCopy(Effect *oldfx, EffectRef &effect_reference, ieDword param1, ieDword param2) +{ + ResolveEffectRef(effect_reference); + if( effect_reference.opcode<0) { + return NULL; + } + return CreateEffectCopy(oldfx, effect_reference.opcode, param1, param2); +} + +static EffectRef fx_unsummon_creature_ref={"UnsummonCreature",NULL,-1}; + +Effect *EffectQueue::CreateUnsummonEffect(Effect *fx) +{ + Effect *newfx = NULL; + if( (fx->TimingMode&0xff) == FX_DURATION_INSTANT_LIMITED) { + newfx = CreateEffectCopy(fx, fx_unsummon_creature_ref, 0, 0); + newfx->TimingMode = FX_DURATION_DELAY_PERMANENT; + if( newfx->Resource3[0]) { + strnuprcpy(newfx->Resource,newfx->Resource3, sizeof(ieResRef)-1 ); + } else { + strnuprcpy(newfx->Resource,"SPGFLSH1", sizeof(ieResRef)-1 ); + } + if( fx->TimingMode == FX_DURATION_ABSOLUTE) { + //unprepare duration + newfx->Duration = (newfx->Duration-core->GetGame()->GameTime)/AI_UPDATE_TIME; + } + } + + return newfx; +} + +void EffectQueue::AddEffect(Effect* fx, bool insert) +{ + Effect* new_fx = new Effect; + memcpy( new_fx, fx, sizeof( Effect ) ); + if( insert) { + effects.insert( effects.begin(), new_fx ); + } else { + effects.push_back( new_fx ); + } +} + +//This method can remove an effect described by a pointer to it, or +//an exact matching effect +bool EffectQueue::RemoveEffect(Effect* fx) +{ + int invariant_size = offsetof( Effect, random_value ); + + for (std::list< Effect* >::iterator f = effects.begin(); f != effects.end(); f++ ) { + Effect* fx2 = *f; + + if( (fx==fx2) || !memcmp( fx, fx2, invariant_size)) { + delete fx2; + effects.erase( f ); + return true; + } + } + return false; +} + +//this is where we reapply all effects when loading a saved game +//The effects are already in the fxqueue of the target +void EffectQueue::ApplyAllEffects(Actor* target) const +{ + std::list< Effect* >::const_iterator f; + for ( f = effects.begin(); f != effects.end(); f++ ) { + ApplyEffect( target, *f, 0 ); + } +} + +void EffectQueue::Cleanup() +{ + std::list< Effect* >::iterator f; + + for ( f = effects.begin(); f != effects.end(); ) { + if( (*f)->TimingMode == FX_DURATION_JUST_EXPIRED) { + delete *f; + effects.erase(f++); + } else { + f++; + } + } +} + +//Handle the target flag when the effect is applied first +int EffectQueue::AddEffect(Effect* fx, Scriptable* self, Actor* pretarget, const Point &dest) const +{ + int i; + Game *game; + Map *map; + int flg; + ieDword spec = 0; + Actor *st = (self && (self->Type==ST_ACTOR)) ?(Actor *) self:NULL; + + switch (fx->Target) { + case FX_TARGET_ORIGINAL: + fx->SetPosition(self->Pos); + + flg = ApplyEffect( st, fx, 1 ); + if( fx->TimingMode != FX_DURATION_JUST_EXPIRED) { + if( st) { + st->fxqueue.AddEffect( fx, flg==FX_INSERT ); + } + } + break; + case FX_TARGET_SELF: + fx->SetPosition(dest); + + flg = ApplyEffect( st, fx, 1 ); + if( fx->TimingMode != FX_DURATION_JUST_EXPIRED) { + if( st) { + st->fxqueue.AddEffect( fx, flg==FX_INSERT ); + } + } + break; + + case FX_TARGET_ALL_BUT_SELF: + map=self->GetCurrentArea(); + i= map->GetActorCount(true); + while(i--) { + Actor* actor = map->GetActor( i, true ); + //don't pick ourselves + if( st==actor) { + continue; + } + fx->SetPosition(actor->Pos); + + flg = ApplyEffect( actor, fx, 1 ); + if( fx->TimingMode != FX_DURATION_JUST_EXPIRED) { + actor->fxqueue.AddEffect( fx, flg==FX_INSERT ); + } + } + flg = FX_APPLIED; + break; + + case FX_TARGET_OWN_SIDE: + if( !st || st->InParty) { + goto all_party; + } + map = self->GetCurrentArea(); + spec = st->GetStat(IE_SPECIFIC); + + //GetActorCount(false) returns all nonparty critters + i = map->GetActorCount(false); + while(i--) { + Actor* actor = map->GetActor( i, false ); + if( actor->GetStat(IE_SPECIFIC)!=spec) { + continue; + } + fx->SetPosition(actor->Pos); + + flg = ApplyEffect( actor, fx, 1 ); + if( fx->TimingMode != FX_DURATION_JUST_EXPIRED) { + actor->fxqueue.AddEffect( fx, flg==FX_INSERT ); + } + } + flg = FX_APPLIED; + break; + case FX_TARGET_OTHER_SIDE: + if( !pretarget || pretarget->InParty) { + goto all_party; + } + map = self->GetCurrentArea(); + spec = pretarget->GetStat(IE_SPECIFIC); + + //GetActorCount(false) returns all nonparty critters + i = map->GetActorCount(false); + while(i--) { + Actor* actor = map->GetActor( i, false ); + if( actor->GetStat(IE_SPECIFIC)!=spec) { + continue; + } + fx->SetPosition(actor->Pos); + + flg = ApplyEffect( actor, fx, 1 ); + //GetActorCount can now return all nonparty critters + if( fx->TimingMode != FX_DURATION_JUST_EXPIRED) { + actor->fxqueue.AddEffect( fx, flg==FX_INSERT ); + } + } + flg = FX_APPLIED; + break; + case FX_TARGET_PRESET: + fx->SetPosition(pretarget->Pos); + + flg = ApplyEffect( pretarget, fx, 1 ); + if( fx->TimingMode != FX_DURATION_JUST_EXPIRED) { + if( pretarget) { + pretarget->fxqueue.AddEffect( fx, flg==FX_INSERT ); + } + } + break; + + case FX_TARGET_PARTY: +all_party: + game = core->GetGame(); + i = game->GetPartySize(true); + while(i--) { + Actor* actor = game->GetPC( i, true ); + fx->SetPosition(actor->Pos); + + flg = ApplyEffect( actor, fx, 1 ); + if( fx->TimingMode != FX_DURATION_JUST_EXPIRED) { + actor->fxqueue.AddEffect( fx, flg==FX_INSERT ); + } + } + flg = FX_APPLIED; + break; + + case FX_TARGET_ALL: + map = self->GetCurrentArea(); + i = map->GetActorCount(true); + while(i--) { + Actor* actor = map->GetActor( i, true ); + fx->SetPosition(actor->Pos); + + flg = ApplyEffect( actor, fx, 1 ); + if( fx->TimingMode != FX_DURATION_JUST_EXPIRED) { + actor->fxqueue.AddEffect( fx, flg==FX_INSERT ); + } + } + flg = FX_APPLIED; + break; + + case FX_TARGET_ALL_BUT_PARTY: + map = self->GetCurrentArea(); + i = map->GetActorCount(false); + while(i--) { + Actor* actor = map->GetActor( i, false ); + fx->SetPosition(actor->Pos); + + flg = ApplyEffect( actor, fx, 1 ); + //GetActorCount can now return all nonparty critters + if( fx->TimingMode != FX_DURATION_JUST_EXPIRED) { + actor->fxqueue.AddEffect( fx, flg==FX_INSERT ); + } + } + flg = FX_APPLIED; + break; + + case FX_TARGET_UNKNOWN: + default: + printf( "Unknown FX target type: %d\n", fx->Target); + flg = FX_ABORT; + break; + } + + return flg; +} + +//this is where effects from spells first get in touch with the target +//the effects are currently NOT in the target's fxqueue, those that stick +//will get copied (hence the fxqueue.AddEffect call) +//if this returns FX_NOT_APPLIED, then the whole stack was resisted +//or expired +int EffectQueue::AddAllEffects(Actor* target, const Point &destination) const +{ + int res = FX_NOT_APPLIED; + // pre-roll dice for fx needing them and stow them in the effect + ieDword random_value = core->Roll( 1, 100, -1 ); + + if( target) { + target->RollSaves(); + } + std::list< Effect* >::const_iterator f; + for ( f = effects.begin(); f != effects.end(); f++ ) { + //handle resistances and saving throws here + (*f)->random_value = random_value; + //if applyeffect returns true, we stop adding the future effects + //this is to simulate iwd2's on the fly spell resistance + + int tmp = AddEffect(*f, Owner, target, destination); + //lets try without Owner, any crash? + //If yes, then try to fix the individual effect + //If you use target for Owner here, the wand in chateau irenicus will work + //the same way as Imoen's monster summoning, which is a BAD THING (TM) + //int tmp = AddEffect(*f, Owner?Owner:target, target, destination); + if( tmp == FX_ABORT) { + res = FX_NOT_APPLIED; + break; + } + if( tmp != FX_NOT_APPLIED) { + res = FX_APPLIED; + } + } + return res; +} + +//check if an effect has no level based resistance, but instead the dice sizes/count +//adjusts Parameter1 (like a damage causing effect) +inline static bool IsDicedEffect(int opcode) +{ + int i; + + for(i=0;diced_effects[i].Name;i++) { + if( diced_effects[i].opcode==opcode) { + return true; + } + } + return false; +} + +//there is no level based resistance, but Parameter1 cannot be precalculated +//these effects use the Dice fields in a special way +inline static bool IsDicedEffect2(int opcode) +{ + int i; + + for(i=0;diced_effects2[i].Name;i++) { + if( diced_effects2[i].opcode==opcode) { + return true; + } + } + return false; +} + +//resisted effect based on level +inline bool check_level(Actor *target, Effect *fx) +{ + //skip non level based effects + if( IsDicedEffect((int) fx->Opcode)) { + fx->Parameter1 = DICE_ROLL((signed)fx->Parameter1); + //this is a hack for PST style diced effects + if( core->HasFeature(GF_SAVE_FOR_HALF) ) { + if( memcmp(fx->Resource,"NEG",4) ) { + fx->IsSaveForHalfDamage=1; + } + } else { + if( (fx->Parameter2&3)==3) { + fx->IsSaveForHalfDamage=1; + } + } + return false; + } + if( IsDicedEffect2((int) fx->Opcode)) { + return false; + } + + if( !target) { + return false; + } + if(fx->Target == FX_TARGET_SELF) { + return false; + } + + ieDword level = (ieDword) target->GetXPLevel( true ); + //return true if resisted + if (fx->MinAffectedLevel > 0 || fx->MaxAffectedLevel > 0) { + if (level < fx->MinAffectedLevel || level > fx->MaxAffectedLevel) { + return true; + } + } + return false; +} + +//roll for the effect probability, there is a high and a low treshold, the d100 +//roll should hit in the middle +inline bool check_probability(Effect* fx) +{ + //watch for this, probability1 is the high number + //probability2 is the low number + //random value is 0-99 + if( fx->random_value<=fx->Probability2 || fx->random_value>fx->Probability1) { + return false; + } + return true; +} + +//immunity effects +static EffectRef fx_level_immunity_ref={"Protection:Spelllevel",NULL,-1}; +static EffectRef fx_opcode_immunity_ref={"Protection:Opcode",NULL,-1}; //bg2 +static EffectRef fx_opcode_immunity2_ref={"Protection:Opcode2",NULL,-1};//iwd +static EffectRef fx_spell_immunity_ref={"Protection:Spell",NULL,-1}; //bg2 +static EffectRef fx_spell_immunity2_ref={"Protection:Spell2",NULL,-1};//iwd +static EffectRef fx_store_spell_sequencer_ref={"Sequencer:Store",NULL,-1}; //bg2, works against sequencers +static EffectRef fx_school_immunity_ref={"Protection:School",NULL,-1}; +static EffectRef fx_secondary_type_immunity_ref={"Protection:SecondaryType",NULL,-1}; + +//decrementing immunity effects +static EffectRef fx_level_immunity_dec_ref={"Protection:SpellLevelDec",NULL,-1}; +static EffectRef fx_spell_immunity_dec_ref={"Protection:SpellDec",NULL,-1}; +static EffectRef fx_school_immunity_dec_ref={"Protection:SchoolDec",NULL,-1}; +static EffectRef fx_secondary_type_immunity_dec_ref={"Protection:SecondaryTypeDec",NULL,-1}; + +//bounce effects +static EffectRef fx_level_bounce_ref={"Bounce:SpellLevel",NULL,-1}; +//static EffectRef fx_opcode_bounce_ref={"Bounce:Opcode",NULL,-1}; +static EffectRef fx_spell_bounce_ref={"Bounce:Spell",NULL,-1}; +static EffectRef fx_school_bounce_ref={"Bounce:School",NULL,-1}; +static EffectRef fx_secondary_type_bounce_ref={"Bounce:SecondaryType",NULL,-1}; + +//decrementing bounce effects +static EffectRef fx_level_bounce_dec_ref={"Bounce:SpellLevelDec",NULL,-1}; +static EffectRef fx_spell_bounce_dec_ref={"Bounce:SpellDec",NULL,-1}; +static EffectRef fx_school_bounce_dec_ref={"Bounce:SchoolDec",NULL,-1}; +static EffectRef fx_secondary_type_bounce_dec_ref={"Bounce:SecondaryTypeDec",NULL,-1}; + +//spelltrap (multiple decrementing immunity) +static EffectRef fx_spelltrap={"SpellTrap", NULL,-1}; + +//this is for whole spell immunity/bounce +inline static void DecreaseEffect(Effect *efx) +{ + efx->Parameter1--; + if( (int) efx->Parameter1<1) { + //don't remove effects directly!!! + efx->TimingMode = FX_DURATION_JUST_EXPIRED; + } +} + +//lower decreasing immunities/bounces +static int check_type(Actor* actor, Effect* fx) +{ + //the protective effect (if any) + Effect *efx; + + ieDword bounce = actor->GetStat(IE_BOUNCE); + + //immunity checks +/*opcode immunity is in the per opcode checks + if( actor->fxqueue.HasEffectWithParam(fx_opcode_immunity_ref, fx->Opcode) ) { + return 0; + } + if( actor->fxqueue.HasEffectWithParam(fx_opcode_immunity2_ref, fx->Opcode) ) { + return 0; + } +*/ + //spell level immunity + if(fx->Power && actor->fxqueue.HasEffectWithParamPair(fx_level_immunity_ref, fx->Power, 0) ) { + return 0; + } + + //source immunity (spell name) + //if source is unspecified, don't resist it + if( fx->Source[0]) { + if( actor->fxqueue.HasEffectWithResource(fx_spell_immunity_ref, fx->Source) ) { + return 0; + } + if( actor->fxqueue.HasEffectWithResource(fx_spell_immunity2_ref, fx->Source) ) { + return 0; + } + if (actor->fxqueue.HasEffectWithResource(fx_store_spell_sequencer_ref, fx->Source) ) { + //TODO: display strref 0x806C + return 0; + } + } + + //primary type immunity (school) + if( fx->PrimaryType) { + if( actor->fxqueue.HasEffectWithParam(fx_school_immunity_ref, fx->PrimaryType)) { + return 0; + } + } + + //secondary type immunity (usage) + if( fx->SecondaryType) { + if( actor->fxqueue.HasEffectWithParam(fx_secondary_type_immunity_ref, fx->SecondaryType) ) { + return 0; + } + } + + //decrementing immunity checks + //decrementing level immunity + efx = actor->fxqueue.HasEffectWithParamPair(fx_level_immunity_dec_ref, fx->Power, 0); + if( efx ) { + DecreaseEffect(efx); + return 0; + } + + //decrementing spell immunity + if( fx->Source[0]) { + efx = actor->fxqueue.HasEffectWithResource(fx_spell_immunity_dec_ref, fx->Source); + if( efx) { + DecreaseEffect(efx); + return 0; + } + } + //decrementing primary type immunity (school) + if( fx->PrimaryType) { + efx = actor->fxqueue.HasEffectWithParam(fx_school_immunity_dec_ref, fx->PrimaryType); + if( efx) { + DecreaseEffect(efx); + return 0; + } + } + + //decrementing secondary type immunity (usage) + if( fx->SecondaryType) { + efx = actor->fxqueue.HasEffectWithParam(fx_secondary_type_immunity_dec_ref, fx->SecondaryType); + if( efx) { + DecreaseEffect(efx); + return 0; + } + } + + //spelltrap (absorb) + //FIXME: + //if the spelltrap effect already absorbed enough levels + //but still didn't get removed, it will absorb levels it shouldn't + //it will also absorb multiple spells in a single round + efx=actor->fxqueue.HasEffectWithParamPair(fx_spelltrap, 0, fx->Power); + if( efx) { + //storing the absorbed spell level + efx->Parameter3+=fx->Power; + //instead of a single effect, they had to create an effect for each level + //HOW DAMN LAME + //if decrease needs the spell level, use fx->Power here + actor->fxqueue.DecreaseParam1OfEffect(fx_spelltrap, 1); + //efx->Parameter1--; + return 0; + } + + //bounce checks + if( (bounce&BNC_LEVEL) && actor->fxqueue.HasEffectWithParamPair(fx_level_bounce_ref, fx->Power, 0) ) { + return 0; + } + + if( fx->Source[0] && (bounce&BNC_RESOURCE) && actor->fxqueue.HasEffectWithResource(fx_spell_bounce_ref, fx->Source) ) { + return -1; + } + + if( fx->PrimaryType && (bounce&BNC_SCHOOL) ) { + if( actor->fxqueue.HasEffectWithParam(fx_school_bounce_ref, fx->PrimaryType)) { + return -1; + } + } + + if( fx->SecondaryType && (bounce&BNC_SECTYPE) ) { + if( actor->fxqueue.HasEffectWithParam(fx_secondary_type_bounce_ref, fx->SecondaryType)) { + return -1; + } + } + //decrementing bounce checks + + //level decrementing bounce check + if( (bounce&BNC_LEVEL_DEC)) { + efx=actor->fxqueue.HasEffectWithParamPair(fx_level_bounce_dec_ref, fx->Power, 0); + if( efx) { + DecreaseEffect(efx); + return -1; + } + } + + if( fx->Source[0] && (bounce&BNC_RESOURCE_DEC)) { + efx=actor->fxqueue.HasEffectWithResource(fx_spell_bounce_dec_ref, fx->Resource); + if( efx) { + DecreaseEffect(efx); + return -1; + } + } + + if( fx->PrimaryType && (bounce&BNC_SCHOOL_DEC) ) { + efx=actor->fxqueue.HasEffectWithParam(fx_school_bounce_dec_ref, fx->PrimaryType); + if( efx) { + DecreaseEffect(efx); + return -1; + } + } + + if( fx->SecondaryType && (bounce&BNC_SECTYPE_DEC) ) { + efx=actor->fxqueue.HasEffectWithParam(fx_secondary_type_bounce_dec_ref, fx->SecondaryType); + if( efx) { + DecreaseEffect(efx); + return -1; + } + } + + return 1; +} + +//check resistances, saving throws +static bool check_resistance(Actor* actor, Effect* fx) +{ + if( !actor) { + return false; + } + + //opcode immunity + if( actor->fxqueue.HasEffectWithParam(fx_opcode_immunity_ref, fx->Opcode) ) { + printf ("immune to effect: %s\n", (char*) Opcodes[fx->Opcode].Name); + return true; + } + if( actor->fxqueue.HasEffectWithParam(fx_opcode_immunity2_ref, fx->Opcode) ) { + printf ("immune2 to effect: %s\n", (char*) Opcodes[fx->Opcode].Name); + return true; + } + +/* opcode bouncing isn't implemented? + //opcode bouncing + if( actor->fxqueue.HasEffectWithParam(fx_opcode_bounce_ref, fx->Opcode) ) { + return false; + } +*/ + + //not resistable (no saves either?) + if( fx->Resistance != FX_CAN_RESIST_CAN_DISPEL) { + return false; + } + + if (pstflags && (actor->GetSafeStat(IE_STATE_ID) & (STATE_ANTIMAGIC) ) ) { + return false; + } + + //don't resist self + bool selective_mr = core->HasFeature(GF_SELECTIVE_MAGIC_RES); + if (fx->Target==FX_TARGET_SELF) { + if (selective_mr) { + return false; + } + } + + //magic immunity + ieDword val = actor->GetStat(IE_RESISTMAGIC); + if( fx->random_value < val) { + // when using biased magic resistance non-hostile spells aren't resisted + if ((selective_mr && (fx->SourceFlags&SF_HOSTILE)) || !selective_mr) { + printf ("effect resisted: %s\n", (char*) Opcodes[fx->Opcode].Name); + return true; + } + } + + //saving throws + bool saved = false; + for (int i=0;i<5;i++) { + if( fx->SavingThrowType&(1<GetSavingThrow(i, fx->SavingThrowBonus); + if( saved) { + break; + } + } + } + if( saved) { + if( fx->IsSaveForHalfDamage) { + fx->Parameter1/=2; + } else { + printf ("%s saved against effect: %s\n", actor->GetName(1), (char*) Opcodes[fx->Opcode].Name); + return true; + } + } + return false; +} + +// this function is called two different ways +// when FirstApply is set, then the effect isn't stuck on the target +// this happens when a new effect comes in contact with the target. +// if the effect returns FX_DURATION_JUST_EXPIRED then it won't stick +// when first_apply is unset, the effect is already on the target +// this happens on load time too! +// returns FX_NOT_APPLIED if the process shouldn't be calling applyeffect anymore +// returns FX_ABORT if the whole spell this effect is in should be aborted +// it will disable all future effects of same source (only on first apply) + +int EffectQueue::ApplyEffect(Actor* target, Effect* fx, ieDword first_apply, ieDword resistance) const +{ + //printf( "FX 0x%02x: %s(%d, %d)\n", fx->Opcode, effectnames[fx->Opcode].Name, fx->Parameter1, fx->Parameter2 ); + if( fx->Opcode >= MAX_EFFECTS) { + fx->TimingMode = FX_DURATION_JUST_EXPIRED; + return FX_NOT_APPLIED; + } + + ieDword GameTime = core->GetGame()->GameTime; + + fx->FirstApply=first_apply; + if( first_apply) { + if( (fx->PosX==0xffffffff) && (fx->PosY==0xffffffff)) { + fx->PosX = target->Pos.x; + fx->PosY = target->Pos.y; + } + + //gemrb specific, stat based chance + if ((fx->Probability2 == 100) && Owner && (Owner->Type==ST_ACTOR) ) { + fx->Probability2 = 0; + fx->Probability1 = ((Actor *) Owner)->GetSafeStat(fx->Probability1); + } + + if (resistance) { + //the effect didn't pass the probability check + if( !check_probability(fx) ) { + fx->TimingMode = FX_DURATION_JUST_EXPIRED; + return FX_NOT_APPLIED; + } + + //the effect didn't pass the target level check + if( check_level(target, fx) ) { + fx->TimingMode = FX_DURATION_JUST_EXPIRED; + return FX_NOT_APPLIED; + } + + //the effect didn't pass the resistance check + if( check_resistance(target, fx) ) { + fx->TimingMode = FX_DURATION_JUST_EXPIRED; + return FX_NOT_APPLIED; + } + } + + //Same as in items and spells + if (fx->SourceFlags & SF_HOSTILE) { + if (target && (target != Owner) && Owner && (Owner->Type==ST_ACTOR) ) { + target->AttackedBy((Actor *) Owner); + } + } + + if( NeedPrepare(fx->TimingMode) ) { + //save delay for later + fx->SecondaryDelay = fx->Duration; + if( fx->TimingMode == FX_DURATION_INSTANT_LIMITED) { + fx->TimingMode = FX_DURATION_ABSOLUTE; + } + PrepareDuration(fx); + } + } + //check if the effect has triggered or expired + switch (DelayType(fx->TimingMode&0xff) ) { + case DELAYED: + if( fx->Duration>GameTime) { + return FX_NOT_APPLIED; + } + //effect triggered + //delayed duration (3) + if( NeedPrepare(fx->TimingMode) ) { + //prepare for delayed duration effects + fx->Duration = fx->SecondaryDelay; + PrepareDuration(fx); + } + fx->TimingMode=TriggeredEffect(fx->TimingMode); + break; + case DURATION: + if( fx->Duration<=GameTime) { + fx->TimingMode = FX_DURATION_JUST_EXPIRED; + //add a return here, if 0 duration effects shouldn't work + } + break; + //permanent effect (so there is no warning) + case PERMANENT: + break; + //this shouldn't happen + default: + printf("Unknown delay type: %d (from %d)\n", DelayType(fx->TimingMode&0xff), fx->TimingMode); + abort(); + } + + EffectFunction fn = 0; + if( fx->OpcodeOpcode].Function; + } + int res = FX_ABORT; + if( fn) { + if( target && first_apply ) { + if( !target->fxqueue.HasEffectWithParamPair(fx_protection_from_display_string_ref, fx->Parameter1, 0) ) { + displaymsg->DisplayStringName( Opcodes[fx->Opcode].Strref, 0xf0f0f0, + target, IE_STR_SOUND); + } + } + + res=fn( Owner, target, fx ); + + //if there is no owner, we assume it is the target + switch( res ) { + case FX_APPLIED: + //normal effect with duration + break; + case FX_NOT_APPLIED: + //instant effect, pending removal + //for example, a damage effect + fx->TimingMode = FX_DURATION_JUST_EXPIRED; + break; + case FX_INSERT: + //put this effect in the beginning of the queue + //all known insert effects are 'permanent' too + //that is the AC effect only + //actually, permanent effects seem to be + //inserted by the game engine too + case FX_PERMANENT: + //don't stick around if it was executed permanently + //for example, a permanent strength modifier effect + if( (fx->TimingMode == FX_DURATION_INSTANT_PERMANENT) ) { + fx->TimingMode = FX_DURATION_JUST_EXPIRED; + } + break; + case FX_ABORT: + break; + default: + abort(); + } + } else { + //effect not found, it is going to be discarded + fx->TimingMode = FX_DURATION_JUST_EXPIRED; + } + return res; +} + +// looks for opcode with param2 + +#define MATCH_OPCODE() if((*f)->Opcode!=opcode) { continue; } + +// useful for: remove equipped item +#define MATCH_SLOTCODE() if((*f)->InventorySlot!=slotcode) { continue; } + +// useful for: remove projectile type +#define MATCH_PROJECTILE() if((*f)->Projectile!=projectile) { continue; } + +static const bool fx_live[MAX_TIMING_MODE]={true,true,true,false,false,false,false,false,true,true,false}; +inline bool IsLive(ieByte timingmode) +{ + if( timingmode>=MAX_TIMING_MODE) return false; + return fx_live[timingmode]; +} + +#define MATCH_LIVE_FX() if(!IsLive((*f)->TimingMode)) { continue; } +#define MATCH_PARAM1() if((*f)->Parameter1!=param1) { continue; } +#define MATCH_PARAM2() if((*f)->Parameter2!=param2) { continue; } +#define MATCH_RESOURCE() if( strnicmp( (*f)->Resource, resource, 8) ) { continue; } +#define MATCH_SOURCE() if( strnicmp( (*f)->Source, Removed, 8) ) { continue; } +#define MATCH_TIMING() if( (*f)->TimingMode!=timing) { continue; } + +//call this from an applied effect, after it returns, these effects +//will be killed along with it +void EffectQueue::RemoveAllEffects(ieDword opcode) const +{ + std::list< Effect* >::const_iterator f; + for ( f = effects.begin(); f != effects.end(); f++ ) { + MATCH_OPCODE(); + MATCH_LIVE_FX(); + + (*f)->TimingMode = FX_DURATION_JUST_EXPIRED; + } +} + +//removes all equipping effects that match slotcode +void EffectQueue::RemoveEquippingEffects(ieDwordSigned slotcode) const +{ + std::list< Effect* >::const_iterator f; + for ( f = effects.begin(); f != effects.end(); f++ ) { + if( !IsEquipped((*f)->TimingMode)) continue; + MATCH_SLOTCODE(); + + (*f)->TimingMode = FX_DURATION_JUST_EXPIRED; + } +} + +//removes all effects that match projectile +void EffectQueue::RemoveAllEffectsWithProjectile(ieDword projectile) const +{ + std::list< Effect* >::const_iterator f; + for ( f = effects.begin(); f != effects.end(); f++ ) { + MATCH_PROJECTILE(); + + (*f)->TimingMode = FX_DURATION_JUST_EXPIRED; + } +} + +//remove effects belonging to a given spell +void EffectQueue::RemoveAllEffects(const ieResRef Removed) const +{ + std::list< Effect* >::const_iterator f; + for ( f = effects.begin(); f != effects.end(); f++ ) { + MATCH_LIVE_FX(); + MATCH_SOURCE(); + + (*f)->TimingMode = FX_DURATION_JUST_EXPIRED; + } +} + +//remove effects belonging to a given spell, but only if they match timing method x +void EffectQueue::RemoveAllEffects(const ieResRef Removed, ieByte timing) const +{ + std::list< Effect* >::const_iterator f; + for ( f = effects.begin(); f != effects.end(); f++ ) { + MATCH_TIMING(); + MATCH_SOURCE(); + + (*f)->TimingMode = FX_DURATION_JUST_EXPIRED; + } +} + +//this will modify effect reference +void EffectQueue::RemoveAllEffects(EffectRef &effect_reference) const +{ + ResolveEffectRef(effect_reference); + if( effect_reference.opcode<0) { + return; + } + RemoveAllEffects(effect_reference.opcode); +} + +//Removes all effects with a matching resource field +void EffectQueue::RemoveAllEffectsWithResource(ieDword opcode, const ieResRef resource) const +{ + std::list< Effect* >::const_iterator f; + for ( f = effects.begin(); f != effects.end(); f++ ) { + MATCH_OPCODE(); + MATCH_LIVE_FX(); + MATCH_RESOURCE(); + + (*f)->TimingMode = FX_DURATION_JUST_EXPIRED; + } +} + +void EffectQueue::RemoveAllEffectsWithResource(EffectRef &effect_reference, const ieResRef resource) const +{ + ResolveEffectRef(effect_reference); + RemoveAllEffectsWithResource(effect_reference.opcode, resource); +} + +//This method could be used to remove stat modifiers that would lower a stat +//(works only if a higher stat means good for the target) +void EffectQueue::RemoveAllDetrimentalEffects(ieDword opcode, ieDword current) const +{ + std::list< Effect* >::const_iterator f; + for ( f = effects.begin(); f != effects.end(); f++ ) { + MATCH_OPCODE(); + MATCH_LIVE_FX(); + switch((*f)->Parameter2) { + case 0:case 3: + if( ((signed) (*f)->Parameter1)>=0) continue; + break; + case 1:case 4: + if( ((signed) (*f)->Parameter1)>=(signed) current) continue; + break; + case 2:case 5: + if( ((signed) (*f)->Parameter1)>=100) continue; + break; + default: + break; + } + (*f)->TimingMode = FX_DURATION_JUST_EXPIRED; + } +} + +//Removes all effects with a matching param2 +//param2 is usually an effect's subclass (quality) while param1 is more like quantity. +//So opcode+param2 usually pinpoints an effect better when not all effects of a given +//opcode need to be removed (see removal of portrait icon) +void EffectQueue::RemoveAllEffectsWithParam(ieDword opcode, ieDword param2) const +{ + std::list< Effect* >::const_iterator f; + for ( f = effects.begin(); f != effects.end(); f++ ) { + MATCH_OPCODE(); + MATCH_LIVE_FX(); + MATCH_PARAM2(); + + (*f)->TimingMode = FX_DURATION_JUST_EXPIRED; + } +} + +//this function is called by FakeEffectExpiryCheck +//probably also called by rest +void EffectQueue::RemoveExpiredEffects(ieDword futuretime) const +{ + ieDword GameTime = core->GetGame()->GameTime; + if( GameTime+futuretime*AI_UPDATE_TIME::const_iterator f; + for ( f = effects.begin(); f != effects.end(); f++ ) { + //FIXME: how this method handles delayed effects??? + //it should remove them as well, i think + if( DelayType( ((*f)->TimingMode) )!=PERMANENT ) { + if( (*f)->Duration<=GameTime) { + (*f)->TimingMode = FX_DURATION_JUST_EXPIRED; + } + } + } +} + +//this effect will expire all effects that are not truly permanent +//which i call permanent after death (iesdp calls it permanent after bonuses) +void EffectQueue::RemoveAllNonPermanentEffects() const +{ + std::list< Effect* >::const_iterator f; + for ( f = effects.begin(); f != effects.end(); f++ ) { + if( IsRemovable((*f)->TimingMode) ) { + (*f)->TimingMode = FX_DURATION_JUST_EXPIRED; + } + } +} + +//this will modify effect reference + +void EffectQueue::RemoveAllDetrimentalEffects(EffectRef &effect_reference, ieDword current) const +{ + ResolveEffectRef(effect_reference); + RemoveAllDetrimentalEffects(effect_reference.opcode, current); +} + +void EffectQueue::RemoveAllEffectsWithParam(EffectRef &effect_reference, ieDword param2) const +{ + ResolveEffectRef(effect_reference); + RemoveAllEffectsWithParam(effect_reference.opcode, param2); +} + +//remove certain levels of effects, possibly matching school/secondary type +//this method removes whole spells (tied together by their source) +//FIXME: probably this isn't perfect +void EffectQueue::RemoveLevelEffects(ieResRef &Removed, ieDword level, ieDword Flags, ieDword match) const +{ + Removed[0]=0; + std::list< Effect* >::const_iterator f; + for ( f = effects.begin(); f != effects.end(); f++ ) { + if( (*f)->Power>level) { + continue; + } + + if( Removed[0]) { + MATCH_SOURCE(); + } + if( Flags&RL_MATCHSCHOOL) { + if( (*f)->PrimaryType!=match) { + continue; + } + } + if( Flags&RL_MATCHSECTYPE) { + if( (*f)->SecondaryType!=match) { + continue; + } + } + //if dispellable was not set, or the effect is dispellable + //then remove it + if( Flags&RL_DISPELLABLE) { + if( !((*f)->Resistance&FX_CAN_DISPEL)) { + continue; + } + } + (*f)->TimingMode = FX_DURATION_JUST_EXPIRED; + if( Flags&RL_REMOVEFIRST) { + memcpy(Removed,(*f)->Source, sizeof(Removed)); + } + } +} + +Effect *EffectQueue::HasOpcode(ieDword opcode) const +{ + std::list< Effect* >::const_iterator f; + for ( f = effects.begin(); f != effects.end(); f++ ) { + MATCH_OPCODE(); + MATCH_LIVE_FX(); + + return (*f); + } + return NULL; +} + +Effect *EffectQueue::HasEffect(EffectRef &effect_reference) const +{ + ResolveEffectRef(effect_reference); + if( effect_reference.opcode<0) { + return NULL; + } + return HasOpcode(effect_reference.opcode); +} + +Effect *EffectQueue::HasOpcodeWithParam(ieDword opcode, ieDword param2) const +{ + std::list< Effect* >::const_iterator f; + for ( f = effects.begin(); f != effects.end(); f++ ) { + MATCH_OPCODE(); + MATCH_LIVE_FX(); + MATCH_PARAM2(); + + return (*f); + } + return NULL; +} + +Effect *EffectQueue::HasEffectWithParam(EffectRef &effect_reference, ieDword param2) const +{ + ResolveEffectRef(effect_reference); + if( effect_reference.opcode<0) { + return NULL; + } + return HasOpcodeWithParam(effect_reference.opcode, param2); +} + +//looks for opcode with pairs of parameters (useful for protection against creature, extra damage or extra thac0 against creature) +//generally an IDS targeting + +Effect *EffectQueue::HasOpcodeWithParamPair(ieDword opcode, ieDword param1, ieDword param2) const +{ + std::list< Effect* >::const_iterator f; + for ( f = effects.begin(); f != effects.end(); f++ ) { + MATCH_OPCODE(); + MATCH_LIVE_FX(); + MATCH_PARAM2(); + //0 is always accepted as first parameter + if( param1) { + MATCH_PARAM1(); + } + + return (*f); + } + return NULL; +} + +Effect *EffectQueue::HasEffectWithParamPair(EffectRef &effect_reference, ieDword param1, ieDword param2) const +{ + ResolveEffectRef(effect_reference); + if( effect_reference.opcode<0) { + return NULL; + } + return HasOpcodeWithParamPair(effect_reference.opcode, param1, param2); +} + +// sums all the values of the specific damage bonus effects of the passed "damage type" +int EffectQueue::SpecificDamageBonus(ieDword opcode, ieDword param2) const +{ + int bonus = 0; + std::list< Effect* >::const_iterator f; + for ( f = effects.begin(); f != effects.end(); f++ ) { + MATCH_OPCODE(); + MATCH_LIVE_FX(); + MATCH_PARAM2(); + bonus += (signed) (*f)->Parameter1; + } + return bonus; +} + +static EffectRef fx_damage_bonus_modifier_ref={"DamageBonusModifier",NULL,-1}; +int EffectQueue::SpecificDamageBonus(ieDword damage_type) const +{ + ResolveEffectRef(fx_damage_bonus_modifier_ref); + if(fx_damage_bonus_modifier_ref.opcode < 0) { + return 0; + } + return SpecificDamageBonus(fx_damage_bonus_modifier_ref.opcode, damage_type); +} + +//this could be used for stoneskins and mirror images as well +void EffectQueue::DecreaseParam1OfEffect(ieDword opcode, ieDword amount) const +{ + std::list< Effect* >::const_iterator f; + for ( f = effects.begin(); f != effects.end(); f++ ) { + MATCH_OPCODE(); + MATCH_LIVE_FX(); + ieDword value = (*f)->Parameter1; + if( value>amount) value-=amount; + else value = 0; + (*f)->Parameter1=value; + } +} + +void EffectQueue::DecreaseParam1OfEffect(EffectRef &effect_reference, ieDword amount) const +{ + ResolveEffectRef(effect_reference); + if( effect_reference.opcode<0) { + return; + } + DecreaseParam1OfEffect(effect_reference.opcode, amount); +} + + +//this function does IDS targeting for effects (extra damage/thac0 against creature) +static const int ids_stats[7]={IE_EA, IE_GENERAL, IE_RACE, IE_CLASS, IE_SPECIFIC, IE_SEX, IE_ALIGNMENT}; + +int EffectQueue::BonusAgainstCreature(ieDword opcode, Actor *actor) const +{ + int sum = 0; + std::list< Effect* >::const_iterator f; + for ( f = effects.begin(); f != effects.end(); f++ ) { + MATCH_OPCODE(); + MATCH_LIVE_FX(); + if( (*f)->Parameter1) { + ieDword ids = (*f)->Parameter2; + if( ids<2 || ids>8) { + ids=2; + } + ieDword param1 = actor->GetStat(ids_stats[ids-2]); + MATCH_PARAM1(); + } + int val = (int) (*f)->Parameter3; + if( !val) val = 2; + sum += val; + } + return sum; +} + +int EffectQueue::BonusAgainstCreature(EffectRef &effect_reference, Actor *actor) const +{ + ResolveEffectRef(effect_reference); + if( effect_reference.opcode<0) { + return 0; + } + return BonusAgainstCreature(effect_reference.opcode, actor); +} + +bool EffectQueue::WeaponImmunity(ieDword opcode, int enchantment, ieDword weapontype) const +{ + std::list< Effect* >::const_iterator f; + for ( f = effects.begin(); f != effects.end(); f++ ) { + MATCH_OPCODE(); + MATCH_LIVE_FX(); + // + int magic = (int) (*f)->Parameter1; + ieDword mask = (*f)->Parameter3; + ieDword value = (*f)->Parameter4; + if( magic==0) { + if( enchantment) continue; + } else if( magic>0) { + if( enchantment>magic) continue; + } + + if( (weapontype&mask) != value) { + continue; + } + return true; + } + return false; +} + +static EffectRef fx_weapon_immunity_ref={"Protection:Weapons",NULL,-1}; + +bool EffectQueue::WeaponImmunity(int enchantment, ieDword weapontype) const +{ + ResolveEffectRef(fx_weapon_immunity_ref); + if( fx_weapon_immunity_ref.opcode<0) { + return 0; + } + return WeaponImmunity(fx_weapon_immunity_ref.opcode, enchantment, weapontype); +} + +void EffectQueue::AddWeaponEffects(EffectQueue *fxqueue, EffectRef &fx_ref) const +{ + ResolveEffectRef(fx_ref); + if( fx_ref.opcode<0) { + return; + } + + ieDword opcode = fx_ref.opcode; + Point p(-1,-1); + + std::list< Effect* >::const_iterator f; + for ( f = effects.begin(); f != effects.end(); f++ ) { + MATCH_OPCODE(); + MATCH_LIVE_FX(); + // + Effect *fx = core->GetEffect( (*f)->Resource, (*f)->Power, p); + if (!fx) continue; + fx->Target = FX_TARGET_PRESET; + fxqueue->AddEffect(fx, true); + } +} + +/* no longer needed, use IE_CASTING stat +static EffectRef fx_disable_spellcasting_ref={ "DisableCasting", NULL, -1 }; +int EffectQueue::DisabledSpellcasting(int types) const +{ + ResolveEffectRef(fx_disable_spellcasting_ref); + if( fx_disable_spellcasting_ref.opcode < 0) { + return 0; + } + + unsigned int spelltype_mask = 0; + bool iwd2 = !!core->HasFeature(GF_ENHANCED_EFFECTS); + ieDword opcode = fx_disable_spellcasting_ref.opcode; + std::list< Effect* >::const_iterator f; + for ( f = effects.begin(); f != effects.end(); f++ ) { + MATCH_OPCODE(); + MATCH_LIVE_FX(); + + if (iwd2) { + switch((*f)->Parameter2) { + case 0: // all + spelltype_mask |= 7; + break; + case 1: // mage and cleric + spelltype_mask |= 3; + break; + case 2: // mage + spelltype_mask |= 2; + break; + case 3: // cleric + spelltype_mask |= 1; + break; + case 4: // innate + spelltype_mask |= 4; + break; + } + } else { + switch((*f)->Parameter2) { + case 0: // mage + spelltype_mask |= 2; + break; + case 1: // cleric + spelltype_mask |= 1; + break; + case 2: // innate + spelltype_mask |= 4; + break; + } + } + } + return spelltype_mask & types; +} +*/ + +//useful for immunity vs spell, can't use item, etc. +Effect *EffectQueue::HasOpcodeWithResource(ieDword opcode, const ieResRef resource) const +{ + std::list< Effect* >::const_iterator f; + for ( f = effects.begin(); f != effects.end(); f++ ) { + MATCH_OPCODE(); + MATCH_LIVE_FX(); + MATCH_RESOURCE(); + + return (*f); + } + return NULL; +} + +Effect *EffectQueue::HasEffectWithResource(EffectRef &effect_reference, const ieResRef resource) const +{ + ResolveEffectRef(effect_reference); + return HasOpcodeWithResource(effect_reference.opcode, resource); +} + +//used in contingency/sequencer code (cannot have the same contingency twice) +Effect *EffectQueue::HasOpcodeWithSource(ieDword opcode, const ieResRef Removed) const +{ + std::list< Effect* >::const_iterator f; + for ( f = effects.begin(); f != effects.end(); f++ ) { + MATCH_OPCODE(); + MATCH_LIVE_FX(); + MATCH_SOURCE(); + + return (*f); + } + return NULL; +} + +Effect *EffectQueue::HasEffectWithSource(EffectRef &effect_reference, const ieResRef resource) const +{ + ResolveEffectRef(effect_reference); + return HasOpcodeWithSource(effect_reference.opcode, resource); +} + +bool EffectQueue::HasAnyDispellableEffect() const +{ + std::list< Effect* >::const_iterator f; + for ( f = effects.begin(); f != effects.end(); f++ ) { + if( (*f)->Resistance&FX_CAN_DISPEL) { + return true; + } + } + return false; +} + +void EffectQueue::dump() const +{ + printf( "EFFECT QUEUE:\n" ); + int i = 0; + std::list< Effect* >::const_iterator f; + for ( f = effects.begin(); f != effects.end(); f++ ) { + Effect* fx = *f; + if( fx) { + char *Name = NULL; + if( fx->Opcode < MAX_EFFECTS) + Name = (char*) Opcodes[fx->Opcode].Name; + + printf( " %2d: 0x%02x: %s (%d, %d) S:%s\n", i++, fx->Opcode, Name, fx->Parameter1, fx->Parameter2, fx->Source ); + } + } +} +/* +Effect *EffectQueue::GetEffect(ieDword idx) const +{ + if( effects.size()<=idx) { + return NULL; + } + return effects[idx]; +} +*/ + +//returns true if the effect supports simplified duration +bool EffectQueue::HasDuration(Effect *fx) +{ + switch(fx->TimingMode) { + case FX_DURATION_INSTANT_LIMITED: //simple duration + case FX_DURATION_DELAY_LIMITED: //delayed duration + case FX_DURATION_DELAY_PERMANENT: //simple delayed + return true; + } + return false; +} + +static EffectRef fx_variable_ref={"Variable:StoreLocalVariable",NULL,-1}; + +//returns true if the effect must be saved +//variables are saved differently +bool EffectQueue::Persistent(Effect* fx) +{ + //we save this as variable + if( fx->Opcode==(ieDword) ResolveEffect(fx_variable_ref)) { + return false; + } + + switch (fx->TimingMode) { + //normal equipping fx of items + case FX_DURATION_INSTANT_WHILE_EQUIPPED: + //delayed effect not saved + case FX_DURATION_DELAY_UNSAVED: + //permanent effect not saved + case FX_DURATION_PERMANENT_UNSAVED: + //just expired effect + case FX_DURATION_JUST_EXPIRED: + return false; + } + return true; +} + +//alter the color effect in case the item is equipped in the shield slot +void EffectQueue::HackColorEffects(Actor *Owner, Effect *fx) +{ + if( fx->InventorySlot!=Owner->inventory.GetShieldSlot()) return; + + unsigned int gradienttype = fx->Parameter2 & 0xF0; + if( gradienttype == 0x10) { + gradienttype = 0x20; // off-hand + fx->Parameter2 &= ~0xF0; + fx->Parameter2 |= gradienttype; + } +} + +//iterate through saved effects +const Effect *EffectQueue::GetNextSavedEffect(std::list< Effect* >::const_iterator &f) const +{ + while(f!=effects.end()) { + Effect *effect = *f; + f++; + if( Persistent(effect)) { + return effect; + } + } + return NULL; +} + +Effect *EffectQueue::GetNextEffect(std::list< Effect* >::const_iterator &f) const +{ + if( f!=effects.end()) return *f++; + return NULL; +} + +ieDword EffectQueue::CountEffects(ieDword opcode, ieDword param1, ieDword param2, const char *resource) const +{ + ieDword cnt = 0; + + std::list< Effect* >::const_iterator f; + + for ( f = effects.begin(); f != effects.end(); f++ ) { + MATCH_OPCODE(); + if( param1!=0xffffffff) + MATCH_PARAM1(); + if( param2!=0xffffffff) + MATCH_PARAM2(); + if( resource) { + MATCH_RESOURCE(); + } + cnt++; + } + return cnt; +} + +void EffectQueue::ModifyEffectPoint(ieDword opcode, ieDword x, ieDword y) const +{ + std::list< Effect* >::const_iterator f; + + for ( f = effects.begin(); f != effects.end(); f++ ) { + MATCH_OPCODE(); + (*f)->PosX=x; + (*f)->PosY=y; + (*f)->Parameter3=0; + return; + } +} + +//count effects that get saved +ieDword EffectQueue::GetSavedEffectsCount() const +{ + ieDword cnt = 0; + + std::list< Effect* >::const_iterator f; + + for ( f = effects.begin(); f != effects.end(); f++ ) { + Effect* fx = *f; + if( Persistent(fx)) + cnt++; + } + return cnt; +} + +void EffectQueue::TransformToDelay(ieByte &TimingMode) +{ + if( TimingModeImmuneToProjectile(fx->Projectile)) return 0; + + //don't resist item projectile payloads based on spell school, bounce, etc. + if( fx->InventorySlot) { + return 1; + } + + //check level resistances + //check specific spell immunity + //check school/sectype immunity + return check_type(target, fx); + } + return 0; +} + +void EffectQueue::AffectAllInRange(Map *map, const Point &pos, int idstype, int idsvalue, + unsigned int range, Actor *except) +{ + int cnt = map->GetActorCount(true); + while(cnt--) { + Actor *actor = map->GetActor(cnt,true); + if( except==actor) { + continue; + } + //distance + if( Distance(pos, actor)>range) { + continue; + } + //ids targeting + if( !match_ids(actor, idstype, idsvalue)) { + continue; + } + //line of sight + if( !map->IsVisible(actor->Pos, pos)) { + continue; + } + AddAllEffects(actor, actor->Pos); + } +} + diff --git a/project/jni/application/gemrb/src/core/EffectQueue.h b/project/jni/application/gemrb/src/core/EffectQueue.h new file mode 100644 index 000000000..52c9b6e6c --- /dev/null +++ b/project/jni/application/gemrb/src/core/EffectQueue.h @@ -0,0 +1,296 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * + */ + +/** + * @file EffectQueue.h + * Declares EffectQueue class holding and processing all spell effects + * on a single Actor + * @author The GemRB Project + */ + +#ifndef EFFECTQUEUE_H +#define EFFECTQUEUE_H + +#include "exports.h" + +#include "Effect.h" +#include "Region.h" + +#include + +class Actor; +class Map; +class Scriptable; + +/** Maximum number of different Effect opcodes */ +#define MAX_EFFECTS 512 + +///** if the effect returns this, stop adding any other effect */ +#define FX_ABORT 0 +/** these effects don't stick around if used as permanent, + * in that case they modify a base stat like charisma modifier */ +#define FX_PERMANENT 2 +/** these effects never stick around, use them for instant effects like damage */ +#define FX_NOT_APPLIED 3 +/** these effects always stick around when applied as permanent or duration */ +#define FX_APPLIED 1 +///** insert the effect instead of push back */ +#define FX_INSERT 4 + +//remove level effects flags +#define RL_DISPELLABLE 1 //only dispellables +#define RL_MATCHSCHOOL 2 //match school +#define RL_MATCHSECTYPE 4 //match secondary type +#define RL_REMOVEFIRST 8 //remove only one spell (could be more effects) + +//bouncing immunities +#define BNC_PROJECTILE 1 +#define BNC_OPCODE 2 +#define BNC_LEVEL 4 +#define BNC_SCHOOL 8 +#define BNC_SECTYPE 0x10 +#define BNC_RESOURCE 0x20 +#define BNC_PROJECTILE_DEC 0x100 +#define BNC_OPCODE_DEC 0x200 +#define BNC_LEVEL_DEC 0x400 +#define BNC_SCHOOL_DEC 0x800 +#define BNC_SECTYPE_DEC 0x1000 +#define BNC_RESOURCE_DEC 0x2000 + +//normal immunities +#define IMM_PROJECTILE 1 +#define IMM_OPCODE 2 +#define IMM_LEVEL 4 +#define IMM_SCHOOL 8 +#define IMM_SECTYPE 16 +#define IMM_RESOURCE 32 +#define IMM_PROJECTILE_DEC 0x100 +#define IMM_OPCODE_DEC 0x200 +#define IMM_LEVEL_DEC 0x400 +#define IMM_SCHOOL_DEC 0x800 +#define IMM_SECTYPE_DEC 0x1000 +#define IMM_RESOURCE_DEC 0x2000 + +// FIXME: Dice roll should be probably done just once, e.g. when equipping +// the item, not each time the fx are applied +// the dice values are actually level limits, except in 3 hp modifier functions +// the damage function is an instant (the other 2 functions might be tricky with random values) +//#define DICE_ROLL(max_val) ((fx->DiceThrown && fx->DiceSides) ? ((max_val >=0) ? (MIN( core->Roll( fx->DiceThrown, fx->DiceSides, 0 ), max_val )) : (MAX( core->Roll( fx->DiceThrown, fx->DiceSides, 0 ), max_val ))) : max_val) + +//sometimes damage doesn't comply with the calculated value +#define DICE_ROLL(adjustment) (core->Roll( fx->DiceThrown, fx->DiceSides, adjustment) ) + +// You will need to get GameTime somehow to use this macro +#define PrepareDuration(fx) fx->Duration = (fx->Duration*AI_UPDATE_TIME + GameTime) + +// often used stat modifications, usually Parameter2 types 0, 1 and 2 +//these macros should work differently in permanent mode (modify base too) +#define STAT_GET(stat) (target->Modified[ stat ]) +#define STAT_ADD(stat, mod) target->SetStat( stat, STAT_GET( stat ) + ( mod ), 0 ) +#define STAT_SUB(stat, mod) target->SetStat( stat, STAT_GET( stat ) - ( mod ), 0 ) +#define STAT_BIT_OR(stat, mod) target->SetStat( stat, STAT_GET( stat ) | ( mod ), 0 ) +#define STAT_SET(stat, mod) target->SetStat( stat, ( mod ), 0 ) +#define STAT_SET_PCF(stat, mod) target->SetStat( stat, ( mod ), 1 ) +#define STAT_BIT_OR_PCF(stat, mod) target->SetStat( stat, STAT_GET( stat ) | ( mod ), 1 ) +#define STAT_MUL(stat, mod) target->SetStat( stat, STAT_GET(stat) * ( mod ) / 100, 0 ) +//if an effect sticks around +#define STATE_CURE( mod ) target->Modified[ IE_STATE_ID ] &= ~(ieDword) ( mod ) +#define STATE_SET( mod ) target->Modified[ IE_STATE_ID ] |= (ieDword) ( mod ) +#define EXTSTATE_SET( mod ) target->Modified[ IE_EXTSTATE_ID ] |= (ieDword) ( mod ) +#define STATE_GET( mod ) (target->Modified[ IE_STATE_ID ] & (ieDword) ( mod ) ) +#define EXTSTATE_GET( mod ) (target->Modified[ IE_EXTSTATE_ID ] & (ieDword) ( mod ) ) +#define STAT_MOD( stat ) target->NewStat(stat, fx->Parameter1, fx->Parameter2) +#define STAT_MOD_VAR( stat, mod ) target->NewStat(stat, ( mod ) , fx->Parameter2 ) +#define BASE_GET(stat) (target->BaseStats[ stat ]) +#define BASE_SET(stat, mod) target->SetBase( stat, ( mod ) ) +#define BASE_ADD(stat, mod) target->SetBase( stat, BASE_GET(stat)+ ( mod ) ) +#define BASE_SUB(stat, mod) target->SetBase( stat, BASE_GET(stat)- ( mod ) ) +#define BASE_MUL(stat, mod) target->SetBase( stat, BASE_GET(stat)* ( mod ) / 100 ) +#define BASE_MOD(stat) target->NewBase( stat, fx->Parameter1, fx->Parameter2) +#define BASE_MOD_VAR(stat, mod) target->NewBase( stat, (mod), fx->Parameter2 ) +//if an effect doesn't stick (and has permanent until cured effect) then +//it has to modify the base stat (which is saved) +//also use this one if the effect starts a cure effect automatically +#define BASE_STATE_SET( mod ) target->SetBaseBit( IE_STATE_ID, ( mod ), true ) +#define BASE_STATE_CURE( mod ) target->SetBaseBit( IE_STATE_ID, ( mod ), false ) + +/** Prototype of a function implementing a particular Effect opcode */ +typedef int (* EffectFunction)(Scriptable*, Actor*, Effect*); + + +/** Links Effect name to a function implementing the effect */ +struct EffectRef { + const char* Name; + EffectFunction Function; + int opcode; +}; + +/** Initializes table of available spell Effects used by all the queues. */ +/** The available effects should already be registered by the effect plugins */ +bool Init_EffectQueue(); + +/** Registers opcodes implemented by an effect plugin */ +void EffectQueue_RegisterOpcodes(int count, const EffectRef *opcodes); + +/** release effect list when Interface is destroyed */ +void EffectQueue_ReleaseMemory(); + +/** Check if opcode is for an effect that takes a color slot as parameter. */ +bool IsColorslotEffect(int opcode); + +/** + * @class EffectQueue + * Class holding and processing spell Effects on a single Actor + */ + +class GEM_EXPORT EffectQueue { +private: + /** List of Effects applied on the Actor */ + std::list< Effect* > effects; + /** Actor which is target of the Effects */ + Scriptable* Owner; + +public: + EffectQueue(); + virtual ~EffectQueue(); + + /** Sets Actor which is affected by these effects */ + void SetOwner(Scriptable* act) { Owner = act; } + /** Returns Actor affected by these effects */ + Scriptable* GetOwner() const { return Owner; } + + /** adds an effect to the queue, it could also insert it if flagged so + * fx should be freed by the caller + */ + void AddEffect(Effect* fx, bool insert=false); + /** Adds an Effect to the queue, subject to level and other checks. + * Returns FX_ABORT is unsuccessful. fx is just a reference, AddEffect() + * will malloc its own copy */ + int AddEffect(Effect* fx, Scriptable* self, Actor* pretarget, const Point &dest) const; + /** Removes first Effect matching fx from the queue. + * Effects are matched based on their contents */ + bool RemoveEffect(Effect* fx); + + int AddAllEffects(Actor* target, const Point &dest) const; + void ApplyAllEffects(Actor* target) const; + /** remove effects marked for removal */ + void Cleanup(); + + /* directly removes effects with specified opcode, use effect_reference when you can */ + void RemoveAllEffects(ieDword opcode) const; + void RemoveAllEffectsWithResource(ieDword opcode, const ieResRef resource) const; + + /* removes any effects (delayed or not) which were using projectile */ + void RemoveAllEffectsWithProjectile(ieDword projectile) const; + + /* removes equipping effects with specified inventory slot code */ + void RemoveEquippingEffects(ieDwordSigned slotcode) const; + + /* removes all effects of a given spell */ + void RemoveAllEffects(const ieResRef Removed) const; + void RemoveAllEffects(const ieResRef Removed, ieByte timing) const; + /* removes all effects of type */ + void RemoveAllEffects(EffectRef &effect_reference) const; + /* removes expired or to be expired effects */ + void RemoveExpiredEffects(ieDword futuretime) const; + /* removes all effects except timing mode 9 */ + void RemoveAllNonPermanentEffects() const; + void RemoveAllDetrimentalEffects(EffectRef &effect_reference, ieDword current) const; + void RemoveAllEffectsWithParam(EffectRef &effect_reference, ieDword param2) const; + void RemoveAllEffectsWithResource(EffectRef &effect_reference, const ieResRef resource) const; + void RemoveLevelEffects(ieResRef &Removed, ieDword level, ieDword flags, ieDword match) const; + + /* returns true if the timing method supports simplified duration */ + static bool HasDuration(Effect *fx); + /* returns true if the effect should be saved */ + static bool Persistent(Effect* fx); + /* returns next saved effect, increases index */ + std::list< Effect* >::const_iterator GetFirstEffect() const + { + return effects.begin(); + } + const Effect *GetNextSavedEffect(std::list< Effect* >::const_iterator &f) const; + Effect *GetNextEffect(std::list< Effect* >::const_iterator &f) const; + ieDword CountEffects(EffectRef &effect_reference, ieDword param1, ieDword param2, const char *ResRef) const; + void ModifyEffectPoint(EffectRef &effect_reference, ieDword x, ieDword y) const; + /* returns the number of saved effects */ + ieDword GetSavedEffectsCount() const; + size_t GetEffectsCount() const { return effects.size(); } + /* this method hacks the offhand weapon color effects */ + static void HackColorEffects(Actor *Owner, Effect *fx); + static Effect *CreateEffect(EffectRef &effect_reference, ieDword param1, ieDword param2, ieWord timing); + EffectQueue *CopySelf() const; + static Effect *CreateEffectCopy(Effect *oldfx, EffectRef &effect_reference, ieDword param1, ieDword param2); + static Effect *CreateUnsummonEffect(Effect *fx); + //locating opcodes + Effect *HasEffect(EffectRef &effect_reference) const; + Effect *HasEffectWithParam(EffectRef &effect_reference, ieDword param2) const; + Effect *HasEffectWithParamPair(EffectRef &effect_reference, ieDword param1, ieDword param2) const; + Effect *HasEffectWithResource(EffectRef &effect_reference, const ieResRef resource) const; + Effect *HasEffectWithSource(EffectRef &effect_reference, const ieResRef source) const; + void DecreaseParam1OfEffect(EffectRef &effect_reference, ieDword amount) const; + int SpecificDamageBonus(ieDword damage_type) const; + bool HasAnyDispellableEffect() const; + //transforming timing modes + static void TransformToDelay(ieByte &TimingMode); + //getting summarised effects + int BonusAgainstCreature(EffectRef &effect_reference, Actor *actor) const; + //getting weapon immunity flag + bool WeaponImmunity(int enchantment, ieDword weapontype) const; + //melee and ranged effects + void AddWeaponEffects(EffectQueue *fxqueue, EffectRef &fx_ref) const; + // checks if spells of type "types" are disabled (usually by armor) + // returns a bitfield of disabled spelltypes + // it is no longer used + //int DisabledSpellcasting(int types) const; + + // returns -1 if bounced, 0 if resisted, 1 if accepted spell + int CheckImmunity(Actor *target) const; + // apply this effectqueue on all actors matching ids targeting + // from pos, in range (no cone size yet) + void AffectAllInRange(Map *map, const Point &pos, int idstype, int idsvalue, unsigned int range, Actor *except); + /** Lists contents of the queue on a terminal for debugging */ + void dump() const; + //resolve effect + static int ResolveEffect(EffectRef &effect_reference); + static bool match_ids(Actor *target, int table, ieDword value); + /** returns true if the process should abort applying a stack of effects */ + int ApplyEffect(Actor* target, Effect* fx, ieDword first_apply, ieDword resistance=1) const; +private: + /** counts effects of specific opcode, parameters and resource */ + ieDword CountEffects(ieDword opcode, ieDword param1, ieDword param2, const char *ResRef) const; + void ModifyEffectPoint(ieDword opcode, ieDword x, ieDword y) const; + //use the effect reference style calls from outside + static Effect *CreateEffect(ieDword opcode, ieDword param1, ieDword param2, ieWord timing); + static Effect *CreateEffectCopy(Effect *oldfx, ieDword opcode, ieDword param1, ieDword param2); + void RemoveAllDetrimentalEffects(ieDword opcode, ieDword current) const; + void RemoveAllEffectsWithParam(ieDword opcode, ieDword param2) const; + Effect *HasOpcode(ieDword opcode) const; + Effect *HasOpcodeWithParam(ieDword opcode, ieDword param2) const; + Effect *HasOpcodeWithParamPair(ieDword opcode, ieDword param1, ieDword param2) const; + Effect *HasOpcodeWithResource(ieDword opcode, const ieResRef resource) const; + Effect *HasOpcodeWithSource(ieDword opcode, const ieResRef source) const; + void DecreaseParam1OfEffect(ieDword opcode, ieDword amount) const; + int SpecificDamageBonus(ieDword opcode, ieDword param2) const; + int BonusAgainstCreature(ieDword opcode, Actor *actor) const; + bool WeaponImmunity(ieDword opcode, int enchantment, ieDword weapontype) const; +}; + +#endif // ! EFFECTQUEUE_H diff --git a/project/jni/application/gemrb/src/core/Factory.cpp b/project/jni/application/gemrb/src/core/Factory.cpp new file mode 100644 index 000000000..797c4a09d --- /dev/null +++ b/project/jni/application/gemrb/src/core/Factory.cpp @@ -0,0 +1,65 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * + */ + +#include "Factory.h" + +#include "win32def.h" + +#include + +Factory::Factory(void) +{ +} + +Factory::~Factory(void) +{ + for (unsigned int i = 0; i < fobjects.size(); i++) { + delete( fobjects[i] ); + } +} + +void Factory::AddFactoryObject(FactoryObject* fobject) +{ + fobjects.push_back( fobject ); +} + +int Factory::IsLoaded(const char* ResRef, SClass_ID type) const +{ + for (unsigned int i = 0; i < fobjects.size(); i++) { + if (fobjects[i]->SuperClassID == type) { + if (strnicmp( fobjects[i]->ResRef, ResRef, 8 ) == 0) { + return i; + } + } + } + return -1; +} + +FactoryObject* Factory::GetFactoryObject(int pos) const +{ + return fobjects[pos]; +} + +void Factory::FreeObjects(void) +{ + for (unsigned int i = 0; i < fobjects.size(); i++) { + delete( fobjects[i] ); + } +} diff --git a/project/jni/application/gemrb/src/core/Factory.h b/project/jni/application/gemrb/src/core/Factory.h new file mode 100644 index 000000000..d6c1c09e3 --- /dev/null +++ b/project/jni/application/gemrb/src/core/Factory.h @@ -0,0 +1,42 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * + */ + +#ifndef FACTORY_H +#define FACTORY_H + +#include "exports.h" +#include "globals.h" + +#include "AnimationFactory.h" +#include "FactoryObject.h" + +class GEM_EXPORT Factory { +private: + std::vector< FactoryObject*> fobjects; +public: + Factory(void); + ~Factory(void); + void AddFactoryObject(FactoryObject* fobject); + int IsLoaded(const char* ResRef, SClass_ID type) const; + FactoryObject* GetFactoryObject(int pos) const; + void FreeObjects(void); +}; + +#endif diff --git a/project/jni/application/gemrb/src/core/FactoryObject.cpp b/project/jni/application/gemrb/src/core/FactoryObject.cpp new file mode 100644 index 000000000..8c0da9bae --- /dev/null +++ b/project/jni/application/gemrb/src/core/FactoryObject.cpp @@ -0,0 +1,33 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * + */ + +#include "FactoryObject.h" + +#include "win32def.h" + +FactoryObject::FactoryObject(const char* name, SClass_ID SuperClassID) +{ + strnlwrcpy( ResRef, name, 8 ); + this->SuperClassID = SuperClassID; +} + +FactoryObject::~FactoryObject(void) +{ +} diff --git a/project/jni/application/gemrb/src/core/FactoryObject.h b/project/jni/application/gemrb/src/core/FactoryObject.h new file mode 100644 index 000000000..a1189c6eb --- /dev/null +++ b/project/jni/application/gemrb/src/core/FactoryObject.h @@ -0,0 +1,35 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * + */ + +#ifndef FACTORYOBJECT_H +#define FACTORYOBJECT_H + +#include "exports.h" +#include "globals.h" + +class GEM_EXPORT FactoryObject { +public: + SClass_ID SuperClassID; + ieResRef ResRef; + FactoryObject(const char* ResRef, SClass_ID SuperClassID); + virtual ~FactoryObject(void); +}; + +#endif diff --git a/project/jni/application/gemrb/src/core/Font.cpp b/project/jni/application/gemrb/src/core/Font.cpp new file mode 100644 index 000000000..d62afecc4 --- /dev/null +++ b/project/jni/application/gemrb/src/core/Font.cpp @@ -0,0 +1,560 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * + */ + +//This class represents game fonts. Fonts are special .bam files. +//Each cycle stands for a letter. + +#include "Font.h" + +#include "win32def.h" + +#include "GameData.h" +#include "Interface.h" +#include "Palette.h" +#include "Video.h" + +#include + +unsigned int lastX = 0; + +#define PARAGRAPH_START_X 5; + +static const Color black = {0, 0, 0, 0}; + +inline size_t mystrlen(const char* string) +{ + if (!string) { + return ( size_t ) 0; + } + const char* tmp = string; + size_t count = 0; + while (*tmp != 0) { + if (( ( unsigned char ) * tmp ) >= 0xf0) { + tmp += 3; + count += 3; + } + count++; + tmp++; + } + return count; +} + +Font::Font(int w, int h, Palette* pal) +{ + lastX = 0; + count = 0; + FirstChar = 0; + sprBuffer = 0; + + width = w; + height = h; + tmpPixels = (unsigned char*)malloc(width*height); + + memset( xPos, 0, sizeof( xPos) ); + memset( yPos, 0, sizeof( yPos) ); + + pal->IncRef(); + palette = pal; + maxHeight = h; +} + +Font::~Font(void) +{ + Video *video = core->GetVideoDriver(); + gamedata->FreePalette( palette ); + video->FreeSprite( sprBuffer ); +} + +void Font::FinalizeSprite(bool cK, int index) +{ + sprBuffer = core->GetVideoDriver()->CreateSprite8( width, height, 8, tmpPixels, palette ? palette->col : 0, cK, index ); + tmpPixels = 0; +} + +void Font::AddChar(unsigned char* spr, int w, int h, short xPos, short yPos) +{ + if (!spr) { + size[count].x = 0; + size[count].y = 0; + size[count].w = 0; + size[count].h = 0; + this->xPos[count] = 0; + this->yPos[count] = 0; + count++; + return; + } + unsigned char * currPtr = tmpPixels + lastX; + unsigned char * srcPtr = ( unsigned char * ) spr; + for (int y = 0; y < h; y++) { + memcpy( currPtr, srcPtr, w ); + srcPtr += w; + currPtr += width; + } + size[count].x = lastX; + size[count].y = 0; + size[count].w = w; + size[count].h = h; + this->xPos[count] = xPos; + this->yPos[count] = yPos; + count++; + lastX += w; +} + +void Font::PrintFromLine(int startrow, Region rgn, const unsigned char* string, + Palette* hicolor, unsigned char Alignment, Font* initials, + Sprite2D* cursor, unsigned int curpos, bool NoColor) const +{ + bool enablecap=false; + int capital = 0; + if (initials) + { + capital=1; + enablecap=true; + } + int initials_rows = 0; + int initials_x = 0; + + unsigned int psx = PARAGRAPH_START_X; + Palette *pal = hicolor; + if (!pal) { + pal = palette; + } + if (startrow) enablecap=false; + + if (initials==this) { + enablecap=false; + } + + sprBuffer->SetPalette( pal ); + size_t len = strlen( ( char* ) string ); + char* tmp = ( char* ) malloc( len + 1 ); + memcpy( tmp, ( char * ) string, len + 1 ); + SetupString( tmp, rgn.w, NoColor ); + int ystep = 0; + if (Alignment & IE_FONT_SINGLE_LINE) { + for (size_t i = 0; i < len; i++) { + int height = yPos[( unsigned char ) tmp[i] - 1]; + if (ystep < height) + ystep = height; + } + } else { + ystep = size[1].h; + } + if (!ystep) ystep = maxHeight; + int x = psx, y = ystep; + int w = CalcStringWidth( tmp, NoColor ); + if (Alignment & IE_FONT_ALIGN_CENTER) { + x = ( rgn.w - w) / 2; + } else if (Alignment & IE_FONT_ALIGN_RIGHT) { + x = ( rgn.w - w ); + } + if (Alignment & IE_FONT_ALIGN_MIDDLE) { + int h = 0; + for (size_t i = 0; i <= len; i++) { + if (( tmp[i] == 0 ) || ( tmp[i] == '\n' )) + h++; + } + h = h * ystep; + y += ( rgn.h - h ) / 2; + } else if (Alignment & IE_FONT_ALIGN_BOTTOM) { + int h = 1; + for (size_t i = 0; i <= len; i++) { + if (( tmp[i] == 0 ) || ( tmp[i] == '\n' )) + h++; + } + h = h * ystep; + y += ( rgn.h - h ); + } else if (Alignment & IE_FONT_ALIGN_TOP) { + y += 5; + } + + Video* video = core->GetVideoDriver(); + int row = 0; + for (size_t i = 0; i < len; i++) { + if (( ( unsigned char ) tmp[i] ) == '[' && !NoColor) { + i++; + char tag[256]; + tag[0]=0; + + for (int k = 0; k < 256 && i=startrow) ) { + enablecap=true; + } + continue; + } + + + if (strnicmp( tag, "color=", 6 ) == 0) { + unsigned int r,g,b; + if (sscanf( tag, "color=%02X%02X%02X", &r, &g, &b ) != 3) + continue; + const Color c = {(unsigned char) r,(unsigned char)g, (unsigned char)b, 0}; + Palette* newPal = core->CreatePalette( c, black ); + sprBuffer->SetPalette( newPal ); + gamedata->FreePalette( newPal ); + continue; + } + if (stricmp( tag, "/color" ) == 0) { + sprBuffer->SetPalette( pal ); + continue; + } + + if (stricmp( "p", tag ) == 0) { + psx = x; + continue; + } + if (stricmp( "/p", tag ) == 0) { + psx = PARAGRAPH_START_X; + } + continue; + } + + if (row < startrow) { + if (tmp[i] == 0) { + row++; + } + continue; + } + if (( tmp[i] == 0 ) || ( tmp[i] == '\n' )) { + y += ystep; + x = psx; + int w = CalcStringWidth( &tmp[i + 1], NoColor ); + if (initials_rows > 0) { + initials_rows--; + x += initials_x; + w += initials_x; + } + if (Alignment & IE_FONT_ALIGN_CENTER) { + x = ( rgn.w - w ) / 2; + } else if (Alignment & IE_FONT_ALIGN_RIGHT) { + x = ( rgn.w - w ); + } + continue; + } + unsigned char currChar = ( unsigned char ) tmp[i] - 1; + if (initials && capital && enablecap) { + x = initials->PrintInitial( x, y, rgn, currChar ); + initials_x = x; + + //how many more lines to be indented (one was already indented) + initials_rows = (initials->maxHeight-1)/maxHeight; + enablecap = false; + continue; + } + video->BlitSpriteRegion( sprBuffer, size[currChar], + x + rgn.x, y + rgn.y - yPos[currChar], true, &rgn ); + if (cursor && ( i == curpos )) { + video->BlitSprite( cursor, x + rgn.x, + y + rgn.y, true, &rgn ); + } + x += size[currChar].w; + } + if (cursor && ( curpos == len )) { + video->BlitSprite( cursor, x + rgn.x, + y + rgn.y, true, &rgn ); + } + free( tmp ); +} + +void Font::Print(Region rgn, const unsigned char* string, Palette* hicolor, + unsigned char Alignment, bool anchor, Font* initials, + Sprite2D* cursor, unsigned int curpos, bool NoColor) const +{ + Print(rgn, rgn, string, hicolor, Alignment, anchor, initials, cursor, curpos, NoColor); +} + +void Font::Print(Region cliprgn, Region rgn, const unsigned char* string, + Palette* hicolor, unsigned char Alignment, bool anchor, Font* initials, + Sprite2D* cursor, unsigned int curpos, bool NoColor) const +{ + bool enablecap=false; + int capital = 0; + if (initials) + { + capital=1; + enablecap=true; + } + + unsigned int psx = PARAGRAPH_START_X; + Palette* pal = hicolor; + if (!pal) { + pal = palette; + } + if (initials==this) { + initials = NULL; + } + + sprBuffer->SetPalette( pal ); + size_t len = strlen( ( char* ) string ); + char* tmp = ( char* ) malloc( len + 1 ); + memcpy( tmp, ( char * ) string, len + 1 ); + while (len > 0 && (tmp[len - 1] == '\n' || tmp[len - 1] == '\r')) { + // ignore trailing newlines + tmp[len - 1] = 0; + len--; + } + + SetupString( tmp, rgn.w, NoColor ); + int ystep = 0; + if (Alignment & IE_FONT_SINGLE_LINE) { + + for (size_t i = 0; i < len; i++) { + if (tmp[i] == 0) continue; + int height = yPos[( unsigned char ) tmp[i] - 1]; + if (ystep < height) + ystep = height; + } + } else { + ystep = size[1].h; + } + if (!ystep) ystep = maxHeight; + int x = psx, y = ystep; + Video* video = core->GetVideoDriver(); + + if (Alignment & IE_FONT_ALIGN_CENTER) { + int w = CalcStringWidth( tmp, NoColor ); + x = ( rgn.w - w ) / 2; + } else if (Alignment & IE_FONT_ALIGN_RIGHT) { + int w = CalcStringWidth( tmp, NoColor ); + x = ( rgn.w - w ); + } + + if (Alignment & IE_FONT_ALIGN_MIDDLE) { + int h = 0; + for (size_t i = 0; i <= len; i++) { + if (tmp[i] == 0) + h++; + } + h = h * ystep; + y += ( rgn.h - h ) / 2; + } else if (Alignment & IE_FONT_ALIGN_BOTTOM) { + int h = 1; + for (size_t i = 0; i <= len; i++) { + if (tmp[i] == 0) + h++; + } + h = h * ystep; + y += ( rgn.h - h ); + } else if (Alignment & IE_FONT_ALIGN_TOP) { + y += 5; + } + for (size_t i = 0; i < len; i++) { + if (( ( unsigned char ) tmp[i] ) == '[' && !NoColor) { + i++; + char tag[256]; + tag[0]=0; + for (int k = 0; k < 256 && iCreatePalette( c, black ); + sprBuffer->SetPalette( newPal ); + gamedata->FreePalette( newPal ); + continue; + } + if (stricmp( tag, "/color" ) == 0) { + sprBuffer->SetPalette( pal ); + continue; + } + if (stricmp( "p", tag ) == 0) { + psx = x; + continue; + } + if (stricmp( "/p", tag ) == 0) { + psx = PARAGRAPH_START_X; + continue; + } + continue; + } + + if (tmp[i] == 0) { + y += ystep; + x = psx; + int w = CalcStringWidth( &tmp[i + 1], NoColor ); + if (Alignment & IE_FONT_ALIGN_CENTER) { + x = ( rgn.w - w ) / 2; + } else if (Alignment & IE_FONT_ALIGN_RIGHT) { + x = ( rgn.w - w ); + } + continue; + } + unsigned char currChar = ( unsigned char ) tmp[i] - 1; + if (initials && capital) { + x = initials->PrintInitial( x, y, rgn, currChar ); + enablecap=false; + continue; + } + video->BlitSpriteRegion( sprBuffer, size[currChar], + x + rgn.x, y + rgn.y - yPos[currChar], + anchor, &cliprgn ); + if (cursor && ( curpos == i )) + video->BlitSprite( cursor, x + rgn.x, y + rgn.y, anchor, &cliprgn ); + x += size[currChar].w; + } + if (cursor && ( curpos == len )) { + video->BlitSprite( cursor, x + rgn.x, y + rgn.y, anchor, &cliprgn ); + } + free( tmp ); +} + +int Font::PrintInitial(int x, int y, const Region &rgn, unsigned char currChar) const +{ + Video *video = core->GetVideoDriver(); + video->BlitSpriteRegion( sprBuffer, size[currChar], + x + rgn.x, y + rgn.y - yPos[currChar], true, &rgn ); + x += size[currChar].w; + return x; +} + +int Font::CalcStringWidth(const char* string, bool NoColor) const +{ + size_t ret = 0, len = strlen( string ); + for (size_t i = 0; i < len; i++) { + if (( ( unsigned char ) string[i] ) == '[' && !NoColor) { + i++; + if (i>=len) + break; + char tag[256]; + int k = 0; + for (k = 0; k < 256; k++) { + if (string[i] == ']') { + tag[k] = 0; + break; + } + tag[k] = string[i++]; + } + continue; + } + ret += size[( unsigned char ) string[i] - 1].w; + } + return ( int ) ret; +} + +void Font::SetupString(char* string, unsigned int width, bool NoColor) const +{ + size_t len = strlen( string ); + unsigned int psx = PARAGRAPH_START_X; + int lastpos = 0; + unsigned int x = psx, wx = 0; + bool endword = false; + for (size_t pos = 0; pos < len; pos++) { + if (x + wx > width) { + if (!endword && ( x == psx )) + lastpos = ( int ) pos; + else + string[lastpos] = 0; + x = psx; + } + if (string[pos] == 0) { + continue; + } + endword = false; + if (string[pos] == '\r') + string[pos] = ' '; + if (string[pos] == '\n') { + string[pos] = 0; + x = psx; + wx = 0; + lastpos = ( int ) pos; + endword = true; + continue; + } + if (( ( unsigned char ) string[pos] ) == '[' && !NoColor) { + pos++; + if (pos>=len) + break; + char tag[256]; + int k = 0; + for (k = 0; k < 256; k++) { + if (string[pos] == ']') { + tag[k] = 0; + break; + } + tag[k] = string[pos++]; + } + if (stricmp( "p", tag ) == 0) { + psx = x; + continue; + } + if (stricmp( "/p", tag ) == 0) { + psx = PARAGRAPH_START_X; + continue; + } + continue; + } + + if (string[pos] && string[pos] != ' ') { + string[pos] = ( unsigned char ) (string[pos] - FirstChar); + } + + wx += size[( unsigned char ) string[pos] - 1].w; + if (( string[pos] == ' ' ) || ( string[pos] == '-' )) { + x += wx; + wx = 0; + lastpos = ( int ) pos; + endword = true; + } + } +} + +Palette* Font::GetPalette() const +{ + assert(palette); + palette->IncRef(); + return palette; +} + +void Font::SetPalette(Palette* pal) +{ + if (palette) palette->Release(); + pal->IncRef(); + palette = pal; +} + +void Font::SetFirstChar( unsigned char first) +{ + FirstChar = first; +} diff --git a/project/jni/application/gemrb/src/core/Font.h b/project/jni/application/gemrb/src/core/Font.h new file mode 100644 index 000000000..240bdf436 --- /dev/null +++ b/project/jni/application/gemrb/src/core/Font.h @@ -0,0 +1,111 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * + */ + +/** + * @file Font.h + * Declares Font, class for manipulating images serving as fonts + * @author The GemRB Project + */ + +#ifndef FONT_H +#define FONT_H + +#include "globals.h" +#include "exports.h" + +#include + +class Palette; + +struct StringList { + Sprite2D*** strings; + unsigned int* heights; + unsigned int* lengths; + int StringCount; + int starty; + int curx; + int cury; +}; + +#define IE_FONT_ALIGN_LEFT 0x00 +#define IE_FONT_ALIGN_CENTER 0x01 +#define IE_FONT_ALIGN_RIGHT 0x02 +#define IE_FONT_ALIGN_BOTTOM 0x04 +#define IE_FONT_ALIGN_TOP 0x10 //Single-Line and Multi-Line Text +#define IE_FONT_ALIGN_MIDDLE 0x20 //Only for single line Text +#define IE_FONT_SINGLE_LINE 0x40 + +/** + * @class Font + * Class for using and manipulating images serving as fonts + */ + +class GEM_EXPORT Font { +private: + int count; + Palette* palette; + Sprite2D* sprBuffer; + unsigned char FirstChar; + + short xPos[256]; + short yPos[256]; + + // For the temporary bitmap + unsigned char* tmpPixels; + unsigned int width, height; +public: + /** ResRef of the Font image */ + ieResRef ResRef; + int maxHeight; + Region size[256]; +public: + Font(int w, int h, Palette* palette); + ~Font(void); + void AddChar(unsigned char* spr, int w, int h, short xPos, short yPos); + /** Call this after adding all characters */ + void FinalizeSprite(bool cK, int index); + + void Print(Region cliprgn, Region rgn, const unsigned char* string, + Palette* color, unsigned char Alignment, bool anchor = false, + Font* initials = NULL, Sprite2D* cursor = NULL, + unsigned int curpos = 0, bool NoColor = false) const; + void Print(Region rgn, const unsigned char* string, Palette* color, + unsigned char Alignment, bool anchor = false, + Font* initials = NULL, Sprite2D* cursor = NULL, + unsigned int curpos = 0, bool NoColor = false) const; + void PrintFromLine(int startrow, Region rgn, const unsigned char* string, + Palette* color, unsigned char Alignment, + Font* initials = NULL, Sprite2D* cursor = NULL, + unsigned int curpos = 0, bool NoColor = false) const; + + Palette* GetPalette() const; + void SetPalette(Palette* pal); + /** Returns width of the string rendered in this font in pixels */ + int CalcStringWidth(const char* string, bool NoColor = false) const; + void SetupString(char* string, unsigned int width, bool NoColor = false) const; + /** Sets ASCII code of the first character in the font. + * (it allows remapping numeric fonts from \000 to '0') */ + void SetFirstChar(unsigned char first); + +private: + int PrintInitial(int x, int y, const Region &rgn, unsigned char currChar) const; +}; + +#endif diff --git a/project/jni/application/gemrb/src/core/GUI/Button.cpp b/project/jni/application/gemrb/src/core/GUI/Button.cpp new file mode 100644 index 000000000..83930d2be --- /dev/null +++ b/project/jni/application/gemrb/src/core/GUI/Button.cpp @@ -0,0 +1,726 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * + */ + +#include "GUI/Button.h" + +#include "GUI/GameControl.h" + +#include "defsounds.h" +#include "win32def.h" + +#include "GameData.h" +#include "Interface.h" +#include "Palette.h" +#include "Variables.h" +#include "Video.h" + +Button::Button() +{ + Unpressed = Pressed = Selected = Disabled = NULL; + State = IE_GUI_BUTTON_UNPRESSED; + ResetEventHandler( ButtonOnPress ); + ResetEventHandler( ButtonOnDoublePress ); + ResetEventHandler( ButtonOnShiftPress ); + ResetEventHandler( ButtonOnRightPress ); + ResetEventHandler( ButtonOnDragDrop ); + ResetEventHandler( ButtonOnDrag ); + ResetEventHandler( MouseEnterButton ); + ResetEventHandler( MouseLeaveButton ); + ResetEventHandler( MouseOverButton ); + //Text = ( char * ) calloc( 64, sizeof(char) ); + Text = NULL; + hasText = false; + font = core->GetButtonFont(); + normal_palette = NULL; + disabled_palette = font->GetPalette()->Copy(); + for (int i = 0; i < 256; i++) { + disabled_palette->col[i].r = ( disabled_palette->col[i].r * 2 ) / 3; + disabled_palette->col[i].g = ( disabled_palette->col[i].g * 2 ) / 3; + disabled_palette->col[i].b = ( disabled_palette->col[i].b * 2 ) / 3; + } + Flags = IE_GUI_BUTTON_NORMAL; + ToggleState = false; + Picture = NULL; + Clipping = 1.0; + memset(&SourceRGB,0,sizeof(SourceRGB)); + memset(&DestRGB,0,sizeof(DestRGB)); + memset( borders, 0, sizeof( borders )); + starttime = 0; + Anchor.null(); +} +Button::~Button() +{ + Video* video = core->GetVideoDriver(); + video->FreeSprite( Disabled ); + video->FreeSprite( Selected ); + video->FreeSprite( Pressed ); + video->FreeSprite( Unpressed ); + video->FreeSprite( Picture ); + ClearPictureList(); + if (Text) { + free( Text ); + } + gamedata->FreePalette( normal_palette); + gamedata->FreePalette( disabled_palette); +} +/** Sets the 'type' Image of the Button to 'img'. +'type' may assume the following values: +- IE_GUI_BUTTON_UNPRESSED +- IE_GUI_BUTTON_PRESSED +- IE_GUI_BUTTON_SELECTED +- IE_GUI_BUTTON_DISABLED */ +void Button::SetImage(unsigned char type, Sprite2D* img) +{ + switch (type) { + case IE_GUI_BUTTON_UNPRESSED: + case IE_GUI_BUTTON_LOCKED: + case IE_GUI_BUTTON_LOCKED_PRESSED: + core->GetVideoDriver()->FreeSprite( Unpressed ); + Unpressed = img; + break; + + case IE_GUI_BUTTON_SECOND: + case IE_GUI_BUTTON_PRESSED: + core->GetVideoDriver()->FreeSprite( Pressed ); + Pressed = img; + break; + + case IE_GUI_BUTTON_SELECTED: + core->GetVideoDriver()->FreeSprite( Selected ); + Selected = img; + break; + + case IE_GUI_BUTTON_DISABLED: + case IE_GUI_BUTTON_THIRD: + core->GetVideoDriver()->FreeSprite( Disabled ); + Disabled = img; + break; + } + Changed = true; +} + +/** make SourceRGB go closer to DestRGB */ +void Button::CloseUpColor() +{ + if (!starttime) return; + //using the realtime timer, because i don't want to + //handle Game at this point + unsigned long newtime; + + Changed = true; + GetTime( newtime ); + if (newtimeFlags&WF_FLOAT) ) { + return; + } + Changed = false; + if (XPos == 65535 || Width == 0) { + return; + } + + Video * video = core->GetVideoDriver(); + + // Button image + if (!( Flags & IE_GUI_BUTTON_NO_IMAGE )) { + Sprite2D* Image = NULL; + + switch (State) { + case IE_GUI_BUTTON_UNPRESSED: + case IE_GUI_BUTTON_LOCKED: + case IE_GUI_BUTTON_LOCKED_PRESSED: + Image = Unpressed; + break; + + case IE_GUI_BUTTON_SECOND: + case IE_GUI_BUTTON_PRESSED: + Image = Pressed; + if (! Image) + Image = Unpressed; + break; + + case IE_GUI_BUTTON_SELECTED: + Image = Selected; + if (! Image) + Image = Unpressed; + break; + + case IE_GUI_BUTTON_DISABLED: + case IE_GUI_BUTTON_THIRD: + Image = Disabled; + if (! Image) + Image = Unpressed; + break; + } + if (Image) { + // FIXME: maybe it's useless... + int xOffs = ( Width / 2 ) - ( Image->Width / 2 ); + int yOffs = ( Height / 2 ) - ( Image->Height / 2 ); + + video->BlitSprite( Image, x + XPos + xOffs, y + YPos + yOffs, true ); + } + } + + if (State == IE_GUI_BUTTON_PRESSED) { + //shift the writing/border a bit + x+= 2; + y+= 2; + } + + // Button picture + if (Picture && (Flags & IE_GUI_BUTTON_PICTURE) ) { + // Picture is drawn centered + int xOffs = ( Width / 2 ) - ( Picture->Width / 2 ); + int yOffs = ( Height / 2 ) - ( Picture->Height / 2 ); + if (Flags & IE_GUI_BUTTON_HORIZONTAL) { + xOffs += x + XPos + Picture->XPos; + yOffs += y + YPos + Picture->YPos; + video->BlitSprite( Picture, xOffs, yOffs, true ); + Region r = Region( xOffs, yOffs + (int) (Picture->Height * Clipping), Picture->Width, (int) (Picture->Height*(1.0 - Clipping)) ); + video->DrawRect( r, SourceRGB, true ); + // do NOT uncomment this, you can't change Changed or invalidate things from + // the middle of Window::DrawWindow() -- it needs moving to somewhere else + //CloseUpColor(); + } + else { + Region r( x + XPos + xOffs, y + YPos + yOffs, (int)(Picture->Width * Clipping), Picture->Height ); + video->BlitSprite( Picture, x + XPos + xOffs + Picture->XPos, y + YPos + yOffs + Picture->YPos, true, &r ); + } + } + + // Composite pictures (paperdolls/description icons) + if (!PictureList.empty() && (Flags & IE_GUI_BUTTON_PICTURE) ) { + std::list::iterator iter = PictureList.begin(); + int xOffs = 0, yOffs = 0; + if (Flags & IE_GUI_BUTTON_CENTER_PICTURES) { + // Center the hotspots of all pictures + xOffs = Width/2; + yOffs = Height/2; + } else if (Flags & IE_GUI_BUTTON_BG1_PAPERDOLL) { + // Display as-is + xOffs = 0; + yOffs = 0; + } else { + // Center the first picture, and align the rest to that + xOffs = Width/2 - (*iter)->Width/2 + (*iter)->XPos; + yOffs = Height/2 - (*iter)->Height/2 + (*iter)->YPos; + } + + for (; iter != PictureList.end(); ++iter) { + video->BlitSprite( *iter, x + XPos + xOffs, y + YPos + yOffs, true ); + } + } + + // Button picture + if (AnimPicture) { + int xOffs = ( Width / 2 ) - ( AnimPicture->Width / 2 ); + int yOffs = ( Height / 2 ) - ( AnimPicture->Height / 2 ); + Region r( x + XPos + xOffs, y + YPos + yOffs, (int)(AnimPicture->Width * Clipping), AnimPicture->Height ); + + if (Flags & IE_GUI_BUTTON_CENTER_PICTURES) { + video->BlitSprite( AnimPicture, x + XPos + xOffs + AnimPicture->XPos, y + YPos + yOffs + AnimPicture->YPos, true, &r ); + } else { + video->BlitSprite( AnimPicture, x + XPos + xOffs, y + YPos + yOffs, true, &r ); + } + } + + // Button label + if (hasText && ! ( Flags & IE_GUI_BUTTON_NO_TEXT )) { + Palette* ppoi = normal_palette; + int align = 0; + + if (State == IE_GUI_BUTTON_DISABLED) + ppoi = disabled_palette; + // FIXME: hopefully there's no button which sinks when selected + // AND has text label + //else if (State == IE_GUI_BUTTON_PRESSED || State == IE_GUI_BUTTON_SELECTED) { + + if (Flags & IE_GUI_BUTTON_ALIGN_LEFT) + align |= IE_FONT_ALIGN_LEFT; + else if (Flags & IE_GUI_BUTTON_ALIGN_RIGHT) + align |= IE_FONT_ALIGN_RIGHT; + else + align |= IE_FONT_ALIGN_CENTER; + + if (Flags & IE_GUI_BUTTON_ALIGN_TOP) + align |= IE_FONT_ALIGN_TOP; + else if (Flags & IE_GUI_BUTTON_ALIGN_BOTTOM) + align |= IE_FONT_ALIGN_BOTTOM; + else + align |= IE_FONT_ALIGN_MIDDLE; + + if (! (Flags & IE_GUI_BUTTON_MULTILINE)) { + align |= IE_FONT_SINGLE_LINE; + } + font->Print( Region( x + XPos, y + YPos, Width - 2, Height - 2), + ( unsigned char * ) Text, ppoi, + (ieByte) align, true ); + } + + if (! (Flags&IE_GUI_BUTTON_NO_IMAGE)) { + for (int i = 0; i < MAX_NUM_BORDERS; i++) { + ButtonBorder *fr = &borders[i]; + if (! fr->enabled) continue; + + Region r = Region( x + XPos + fr->dx1, y + YPos + fr->dy1, Width - (fr->dx1 + fr->dx2 + 1), Height - (fr->dy1 + fr->dy2 + 1) ); + video->DrawRect( r, fr->color, fr->filled ); + } + } +} +/** Sets the Button State */ +void Button::SetState(unsigned char state) +{ + if (state > IE_GUI_BUTTON_LOCKED_PRESSED) {// If wrong value inserted + return; + } + if (State != state) { + Changed = true; + State = state; + } +} +void Button::SetBorder(int index, int dx1, int dy1, int dx2, int dy2, const Color &color, bool enabled, bool filled) +{ + if (index >= MAX_NUM_BORDERS) + return; + + ButtonBorder *fr = &borders[index]; + fr->dx1 = dx1; + fr->dy1 = dy1; + fr->dx2 = dx2; + fr->dy2 = dy2; + fr->color = color; + fr->enabled = enabled; + fr->filled = filled; + Changed = true; +} + +void Button::EnableBorder(int index, bool enabled) +{ + if (index >= MAX_NUM_BORDERS) + return; + + if (borders[index].enabled != enabled) { + borders[index].enabled = enabled; + Changed = true; + } +} + +void Button::SetFont(Font* newfont) +{ + font = newfont; +} +/** Handling The default button (enter) */ +void Button::OnSpecialKeyPress(unsigned char Key) +{ + if (State != IE_GUI_BUTTON_DISABLED && State != IE_GUI_BUTTON_LOCKED) { + if (Key == GEM_RETURN) { + if (Flags & IE_GUI_BUTTON_DEFAULT ) { + RunEventHandler( ButtonOnPress ); + return; + } + } + else if (Key == GEM_ESCAPE) { + if (Flags & IE_GUI_BUTTON_CANCEL ) { + RunEventHandler( ButtonOnPress ); + return; + } + } + } + Control::OnSpecialKeyPress(Key); +} + +/** Mouse Button Down */ +void Button::OnMouseDown(unsigned short x, unsigned short y, + unsigned short Button, unsigned short Mod) +{ + if (State == IE_GUI_BUTTON_DISABLED) { + Control::OnMouseDown(x,y,Button,Mod); + return; + } + + if (core->GetDraggedItem () && !ButtonOnDragDrop) { + Control::OnMouseDown(x,y,Button,Mod); + return; + } + + ScrollBar* scrlbr = (ScrollBar*) sb; + if (!scrlbr) { + Control *ctrl = Owner->GetScrollControl(); + if (ctrl && (ctrl->ControlType == IE_GUI_SCROLLBAR)) { + scrlbr = (ScrollBar *) ctrl; + } + } + + //Button == 1 means Left Mouse Button + switch(Button&GEM_MB_NORMAL) { + case GEM_MB_ACTION: + // We use absolute screen position here, so drag_start + // remains valid even after window/control is moved + drag_start.x = Owner->XPos + XPos + x; + drag_start.y = Owner->YPos + YPos + y; + + if (State == IE_GUI_BUTTON_LOCKED) { + SetState( IE_GUI_BUTTON_LOCKED_PRESSED ); + return; + } + SetState( IE_GUI_BUTTON_PRESSED ); + if (Flags & IE_GUI_BUTTON_SOUND) { + core->PlaySound( DS_BUTTON_PRESSED ); + } + if ((Button & GEM_MB_DOUBLECLICK) && ButtonOnDoublePress) { + RunEventHandler( ButtonOnDoublePress ); + printMessage("Button","Doubleclick detected\n",GREEN); + } + break; + case GEM_MB_SCRLUP: + if (scrlbr) { + scrlbr->ScrollUp(); + core->RedrawAll(); + } + break; + case GEM_MB_SCRLDOWN: + if (scrlbr) { + scrlbr->ScrollDown(); + core->RedrawAll(); + } + break; + } +} +/** Mouse Button Up */ +void Button::OnMouseUp(unsigned short x, unsigned short y, + unsigned short Button, unsigned short Mod) +{ + if (State == IE_GUI_BUTTON_DISABLED) { + return; + } + + //what was just dropped? + int dragtype = 0; + if (core->GetDraggedItem ()) dragtype=1; + if (core->GetDraggedPortrait ()) dragtype=2; + + //if something was dropped, but it isn't handled here: it didn't happen + if (dragtype && !ButtonOnDragDrop) + return; + + switch (State) { + case IE_GUI_BUTTON_PRESSED: + if (ToggleState) { + SetState( IE_GUI_BUTTON_SELECTED ); + } else { + SetState( IE_GUI_BUTTON_UNPRESSED ); + } + break; + case IE_GUI_BUTTON_LOCKED_PRESSED: + SetState( IE_GUI_BUTTON_LOCKED ); + break; + } + + //in case of dragged/dropped portraits, allow the event to happen even + //when we are out of bound + if (dragtype!=2) { + if (( x >= Width ) || ( y >= Height )) { + return; + } + } + if (Flags & IE_GUI_BUTTON_CHECKBOX) { + //checkbox + ToggleState = !ToggleState; + if (ToggleState) + SetState( IE_GUI_BUTTON_SELECTED ); + else + SetState( IE_GUI_BUTTON_UNPRESSED ); + if (VarName[0] != 0) { + ieDword tmp = 0; + core->GetDictionary()->Lookup( VarName, tmp ); + tmp ^= Value; + core->GetDictionary()->SetAt( VarName, tmp ); + Owner->RedrawControls( VarName, tmp ); + } + } else { + if (Flags & IE_GUI_BUTTON_RADIOBUTTON) { + //radio button + ToggleState = true; + SetState( IE_GUI_BUTTON_SELECTED ); + } + if (VarName[0] != 0) { + core->GetDictionary()->SetAt( VarName, Value ); + Owner->RedrawControls( VarName, Value ); + } + } + + switch (dragtype) { + case 1: + RunEventHandler( ButtonOnDragDrop ); + return; + case 2: + RunEventHandler( ButtonOnDragDropPortrait ); + return; + } + + if ((Button&GEM_MB_NORMAL) == GEM_MB_ACTION) { + if ((Mod & GEM_MOD_SHIFT) && ButtonOnShiftPress) + RunEventHandler( ButtonOnShiftPress ); + else + RunEventHandler( ButtonOnPress ); + } else { + if (Button == GEM_MB_MENU && ButtonOnRightPress) + RunEventHandler( ButtonOnRightPress ); + } +} + +void Button::OnMouseOver(unsigned short x, unsigned short y) +{ + Owner->Cursor = IE_CURSOR_NORMAL; + if (State == IE_GUI_BUTTON_DISABLED) { + return; + } + + if ( RunEventHandler( MouseOverButton )) { + //event handler destructed this object + return; + } + + //well, no more flags for buttons, and the portraits we can perform action on + //are in fact 'draggable multiline pictures' (with image) + if ((Flags & IE_GUI_BUTTON_DISABLED_P) == IE_GUI_BUTTON_PORTRAIT) { + GameControl *gc = core->GetGameControl(); + if (gc) { + Owner->Cursor = gc->GetDefaultCursor(); + } + } + + if (State == IE_GUI_BUTTON_LOCKED) { + return; + } + + //portrait buttons are draggable and locked + if ((Flags & IE_GUI_BUTTON_DRAGGABLE) && + (State == IE_GUI_BUTTON_PRESSED || State ==IE_GUI_BUTTON_LOCKED_PRESSED)) { + // We use absolute screen position here, so drag_start + // remains valid even after window/control is moved + int dx = Owner->XPos + XPos + x - drag_start.x; + int dy = Owner->YPos + YPos + y - drag_start.y; + core->GetDictionary()->SetAt( "DragX", dx ); + core->GetDictionary()->SetAt( "DragY", dy ); + drag_start.x = (ieWord) (drag_start.x + dx); + drag_start.y = (ieWord) (drag_start.y + dy); + RunEventHandler( ButtonOnDrag ); + } +} + +void Button::OnMouseEnter(unsigned short /*x*/, unsigned short /*y*/) +{ + if (State == IE_GUI_BUTTON_DISABLED) { + return; + } + + if (MouseEnterButton !=0 && VarName[0] != 0) { + core->GetDictionary()->SetAt( VarName, Value ); + } + + RunEventHandler( MouseEnterButton ); +} + +void Button::OnMouseLeave(unsigned short /*x*/, unsigned short /*y*/) +{ + if (State == IE_GUI_BUTTON_DISABLED) { + return; + } + + if (MouseLeaveButton !=0 && VarName[0] != 0) { + core->GetDictionary()->SetAt( VarName, Value ); + } + + RunEventHandler( MouseLeaveButton ); +} + + +/** Sets the Text of the current control */ +int Button::SetText(const char* string, int /*pos*/) +{ + free(Text); + Text = NULL; + if (string == NULL) { + hasText = false; + } else if (string[0] == 0) { + hasText = false; + } else { + Text = strndup( string, 255 ); + if (Flags&IE_GUI_BUTTON_LOWERCASE) + strlwr( Text ); + else if (Flags&IE_GUI_BUTTON_CAPS) + strupr( Text ); + hasText = true; + } + Changed = true; + return 0; +} + +/** Set Event Handler */ +bool Button::SetEvent(int eventType, EventHandler handler) +{ + Changed = true; + + switch (eventType) { + case IE_GUI_BUTTON_ON_PRESS: + ButtonOnPress = handler; + break; + case IE_GUI_MOUSE_OVER_BUTTON: + MouseOverButton = handler; + break; + case IE_GUI_MOUSE_ENTER_BUTTON: + MouseEnterButton = handler; + break; + case IE_GUI_MOUSE_LEAVE_BUTTON: + MouseLeaveButton = handler; + break; + case IE_GUI_BUTTON_ON_SHIFT_PRESS: + ButtonOnShiftPress = handler; + break; + case IE_GUI_BUTTON_ON_RIGHT_PRESS: + ButtonOnRightPress = handler; + break; + case IE_GUI_BUTTON_ON_DRAG_DROP: + ButtonOnDragDrop = handler; + break; + case IE_GUI_BUTTON_ON_DRAG_DROP_PORTRAIT: + ButtonOnDragDropPortrait = handler; + break; + case IE_GUI_BUTTON_ON_DRAG: + ButtonOnDrag = handler; + break; + case IE_GUI_BUTTON_ON_DOUBLE_PRESS: + ButtonOnDoublePress = handler; + break; + default: + return false; + } + + return true; +} + +/** Redraws a button from a given radio button group */ +void Button::RedrawButton(const char* VariableName, unsigned int Sum) +{ + if (strnicmp( VarName, VariableName, MAX_VARIABLE_LENGTH )) { + return; + } + if (State == IE_GUI_BUTTON_DISABLED) { + return; + } + if (Flags & IE_GUI_BUTTON_RADIOBUTTON) { + ToggleState = ( Sum == Value ); + } //radio button, exact value + else if (Flags & IE_GUI_BUTTON_CHECKBOX) { + ToggleState = !!( Sum & Value ); + } //checkbox, bitvalue + else { + return; + } //other buttons, nothing to redraw + if (ToggleState) { + SetState(IE_GUI_BUTTON_SELECTED); + } else { + SetState(IE_GUI_BUTTON_UNPRESSED); + } +} +/** Sets the Picture */ +void Button::SetPicture(Sprite2D* newpic) +{ + core->GetVideoDriver()->FreeSprite( Picture ); + ClearPictureList(); + Picture = newpic; + Changed = true; + Flags |= IE_GUI_BUTTON_PICTURE; + Owner->Invalidate(); +} + +/** Clears the list of Pictures */ +void Button::ClearPictureList() +{ + Video* video = core->GetVideoDriver(); + for (std::list::iterator iter = PictureList.begin(); + iter != PictureList.end(); ++iter) + video->FreeSprite( *iter ); + PictureList.clear(); + Changed = true; + Owner->Invalidate(); +} + +/** Add picture to the end of the list of Pictures */ +void Button::StackPicture(Sprite2D* Picture) +{ + PictureList.push_back(Picture); + Changed = true; + Flags |= IE_GUI_BUTTON_PICTURE; + Owner->Invalidate(); +} + +bool Button::IsPixelTransparent(unsigned short x, unsigned short y) +{ + // some buttons have hollow Image frame filled w/ Picture + // some buttons in BG2 are text only (if BAM == 'GUICTRL') + if (Picture || PictureList.size() || ! Unpressed) return false; + return Unpressed->IsPixelTransparent(x, y); +} + +// Set palette used for drawing button label in normal state +void Button::SetTextColor(const Color &fore, const Color &back) +{ + gamedata->FreePalette( normal_palette ); + normal_palette = core->CreatePalette( fore, back ); + Changed = true; +} + +void Button::SetHorizontalOverlay(double clip, const Color &src, const Color &dest) +{ + if ((Clipping>clip) || !(Flags&IE_GUI_BUTTON_HORIZONTAL) ) { + Flags |= IE_GUI_BUTTON_HORIZONTAL; + SourceRGB=src; + DestRGB=dest; + GetTime( starttime ); + starttime += 40; + } + Clipping = clip; + Changed = true; +} + +void Button::SetAnchor(ieWord x, ieWord y) +{ + Anchor = Point(x,y); +} diff --git a/project/jni/application/gemrb/src/core/GUI/Button.h b/project/jni/application/gemrb/src/core/GUI/Button.h new file mode 100644 index 000000000..1ae3d9126 --- /dev/null +++ b/project/jni/application/gemrb/src/core/GUI/Button.h @@ -0,0 +1,219 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * + */ + +/** + * @file Button.h + * Declares Button widget, for displaying buttons in the GUI + * @author GemRB Development Team + */ + + +#ifndef BUTTON_H +#define BUTTON_H + +#include "GUI/Control.h" + +#include "exports.h" + +#include "Font.h" +#include "Sprite2D.h" + +#include + +class Palette; + +// NOTE: keep these synchronized with GUIDefines.py!!! +#define IE_GUI_BUTTON_UNPRESSED 0 +#define IE_GUI_BUTTON_PRESSED 1 +#define IE_GUI_BUTTON_SELECTED 2 +#define IE_GUI_BUTTON_DISABLED 3 +// Like DISABLED, but processes MouseOver events and draws UNPRESSED bitmap +#define IE_GUI_BUTTON_LOCKED 4 +// Draws the disabled bitmap, but otherwise works like unpressed +#define IE_GUI_BUTTON_THIRD 5 +#define IE_GUI_BUTTON_SECOND 6 +#define IE_GUI_BUTTON_LOCKED_PRESSED 7 //all the same as LOCKED + +#define IE_GUI_BUTTON_NO_IMAGE 0x00000001 // don't draw image (BAM) +#define IE_GUI_BUTTON_PICTURE 0x00000002 // draw picture (BMP, MOS, ...) +#define IE_GUI_BUTTON_SOUND 0x00000004 +#define IE_GUI_BUTTON_ALT_SOUND 0x00000008 +#define IE_GUI_BUTTON_CHECKBOX 0x00000010 // or radio button +#define IE_GUI_BUTTON_RADIOBUTTON 0x00000020 // sticks in a state +#define IE_GUI_BUTTON_DEFAULT 0x00000040 // enter key triggers it +#define IE_GUI_BUTTON_ANIMATED 0x00000080 + +//these bits are hardcoded in the .chu structure +#define IE_GUI_BUTTON_ALIGN_LEFT 0x00000100 +#define IE_GUI_BUTTON_ALIGN_RIGHT 0x00000200 +#define IE_GUI_BUTTON_ALIGN_TOP 0x00000400 +#define IE_GUI_BUTTON_ALIGN_BOTTOM 0x00000800 +#define IE_GUI_BUTTON_ANCHOR 0x00001000 //not implemented yet +#define IE_GUI_BUTTON_LOWERCASE 0x00002000 +#define IE_GUI_BUTTON_MULTILINE 0x00004000 // don't set the single line flag +//end of hardcoded part +#define IE_GUI_BUTTON_DRAGGABLE 0x00008000 +#define IE_GUI_BUTTON_NO_TEXT 0x00010000 // don't draw button label +#define IE_GUI_BUTTON_PLAYRANDOM 0x00020000 +#define IE_GUI_BUTTON_PLAYONCE 0x00040000 + +#define IE_GUI_BUTTON_CENTER_PICTURES 0x00080000 // center button's PictureList +#define IE_GUI_BUTTON_BG1_PAPERDOLL 0x00100000 // BG1-style paperdoll PictureList +#define IE_GUI_BUTTON_HORIZONTAL 0x00200000 // horizontal clipping of overlay +#define IE_GUI_BUTTON_CANCEL 0x00400000 // cancel key triggers it +#define IE_GUI_BUTTON_CAPS 0x00800000 // convert text to uppercase + +//composite button flags +#define IE_GUI_BUTTON_NORMAL 0x00000004 // default button, doesn't stick +#define IE_GUI_BUTTON_PORTRAIT 0x0000c002 // portrait +#define IE_GUI_BUTTON_DISABLED_P 0x0000c003 // disabled portrait + +// !!! Keep these synchronized with GUIDefines.py !!! +#define IE_GUI_BUTTON_ON_PRESS 0x00000000 +#define IE_GUI_MOUSE_OVER_BUTTON 0x00000001 +#define IE_GUI_MOUSE_ENTER_BUTTON 0x00000002 +#define IE_GUI_MOUSE_LEAVE_BUTTON 0x00000003 +#define IE_GUI_BUTTON_ON_SHIFT_PRESS 0x00000004 +#define IE_GUI_BUTTON_ON_RIGHT_PRESS 0x00000005 +#define IE_GUI_BUTTON_ON_DRAG_DROP 0x00000006 +#define IE_GUI_BUTTON_ON_DRAG_DROP_PORTRAIT 0x00000007 +#define IE_GUI_BUTTON_ON_DRAG 0x00000008 +#define IE_GUI_BUTTON_ON_DOUBLE_PRESS 0x00000009 + +/** Border/frame settings for a button */ +struct ButtonBorder { + int dx1; + int dy1; + int dx2; + int dy2; + Color color; + bool filled; + bool enabled; +}; + +#define MAX_NUM_BORDERS 3 + + +/** + * @class Button + * Button widget, used mainly for buttons, but also for PixMaps (static images) + * or for Toggle Buttons. + */ + +class GEM_EXPORT Button : public Control { +public: + Button(); + ~Button(); + /** Sets the 'type' Image of the Button to 'img'. + 'type' may assume the following values: + - IE_GUI_BUTTON_UNPRESSED + - IE_GUI_BUTTON_PRESSED + - IE_GUI_BUTTON_SELECTED + - IE_GUI_BUTTON_DISABLED */ + void SetImage(unsigned char type, Sprite2D* img); + /** Draws the Control on the Output Display */ + void Draw(unsigned short x, unsigned short y); + /** Sets the Button State */ + void SetState(unsigned char state); + /** Sets the Text of the current control */ + int SetText(const char* string, int pos = 0); + /** Sets the Picture */ + void SetPicture(Sprite2D* Picture); + /** Clears the list of Pictures */ + void ClearPictureList(); + /** Add picture to the end of the list of Pictures */ + void StackPicture(Sprite2D* Picture); + /** Sets border/frame parameters */ + void SetBorder(int index, int dx1, int dy1, int dx2, int dy2, const Color &color, bool enabled = false, bool filled = false); + /** Sets horizontal overlay, used in portrait hp overlay */ + void SetHorizontalOverlay(double clip, const Color &src, const Color &dest); + /** Sets font used for drawing button label */ + void SetFont(Font* newfont); + /** Enables or disables specified border/frame */ + void EnableBorder(int index, bool enabled); +public: // Public Events + /** Mouse Enter */ + void OnMouseEnter(unsigned short x, unsigned short y); + /** Mouse Leave */ + void OnMouseLeave(unsigned short x, unsigned short y); + /** Mouse Over */ + void OnMouseOver(unsigned short x, unsigned short y); + /** Mouse Button Down */ + void OnMouseDown(unsigned short x, unsigned short y, unsigned short Button, + unsigned short Mod); + /** Mouse Button Up */ + void OnMouseUp(unsigned short x, unsigned short y, unsigned short Button, + unsigned short Mod); + /** A special key has been pressed */ + void OnSpecialKeyPress(unsigned char Key); + /** Set handler for specified event */ + bool SetEvent(int eventType, EventHandler handler); + /** Button Pressed Event Script Function Name */ + EventHandler ButtonOnPress; + EventHandler ButtonOnShiftPress; + EventHandler ButtonOnRightPress; + EventHandler ButtonOnDoublePress; + EventHandler ButtonOnDragDrop; + EventHandler ButtonOnDragDropPortrait; + EventHandler ButtonOnDrag; + EventHandler MouseEnterButton; + EventHandler MouseLeaveButton; + EventHandler MouseOverButton; + /** Refreshes the button from a radio group */ + void RedrawButton(const char* VariableName, unsigned int Sum); + /** Set palette used for drawing button label in normal state. */ + void SetTextColor(const Color &fore, const Color &back); + /** Sets percent (0-1.0) of width for clipping picture */ + void SetPictureClipping(double clip) { Clipping = clip; } + void SetAnchor(ieWord x, ieWord y); +private: // Private attributes + char* Text; + bool hasText; + Font* font; + bool ToggleState; + Palette* normal_palette; + Palette* disabled_palette; + /** Button Unpressed Image */ + Sprite2D* Unpressed; + /** Button Pressed Image */ + Sprite2D* Pressed; + /** Button Selected Image */ + Sprite2D* Selected; + /** Button Disabled Image */ + Sprite2D* Disabled; + /** Pictures to Apply when the hasPicture flag is set */ + Sprite2D* Picture; + /** If non-empty, list of Pictures to draw when hasPicture is set */ + std::list PictureList; + /** The current state of the Button */ + unsigned char State; + double Clipping; + Point drag_start; + /** HP Bar over portraits */ + unsigned long starttime; + Color SourceRGB, DestRGB; + Point Anchor; + /** frame settings */ + ButtonBorder borders[MAX_NUM_BORDERS]; + bool IsPixelTransparent (unsigned short x, unsigned short y); + void CloseUpColor(); +}; + +#endif diff --git a/project/jni/application/gemrb/src/core/GUI/Console.cpp b/project/jni/application/gemrb/src/core/GUI/Console.cpp new file mode 100644 index 000000000..f9e60fa93 --- /dev/null +++ b/project/jni/application/gemrb/src/core/GUI/Console.cpp @@ -0,0 +1,221 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * + */ + +#include "GUI/Console.h" + +#include "win32def.h" + +#include "GameData.h" +#include "Interface.h" +#include "Palette.h" +#include "ScriptEngine.h" +#include "Video.h" + +Console::Console(void) +{ + Cursor = NULL; + Back = NULL; + max = 128; + Buffer = ( unsigned char * ) malloc( max ); + Buffer[0] = 0; + for(size_t i=0;iGetVideoDriver(); + + gamedata->FreePalette( palette ); + video->FreeSprite( Cursor ); +} + +/** Draws the Console on the Output Display */ +void Console::Draw(unsigned short x, unsigned short y) +{ + if (Back) { + core->GetVideoDriver()->BlitSprite( Back, 0, y, true ); + } + Color black = { + 0x00, 0x00, 0x00, 0xff + }; + Region r( x + XPos, y + YPos, Width, Height ); + core->GetVideoDriver()->DrawRect( r, black ); + font->Print( r, Buffer, palette, + IE_FONT_ALIGN_LEFT | IE_FONT_ALIGN_MIDDLE, true, NULL, + Cursor, CurPos, true ); +} +/** Set Font */ +void Console::SetFont(Font* f) +{ + if (f != NULL) { + font = f; + } +} +/** Set Cursor */ +void Console::SetCursor(Sprite2D* cur) +{ + if (cur != NULL) { + Cursor = cur; + } +} +/** Set BackGround */ +void Console::SetBackGround(Sprite2D* back) +{ + //if 'back' is NULL then no BackGround will be drawn + Back = back; +} +/** Sets the Text of the current control */ +int Console::SetText(const char* string, int /*pos*/) +{ + strncpy( ( char * ) Buffer, string, max ); + return 0; +} +/** Key Press Event */ +void Console::OnKeyPress(unsigned char Key, unsigned short /*Mod*/) +{ + if (Key >= 0x20) { + size_t len = strlen( ( char* ) Buffer ); + if (len + 1 < max) { + for (size_t i = len; i > CurPos; i--) { + Buffer[i] = Buffer[i - 1]; + } + Buffer[CurPos++] = Key; + Buffer[len + 1] = 0; + } + } +} +/** Special Key Press */ +void Console::OnSpecialKeyPress(unsigned char Key) +{ + size_t len; + + switch (Key) { + case GEM_BACKSP: + if (CurPos != 0) { + size_t len = strlen( ( const char * ) Buffer ); + for (size_t i = CurPos; i < len; i++) { + Buffer[i - 1] = Buffer[i]; + } + Buffer[len - 1] = 0; + CurPos--; + } + break; + case GEM_HOME: + CurPos = 0; + break; + case GEM_END: + CurPos = (unsigned short) strlen( (const char * ) Buffer); + break; + case GEM_UP: + HistoryBack(); + break; + case GEM_DOWN: + HistoryForward(); + break; + case GEM_LEFT: + if (CurPos > 0) + CurPos--; + break; + case GEM_RIGHT: + len = strlen( ( const char * ) Buffer ); + if (CurPos < len) { + CurPos++; + } + break; + case GEM_DELETE: + len = strlen( ( const char * ) Buffer ); + if (CurPos < len) { + for (size_t i = CurPos; i < len; i++) { + Buffer[i] = Buffer[i + 1]; + } + } + break; + case GEM_RETURN: + core->GetGUIScriptEngine()->ExecString( ( char* ) Buffer ); + HistoryAdd(false); + Buffer[0] = 0; + CurPos = 0; + HistPos = 0; + Changed = true; + break; + } +} + +//ctrl-up +void Console::HistoryBack() +{ + HistoryAdd(false); + if (HistPos < HistMax-1 && Buffer[0]) { + HistPos++; + } + memcpy(Buffer, History[HistPos], max); + CurPos = (unsigned short) strlen ((const char *) Buffer); +} + +//ctrl-down +void Console::HistoryForward() +{ + HistoryAdd(false); + if (HistPos == 0) { + Buffer[0]=0; + CurPos=0; + return; + } + HistPos--; + memcpy(Buffer, History[HistPos], max); + CurPos = (unsigned short) strlen ((const char *) Buffer); +} + +void Console::HistoryAdd(bool force) +{ + int i; + + if (!force && !Buffer[0]) + return; + for (i=0;i0; i--) { + memcpy(History[i], History[i-1], max); + } + } + memcpy(History[0], Buffer, max); + if (HistMax +#include + +Control::Control() +{ + hasFocus = false; + Changed = true; + InHandler = false; + VarName[0] = 0; + Value = 0; + Flags = 0; + Tooltip = NULL; + Owner = NULL; + XPos = 0; + YPos = 0; + + sb = NULL; + animation = NULL; + AnimPicture = NULL; + ControlType = IE_GUI_INVALID; +} + +Control::~Control() +{ + if (InHandler) { + printMessage("Control","Destroying control inside event handler, crash may occur!\n", LIGHT_RED); + } + core->DisplayTooltip( 0, 0, NULL ); + free (Tooltip); + + delete animation; + + core->GetVideoDriver()->FreeSprite(AnimPicture); +} + +/** Sets the Tooltip text of the current control */ +int Control::SetTooltip(const char* string) +{ + free(Tooltip); + + if ((string == NULL) || (string[0] == 0)) { + Tooltip = NULL; + } else { + Tooltip = strdup (string); + } + Changed = true; + return 0; +} + +/** Sets the tooltip to be displayed on the screen now */ +void Control::DisplayTooltip() +{ + if (Tooltip) + core->DisplayTooltip( Owner->XPos + XPos + Width / 2, Owner->YPos + YPos + Height / 2, this ); + else + core->DisplayTooltip( 0, 0, NULL ); +} + +void Control::ResetEventHandler(EventHandler handler) +{ + handler = NULL; +} + +int Control::RunEventHandler(EventHandler handler) +{ + if (InHandler) { + printMessage("Control","Nested event handlers are not supported!\n", YELLOW); + return -1; + } + if (handler) { + Window *wnd = Owner; + if (!wnd) { + return -1; + } + unsigned short WID = wnd->WindowID; + unsigned short ID = (unsigned short) ControlID; + InHandler = true; + handler->call(); + if (!core->IsValidWindow(WID,wnd) ) { + printMessage ("Control","Owner window destructed!\n", LIGHT_RED); + return -1; + } + if (!wnd->IsValidControl(ID,this) ) { + printMessage ("Control","Control destructed!\n", LIGHT_RED); + return -1; + } + InHandler = false; + } + return 0; +} + +/** Key Press Event */ +void Control::OnKeyPress(unsigned char Key, unsigned short /*Mod*/) +{ + //printf("OnKeyPress: CtrlID = 0x%08X, Key = %c (0x%02hX)\n", (unsigned int) ControlID, Key, Key); +#ifdef ANDROID // mapping volume control to volume control keys on device, these keys must be set up in AndroidAppSettings.cfg + switch(Key) + { + case 'o': // volume down + case 'p': // volume up + int Ambients, Movie, Music, SFX, Voices; + core->GetDictionary()->Lookup( "Volume Ambients", (ieDword&)Ambients ); + core->GetDictionary()->Lookup( "Volume Movie", (ieDword&)Movie ); + core->GetDictionary()->Lookup( "Volume Music", (ieDword&)Music ); + core->GetDictionary()->Lookup( "Volume SFX", (ieDword&)SFX ); + core->GetDictionary()->Lookup( "Volume Voices", (ieDword&)Voices ); + if(Key=='o') + { + if(Ambients>0) Ambients-=10; if(Ambients<0) Ambients=0; + if(Movie>0) Movie-=10; if(Movie<0) Movie=0; + if(Music>0) Music-=10; if(Music<0) Music=0; + if(SFX>0) SFX-=10; if(SFX<0) SFX=0; + if(Voices>0) Voices-=10; if(Voices<0) Voices=0; + } + else + { + if(Ambients<100) Ambients+=10; if(Ambients>100) Ambients=100; + if(Movie<100) Movie+=10; if(Movie>100) Movie=100; + if(Music<100) Music+=10; if(Music>100) Music=100; + if(SFX<100) SFX+=10; if(SFX>100) SFX=100; + if(Voices<100) Voices+=10; if(Voices>100) Voices=100; + } + core->GetDictionary()->SetAt( "Volume Ambients", (ieDword)Ambients ); + core->GetDictionary()->SetAt( "Volume Movie", Movie ); + core->GetDictionary()->SetAt( "Volume Music", Music ); + core->GetDictionary()->SetAt( "Volume SFX", SFX ); + core->GetDictionary()->SetAt( "Volume Voices", Voices ); + core->GetAudioDrv()->UpdateVolume(); + break; + } +#else +(void)Key; // unused, fool the compiler +#endif +} + +/** Key Release Event */ +void Control::OnKeyRelease(unsigned char /*Key*/, unsigned short /*Mod*/) +{ + //printf( "OnKeyRelease: CtrlID = 0x%08X, Key = %c (0x%02hX)\n", (unsigned int) ControlID, Key, Key ); +} + +/** Mouse Enter Event */ +void Control::OnMouseEnter(unsigned short /*x*/, unsigned short /*y*/) +{ +// printf("OnMouseEnter: CtrlID = 0x%08X, x = %hd, y = %hd\n", (unsigned int) ControlID, x, y); +} + +/** Mouse Leave Event */ +void Control::OnMouseLeave(unsigned short /*x*/, unsigned short /*y*/) +{ +// printf("OnMouseLeave: CtrlID = 0x%08X, x = %hd, y = %hd\n", (unsigned int) ControlID, x, y); +} + +/** Mouse Over Event */ +void Control::OnMouseOver(unsigned short /*x*/, unsigned short /*y*/) +{ + //printf("OnMouseOver: CtrlID = 0x%08X, x = %hd, y = %hd\n", (unsigned int) ControlID, x, y); +} + +/** Mouse Button Down */ +void Control::OnMouseDown(unsigned short x, unsigned short y, + unsigned short Button, unsigned short Mod) +{ + if (Button == GEM_MB_SCRLUP || Button == GEM_MB_SCRLDOWN) { + Control *ctrl = Owner->GetScrollControl(); + if (ctrl && (ctrl!=this)) { + ctrl->OnMouseDown(x,y,Button,Mod); + } + } +} + +/** Mouse Button Up */ +void Control::OnMouseUp(unsigned short /*x*/, unsigned short /*y*/, + unsigned short /*Button*/, unsigned short /*Mod*/) +{ + //printf("OnMouseUp: CtrlID = 0x%08X, x = %hd, y = %hd, Button = %d, Mos = %hd\n", (unsigned int) ControlID, x, y, Button, Mod); +} + +/** Special Key Press */ +void Control::OnSpecialKeyPress(unsigned char Key) +{ + if (Key == GEM_UP || Key == GEM_DOWN) { + Control *ctrl = Owner->GetScrollControl(); + if (ctrl && (ctrl!=this)) { + ctrl->OnSpecialKeyPress(Key); + } + } +} + +/** Sets the Display Flags */ +int Control::SetFlags(int arg_flags, int opcode) +{ + if ((arg_flags >>24) != ControlType) + return -2; + switch (opcode) { + case BM_SET: + Flags = arg_flags; //set + break; + case BM_AND: + Flags &= arg_flags; + break; + case BM_OR: + Flags |= arg_flags; //turn on + break; + case BM_XOR: + Flags ^= arg_flags; + break; + case BM_NAND: + Flags &= ~arg_flags;//turn off + break; + default: + return -1; + } + Changed = true; + Owner->Invalidate(); + return 0; +} + +void Control::SetAnimPicture(Sprite2D* newpic) +{ + core->GetVideoDriver()->FreeSprite(AnimPicture); + AnimPicture = newpic; + //apparently this is needed too, so the artifacts are not visible + if (Owner->Visible==WINDOW_VISIBLE) { + Changed = true; + Owner->InvalidateForControl(this); + } +} + +/** Sets the Scroll Bar Pointer. If 'ptr' is NULL no Scroll Bar will be linked + to this Control. */ +int Control::SetScrollBar(Control* ptr) +{ + if (ptr && (ptr->ControlType!=IE_GUI_SCROLLBAR)) { + ptr = NULL; + printMessage("Control","Attached control is not a ScrollBar!\n",YELLOW); + return -1; + } + sb = ptr; + Changed = true; + if (ptr) return 1; + return 0; +} diff --git a/project/jni/application/gemrb/src/core/GUI/Control.h b/project/jni/application/gemrb/src/core/GUI/Control.h new file mode 100644 index 000000000..50a5ad2d4 --- /dev/null +++ b/project/jni/application/gemrb/src/core/GUI/Control.h @@ -0,0 +1,149 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * + */ + +/** + * @file Control.h + * Declares Control, root class for all widgets except of windows + */ + +#ifndef CONTROL_H +#define CONTROL_H + +#define IE_GUI_BUTTON 0 +#define IE_GUI_PROGRESSBAR 1 //gemrb extension +#define IE_GUI_SLIDER 2 +#define IE_GUI_EDIT 3 +#define IE_GUI_TEXTAREA 5 +#define IE_GUI_LABEL 6 +#define IE_GUI_SCROLLBAR 7 +#define IE_GUI_WORLDMAP 8 // gemrb extension +#define IE_GUI_MAP 9 // gemrb extension +#define IE_GUI_GAMECONTROL 128 +#define IE_GUI_INVALID 255 + +#define IE_GUI_CONTROL_FOCUSED 0x80 + +//this is in the control ID +#define IGNORE_CONTROL 0x10000000 + +#include "RGBAColor.h" +#include "exports.h" +#include "ie_types.h" +#include "win32def.h" + +#include "Callback.h" + +class ControlAnimation; +class Sprite2D; +class Window; + +/** + * @class Control + * Basic Control Object, also called widget or GUI element. Parent class for Labels, Buttons, etc. + * Every GUI element except of a Window is a descendant of this class. + */ + +class GEM_EXPORT Control { +public: + Control(); + virtual ~Control(); + /** Draws the Control on the Output Display */ + virtual void Draw(unsigned short x, unsigned short y) = 0; + /** Sets the Text of the current control */ + virtual int SetText(const char* string, int pos = 0) = 0; + /** Sets the Tooltip text of the current control */ + int SetTooltip(const char* string); + /** Displays the tooltip text, Worldmap handles this differently */ + virtual void DisplayTooltip(); + /** Variable length is 40-1 (zero terminator) */ + char VarName[MAX_VARIABLE_LENGTH]; + /** the value of the control to add to the variable */ + ieDword Value; + /** various flags based on the control type */ + ieDword Flags; + ControlAnimation* animation; + Sprite2D* AnimPicture; + +public: // Public attributes + /** Defines the Control ID Number used for GUI Scripting */ + ieDword ControlID; + /** X position of control relative to containing window */ + ieWord XPos; + /** Y position of control relative to containing window */ + ieWord YPos; + /** Width of control */ + ieWord Width; + /** Height of control */ + ieWord Height; + /** Type of control */ + ieByte ControlType; + /** Text to display as a tooltip when the mouse cursor hovers + * for some time over the control */ + char* Tooltip; + /** Focused Control */ + bool hasFocus; + /** If true, control is redrawn during next call to gc->DrawWindows. + * Then it's set back to false. */ + bool Changed; + /** True if we are currently in an event handler */ + bool InHandler; + /** Owner Window */ + Window* Owner; + /** Attached Scroll Bar Pointer*/ + Control* sb; +public: //Events + /** Reset/init event handler */ + void ResetEventHandler(EventHandler handler); + /** Returns the Owner */ + Window *GetOwner() const { return Owner; } + /** Set the Flags */ + int SetFlags(int arg_flags, int opcode); + /** Set handler for specified event. Override in child classes */ + virtual bool SetEvent(int eventType, EventHandler handler) = 0; + /** Run specified handler, it may return error code */ + int RunEventHandler(EventHandler handler); + /** Key Press Event */ + virtual void OnKeyPress(unsigned char Key, unsigned short Mod); + /** Key Release Event */ + virtual void OnKeyRelease(unsigned char Key, unsigned short Mod); + /** Mouse Enter Event */ + virtual void OnMouseEnter(unsigned short x, unsigned short y); + /** Mouse Leave Event */ + virtual void OnMouseLeave(unsigned short x, unsigned short y); + /** Mouse Over Event */ + virtual void OnMouseOver(unsigned short x, unsigned short y); + /** Mouse Button Down */ + virtual void OnMouseDown(unsigned short x, unsigned short y, + unsigned short Button, unsigned short Mod); + /** Mouse Button Up */ + virtual void OnMouseUp(unsigned short x, unsigned short y, + unsigned short Button, unsigned short Mod); + /** Special Key Press */ + virtual void OnSpecialKeyPress(unsigned char Key); + virtual bool IsPixelTransparent(unsigned short /*x*/, unsigned short /*y*/) { + return false; + } + /** Sets the animation picture ref */ + void SetAnimPicture(Sprite2D* Picture); + /** Sets the Scroll Bar Pointer */ + int SetScrollBar(Control* ptr); +}; + +#endif diff --git a/project/jni/application/gemrb/src/core/GUI/EventMgr.cpp b/project/jni/application/gemrb/src/core/GUI/EventMgr.cpp new file mode 100644 index 000000000..41e22b2b0 --- /dev/null +++ b/project/jni/application/gemrb/src/core/GUI/EventMgr.cpp @@ -0,0 +1,439 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * + */ + +#include "GUI/EventMgr.h" + +#include "GUI/GameControl.h" + +#include "win32def.h" + +#include "Interface.h" +#include "Video.h" + +EventMgr::EventMgr(void) +{ + last_win_focused = NULL; + // Last window focused for mouse events (eg, with a click). Used to determine MouseUp events + last_win_mousefocused = NULL; + // Last window we were over. Used to determine MouseEnter and MouseLeave events + last_win_over = NULL; + MButtons = 0; + dc_x = 0; + dc_y = 0; + dc_time = 0; + dc_delay = 250; + rk_delay = 250; + rk_flags = GEM_RK_DISABLE; +} + +EventMgr::~EventMgr(void) +{ +} + +void EventMgr::SetOnTop(int Index) +{ + std::vector< int>::iterator t; + for (t = topwin.begin(); t != topwin.end(); ++t) { + if (( *t ) == Index) { + topwin.erase( t ); + break; + } + } + if (topwin.size() != 0) { + topwin.insert( topwin.begin(), Index ); + } else { + topwin.push_back( Index ); + } +} + +void EventMgr::SetDefaultFocus(Window *win) +{ + if (!last_win_focused) { + last_win_focused = win; + last_win_focused->SetFocused(last_win_focused->GetControl(0)); + } + last_win_over = NULL; +} + +/** Adds a Window to the Event Manager */ +void EventMgr::AddWindow(Window* win) +{ + unsigned int i; + + if (win == NULL) { + return; + } + bool found = false; + for (i = 0; i < windows.size(); i++) { + if (windows[i] == win) { + goto ok; + } + if(windows[i]==NULL) { + windows[i] = win; +ok: + SetOnTop( i ); + found = true; + break; + } + } + if (!found) { + windows.push_back( win ); + if (windows.size() == 1) + topwin.push_back( 0 ); + else + SetOnTop( ( int ) windows.size() - 1 ); + } + SetDefaultFocus(win); +} +/** Frees and Removes all the Windows in the Array */ +void EventMgr::Clear() +{ + topwin.clear(); + windows.clear(); + last_win_focused = NULL; + last_win_mousefocused = NULL; + last_win_over = NULL; +} + +/** Remove a Window from the array */ +void EventMgr::DelWindow(Window *win) +//unsigned short WindowID, const char *WindowPack) +{ + if (last_win_focused == win) { + last_win_focused = NULL; + } + if (last_win_mousefocused == win) { + last_win_mousefocused = NULL; + } + if (last_win_over == win) { + last_win_over = NULL; + } + + if (windows.size() == 0) { + return; + } + int pos = -1; + std::vector< Window*>::iterator m; + for (m = windows.begin(); m != windows.end(); ++m) { + pos++; + if ( (*m) == win) { + (*m) = NULL; + std::vector< int>::iterator t; + for (t = topwin.begin(); t != topwin.end(); ++t) { + if ( (*t) == pos) { + topwin.erase( t ); + return; + } + } + printMessage("EventManager","Couldn't find window",YELLOW); + } + } +} + +/** BroadCast Mouse Move Event */ +void EventMgr::MouseMove(unsigned short x, unsigned short y) +{ + if (windows.size() == 0) { + return; + } + if (!last_win_focused) { + return; + } + GameControl *gc = core->GetGameControl(); + if (gc) { + // for scrolling + gc->OnGlobalMouseMove(x, y); + } + std::vector< int>::iterator t; + std::vector< Window*>::iterator m; + for (t = topwin.begin(); t != topwin.end(); ++t) { + m = windows.begin(); + m += ( *t ); + Window *win = *m; + if (win == NULL) + continue; + if (!win->Visible) + continue; + if (( win->XPos <= x ) && ( win->YPos <= y )) { + //Maybe we are on the window, let's check + if (( win->XPos + win->Width >= x ) && + ( win->YPos + win->Height >= y )) { + //Yes, we are on the Window + //Let's check if we have a Control under the Mouse Pointer + Control* ctrl = win->GetControl( x, y, true ); + //look for the low priority flagged controls (mostly static labels) + if (ctrl == NULL) { + ctrl = win->GetControl( x, y, false ); + } + if (win != last_win_over || ctrl != win->GetOver()) { + // Remove tooltip if mouse moved to different control + core->DisplayTooltip( 0, 0, NULL ); + if (last_win_over) { + last_win_over->OnMouseLeave( x, y ); + } + last_win_over = win; + win->OnMouseEnter( x, y, ctrl ); + } + if (ctrl != NULL) { + win->OnMouseOver( x, y ); + } + RefreshCursor(win->Cursor); + return; + } + } + //stop going further + if (( *m )->Visible == WINDOW_FRONT) + break; + } + core->DisplayTooltip( 0, 0, NULL ); +} + +void EventMgr::RefreshCursor(int idx) +{ + Video *video = core->GetVideoDriver(); + if (idx&IE_CURSOR_GRAY) { + video->SetMouseGrayed(true); + } else { + video->SetMouseGrayed(false); + } + idx &= IE_CURSOR_MASK; + video->SetCursor( core->Cursors[idx], core->Cursors[idx ^ 1] ); +} + +bool EventMgr::ClickMatch(unsigned short x, unsigned short y, unsigned long thisTime) +{ + if (dc_x+10x+10) return false; + if (dc_y+10y+10) return false; + if (dc_time::iterator t; + std::vector< Window*>::iterator m; + Control *ctrl; + unsigned long thisTime; + + GetTime( thisTime ); + if (ClickMatch(x, y, thisTime)) { + Button |= GEM_MB_DOUBLECLICK; + dc_x = 0; + dc_y = 0; + dc_time = 0; + } else { + dc_x = x; + dc_y = y; + dc_time = thisTime+dc_delay; + } + MButtons |= Button; + for (t = topwin.begin(); t != topwin.end(); ++t) { + m = windows.begin(); + m += ( *t ); + if (( *m ) == NULL) + continue; + if (!( *m )->Visible) + continue; + if (( ( *m )->XPos <= x ) && ( ( *m )->YPos <= y )) { + //Maybe we are on the window, let's check + if (( ( *m )->XPos + ( *m )->Width >= x ) && + ( ( *m )->YPos + ( *m )->Height >= y )) { + //Yes, we are on the Window + //Let's check if we have a Control under the Mouse Pointer + ctrl = ( *m )->GetControl( x, y, true ); + if (!ctrl) { + ctrl = ( *m )->GetControl( x, y, false); + } + last_win_mousefocused = *m; + if (ctrl != NULL) { + last_win_mousefocused->SetMouseFocused( ctrl ); + ctrl->OnMouseDown( x - last_win_mousefocused->XPos - ctrl->XPos, y - last_win_mousefocused->YPos - ctrl->YPos, Button, Mod ); + return; + } + } + } + if (( *m )->Visible == WINDOW_FRONT) //stop looking further + break; + } + + if ((Button == GEM_MB_SCRLUP || Button == GEM_MB_SCRLDOWN) && last_win_mousefocused) { + ctrl = last_win_mousefocused->GetScrollControl(); + if (ctrl) { + ctrl->OnMouseDown( x - last_win_mousefocused->XPos - ctrl->XPos, y - last_win_mousefocused->YPos - ctrl->YPos, Button, Mod ); + } + } + + if (last_win_mousefocused) { + last_win_mousefocused->SetMouseFocused(NULL); + } +} +/** BroadCast Mouse Up Event */ +void EventMgr::MouseUp(unsigned short x, unsigned short y, unsigned short Button, + unsigned short Mod) +{ + MButtons &= ~Button; + if (last_win_mousefocused == NULL) return; + Control *last_ctrl_mousefocused = last_win_mousefocused->GetMouseFocus(); + if (last_ctrl_mousefocused == NULL) return; + last_ctrl_mousefocused->OnMouseUp( x - last_win_mousefocused->XPos - last_ctrl_mousefocused->XPos, + y - last_win_mousefocused->YPos - last_ctrl_mousefocused->YPos, Button, Mod ); +} + +/** BroadCast Mouse Idle Event */ +void EventMgr::MouseIdle(unsigned long /*time*/) +{ + if (last_win_over == NULL) return; + Control *ctrl = last_win_over->GetOver(); + if (ctrl == NULL) return; + ctrl->DisplayTooltip(); +} + +/** BroadCast Key Press Event */ +void EventMgr::KeyPress(unsigned char Key, unsigned short Mod) +{ + if (last_win_focused == NULL) return; + Control *ctrl = last_win_focused->GetFocus(); + if (ctrl == NULL) return; + ctrl->OnKeyPress( Key, Mod ); +} +/** BroadCast Key Release Event */ +void EventMgr::KeyRelease(unsigned char Key, unsigned short Mod) +{ + if (last_win_focused == NULL) return; + Control *ctrl = last_win_focused->GetFocus(); + if (Key == GEM_GRAB) { + core->GetVideoDriver()->ToggleGrabInput(); + return; + } + if (ctrl == NULL) return; + ctrl->OnKeyRelease( Key, Mod ); +} + +/** Special Key Press Event */ +void EventMgr::OnSpecialKeyPress(unsigned char Key) +{ + if (!last_win_focused) { + return; + } + Control *ctrl = NULL; + + // tab shows tooltips + if (Key == GEM_TAB) { + if (last_win_over != NULL) { + Control *ctrl = last_win_over->GetOver(); + if (ctrl != NULL) { + ctrl->DisplayTooltip(); + } + } + } + //the default control will get only GEM_RETURN + else if (Key == GEM_RETURN) { + ctrl = last_win_focused->GetDefaultControl(0); + } + //the default cancel control will get only GEM_ESCAPE + else if (Key == GEM_ESCAPE) { + ctrl = last_win_focused->GetDefaultControl(1); + } + + //if there was no default button set, then the current focus will get it + if (!ctrl) { + ctrl = last_win_focused->GetFocus(); + } + //if one is under focus, use the default scroll focus + if (!ctrl) { + if (Key == GEM_UP || Key == GEM_DOWN) { + ctrl = last_win_focused->GetScrollControl(); + } + } + if (ctrl) { + switch (ctrl->ControlType) { + //scrollbars will receive only mousewheel events + case IE_GUI_SCROLLBAR: + if (Key != GEM_UP && Key != GEM_DOWN) { + return; + } + break; + //buttons will receive only GEM_RETURN + case IE_GUI_BUTTON: + if (Key != GEM_RETURN && Key!=GEM_ESCAPE) { + return; + } + break; + case IE_GUI_GAMECONTROL: + //gamecontrols will receive all special keys + break; + case IE_GUI_EDIT: + case IE_GUI_TEXTAREA: + //editboxes and textareas will receive all special keys + break; + default: + //other controls don't receive any + return; + } + ctrl->OnSpecialKeyPress( Key ); + } +} + +void EventMgr::SetFocused(Window *win, Control *ctrl) +{ + last_win_focused = win; + last_win_focused->SetFocused(ctrl); + //this is to refresh changing mouse cursors should the focus change) + int x,y; + core->GetVideoDriver()->GetMousePos(x,y); + MouseMove((unsigned short) x, (unsigned short) y); +} + +void EventMgr::SetDCDelay(unsigned long t) +{ + dc_delay = t; +} + +void EventMgr::SetRKDelay(unsigned long t) +{ + rk_delay = t; +} + +unsigned long EventMgr::GetRKDelay() +{ + if (rk_flags&GEM_RK_DISABLE) return (unsigned long) ~0; + if (rk_flags&GEM_RK_DOUBLESPEED) return rk_delay/2; + if (rk_flags&GEM_RK_QUADRUPLESPEED) return rk_delay/4; + return rk_delay; +} + +unsigned long EventMgr::SetRKFlags(unsigned long arg, unsigned int op) +{ + unsigned long tmp = rk_flags; + switch (op) { + case BM_SET: tmp = arg; break; + case BM_OR: tmp |= arg; break; + case BM_NAND: tmp &= ~arg; break; + case BM_XOR: tmp ^= arg; break; + case BM_AND: tmp &= arg; break; + default: tmp = 0; break; + } + rk_flags=tmp; + return rk_flags; +} diff --git a/project/jni/application/gemrb/src/core/GUI/EventMgr.h b/project/jni/application/gemrb/src/core/GUI/EventMgr.h new file mode 100644 index 000000000..bda4ae8b8 --- /dev/null +++ b/project/jni/application/gemrb/src/core/GUI/EventMgr.h @@ -0,0 +1,142 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * + */ + +/** + * @file EventMgr.h + * Declares EventMgr, class distributing events from input devices to GUI windows + * @author The GemRB Project + */ + + +#ifndef EVENTMGR_H +#define EVENTMGR_H + +#include "GUI/Control.h" + +#include "exports.h" + +#include "WindowMgr.h" + +#include + +#define GEM_LEFT 0x81 +#define GEM_RIGHT 0x82 +#define GEM_UP 0x83 +#define GEM_DOWN 0x84 +#define GEM_DELETE 0x85 +#define GEM_RETURN 0x86 +#define GEM_BACKSP 0x87 +#define GEM_TAB 0x88 +#define GEM_ALT 0x89 +#define GEM_HOME 0x8a +#define GEM_END 0x8b +#define GEM_ESCAPE 0x8c +#define GEM_PGUP 0x8d +#define GEM_PGDOWN 0x8e +#define GEM_GRAB 0x8f + + +#define GEM_MOD_SHIFT 1 +#define GEM_MOD_CTRL 2 +#define GEM_MOD_ALT 4 + +#define GEM_MOUSEOUT 128 + +// Mouse buttons +#define GEM_MB_ACTION 1 +#define GEM_MB_MENU 4 +#define GEM_MB_SCRLUP 8 +#define GEM_MB_SCRLDOWN 16 + +#define GEM_MB_NORMAL 255 +#define GEM_MB_DOUBLECLICK 256 + +#define GEM_RK_DOUBLESPEED 1 +#define GEM_RK_DISABLE 2 +#define GEM_RK_QUADRUPLESPEED 4 + +/** + * @class EventMgr + * Class distributing events from input devices to GUI windows. + * The events are pumped into instance of this class from a Video driver plugin + */ + +class GEM_EXPORT EventMgr { +private: + std::vector< Window*> windows; + std::vector< int> topwin; + + unsigned short dc_x, dc_y; + unsigned long dc_time, dc_delay; + unsigned long rk_delay, rk_flags; +public: + EventMgr(void); + ~EventMgr(void); + /** Adds a Window to the Event Manager */ + void AddWindow(Window* win); + /** Removes a Window from the Event chain */ + //void DelWindow(unsigned short WindowID, const char *WindowPack); + void DelWindow(Window* win); + /** Frees and Removes all the Windows in the Array */ + void Clear(); + /** Call this to change the cursor (moving over windows will change it back) */ + void RefreshCursor(int idx); + /** BroadCast Mouse Move Event */ + void MouseMove(unsigned short x, unsigned short y); + /** BroadCast Mouse Move Event */ + void MouseDown(unsigned short x, unsigned short y, unsigned short Button, + unsigned short Mod); + /** BroadCast Mouse Move Event */ + void MouseUp(unsigned short x, unsigned short y, unsigned short Button, + unsigned short Mod); + /** BroadCast Mouse Idle Event */ + void MouseIdle(unsigned long time); + /** BroadCast Key Press Event */ + void KeyPress(unsigned char Key, unsigned short Mod); + /** BroadCast Key Release Event */ + void KeyRelease(unsigned char Key, unsigned short Mod); + /** Special Ket Press Event */ + void OnSpecialKeyPress(unsigned char Key); + /** Sets focus to the control of the window */ + void SetFocused(Window *win, Control *ctrl); + /** Sets mouse event focus to the control of the window */ + void SetMouseFocused(Window *win, Control *ctrl); + /** Sets the maximum accepted doubleclick delay */ + void SetDCDelay(unsigned long t); + void SetRKDelay(unsigned long t); + unsigned long GetRKDelay(); + unsigned long SetRKFlags(unsigned long arg, unsigned int op); + + /** Mask of which Mouse Buttons are pressed */ + unsigned char MButtons; +private: + /** Last Window focused */ + Window* last_win_focused; + /** Last Window mouse event focused */ + Window* last_win_mousefocused; + /** Last Window under Mouse Pointer*/ + Window* last_win_over; + /** Sets a Window on the Top of the Window Queue */ + void SetDefaultFocus(Window *win); + void SetOnTop(int Index); + bool ClickMatch(unsigned short x, unsigned short y, unsigned long thisTime); +}; + +#endif // ! EVENTMGR_H diff --git a/project/jni/application/gemrb/src/core/GUI/GameControl.cpp b/project/jni/application/gemrb/src/core/GUI/GameControl.cpp new file mode 100644 index 000000000..a907b6f49 --- /dev/null +++ b/project/jni/application/gemrb/src/core/GUI/GameControl.cpp @@ -0,0 +1,2718 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "GUI/GameControl.h" + +#include "strrefs.h" +#include "win32def.h" + +#include "DialogHandler.h" +#include "DisplayMessage.h" +#include "Effect.h" +#include "Game.h" +#include "GameData.h" +#include "ImageMgr.h" +#include "Interface.h" +#include "Item.h" +#include "SaveGameIterator.h" +#include "ScriptEngine.h" +#include "TileMap.h" +#include "Video.h" +#include "damages.h" +#include "GameScript/GSUtils.h" + +#include + +#define DEBUG_SHOW_INFOPOINTS 0x01 +#define DEBUG_SHOW_CONTAINERS 0x02 +#define DEBUG_SHOW_DOORS DEBUG_SHOW_CONTAINERS +#define DEBUG_SHOW_LIGHTMAP 0x08 + +static const Color cyan = { + 0x00, 0xff, 0xff, 0xff +}; +static const Color red = { + 0xff, 0x00, 0x00, 0xff +}; +static const Color magenta = { + 0xff, 0x00, 0xff, 0xff +}; +static const Color green = { + 0x00, 0xff, 0x00, 0xff +}; +/* +static Color white = { + 0xff, 0xff, 0xff, 0xff +}; +*/ +static const Color black = { + 0x00, 0x00, 0x00, 0xff +}; +static const Color blue = { + 0x00, 0x00, 0xff, 0x80 +}; + +//Animation* effect; + +#define FORMATIONSIZE 10 +typedef Point formation_type[FORMATIONSIZE]; +ieDword formationcount; +static formation_type *formations=NULL; +static bool mqs = false; +static ieResRef TestSpell="SPWI207"; + +//If one of the actors has tracking on, the gamecontrol needs to display +//arrow markers on the edges to point at detected monsters +//tracterID is the tracker actor's global ID +//distance is the detection distance +void GameControl::SetTracker(Actor *actor, ieDword dist) +{ + trackerID = actor->GetGlobalID(); + distance = dist; +} + +//Multiple Quick saves is an experimental GemRB feature. +//multiple quick saves are kept, their age is determined by the slot +//number. There is an algorithm which keeps about log2(n) slots alive. +//The algorithm is implemented in SaveGameIterator +void GameControl::MultipleQuickSaves(int arg) +{ + mqs=arg==1; +} + +GameControl::GameControl(void) +{ + if (!formations) { + ReadFormations(); + } + //this is the default action, individual actors should have one too + //at this moment we use only this + //maybe we don't even need it + Changed = true; + spellCount = 0; + user = NULL; + lastActorID = 0; + trackerID = 0; + distance = 0; + MouseIsDown = false; + DrawSelectionRect = false; + overDoor = NULL; + overContainer = NULL; + overInfoPoint = NULL; + drawPath = NULL; + pfs.null(); + lastCursor = IE_CURSOR_NORMAL; + moveX = moveY = 0; + scrolling = false; + numScrollCursor = 0; + DebugFlags = 0; + AIUpdateCounter = 1; + EnableRunning = true; //make this a game flag if you wish + ieDword tmp=0; + + ResetTargetMode(); + + core->GetDictionary()->Lookup("Center",tmp); + if (tmp) { + ScreenFlags=SF_ALWAYSCENTER|SF_CENTERONACTOR; + } + else { + ScreenFlags = SF_CENTERONACTOR; + } + LeftCount = 0; + BottomCount = 0; + RightCount = 0; + TopCount = 0; + DialogueFlags = 0; + dialoghandler = new DialogHandler(); + DisplayText = NULL; +} + +//TODO: +//There could be a custom formation which is saved in the save game +//alternatively, all formations could be saved in some compatible way +//so it doesn't cause problems with the original engine +void GameControl::ReadFormations() +{ + unsigned int i,j; + AutoTable tab("formatio"); + if (!tab) { + // fallback + formationcount = 1; + formations = (formation_type *) calloc(1,sizeof(formation_type) ); + return; + } + formationcount = tab->GetRowCount(); + formations = (formation_type *) calloc(formationcount, sizeof(formation_type)); + for(i=0; iQueryField(i,j*2)); + formations[i][j].x=k; + k=(short) atoi(tab->QueryField(i,j*2+1)); + formations[i][j].y=k; + } + } +} + +//returns a single point offset for a formation +//formation: the formation type +//pos: the actor's slot ID +Point GameControl::GetFormationOffset(ieDword formation, ieDword pos) +{ + if (formation>=formationcount) formation = 0; + if (pos>=FORMATIONSIZE) pos=FORMATIONSIZE-1; + return formations[formation][pos]; +} + +//Moves an actor to a new position, keeping the current formation +//WARNING: don't pass p as a reference because it gets modified +void GameControl::MoveToPointFormation(Actor *actor, unsigned int pos, Point src, Point p) +{ + Map* map = actor->GetCurrentArea() ; + + int formation=core->GetGame()->GetFormation(); + if (pos>=FORMATIONSIZE) pos=FORMATIONSIZE-1; + + // calculate angle + double angle; + double xdiff = src.x - p.x; + double ydiff = src.y - p.y; + if (ydiff == 0) { + if (xdiff > 0) { + angle = M_PI_2; + } else { + angle = -M_PI_2; + } + } else { + angle = atan(xdiff/ydiff); + if (ydiff < 0) angle += M_PI; + } + + // calculate new coordinates by rotating formation around (0,0) + double newx = -formations[formation][pos].x * cos(angle) + formations[formation][pos].y * sin(angle); + double newy = formations[formation][pos].x * sin(angle) + formations[formation][pos].y * cos(angle); + p.x += (int)newx; + p.y += (int)newy; + + if (p.x < 0) p.x = 8; + if (p.y < 0) p.y = 8; + if (p.x > map->GetWidth()*16) p.x = map->GetWidth()*16 - 8; + if (p.y > map->GetHeight()*12) p.y = map->GetHeight()*12 - 8; + + if(map->GetCursor(p) == IE_CURSOR_BLOCKED) { + //we can't get there --> adjust position + p.x/=16; + p.y/=12; + map->AdjustPosition(p); + p.x*=16; + p.y*=12; + } + CreateMovement(actor, p); +} + +void GameControl::Center(unsigned short x, unsigned short y) +{ + Video *video = core->GetVideoDriver(); + Region Viewport = video->GetViewport(); + Viewport.x += x - Viewport.w / 2; + Viewport.y += y - Viewport.h / 2; + core->timer->SetMoveViewPort( Viewport.x, Viewport.y, 0, false ); + video->MoveViewportTo( Viewport.x, Viewport.y ); +} + +// generate an action to do the actual movement +// only PST supports RunToPoint +void GameControl::CreateMovement(Actor *actor, const Point &p) +{ + char Tmp[256]; + + Action *action = NULL; + if (DoubleClick && EnableRunning) { + sprintf( Tmp, "RunToPoint([%d.%d])", p.x, p.y ); + action = GenerateAction( Tmp ); + //if it didn't work don't insist + if (!action) + EnableRunning = false; + } + if (!action) { + sprintf( Tmp, "MoveToPoint([%d.%d])", p.x, p.y ); + action = GenerateAction( Tmp ); + } + + actor->AddAction( action ); + // force action so that we get target recticles immediately + actor->ProcessActions(true); +} + +GameControl::~GameControl(void) +{ + //releasing the viewport of GameControl + core->GetVideoDriver()->SetViewport( 0,0,0,0 ); + if (formations) { + free( formations ); + formations = NULL; + } + delete dialoghandler; + if (DisplayText) { + core->FreeString(DisplayText); + } +} + +//Autosave was triggered by the GUI +void GameControl::AutoSave() +{ + core->GetSaveGameIterator()->CreateSaveGame(0, false); +} + +//QuickSave was triggered by the GUI +//mqs is the 'multiple quick saves' flag +void GameControl::QuickSave() +{ + core->GetSaveGameIterator()->CreateSaveGame(1, mqs == 1); +} + +// ArrowSprite cycles +// 321 +// 4 0 +// 567 + +#define D_LEFT 1 +#define D_UP 2 +#define D_RIGHT 4 +#define D_BOTTOM 8 +// Direction Bits +// 326 +// 1 4 +// 98c + +static const int arrow_orientations[16]={ +// 0 1 2 3 4 5 6 7 8 9 a b c d e f + -1, 4, 2, 3, 0,-1, 1,-1, 6, 5,-1,-1, 7,-1,-1,-1 +}; + +//Draws arrow markers along the edge of the game window +//WARNING:don't use reference for point, because it is altered +void GameControl::DrawArrowMarker(const Region &screen, Point p, const Region &viewport) +{ + Video* video = core->GetVideoDriver(); + + //p.x-=viewport.x; + //p.y-=viewport.y; + ieDword draw = 0; + if (p.xGetScrollCursorSprite(0,0); + + tmp = spr->Width; + //tmp = core->ArrowSprites[0]->Width; + + if (p.x>viewport.x+viewport.w-tmp) { + p.x=viewport.x+viewport.w;//-tmp; + draw |= D_RIGHT; + } + + tmp = spr->Height; + //tmp = core->ArrowSprites[0]->Height; + + if (p.y>viewport.y+viewport.h-tmp) { + p.y=viewport.y+viewport.h;//-tmp; + draw |= D_BOTTOM; + } + if (arrow_orientations[draw]>=0) { + video->BlitGameSprite( core->GetScrollCursorSprite(arrow_orientations[draw], 0), p.x+screen.x, p.y+screen.y, 0, black, NULL); + } +} + +/** Draws the Control on the Output Display */ +void GameControl::Draw(unsigned short x, unsigned short y) +{ + bool update_scripts = !(DialogueFlags & DF_FREEZE_SCRIPTS); + + Game* game = core->GetGame(); + if (!game) + return; + + if (((short) Width) <=0 || ((short) Height) <= 0) { + return; + } + + if (Owner->Visible!=WINDOW_VISIBLE) { + return; + } + + Region screen( x + XPos, y + YPos, Width, Height ); + Map *area = core->GetGame()->GetCurrentArea(); + Video* video = core->GetVideoDriver(); + if (!area) { + video->DrawRect( screen, blue, true ); + return; + } + + Region viewport = video->GetViewport(); + if (moveX || moveY) { + viewport.x += moveX; + viewport.y += moveY; + Point mapsize = area->TMap->GetMapSize(); + if ( viewport.x < 0 )//if we are at top of the map + viewport.x = 0; + else if ( (viewport.x + viewport.w) >= mapsize.x) //if we are at the bottom + viewport.x = mapsize.x - viewport.w; + + if ( viewport.y < 0 ) //if we are at the left of the map + viewport.y = 0; + else if ( (viewport.y + viewport.h ) >= mapsize.y ) //if we are at the right + viewport.y = mapsize.y - viewport.h; + + // override any existing viewport moves which may be in progress + core->timer->SetMoveViewPort( viewport.x, viewport.y, 0, false ); + // move it directly ourselves, since we might be paused + video->MoveViewportTo( viewport.x, viewport.y ); + } + video->DrawRect( screen, black, true ); + + // setup outlines + InfoPoint *i; + unsigned int idx; + for (idx = 0; (i = area->TMap->GetInfoPoint( idx )); idx++) { + i->Highlight = false; + if (overInfoPoint == i && target_mode) { + if (i->VisibleTrap(0)) { + i->outlineColor = green; + i->Highlight = true; + continue; + } + } + if (i->VisibleTrap(DebugFlags & DEBUG_SHOW_INFOPOINTS)) { + i->outlineColor = red; // traps + } else if (DebugFlags & DEBUG_SHOW_INFOPOINTS) { + i->outlineColor = blue; // debug infopoints + } else { + continue; + } + i->Highlight = true; + } + + Door *d; + for (idx = 0; (d = area->TMap->GetDoor( idx )); idx++) { + d->Highlight = false; + if (overDoor == d) { + if (target_mode) { + if (d->Visible() && (d->VisibleTrap(0) || (d->Flags & DOOR_LOCKED))) { + // only highlight targettable doors + d->outlineColor = green; + d->Highlight = true; + continue; + } + } else if (!(d->Flags & DOOR_SECRET)) { + // mouse over, not in target mode, no secret door + d->outlineColor = cyan; + d->Highlight = true; + continue; + } + } + if (d->VisibleTrap(0)) { + d->outlineColor = red; // traps + } else if (d->Flags & DOOR_SECRET) { + if (DebugFlags & DEBUG_SHOW_DOORS || d->Flags & DOOR_FOUND) { + d->outlineColor = magenta; // found hidden door + } else { + // secret door is invisible + continue; + } + } else if (DebugFlags & DEBUG_SHOW_DOORS) { + d->outlineColor = cyan; // debug doors + } else { + continue; + } + d->Highlight = true; + } + + Container *c; + for (idx = 0; (c = area->TMap->GetContainer( idx )); idx++) { + c->Highlight = false; + if (overContainer == c && target_mode) { + if (c->VisibleTrap(0) || (c->Flags & CONT_LOCKED)) { + // only highlight targettable containers + c->outlineColor = green; + c->Highlight = true; + continue; + } + } else if (overContainer == c) { + // mouse over, not in target mode + c->outlineColor = cyan; + c->Highlight = true; + continue; + } + if (c->VisibleTrap(0)) { + c->outlineColor = red; // traps + } else if (DebugFlags & DEBUG_SHOW_CONTAINERS) { + c->outlineColor = cyan; // debug containers + } else { + continue; + } + c->Highlight = true; + } + + //drawmap should be here so it updates fog of war + area->DrawMap( screen ); + game->DrawWeather(screen, update_scripts); + + if (trackerID) { + Actor *actor = area->GetActorByGlobalID(trackerID); + + if (actor) { + Actor **monsters = area->GetAllActorsInRadius(actor->Pos, GA_NO_DEAD, distance); + + int i = 0; + while(monsters[i]) { + Actor *target = monsters[i++]; + if (target->InParty) continue; + if (target->GetStat(IE_NOTRACKING)) continue; + DrawArrowMarker(screen, target->Pos, viewport); + } + delete monsters; + } else { + trackerID = 0; + } + } + + if (ScreenFlags & SF_DISABLEMOUSE) + return; + Point p(lastMouseX, lastMouseY); + video->ConvertToGame( p.x, p.y ); + + // Draw selection rect + if (DrawSelectionRect) { + CalculateSelection( p ); + video->DrawRect( SelectionRect, green, false, true ); + } + + // Show wallpolygons + if (DebugFlags & DEBUG_SHOW_INFOPOINTS) { + + unsigned int count = area->GetWallCount(); + for (unsigned int i = 0; i < count; ++i) { + Wall_Polygon* poly = area->GetWallGroup(i); + if (!poly) continue; + // yellow + Color c; + c.r = 0x7F; + c.g = 0x7F; + c.b = 0; + c.a = 0; + //if polygon is disabled, make it grey + if (poly->wall_flag&WF_DISABLED) { + c.b = 0x7F; + } + + video->DrawPolyline( poly, c, true ); + } + } + + // Draw path + if (drawPath) { + PathNode* node = drawPath; + while (true) { + Point p( ( node-> x*16) + 8, ( node->y*12 ) + 6 ); + if (!node->Parent) { + video->DrawCircle( p.x, p.y, 2, red ); + } else { + short oldX = ( node->Parent-> x*16) + 8, oldY = ( node->Parent->y*12 ) + 6; + video->DrawLine( oldX, oldY, p.x, p.y, green ); + } + if (!node->Next) { + video->DrawCircle( p.x, p.y, 2, green ); + break; + } + node = node->Next; + } + } + + // Draw lightmap + if (DebugFlags & DEBUG_SHOW_LIGHTMAP) { + Sprite2D* spr = area->LightMap->GetSprite2D(); + video->BlitSprite( spr, 0, 0, true ); + video->FreeSprite( spr ); + Region point( p.x / 16, p.y / 12, 2, 2 ); + video->DrawRect( point, red ); + } + + if (core->HasFeature(GF_ONSCREEN_TEXT) && DisplayText) { + core->GetFont(1)->Print(screen, (unsigned char *)DisplayText, core->InfoTextPalette, IE_FONT_ALIGN_CENTER | IE_FONT_ALIGN_MIDDLE, true); + if (update_scripts) { + // just replicating original engine behaviour + if (DisplayTextTime == 0) { + SetDisplayText((char *)NULL, 0); + } else { + DisplayTextTime--; + } + } + } +} + +/** inherited from Control, GameControl doesn't need it */ +int GameControl::SetText(const char* /*string*/, int /*pos*/) +{ + return 0; +} + +/** Key Press Event */ +void GameControl::OnKeyPress(unsigned char Key, unsigned short /*Mod*/) +{ + if (DialogueFlags&DF_IN_DIALOG) { + return; + } + unsigned int i, pc; + Game* game = core->GetGame(); + if (!game) return; + + switch (Key) { + case '0': + game->SelectActor( NULL, false, SELECT_NORMAL ); + i = game->GetPartySize(false)/2+1; + while(i--) { + SelectActor(i, true); + } + break; + case '-': + game->SelectActor( NULL, true, SELECT_NORMAL ); + i = game->GetPartySize(false)/2+1; + while(i--) { + SelectActor(i, false); + } + break; + case '=': + SelectActor(-1); + break; + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + SelectActor(Key-'0'); + break; + case '7': // 1 & 2 + case '8': // 3 & 4 + case '9': // 5 & 6 + game->SelectActor( NULL, false, SELECT_NORMAL ); + i = game->GetPartySize(false); + pc = 2*(Key - '6')-1; + if (pc >= i) { + SelectActor(i, true); + break; + } + SelectActor(pc, true); + SelectActor(pc+1, true); + break; +#ifdef ANDROID + case 'o': + case 'p': + Control::OnKeyPress(Key, 0); + break; + case 'c': // show containers in ANDROID, GEM_ALT is not possible to use + DebugFlags |= DEBUG_SHOW_CONTAINERS; + return; +#endif + default: + core->GetGame()->SetHotKey(toupper(Key)); + break; + } +} + +//Select (or deselect) a new actor (or actors) +void GameControl::SelectActor(int whom, int type) +{ + Game* game = core->GetGame(); + if (whom==-1) { + game->SelectActor( NULL, true, SELECT_NORMAL ); + return; + } + + /* doesn't fall through here */ + Actor* actor = game->FindPC( whom ); + if (!actor) + return; + + if (type==0) { + game->SelectActor( actor, false, SELECT_NORMAL ); + return; + } + if (type==1) { + game->SelectActor( actor, true, SELECT_NORMAL ); + return; + } + + bool was_selected = actor->IsSelected(); + if (game->SelectActor( actor, true, SELECT_REPLACE )) + if (was_selected || (ScreenFlags & SF_ALWAYSCENTER)) { + ScreenFlags |= SF_CENTERONACTOR; + } +} + +//Effect for the ctrl-r cheatkey (resurrect) +static EffectRef heal_ref={"CurrentHPModifier", NULL, -1}; +static EffectRef damage_ref={"Damage", NULL, -1}; + +/** Key Release Event */ +void GameControl::OnKeyRelease(unsigned char Key, unsigned short Mod) +{ + unsigned int i; + Game* game = core->GetGame(); + + if (!game) + return; + + if (DialogueFlags&DF_IN_DIALOG) { + if (Mod) return; + switch(Key) { + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + TextArea *ta = core->GetMessageTextArea(); + if (ta) { + ta->OnKeyPress(Key,Mod); + } + } + break; + } + return; + } + //cheatkeys with ctrl- + if (Mod & GEM_MOD_CTRL) { + if (!core->CheatEnabled()) { + return; + } + Map* area = game->GetCurrentArea( ); + if (!area) + return; + Actor *lastActor = area->GetActorByGlobalID(lastActorID); + Point p(lastMouseX, lastMouseY); + core->GetVideoDriver()->ConvertToGame( p.x, p.y ); + switch (Key) { + case 'f': //toggle full screen mode + core->GetVideoDriver()->ToggleFullscreenMode(); + break; + case 'd': //disarm a trap + if (overInfoPoint) { + overInfoPoint->DetectTrap(256); + } + if (overContainer) { + if (overContainer->Trapped && + !( overContainer->TrapDetected )) { + overContainer->TrapDetected = 1; + } + } + if (overDoor) { + if (overDoor->Trapped && + !( overDoor->TrapDetected )) { + overDoor->TrapDetected = 1; + } + } + break; + case 'l': //play an animation (vvc/bam) over an actor + //the original engine was able to swap through all animations + if (lastActor) { + lastActor->AddAnimation("S056ICBL", 0, 0, 0); + } + break; + + case 'c': //force cast a hardcoded spell + //caster is the last selected actor + //target is the door/actor currently under the pointer + if (game->selected.size() > 0) { + Actor *src = game->selected[0]; + Scriptable *target = lastActor; + if (overDoor) { + target = overDoor; + } + if (target) { + src->CastSpell( TestSpell, target, false ); + if (src->LastTarget) { + src->CastSpellEnd( TestSpell ); + } else { + src->CastSpellPointEnd( TestSpell ); + } + } + } + break; + + case 'b': //draw a path to the target (pathfinder debug) + //You need to select an origin with ctrl-o first + if (drawPath) { + PathNode* nextNode = drawPath->Next; + PathNode* thisNode = drawPath; + while (true) { + delete( thisNode ); + thisNode = nextNode; + if (!thisNode) + break; + nextNode = thisNode->Next; + } + } + drawPath = core->GetGame()->GetCurrentArea()->FindPath( pfs, p, lastActor?lastActor->size:1 ); + + break; + + case 'o': //set up the origin for the pathfinder + // origin + pfs.x = lastMouseX; + pfs.y = lastMouseY; + core->GetVideoDriver()->ConvertToGame( pfs.x, pfs.y ); + break; + case 'a': //switches through the avatar animations + if (lastActor) { + lastActor->GetNextAnimation(); + } + break; + case 's': //switches through the stance animations + if (lastActor) { + lastActor->GetNextStance(); + } + break; + case 'j': //teleports the selected actors + for (i = 0; i < game->selected.size(); i++) { + Actor* actor = game->selected[i]; + MoveBetweenAreasCore(actor, core->GetGame()->CurrentArea, p, -1, true); + } + break; + + case 'm': //prints a debug dump (ctrl-m in the original game too) + if (!lastActor) { + lastActor = area->GetActor( p, GA_DEFAULT); + } + if (!lastActor) { + // ValidTarget never returns immobile targets, making debugging a nightmare + // so if we don't have an actor, we make really really sure by checking manually + unsigned int count = area->GetActorCount(true); + while (count--) { + Actor *actor = area->GetActor(count, true); + if (actor->IsOver(p)) { + actor->DebugDump(); + } + } + } + if (lastActor) { + lastActor->DebugDump(); + break; + } + if (overDoor) { + overDoor->DebugDump(); + break; + } + if (overContainer) { + overContainer->DebugDump(); + break; + } + if (overInfoPoint) { + overInfoPoint->DebugDump(); + break; + } + core->GetGame()->GetCurrentArea()->DebugDump(Mod & GEM_MOD_SHIFT); + break; + case 'v': //marks some of the map visited (random vision distance) + area->ExploreMapChunk( p, rand()%30, 1 ); + break; + case 'w': // consolidates found ground piles under the pointed pc + area->MoveVisibleGroundPiles(p); + break; + case 'x': // shows coordinates on the map + printf( "%s [%d.%d]\n", area->GetScriptName(), p.x, p.y ); + break; + case 'g'://shows loaded areas and other game information + game->DebugDump(); + break; + case 'i'://interact trigger (from the original game) + if (!lastActor) { + lastActor = area->GetActor( p, GA_DEFAULT); + } + if (lastActor && !(lastActor->GetStat(IE_MC_FLAGS)&MC_EXPORTABLE)) { + Actor *target; + int i = game->GetPartySize(true); + if(i<2) break; + i=rand()%i; + do + { + target = game->GetPC(i, true); + if(target==lastActor) continue; + if(target->GetStat(IE_MC_FLAGS)&MC_EXPORTABLE) continue; + + char Tmp[40]; + snprintf(Tmp,sizeof(Tmp),"Interact(\"%s\")",target->GetScriptName() ); + lastActor->AddAction(GenerateAction(Tmp)); + break; + } + while(i--); + } + break; + case 'r'://resurrects actor + if (!lastActor) { + lastActor = area->GetActor( p, GA_DEFAULT); + } + if (lastActor) { + Effect *fx = EffectQueue::CreateEffect(heal_ref, lastActor->GetStat(IE_MAXHITPOINTS), 0x30001, FX_DURATION_INSTANT_PERMANENT); + if (fx) { + core->ApplyEffect(fx, lastActor, lastActor); + } + } + break; + case 't'://advances time + // 7200 (one day) /24 (hours) == 300 + game->AdvanceTime(300*AI_UPDATE_TIME); + //refresh gui here once we got it + break; + + case 'q': //joins actor to the party + if (lastActor && !lastActor->InParty) { + lastActor->ClearActions(); + lastActor->ClearPath(); + char Tmp[40]; + strncpy(Tmp,"JoinParty()",sizeof(Tmp) ); + lastActor->AddAction( GenerateAction(Tmp) ); + } + break; + case 'p': //center on actor + ScreenFlags|=SF_CENTERONACTOR; + ScreenFlags^=SF_ALWAYSCENTER; + break; + case 'k': //kicks out actor + if (lastActor && lastActor->InParty) { + lastActor->ClearActions(); + lastActor->ClearPath(); + char Tmp[40]; + strncpy(Tmp,"LeaveParty()",sizeof(Tmp) ); + lastActor->AddAction( GenerateAction(Tmp) ); + } + break; + case 'y': //kills actor or all enemies + if (Mod & GEM_MOD_SHIFT) { + // mwahaha! + Effect *newfx; + newfx = EffectQueue::CreateEffect(damage_ref, 300, DAMAGE_MAGIC<<16, FX_DURATION_INSTANT_PERMANENT); + Actor *victim; + for (int i = area->GetActorCount(0)-1; i >= 0; i--) { + victim = area->GetActor(i, 0); + if (victim->Modified[IE_EA] == EA_ENEMY) { + core->ApplyEffect(newfx, victim, victim); + } + } + delete newfx; + } else { + if (lastActor) { + //using action so the actor is killed + //correctly (synchronisation) + lastActor->ClearActions(); + lastActor->ClearPath(); + + Effect *newfx; + newfx = EffectQueue::CreateEffect(damage_ref, 300, DAMAGE_MAGIC<<16, FX_DURATION_INSTANT_PERMANENT); + core->ApplyEffect(newfx, lastActor, lastActor); + if (! (lastActor->GetInternalFlag() & IF_REALLYDIED)) { + newfx = EffectQueue::CreateEffect(damage_ref, 300, DAMAGE_ACID<<16, FX_DURATION_INSTANT_PERMANENT); + core->ApplyEffect(newfx, lastActor, lastActor); + newfx = EffectQueue::CreateEffect(damage_ref, 300, DAMAGE_CRUSHING<<16, FX_DURATION_INSTANT_PERMANENT); + core->ApplyEffect(newfx, lastActor, lastActor); + } + delete newfx; + } else if (overContainer) { + overContainer->SetContainerLocked(0); + } else if (overDoor) { + overDoor->SetDoorLocked(0,0); + } + } + break; + case 'z': //shift through the avatar animations backward + if (lastActor) { + lastActor->GetPrevAnimation(); + } + break; + case '1': //change paperdoll armour level + if (! lastActor) + break; + lastActor->NewStat(IE_ARMOR_TYPE,1,MOD_ADDITIVE); + break; + case '4': //show all traps and infopoints + DebugFlags ^= DEBUG_SHOW_INFOPOINTS; + printf("Show traps and infopoints %s\n", DebugFlags & DEBUG_SHOW_INFOPOINTS ? "ON" : "OFF"); + break; + case '6': //show the lightmap + DebugFlags ^= DEBUG_SHOW_LIGHTMAP; + printf("Show lightmap %s\n", DebugFlags & DEBUG_SHOW_LIGHTMAP ? "ON" : "OFF"); + break; + case '7': //toggles fog of war + core->FogOfWar ^= 1; + printf("Show Fog-Of-War: %s\n", core->FogOfWar & 1 ? "ON" : "OFF"); + break; + case '8': //show searchmap over area + core->FogOfWar ^= 2; + printf("Show searchmap %s\n", core->FogOfWar & 2 ? "ON" : "OFF"); + break; + default: + printf( "KeyRelease:%d - %d\n", Key, Mod ); + break; + } + return; //return from cheatkeys + } + switch (Key) { + case 'h': //hard pause + if (DialogueFlags & DF_FREEZE_SCRIPTS) break; + //fallthrough + case ' ': //soft pause + DialogueFlags ^= DF_FREEZE_SCRIPTS; + if (DialogueFlags&DF_FREEZE_SCRIPTS) { + displaymsg->DisplayConstantString(STR_PAUSED,0xff0000); + SetDisplayText(STR_PAUSED, 0); // time 0 = removed instantly on unpause + } else { + displaymsg->DisplayConstantString(STR_UNPAUSED,0xff0000); + } + break; + case 'm': + core->GetGUIScriptEngine()->RunFunction("GUIMA","OpenMapWindow"); + break; + case 'j': + core->GetGUIScriptEngine()->RunFunction("GUIJRNL","OpenJournalWindow"); + break; + case 'i': + core->GetGUIScriptEngine()->RunFunction("GUIINV","OpenInventoryWindow"); + break; + case 'r': + core->GetGUIScriptEngine()->RunFunction("GUIREC","OpenRecordsWindow"); + break; + case 'q': //quicksave + QuickSave(); + break; + case GEM_ALT: //alt key (shows containers) +#ifdef ANDROID + case 'c': // show containers in ANDROID, GEM_ALT is not possible to use +#endif + DebugFlags &= ~DEBUG_SHOW_CONTAINERS; + break; + default: + break; + } +} + +void GameControl::DisplayTooltip() { + Game* game = core->GetGame(); + if (game) { + Map* area = game->GetCurrentArea( ); + if (area) { + Actor *actor = area->GetActorByGlobalID(lastActorID); + if (actor && (actor->GetStat(IE_STATE_ID)&STATE_DEAD || actor->GetInternalFlag()&IF_JUSTDIED)) { + // checking IF_JUSTDIED is kind of horrid, but seems necessary + // no tooltips for dead actors! + actor->SetOver( false ); + lastActorID = 0; + actor = NULL; + } + + if (actor) { + char *name = actor->GetName(-1); + int hp = actor->GetStat(IE_HITPOINTS); + int maxhp = actor->GetStat(IE_MAXHITPOINTS); + + char buffer[100]; + if (!core->TooltipBack) { + // single-line tooltips without background (PS:T) + if (actor->InParty) { + snprintf(buffer, 100, "%s: %d/%d", name, hp, maxhp); + } else { + snprintf(buffer, 100, "%s", name); + } + } else { + // a guess at a neutral check + bool neutral = actor->GetStat(IE_EA) == EA_NEUTRAL; + // test for an injured string being present for this game + int strindex = displaymsg->GetStringReference(STR_UNINJURED); + // normal tooltips + if (actor->InParty) { + // in party: display hp + snprintf(buffer, 100, "%s\n%d/%d", name, hp, maxhp); + } else if (neutral) { + // neutral: display name only + snprintf(buffer, 100, "%s", name); + } else if (strindex == -1) { + // non-neutral, not in party, no injured strings: display hp + snprintf(buffer, 100, "%s\n%d/%d", name, hp, maxhp); + } else { + // non-neutral, not in party: display injured string + int strindex; + char *injuredstring = NULL; + // these boundaries are just a guess + if (hp == maxhp) { + strindex = STR_UNINJURED; + } else if (hp > (maxhp*3)/4) { + strindex = STR_INJURED1; + } else if (hp > maxhp/2) { + strindex = STR_INJURED2; + } else if (hp > maxhp/3) { + strindex = STR_INJURED3; + } else { + strindex = STR_INJURED4; + } + strindex = displaymsg->GetStringReference(strindex); + if (strindex != -1) { + injuredstring = core->GetString(strindex, 0); + } + + if (!injuredstring) { + // eek, where did the string go? + snprintf(buffer, 100, "%s\n%d/%d", name, hp, maxhp); + } else { + snprintf(buffer, 100, "%s\n%s", name, injuredstring); + free(injuredstring); + } + } + } + + Point p = actor->Pos; + core->GetVideoDriver()->ConvertToScreen( p.x, p.y ); + p.x += Owner->XPos + XPos; + p.y += Owner->YPos + YPos; + + // hack to position text above PS:T actors + if (!core->TooltipBack) p.y -= actor->size*50; + + // we should probably cope better with moving actors + SetTooltip(buffer); + core->DisplayTooltip(p.x, p.y, this); + return; + } + } + } + + SetTooltip(NULL); + core->DisplayTooltip(0, 0, NULL); + return; +} + +//returns the appropriate cursor over an active region (trap, infopoint, travel region) +int GameControl::GetCursorOverInfoPoint(InfoPoint *overInfoPoint) const +{ + if (target_mode == TARGET_MODE_PICK) { + if (overInfoPoint->VisibleTrap(0)) { + return IE_CURSOR_TRAP; + } + + return IE_CURSOR_STEALTH|IE_CURSOR_GRAY; + } + // traps always display a walk cursor? + if (overInfoPoint->Type == ST_PROXIMITY) { + return IE_CURSOR_WALK; + } + return overInfoPoint->Cursor; +} + +//returns the appropriate cursor over a door +int GameControl::GetCursorOverDoor(Door *overDoor) const +{ + if (!overDoor->Visible()) { + if (target_mode == TARGET_MODE_NONE) { + return IE_CURSOR_BLOCKED; + } else { + return lastCursor|IE_CURSOR_GRAY; + } + } + if (target_mode == TARGET_MODE_PICK) { + if (overDoor->VisibleTrap(0)) { + return IE_CURSOR_TRAP; + } + if (overDoor->Flags & DOOR_LOCKED) { + return IE_CURSOR_LOCK; + } + + return IE_CURSOR_STEALTH|IE_CURSOR_GRAY; + } + return overDoor->Cursor; +} + +//returns the appropriate cursor over a container (or pile) +int GameControl::GetCursorOverContainer(Container *overContainer) const +{ + if (target_mode == TARGET_MODE_PICK) { + if (overContainer->VisibleTrap(0)) { + return IE_CURSOR_TRAP; + } + if (overContainer->Flags & CONT_LOCKED) { + return IE_CURSOR_LOCK2; + } + + return IE_CURSOR_STEALTH|IE_CURSOR_GRAY; + } + return IE_CURSOR_TAKE; +} + +int GameControl::GetDefaultCursor() const +{ + switch(target_mode) { + case TARGET_MODE_TALK: + return IE_CURSOR_TALK; + case TARGET_MODE_ATTACK: + return IE_CURSOR_ATTACK; + case TARGET_MODE_CAST: + return IE_CURSOR_CAST; + case TARGET_MODE_DEFEND: + return IE_CURSOR_DEFEND; + case TARGET_MODE_PICK: + return IE_CURSOR_PICK; + } + return IE_CURSOR_NORMAL; +} + +/** Mouse Over Event */ +void GameControl::OnMouseOver(unsigned short x, unsigned short y) +{ + if (ScreenFlags & SF_DISABLEMOUSE) { + return; + } + + lastMouseX = x; + lastMouseY = y; + Point p( x,y ); + core->GetVideoDriver()->ConvertToGame( p.x, p.y ); + if (MouseIsDown && ( !DrawSelectionRect )) { + if (( abs( p.x - StartX ) > 5 ) || ( abs( p.y - StartY ) > 5 )) { + DrawSelectionRect = true; + } + } + Game* game = core->GetGame(); + if (!game) return; + Map* area = game->GetCurrentArea( ); + if (!area) return; + int nextCursor = area->GetCursor( p ); + //make the invisible area really invisible + if (nextCursor == IE_CURSOR_INVALID) { + Owner->Cursor = IE_CURSOR_BLOCKED; + lastCursor = IE_CURSOR_BLOCKED; + return; + } + + overInfoPoint = area->TMap->GetInfoPoint( p, true ); + if (overInfoPoint) { + //nextCursor = overInfoPoint->Cursor; + nextCursor = GetCursorOverInfoPoint(overInfoPoint); + } + + if (overDoor) { + overDoor->Highlight = false; + } + if (overContainer) { + overContainer->Highlight = false; + } + Actor *lastActor = area->GetActorByGlobalID(lastActorID); + if (lastActor) { + lastActor->SetOver( false ); + } + + overDoor = area->TMap->GetDoor( p ); + overContainer = area->TMap->GetContainer( p ); + + if (!DrawSelectionRect) { + if (overDoor) { + nextCursor = GetCursorOverDoor(overDoor); + } + + if (overContainer) { + nextCursor = GetCursorOverContainer(overContainer); + } + + Actor *prevActor = lastActor; + lastActor = area->GetActor( p, target_types); + if (lastActor != prevActor) { + // we store prevActor so we can remove the tooltip on actor change + // (maybe we should be checking this and actor movements every frame?) + SetTooltip(NULL); + core->DisplayTooltip(0, 0, this); + } + + if ((target_types & GA_NO_SELF) && lastActor ) { + if (lastActor == core->GetFirstSelectedActor()) { + lastActor=NULL; + } + } + + if (lastActor) { + lastActorID = lastActor->GetGlobalID(); + lastActor->SetOver( true ); + ieDword type = lastActor->GetStat(IE_EA); + if (type >= EA_EVILCUTOFF || type == EA_GOODBUTRED) { + nextCursor = IE_CURSOR_ATTACK; + } else if ( type > EA_CHARMED ) { + nextCursor = IE_CURSOR_TALK; + //don't let the pc to talk to frozen/stoned creatures + ieDword state = lastActor->GetStat(IE_STATE_ID); + if (state & STATE_CANTMOVE) { + nextCursor |= IE_CURSOR_GRAY; + } + } else { + nextCursor = IE_CURSOR_NORMAL; + } + } else { + lastActorID = 0; + } + + if (target_mode == TARGET_MODE_TALK) { + nextCursor = IE_CURSOR_TALK; + if (!lastActor) { + nextCursor |= IE_CURSOR_GRAY; + } else { + //don't let the pc to talk to frozen/stoned creatures + ieDword state = lastActor->GetStat(IE_STATE_ID); + if (state & STATE_CANTMOVE) { + nextCursor |= IE_CURSOR_GRAY; + } + } + } else if (target_mode == TARGET_MODE_ATTACK) { + nextCursor = IE_CURSOR_ATTACK; + if (overDoor) { + if (!overDoor->Visible()) { + nextCursor |= IE_CURSOR_GRAY; + } + } else if (!lastActor && !overContainer) { + nextCursor |= IE_CURSOR_GRAY; + } + } else if (target_mode == TARGET_MODE_CAST) { + nextCursor = IE_CURSOR_CAST; + //point is always valid + if (!(target_types & GA_POINT)) { + if(!lastActor) { + nextCursor |= IE_CURSOR_GRAY; + } + } + } else if (target_mode == TARGET_MODE_DEFEND) { + nextCursor = IE_CURSOR_DEFEND; + if(!lastActor) { + nextCursor |= IE_CURSOR_GRAY; + } + } else if (target_mode == TARGET_MODE_PICK) { + if (lastActor) { + nextCursor = IE_CURSOR_PICK; + } else { + if (!overContainer && !overDoor && !overInfoPoint) { + nextCursor = IE_CURSOR_STEALTH|IE_CURSOR_GRAY; + } + } + goto end_function; + } + + if (lastActor) { + switch (lastActor->GetStat(IE_EA)) { + case EA_EVILCUTOFF: + case EA_GOODCUTOFF: + break; + + case EA_PC: + case EA_FAMILIAR: + case EA_ALLY: + case EA_CONTROLLED: + case EA_CHARMED: + case EA_EVILBUTGREEN: + if (target_types & GA_NO_ENEMY) + nextCursor^=1; + break; + + case EA_ENEMY: + case EA_GOODBUTRED: + if (target_types & GA_NO_ALLY) + nextCursor^=1; + break; + default: + if (!(target_types & GA_NO_NEUTRAL)) + nextCursor^=1; + break; + } + } + } +end_function: + if (lastCursor != nextCursor) { + Owner->Cursor = nextCursor; + lastCursor = (unsigned char) nextCursor; + } +} + +#define SCROLL_BORDER 5 + +/** Global Mouse Move Event */ +void GameControl::OnGlobalMouseMove(unsigned short x, unsigned short y) +{ + if (ScreenFlags & SF_DISABLEMOUSE) { + return; + } + + if (Owner->Visible!=WINDOW_VISIBLE) { + return; + } + + int mousescrollspd = core->GetMouseScrollSpeed(); + + if (x <= SCROLL_BORDER) + moveX = -mousescrollspd; + else { + if (x >= ( core->Width - SCROLL_BORDER )) + moveX = mousescrollspd; + else + moveX = 0; + } + if (y <= SCROLL_BORDER) + moveY = -mousescrollspd; + else { + if (y >= ( core->Height - SCROLL_BORDER )) + moveY = mousescrollspd; + else + moveY = 0; + } + + if (moveX != 0 || moveY != 0) { + scrolling = true; + } else if (scrolling) { + scrolling = false; + + Video* video = core->GetVideoDriver(); + video->SetDragCursor(NULL); + } +} + +void GameControl::UpdateScrolling() { + if (!scrolling) return; + + int mousescrollspd = core->GetMouseScrollSpeed(); // TODO: why check against this value and not +/-? + Video* video = core->GetVideoDriver(); + + if (moveX == mousescrollspd && moveY == 0) { // right + video->SetDragCursor(core->GetScrollCursorSprite(0,numScrollCursor)); + } else if (moveX == mousescrollspd && moveY == -mousescrollspd) { // upper right + video->SetDragCursor(core->GetScrollCursorSprite(1,numScrollCursor)); + } else if (moveX == 0 && moveY == -mousescrollspd) { // up + video->SetDragCursor(core->GetScrollCursorSprite(2,numScrollCursor)); + } else if (moveX == -mousescrollspd && moveY == -mousescrollspd) { // upper left + video->SetDragCursor(core->GetScrollCursorSprite(3,numScrollCursor)); + } else if (moveX == -mousescrollspd && moveY == 0) { // left + video->SetDragCursor(core->GetScrollCursorSprite(4,numScrollCursor)); + } else if (moveX == -mousescrollspd && moveY == mousescrollspd) { // bottom left + video->SetDragCursor(core->GetScrollCursorSprite(5,numScrollCursor)); + } else if (moveX == 0 && moveY == mousescrollspd) { // bottom + video->SetDragCursor(core->GetScrollCursorSprite(6,numScrollCursor)); + } else if (moveX == mousescrollspd && moveY == mousescrollspd) { // bottom right + video->SetDragCursor(core->GetScrollCursorSprite(7,numScrollCursor)); + } + + numScrollCursor = (numScrollCursor+1) % 15; +} + +//generate action code for source actor to try to attack a target +void GameControl::TryToAttack(Actor *source, Actor *tgt) +{ + char Tmp[40]; + + source->ClearPath(); + source->ClearActions(); + strncpy(Tmp,"NIDSpecial3()",sizeof(Tmp) ); + source->AddAction( GenerateActionDirect( Tmp, tgt) ); +} + +//generate action code for source actor to try to defend a target +void GameControl::TryToDefend(Actor *source, Actor *tgt) +{ + char Tmp[40]; + + source->ClearPath(); + source->ClearActions(); + source->SetModal(MS_NONE); + strncpy(Tmp,"NIDSpecial4()",sizeof(Tmp) ); + source->AddAction( GenerateActionDirect( Tmp, tgt) ); +} + +//generate action code for source actor to try to pick pockets of a target +//The -1 flag is a placeholder for dynamic target IDs +void GameControl::TryToPick(Actor *source, Actor *tgt) +{ + char Tmp[40]; + + source->ClearPath(); + source->ClearActions(); + source->SetModal(MS_NONE); + strncpy(Tmp,"PickPockets([-1])", sizeof(Tmp) ); + source->AddAction( GenerateActionDirect( Tmp, tgt) ); +} + +//generate action code for source actor to try to pick a lock/disable trap on a door +void GameControl::TryToPick(Actor *source, Door *tgt) +{ + char Tmp[40]; + + source->ClearPath(); + source->ClearActions(); + source->SetModal(MS_NONE); + if (tgt->Trapped && tgt->TrapDetected) { + snprintf(Tmp, sizeof(Tmp), "RemoveTraps(\"%s\")", tgt->GetScriptName() ); + } else { + snprintf(Tmp, sizeof(Tmp), "PickLock(\"%s\")", tgt->GetScriptName() ); + } + source->AddAction( GenerateAction( Tmp ) ); +} + +//generate action code for source actor to try to pick a lock/disable trap on a container +void GameControl::TryToPick(Actor *source, Container *tgt) +{ + char Tmp[40]; + + source->ClearPath(); + source->ClearActions(); + source->SetModal(MS_NONE); + if (tgt->Trapped && tgt->TrapDetected) { + snprintf(Tmp, sizeof(Tmp), "RemoveTraps(\"%s\")", tgt->GetScriptName() ); + } else { + snprintf(Tmp, sizeof(Tmp), "PickLock(\"%s\")", tgt->GetScriptName() ); + } + source->AddAction( GenerateAction( Tmp ) ); +} + +//generate action code for source actor to try to disable trap (only trap type active regions) +void GameControl::TryToDisarm(Actor *source, InfoPoint *tgt) +{ + if (tgt->Type!=ST_PROXIMITY) return; + + char Tmp[40]; + + source->ClearPath(); + source->ClearActions(); + source->SetModal(MS_NONE); + snprintf(Tmp, sizeof(Tmp), "RemoveTraps(\"%s\")", tgt->GetScriptName() ); + source->AddAction( GenerateAction( Tmp ) ); +} + +//generate action code for source actor to try to force open lock on a door/container +void GameControl::TryToBash(Actor *source, Scriptable *tgt) +{ + char Tmp[40]; + + source->ClearPath(); + source->ClearActions(); + source->SetModal(MS_NONE); + snprintf(Tmp, sizeof(Tmp), "Attack(\"%s\")", tgt->GetScriptName() ); + source->AddAction( GenerateAction( Tmp ) ); +} + +//generate action code for source actor to use item/cast spell on a point +void GameControl::TryToCast(Actor *source, const Point &tgt) +{ + char Tmp[40]; + + if (!spellCount) { + ResetTargetMode(); + return; //not casting or using an own item + } + source->ClearPath(); + source->ClearActions(); + + spellCount--; + if (spellOrItem>=0) { + sprintf(Tmp, "NIDSpecial8()"); + } else { + //using item on target + sprintf(Tmp, "NIDSpecial7()"); + } + Action* action = GenerateAction( Tmp ); + action->pointParameter=tgt; + if (spellOrItem>=0) + { + CREMemorizedSpell *si; + //spell casting at target + si = source->spellbook.GetMemorizedSpell(spellOrItem, spellSlot, spellIndex); + if (!si) + { + ResetTargetMode(); + return; + } + sprintf(action->string0Parameter,"%.8s",si->SpellResRef); + } + else + { + action->int0Parameter=spellSlot; + action->int1Parameter=spellIndex; + } + source->AddAction( action ); + if (!spellCount) { + ResetTargetMode(); + } +} + +//generate action code for source actor to use item/cast spell on another actor +void GameControl::TryToCast(Actor *source, Actor *tgt) +{ + char Tmp[40]; + + if (!spellCount) { + ResetTargetMode(); + return; //not casting or using an own item + } + source->ClearPath(); + source->ClearActions(); + + spellCount--; + if (spellOrItem>=0) { + sprintf(Tmp, "NIDSpecial6()"); + } else { + //using item on target + sprintf(Tmp, "NIDSpecial5()"); + } + Action* action = GenerateActionDirect( Tmp, tgt); + if (spellOrItem>=0) + { + CREMemorizedSpell *si; + //spell casting at target + si = source->spellbook.GetMemorizedSpell(spellOrItem, spellSlot, spellIndex); + if (!si) + { + ResetTargetMode(); + return; + } + sprintf(action->string0Parameter,"%.8s",si->SpellResRef); + } + else + { + action->int0Parameter=spellSlot; + action->int1Parameter=spellIndex; + } + source->AddAction( action ); + if (!spellCount) { + ResetTargetMode(); + } +} + +//generate action code for source actor to use talk to target actor +void GameControl::TryToTalk(Actor *source, Actor *tgt) +{ + char Tmp[40]; + + //Nidspecial1 is just an unused action existing in all games + //(non interactive demo) + //i found no fitting action which would emulate this kind of + //dialog initation + source->ClearPath(); + source->ClearActions(); + source->SetModal(MS_NONE); + strncpy(Tmp,"NIDSpecial1()",sizeof(Tmp) ); + dialoghandler->targetID = tgt->GetGlobalID(); //this is a hack, but not so deadly + source->AddAction( GenerateActionDirect( Tmp, tgt) ); +} + +//generate action code for actor appropriate for the target mode when the target is a container +void GameControl::HandleContainer(Container *container, Actor *actor) +{ + char Tmp[256]; + + if ((target_mode == TARGET_MODE_CAST) && spellCount) { + //we'll get the container back from the coordinates + TryToCast(actor, container->Pos); + //Do not reset target_mode, TryToCast does it for us!! + return; + } + + if (target_mode == TARGET_MODE_ATTACK) { + actor->ClearPath(); + actor->ClearActions(); + snprintf(Tmp, sizeof(Tmp), "BashDoor(\"%s\")", container->GetScriptName()); + actor->AddAction(GenerateAction(Tmp)); + ResetTargetMode(); + return; + } + + if ((target_mode == TARGET_MODE_PICK)) { + TryToPick(actor, container); + ResetTargetMode(); + return; + } + + actor->ClearPath(); + actor->ClearActions(); + strncpy(Tmp,"UseContainer()",sizeof(Tmp) ); + core->SetCurrentContainer( actor, container); + actor->AddAction( GenerateAction( Tmp) ); +} + +//generate action code for actor appropriate for the target mode when the target is a door +void GameControl::HandleDoor(Door *door, Actor *actor) +{ + char Tmp[256]; + + if ((target_mode == TARGET_MODE_CAST) && spellCount) { + //we'll get the door back from the coordinates + Point *p = door->toOpen; + Point *otherp = door->toOpen+1; + if (Distance(*p,actor)>Distance(*otherp,actor)) { + p=otherp; + } + TryToCast(actor, *p); + return; + } + + if (target_mode == TARGET_MODE_ATTACK) { + actor->ClearPath(); + actor->ClearActions(); + snprintf(Tmp, sizeof(Tmp), "BashDoor(\"%s\")", door->GetScriptName()); + actor->AddAction(GenerateAction(Tmp)); + ResetTargetMode(); + return; + } + + if (target_mode == TARGET_MODE_PICK) { + TryToPick(actor, door); + ResetTargetMode(); + return; + } + + actor->ClearPath(); + actor->ClearActions(); + actor->TargetDoor = door->GetGlobalID(); + // internal gemrb toggle door action hack - should we use UseDoor instead? + sprintf( Tmp, "NIDSpecial9()" ); + actor->AddAction( GenerateAction( Tmp) ); +} + +//generate action code for actor appropriate for the target mode when the target is an active region (infopoint, trap or travel) +bool GameControl::HandleActiveRegion(InfoPoint *trap, Actor * actor, Point &p) +{ + if ((target_mode == TARGET_MODE_CAST) && spellCount) { + //we'll get the active region from the coordinates (if needed) + TryToCast(actor, p); + //don't bother with this region further + return true; + } + if ((target_mode == TARGET_MODE_PICK)) { + TryToDisarm(actor, trap); + ResetTargetMode(); + return true; + } + + switch(trap->Type) { + case ST_TRAVEL: + actor->UseExit(true); + return false; + case ST_TRIGGER: + //the importer shouldn't load the script + //if it is unallowed anyway (though + //deactivated scripts could be reactivated) + //only the 'trapped' flag should be honoured + //there. Here we have to check on the + //reset trap and deactivated flags + if (trap->Scripts[0]) { + if (!(trap->Flags&TRAP_DEACTIVATED) ) { + trap->LastTriggerObject = trap->LastTrigger = actor->GetGlobalID(); + trap->ImmediateEvent(); + //directly feeding the event, even if there are actions in the queue + trap->Scripts[0]->Update(); + trap->ProcessActions(true); + //if reset trap flag not set, deactivate it + //hmm, better not, info triggers don't deactivate themselves on click + //if (!(trap->Flags&TRAP_RESET)) { + // trap->Flags|=TRAP_DEACTIVATED; + //} + } + } else { + if (trap->overHeadText) { + if (trap->textDisplaying != 1) { + trap->textDisplaying = 1; + trap->timeStartDisplaying = core->GetGame()->Ticks; + DisplayString( trap ); + } + } + } + if (trap->Flags&TRAP_USEPOINT) { + //overriding the target point + p = trap->UsePoint; + return false; + } + return true; + default:; + } + return false; +} +/** Mouse Button Down */ +void GameControl::OnMouseDown(unsigned short x, unsigned short y, unsigned short Button, + unsigned short /*Mod*/) +{ + if (ScreenFlags&SF_DISABLEMOUSE) + return; + + short px=x; + short py=y; + DoubleClick = false; + switch(Button) + { + case GEM_MB_SCRLUP: + OnSpecialKeyPress(GEM_UP); + break; + case GEM_MB_SCRLDOWN: + OnSpecialKeyPress(GEM_DOWN); + break; + case GEM_MB_ACTION|GEM_MB_DOUBLECLICK: + DoubleClick = true; + case GEM_MB_ACTION: + core->GetVideoDriver()->ConvertToGame( px, py ); + MouseIsDown = true; + SelectionRect.x = px; + SelectionRect.y = py; + StartX = px; + StartY = py; + SelectionRect.w = 0; + SelectionRect.h = 0; + } +} + +/** Mouse Button Up */ +void GameControl::OnMouseUp(unsigned short x, unsigned short y, unsigned short Button, + unsigned short /*Mod*/) +{ + unsigned int i; + char Tmp[256]; + + if (ScreenFlags & SF_DISABLEMOUSE) { + return; + } + //heh, i found no better place + core->CloseCurrentContainer(); + + MouseIsDown = false; + Point p(x,y); + core->GetVideoDriver()->ConvertToGame( p.x, p.y ); + Game* game = core->GetGame(); + if (!game) return; + Map* area = game->GetCurrentArea( ); + if (!area) return; + + if (DrawSelectionRect) { + Actor** ab; + unsigned int count = area->GetActorInRect( ab, SelectionRect,true ); + if (count != 0) { + for (i = 0; i < highlighted.size(); i++) + highlighted[i]->SetOver( false ); + highlighted.clear(); + game->SelectActor( NULL, false, SELECT_NORMAL ); + for (i = 0; i < count; i++) { + // FIXME: should call handler only once + game->SelectActor( ab[i], true, SELECT_NORMAL ); + } + } + free( ab ); + DrawSelectionRect = false; + return; + } + + //hidden actors are not selectable by clicking on them + Actor* actor = area->GetActor( p, GA_DEFAULT | GA_NO_DEAD | GA_NO_HIDDEN); + if (Button == GEM_MB_MENU) { + if (actor) { + //from GSUtils + DisplayStringCore(actor, VB_SELECT+core->Roll(1,3,-1), DS_CONST|DS_CONSOLE); + return; + } + core->GetDictionary()->SetAt( "MenuX", x ); + core->GetDictionary()->SetAt( "MenuY", y ); + core->GetGUIScriptEngine()->RunFunction( "GUICommon", "OpenFloatMenuWindow" ); + return; + } + + if (Button != GEM_MB_ACTION) { + return; + } + + if (!game->selected.size()) { + //TODO: this is a hack, we need some restructuring here + //handling the special case when no one was selected, and + //the player clicks on a partymember + if (actor && (actor->GetStat(IE_EA)GetFirstSelectedPC(false); + if (!pc) { + //this could be a non-PC + pc = game->selected[0]; + } + if (!actor) { + //add a check if you don't want some random monster handle doors and such + if (overDoor) { + HandleDoor(overDoor, pc); + return; + } + if (overContainer) { + HandleContainer(overContainer, pc); + return; + } + if (overInfoPoint) { + if (HandleActiveRegion(overInfoPoint, pc, p)) { + return; + } + } + + //just a single actor, no formation + if (game->selected.size()==1) { + //the player is using an item or spell on the ground + if ((target_mode == TARGET_MODE_CAST) && spellCount) { + if (target_types & GA_POINT) { + TryToCast(pc, p); + } + return; + } + + pc->ClearPath(); + pc->ClearActions(); + CreateMovement(pc, p); + if (DoubleClick) Center(x,y); + //p is a searchmap travel region + if ( pc->GetCurrentArea()->GetCursor(p) == IE_CURSOR_TRAVEL) { + sprintf( Tmp, "NIDSpecial2()" ); + pc->AddAction( GenerateAction( Tmp) ); + } + return; + } + + // construct a sorted party + // TODO: this is still ugly, help? + std::vector party; + // first, from the actual party + int max = game->GetPartySize(false); + for(int idx = 1; idx<=max; idx++) { + Actor *act = game->FindPC(idx); + if(act->IsSelected()) { + party.push_back(act); + } + } + for (i = 0; i < game->selected.size(); i++) { + Actor *act = game->selected[i]; + if (!act->InParty) { + party.push_back(act); + } + } + + //party formation movement + Point src = party[0]->Pos; + for(i = 0; i < party.size(); i++) { + actor = party[i]; + actor->ClearPath(); + actor->ClearActions(); + MoveToPointFormation(actor, i, src, p); + } + if (DoubleClick) Center(x,y); + + //p is a searchmap travel region + if ( party[0]->GetCurrentArea()->GetCursor(p) == IE_CURSOR_TRAVEL) { + sprintf( Tmp, "NIDSpecial2()" ); + party[0]->AddAction( GenerateAction( Tmp) ); + } + return; + } + if (!actor) return; + + //we got an actor past this point + if (target_mode == TARGET_MODE_NONE) { + DisplayStringCore(actor, VB_SELECT+core->Roll(1,3,-1), DS_CONST|DS_CONSOLE); + } + + PerformActionOn(actor); +} + +void GameControl::PerformActionOn(Actor *actor) +{ + Game* game = core->GetGame(); + unsigned int i; + + //determining the type of the clicked actor + ieDword type; + + type = actor->GetStat(IE_EA); + if ( type >= EA_EVILCUTOFF || type == EA_GOODBUTRED ) { + type = ACT_ATTACK; //hostile + } else if ( type > EA_CHARMED ) { + type = ACT_TALK; //neutral + } else { + type = ACT_NONE; //party + } + + if (target_mode == TARGET_MODE_ATTACK) { + type = ACT_ATTACK; + } else if (target_mode == TARGET_MODE_TALK) { + type = ACT_TALK; + } else if (target_mode == TARGET_MODE_CAST) { + type = ACT_CAST; + } else if (target_mode == TARGET_MODE_DEFEND) { + type = ACT_DEFEND; + } else if (target_mode == TARGET_MODE_PICK) { + type = ACT_THIEVING; + } + + if (type != ACT_NONE) { + if(!actor->ValidTarget(target_types)) { + return; + } + } + + //we shouldn't zero this for two reasons in case of spell or item + //1. there could be multiple targets + //2. the target mode is important + if (!(target_mode == TARGET_MODE_CAST) || !spellCount) { + ResetTargetMode(); + } + + switch (type) { + case ACT_NONE: //none + if (!actor->ValidTarget(GA_SELECT)) { + return; + } + + if (actor->InParty) + SelectActor( actor->InParty ); + else if (actor->GetStat(IE_EA) <= EA_CHARMED) { + /*let's select charmed/summoned creatures + EA_CHARMED is the maximum value known atm*/ + core->GetGame()->SelectActor(actor, true, SELECT_REPLACE); + } + break; + case ACT_TALK: + if (!actor->ValidTarget(GA_TALK)) { + return; + } + + //talk (first selected talks) + if (game->selected.size()) { + //if we are in PST modify this to NO! + Actor *source; + if (core->HasFeature(GF_PROTAGONIST_TALKS) ) { + source = game->GetPC(0, false); //protagonist + } else { + source = core->GetFirstSelectedPC(false); + } + // only party members can start conversations + if (source) { + TryToTalk(source, actor); + } + } + break; + case ACT_ATTACK: + //all of them attacks the red circled actor + for(i=0;iselected.size();i++) { + TryToAttack(game->selected[i], actor); + } + break; + case ACT_CAST: //cast on target or use item on target + if (game->selected.size()==1) { + Actor *source; + source = core->GetFirstSelectedActor(); + if(source) { + TryToCast(source, actor); + } + } + break; + case ACT_DEFEND: + for(i=0;iselected.size();i++) { + TryToDefend(game->selected[i], actor); + } + break; + case ACT_THIEVING: + if (game->selected.size()==1) { + Actor *source; + source = core->GetFirstSelectedActor(); + if(source) { + TryToPick(source, actor); + } + } + break; + } +} + +//sets target mode, and resets the cursor +void GameControl::SetTargetMode(int mode) { + int x,y; + + target_mode = mode; + //This hack is to refresh the mouse cursor + core->GetVideoDriver()->GetMousePos(x,y); + //calling into the videodriver to set the mouseposition won't work + core->GetEventMgr()->MouseMove(x,y); +} + +void GameControl::ResetTargetMode() { + SetTargetMode(TARGET_MODE_NONE); + target_types = GA_NO_DEAD|GA_NO_HIDDEN; +} + +/** Special Key Press */ +void GameControl::OnSpecialKeyPress(unsigned char Key) +{ + if (DialogueFlags&DF_IN_DIALOG) { + switch(Key) { + case GEM_RETURN: + //simulating the continue/end button pressed + core->GetGUIScriptEngine()->RunFunction("GUIWORLD", "CloseContinueWindow"); + break; + } + return; //don't accept keys in dialog + } + Region Viewport = core->GetVideoDriver()->GetViewport(); + Game *game = core->GetGame(); + if (!game) return; + Map *map = game->GetCurrentArea(); + if (!map) return; + + Point mapsize = map->TMap->GetMapSize(); + int partysize = game->GetPartySize(false); + int pm; + char tmpstr[10]; + + switch (Key) { + case GEM_LEFT: + if (Viewport.x > 63) + Viewport.x -= 64; + else + Viewport.x = 0; + break; + case GEM_UP: + if (Viewport.y > 63) + Viewport.y -= 64; + else + Viewport.y = 0; + break; + case GEM_DOWN: + if (Viewport.y + Viewport.h + 64 < mapsize.y) + Viewport.y += 64; + else { + Viewport.y = mapsize.y - Viewport.h; + if (Viewport.y<0) Viewport.y=0; + } + break; + case GEM_RIGHT: + if (Viewport.x + Viewport.w + 64 < mapsize.x) + Viewport.x += 64; + else { + Viewport.x = mapsize.x - Viewport.w; + if (Viewport.x<0) Viewport.x=0; + } + break; + case GEM_ALT: + DebugFlags |= DEBUG_SHOW_CONTAINERS; + return; + case GEM_TAB: + // show partymember hp/maxhp as overhead text + for (pm=0; pm < partysize; pm++) { + Actor *pc = game->GetPC(pm, true); + if (!pc) continue; + //sucks but this is set in different places + if (pc->GetStat(IE_MC_FLAGS) & MC_HIDE_HP) continue; + if (pc->GetStat(IE_EXTSTATE_ID) & EXTSTATE_NO_HP) continue; + memset(tmpstr, 0, 10); + snprintf(tmpstr, 10, "%d/%d", pc->Modified[IE_HITPOINTS], pc->Modified[IE_MAXHITPOINTS]); + pc->DisplayHeadText(strdup(tmpstr)); + } + return; + case GEM_MOUSEOUT: + moveX = 0; + moveY = 0; + return; + case GEM_ESCAPE: + core->GetGUIScriptEngine()->RunFunction("GUICommonWindows", "EmptyControls"); + core->SetEventFlag(EF_ACTION); + return; + case GEM_PGUP: + core->GetGUIScriptEngine()->RunFunction("CommonWindow","OnIncreaseSize"); + return; + case GEM_PGDOWN: + core->GetGUIScriptEngine()->RunFunction("CommonWindow","OnDecreaseSize"); + return; + default: + return; + } + if (ScreenFlags & SF_LOCKSCROLL) { + moveX = 0; + moveY = 0; + } + else { + // override any existing viewport moves which may be in progress + core->timer->SetMoveViewPort( Viewport.x, Viewport.y, 0, false ); + // move it directly ourselves, since we might be paused + core->GetVideoDriver()->MoveViewportTo( Viewport.x, Viewport.y ); + } +} + +void GameControl::CalculateSelection(const Point &p) +{ + unsigned int i; + + Game* game = core->GetGame(); + Map* area = game->GetCurrentArea( ); + if (DrawSelectionRect) { + if (p.x < StartX) { + SelectionRect.w = StartX - p.x; + SelectionRect.x = p.x; + } else { + SelectionRect.x = StartX; + SelectionRect.w = p.x - StartX; + } + if (p.y < StartY) { + SelectionRect.h = StartY - p.y; + SelectionRect.y = p.y; + } else { + SelectionRect.y = StartY; + SelectionRect.h = p.y - StartY; + } + Actor** ab; + unsigned int count = area->GetActorInRect( ab, SelectionRect,true ); + for (i = 0; i < highlighted.size(); i++) + highlighted[i]->SetOver( false ); + highlighted.clear(); + if (count != 0) { + for (i = 0; i < count; i++) { + ab[i]->SetOver( true ); + highlighted.push_back( ab[i] ); + } + } + free( ab ); + } else { + Actor* actor = area->GetActor( p, GA_DEFAULT | GA_SELECT | GA_NO_DEAD | GA_NO_ENEMY); + SetLastActor( actor, area->GetActorByGlobalID(lastActorID) ); +/* + Actor *lastActor = area->GetActorByGlobalID(lastActorID); + if (lastActor) + lastActor->SetOver( false ); + if (!actor) { + lastActorID = 0; + } else { + lastActorID = actor->globalID; + actor->SetOver( true ); + } +*/ + } +} + +void GameControl::SetLastActor(Actor *actor, Actor *prevActor) +{ + if (prevActor) + prevActor->SetOver( false ); + if (!actor) { + lastActorID = 0; + } else { + lastActorID = actor->GetGlobalID(); + actor->SetOver( true ); + } +} + +void GameControl::SetCutSceneMode(bool active) +{ + if (active) { + ScreenFlags |= (SF_DISABLEMOUSE | SF_LOCKSCROLL | SF_CUTSCENE); + moveX = 0; + moveY = 0; + } else { + ScreenFlags &= ~(SF_DISABLEMOUSE | SF_LOCKSCROLL | SF_CUTSCENE); + } +} + +//Change game window geometries when a new window gets deactivated +void GameControl::HandleWindowHide(const char *WindowName, const char *WindowPosition) +{ + Variables* dict = core->GetDictionary(); + ieDword index; + + if (dict->Lookup( WindowName, index )) { + if (index != (ieDword) -1) { + Window* w = core->GetWindow( (unsigned short) index ); + if (w) { + core->SetVisible( (unsigned short) index, WINDOW_INVISIBLE ); + if (dict->Lookup( WindowPosition, index )) { + ResizeDel( w, index ); + } + return; + } + printMessage("GameControl", "Invalid Window Index: ", LIGHT_RED); + printf("%s:%u\n",WindowName, index); + } + } +} + +//Hide all other windows on the GUI (gamecontrol is not hidden by this) +int GameControl::HideGUI() +{ + //hidegui is in effect + if (!(ScreenFlags&SF_GUIENABLED) ) { + return 0; + } + //no gamecontrol visible + if (Owner->Visible == WINDOW_INVISIBLE ) { + return 0; + } + ScreenFlags &=~SF_GUIENABLED; + HandleWindowHide("PortraitWindow", "PortraitPosition"); + HandleWindowHide("OtherWindow", "OtherPosition"); + HandleWindowHide("TopWindow", "TopPosition"); + HandleWindowHide("OptionsWindow", "OptionsPosition"); + HandleWindowHide("MessageWindow", "MessagePosition"); + HandleWindowHide("ActionsWindow", "ActionsPosition"); + //FloatWindow doesn't affect gamecontrol, so it is special + Variables* dict = core->GetDictionary(); + ieDword index; + + if (dict->Lookup( "FloatWindow", index )) { + if (index != (ieDword) -1) { + core->SetVisible( (unsigned short) index, WINDOW_INVISIBLE ); + } + } + core->GetVideoDriver()->SetViewport( Owner->XPos, Owner->YPos, Width, Height ); + return 1; +} + +//Change game window geometries when a new window gets activated +void GameControl::HandleWindowReveal(const char *WindowName, const char *WindowPosition) +{ + Variables* dict = core->GetDictionary(); + ieDword index; + + if (dict->Lookup( WindowName, index )) { + if (index != (ieDword) -1) { + Window* w = core->GetWindow( (unsigned short) index ); + if (w) { + core->SetVisible( (unsigned short) index, WINDOW_VISIBLE ); + if (dict->Lookup( WindowPosition, index )) { + ResizeAdd( w, index ); + } + return; + } + printMessage("GameControl", "Invalid Window Index ", LIGHT_RED); + printf("%s:%u\n",WindowName, index); + } + } +} + +//Reveal all windows on the GUI (including this one) +int GameControl::UnhideGUI() +{ + if (ScreenFlags&SF_GUIENABLED) { + return 0; + } + + ScreenFlags |= SF_GUIENABLED; + // Unhide the gamecontrol window + core->SetVisible( 0, WINDOW_VISIBLE ); + + HandleWindowReveal("ActionsWindow", "ActionsPosition"); + HandleWindowReveal("MessageWindow", "MessagePosition"); + HandleWindowReveal("OptionsWindow", "OptionsPosition"); + HandleWindowReveal("TopWindow", "TopPosition"); + HandleWindowReveal("OtherWindow", "OtherPosition"); + HandleWindowReveal("PortraitWindow", "PortraitPosition"); + //the floatwindow is a special case + Variables* dict = core->GetDictionary(); + ieDword index; + + if (dict->Lookup( "FloatWindow", index )) { + if (index != (ieDword) -1) { + Window* fw = core->GetWindow( (unsigned short) index ); + if (fw) { + core->SetVisible( (unsigned short) index, WINDOW_VISIBLE ); + fw->Flags |=WF_FLOAT; + core->SetOnTop( index ); + } + } + } + core->GetVideoDriver()->SetViewport( Owner->XPos, Owner->YPos, Width, Height ); + return 1; +} + +//a window got removed, so the GameControl gets enlarged +void GameControl::ResizeDel(Window* win, int type) +{ + switch (type) { + case 0: //Left + if (LeftCount!=1) { + printMessage("GameControl","More than one left window!\n",LIGHT_RED); + } + LeftCount--; + if (!LeftCount) { + Owner->XPos -= win->Width; + Owner->Width += win->Width; + Width = Owner->Width; + } + break; + + case 1: //Bottom + if (BottomCount!=1) { + printMessage("GameControl","More than one bottom window!\n",LIGHT_RED); + } + BottomCount--; + if (!BottomCount) { + Owner->Height += win->Height; + Height = Owner->Height; + } + break; + + case 2: //Right + if (RightCount!=1) { + printMessage("GameControl","More than one right window!\n",LIGHT_RED); + } + RightCount--; + if (!RightCount) { + Owner->Width += win->Width; + Width = Owner->Width; + } + break; + + case 3: //Top + if (TopCount!=1) { + printMessage("GameControl","More than one top window!\n",LIGHT_RED); + } + TopCount--; + if (!TopCount) { + Owner->YPos -= win->Height; + Owner->Height += win->Height; + Height = Owner->Height; + } + break; + + case 4: //BottomAdded + BottomCount--; + Owner->Height += win->Height; + Height = Owner->Height; + break; + case 5: //Inactivating + BottomCount--; + Owner->Height += win->Height; + Height = Owner->Height; + break; + } +} + +//a window got added, so the GameControl gets shrunk +//Owner is the GameControl's window +//GameControl is the only control on that window +void GameControl::ResizeAdd(Window* win, int type) +{ + switch (type) { + case 0: //Left + LeftCount++; + if (LeftCount == 1) { + Owner->XPos += win->Width; + Owner->Width -= win->Width; + Width = Owner->Width; + } + break; + + case 1: //Bottom + BottomCount++; + if (BottomCount == 1) { + Owner->Height -= win->Height; + Height = Owner->Height; + } + break; + + case 2: //Right + RightCount++; + if (RightCount == 1) { + Owner->Width -= win->Width; + Width = Owner->Width; + } + break; + + case 3: //Top + TopCount++; + if (TopCount == 1) { + Owner->YPos += win->Height; + Owner->Height -= win->Height; + Height = Owner->Height; + } + break; + + case 4: //BottomAdded + BottomCount++; + Owner->Height -= win->Height; + Height = Owner->Height; + break; + + case 5: //Inactivating + BottomCount++; + Owner->Height -= win->Height; + Height = 0; + } +} + +//Create an overhead text over an arbitrary point +void GameControl::DisplayString(const Point &p, const char *Text) +{ + Scriptable* scr = new Scriptable( ST_TRIGGER ); + scr->overHeadText = (char *) Text; + scr->textDisplaying = 1; + scr->timeStartDisplaying = 0; + scr->Pos = p; +} + +//Create an overhead text over a scriptable target +//Multiple texts are possible, as this code copies the text to a new object +void GameControl::DisplayString(Scriptable* target) +{ + Scriptable* scr = new Scriptable( ST_TRIGGER ); + scr->overHeadText = strdup( target->overHeadText ); +/* strdup should work here, we use it elsewhere + size_t len = strlen( target->overHeadText ) + 1; + scr->overHeadText = ( char * ) malloc( len ); + strcpy( scr->overHeadText, target->overHeadText ); +*/ + scr->textDisplaying = 1; + scr->timeStartDisplaying = target->timeStartDisplaying; + scr->Pos = target->Pos; +} + +/** changes displayed map to the currently selected PC */ +void GameControl::ChangeMap(Actor *pc, bool forced) +{ + //swap in the area of the actor + Game* game = core->GetGame(); + if (forced || (stricmp( pc->Area, game->CurrentArea) != 0) ) { + dialoghandler->EndDialog(); + overInfoPoint = NULL; + overContainer = NULL; + overDoor = NULL; + /*this is loadmap, because we need the index, not the pointer*/ + char *areaname = game->CurrentArea; + if (pc) { + areaname = pc->Area; + } + game->GetMap( areaname, true ); + ScreenFlags|=SF_CENTERONACTOR; + } + //center on first selected actor + Video *video = core->GetVideoDriver(); + Region vp = video->GetViewport(); + if (ScreenFlags&SF_CENTERONACTOR) { + core->timer->SetMoveViewPort( pc->Pos.x, pc->Pos.y, 0, true ); + video->MoveViewportTo( pc->Pos.x-vp.w/2, pc->Pos.y-vp.h/2 ); + ScreenFlags&=~SF_CENTERONACTOR; + } +} + +void GameControl::SetScreenFlags(int value, int mode) +{ + switch(mode) { + case BM_OR: ScreenFlags|=value; break; + case BM_NAND: ScreenFlags&=~value; break; + case BM_SET: ScreenFlags=value; break; + case BM_AND: ScreenFlags&=value; break; + case BM_XOR: ScreenFlags^=value; break; + } +} + +void GameControl::SetDialogueFlags(int value, int mode) +{ + switch(mode) { + case BM_OR: DialogueFlags|=value; break; + case BM_NAND: DialogueFlags&=~value; break; + case BM_SET: DialogueFlags=value; break; + case BM_AND: DialogueFlags&=value; break; + case BM_XOR: DialogueFlags^=value; break; + } +} + +//copies a screenshot into a sprite +Sprite2D* GameControl::GetScreenshot(bool show_gui) +{ + Sprite2D* screenshot; + if (show_gui) { + screenshot = core->GetVideoDriver()->GetScreenshot( Region( 0, 0, 0, 0) ); + } else { + int hf = HideGUI (); + Draw (0, 0); + screenshot = core->GetVideoDriver()->GetScreenshot( Region( 0, 0, 0, 0 ) ); + if (hf) { + UnhideGUI (); + } + core->DrawWindows (); + } + + return screenshot; +} + +//copies a downscaled screenshot into a sprite for save game preview +Sprite2D* GameControl::GetPreview() +{ + // We get preview by first taking a screenshot of size 640x405, + // centered in the display. This is to get a decent picture for + // higher screen resolutions. + // FIXME: how do orig games solve that? + Video* video = core->GetVideoDriver(); + int w = video->GetWidth(); + int h = video->GetHeight(); + int x = (w - 640) / 2; + int y = (h - 405) / 2; + + if (x < 0) { + x = 0; + } else { + w = 640; + } + + if (y < 0) { + y = 0; + } else { + h = 405; + } + + if (!x) + y = 0; + + int hf = HideGUI (); + signed char v = Owner->Visible; + Owner->Visible = WINDOW_VISIBLE; + Draw (0, 0); + Owner->Visible = v; + Sprite2D *screenshot = video->GetScreenshot( Region(x, y, w, h) ); + if (hf) { + UnhideGUI (); + } + core->DrawWindows(); + + Sprite2D* preview = video->SpriteScaleDown ( screenshot, 5 ); + video->FreeSprite( screenshot ); + return preview; +} + + +/** + * Returns PC portrait for a currently running game + */ +Sprite2D* GameControl::GetPortraitPreview(int pcslot) +{ + /** Portrait shrink ratio */ + // FIXME: this is just a random PST specific trait + // you can make it less random with a new feature bit + int ratio = (core->HasFeature( GF_ONE_BYTE_ANIMID )) ? 1 : 2; + + Video *video = core->GetVideoDriver(); + + Actor *actor = core->GetGame()->GetPC( pcslot, false ); + if (! actor) { + return NULL; + } + ResourceHolder im(actor->GetPortrait(true)); + if (! im) { + return NULL; + } + + Sprite2D* img = im->GetSprite2D(); + + if (ratio == 1) + return img; + + Sprite2D* img_scaled = video->SpriteScaleDown( img, ratio ); + video->FreeSprite( img ); + + return img_scaled; +} + +Actor *GameControl::GetActorByGlobalID(ieDword globalID) +{ + if (!globalID) + return NULL; + Game* game = core->GetGame(); + if (!game) + return NULL; + + Map* area = game->GetCurrentArea( ); + if (!area) + return NULL; + return + area->GetActorByGlobalID(globalID); +} + +Actor *GameControl::GetLastActor() +{ + return GetActorByGlobalID(lastActorID); +} + +//Set up an item use which needs targeting +//Slot is an inventory slot +//header is the used item extended header +//u is the user +//target type is a bunch of GetActor flags that alter valid targets +//cnt is the number of different targets (usually 1) +void GameControl::SetupItemUse(int slot, int header, Actor *u, int targettype, int cnt) +{ + spellOrItem = -1; + spellUser = u; + spellSlot = slot; + spellIndex = header; + //item use also uses the casting icon, this might be changed in some custom game? + SetTargetMode(TARGET_MODE_CAST); + target_types = targettype; + spellCount = cnt; +} + +//Set up spell casting which needs targeting +//type is the spell's type +//level is the caster level +//idx is the spell's number +//u is the caster +//target type is a bunch of GetActor flags that alter valid targets +//cnt is the number of different targets (usually 1) +void GameControl::SetupCasting(int type, int level, int idx, Actor *u, int targettype, int cnt) +{ + spellOrItem = type; + spellUser = u; + spellSlot = level; + spellIndex = idx; + SetTargetMode(TARGET_MODE_CAST); + target_types = targettype; + spellCount = cnt; +} + +//another method inherited from Control which has no use here +bool GameControl::SetEvent(int /*eventType*/, EventHandler /*handler*/) +{ + return false; +} + +void GameControl::SetDisplayText(char *text, unsigned int time) +{ + if (DisplayText) { + core->FreeString(DisplayText); + } + DisplayTextTime = time; + DisplayText = text; +} + +void GameControl::SetDisplayText(ieStrRef text, unsigned int time) +{ + SetDisplayText(core->GetString(displaymsg->GetStringReference(text), 0), time); +} + diff --git a/project/jni/application/gemrb/src/core/GUI/GameControl.h b/project/jni/application/gemrb/src/core/GUI/GameControl.h new file mode 100644 index 000000000..8faa5e5ad --- /dev/null +++ b/project/jni/application/gemrb/src/core/GUI/GameControl.h @@ -0,0 +1,252 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +/** + * @file GameControl.h + * Declares GameControl widget which is responsible for displaying areas, + * interacting with PCs, NPCs and the rest of the game world. + * @author The GemRB Project + */ + +#ifndef GAMECONTROL_H +#define GAMECONTROL_H + +#include "GUI/Control.h" + +#include "exports.h" + +#include "Dialog.h" +#include "Interface.h" +#include "Map.h" + +class GameControl; +class Window; +class DialogHandler; + +//dialog flags +#define DF_IN_DIALOG 1 +#define DF_TALKCOUNT 2 +#define DF_UNBREAKABLE 4 +#define DF_FREEZE_SCRIPTS 8 +#define DF_INTERACT 16 +#define DF_IN_CONTAINER 32 +#define DF_OPENCONTINUEWINDOW 64 +#define DF_OPENENDWINDOW 128 + +//screen flags +// !!! Keep these synchronized with GUIDefines.py !!! +#define SF_DISABLEMOUSE 1 //no mouse cursor +#define SF_CENTERONACTOR 2 // +#define SF_ALWAYSCENTER 4 +#define SF_GUIENABLED 8 // +#define SF_LOCKSCROLL 16 //don't scroll +#define SF_CUTSCENE 32 //don't push new actions onto the action queue +#define SF_TRACKING 64 //draw blue arrows on the edge for creatures + +// target modes and types +// !!! Keep these synchronized with GUIDefines.py !!! +#define TARGET_MODE_NONE 0 +#define TARGET_MODE_TALK 1 +#define TARGET_MODE_ATTACK 2 +#define TARGET_MODE_CAST 3 +#define TARGET_MODE_DEFEND 4 +#define TARGET_MODE_PICK 5 + +/* +#define TARGET_SELECT 16 +#define TARGET_NO_DEAD 32 +#define TARGET_POINT 64 +#define TARGET_NO_HIDDEN 128 +#define TARGET_TYPE_NONE 0x000 +#define TARGET_NO_ALLY 0x100 //0x100 +#define TARGET_NO_ENEMY 0x200 //0x200 +#define TARGET_NO_NEUTRAL 0x400 +#define TARGET_NO_SELF 0x800 +#define TARGET_TYPE_ALL 0 //(TARGET_TYPE_ALLY | TARGET_TYPE_ENEMY | TARGET_TYPE_NEUTRAL) +*/ + +/** + * @class GameControl + * Widget displaying areas, where most of the game 'happens'. + * It allows for interacting with PCs, NPCs and the rest of the world. + * It's also a very core part of GemRB, as some processes are driven from it. + * It's always assigned Control index 0. + */ + +class GEM_EXPORT GameControl : public Control { +public: + GameControl(void); + ~GameControl(void); +public: + /** Draws the Control on the Output Display */ + void Draw(unsigned short x, unsigned short y); + /** Sets the Text of the current control */ + int SetText(const char* string, int pos = 0); + /** Sets multiple quicksaves flag*/ + static void MultipleQuickSaves(int arg); + void SetTracker(Actor *actor, ieDword dist); +private: + ieDword lastActorID; + ieDword trackerID; + ieDword distance; //tracking distance + std::vector< Actor*> highlighted; + bool DrawSelectionRect; + bool MouseIsDown; + bool DoubleClick; + Region SelectionRect; + short StartX, StartY; + //int action; +public: + Door* overDoor; + Container* overContainer; + InfoPoint* overInfoPoint; + + // allow targetting allies, enemies and/or neutrals (bitmask) + int target_types; +private: + // currently selected targetting type, such as talk, attack, cast, ... + // private to enforce proper cursor changes + int target_mode; + unsigned char lastCursor; + short moveX, moveY; + int numScrollCursor; + bool scrolling; + unsigned short lastMouseX, lastMouseY; + int DebugFlags; + Point pfs; + PathNode* drawPath; + unsigned long AIUpdateCounter; + int ScreenFlags; + int DialogueFlags; + char *DisplayText; + unsigned int DisplayTextTime; + bool EnableRunning; +public: //Events + /** Key Press Event */ + void OnKeyPress(unsigned char Key, unsigned short Mod); + /** Key Release Event */ + void OnKeyRelease(unsigned char Key, unsigned short Mod); + /** Mouse Over Event */ + void OnMouseOver(unsigned short x, unsigned short y); + /** Global Mouse Move Event */ + void OnGlobalMouseMove(unsigned short x, unsigned short y); + /** Mouse Button Down */ + void OnMouseDown(unsigned short x, unsigned short y, unsigned short Button, + unsigned short Mod); + /** Mouse Button Up */ + void OnMouseUp(unsigned short x, unsigned short y, unsigned short Button, + unsigned short Mod); + /** Special Key Press */ + void OnSpecialKeyPress(unsigned char Key); + void DisplayTooltip(); + void UpdateScrolling(); + void SetTargetMode(int mode); + int GetTargetMode() { return target_mode; } + void SetScreenFlags(int value, int mode); + void SetDialogueFlags(int value, int mode); + int GetScreenFlags() { return ScreenFlags; } + int GetDialogueFlags() { return DialogueFlags; } + /** this function is called from the area when autosave is needed */ + void AutoSave(); + void SetDisplayText(char *text, unsigned int time); + void SetDisplayText(ieStrRef text, unsigned int time); + /* centers viewport to the points specified */ + void Center(unsigned short x, unsigned short y); +private: + /** this function is called when the user presses 'q' (or equivalent) */ + void QuickSave(); + /** this function safely retrieves an Actor by ID */ + Actor *GetActorByGlobalID(ieDword ID); + void CalculateSelection(const Point &p); + void ResizeDel(Window* win, int type); + void ResizeAdd(Window* win, int type); + void HandleWindowHide(const char *WindowName, const char *WindowPosition); + void HandleWindowReveal(const char *WindowName, const char *WindowPosition); + void ReadFormations(); + /** Draws an arrow on the edge of the screen based on the point (points at offscreen actors) */ + void DrawArrowMarker(const Region &screen, Point p, const Region &viewport); + +private: + unsigned char LeftCount, BottomCount, RightCount, TopCount; + Actor *user; //the user of item or spell +public: + DialogHandler *dialoghandler; + //using spell or item + int spellOrItem; // -1 = item, otherwise the spell type + //the user of spell or item + Actor *spellUser; + int spellSlot, spellIndex; //or inventorySlot/itemHeader + int spellCount; //multiple targeting +public: + /** Selects one or all PC */ + void SelectActor(int whom, int type = -1); + void SetLastActor(Actor *actor, Actor *prevActor); + void SetCutSceneMode(bool active); + int HideGUI(); + int UnhideGUI(); + void TryToAttack(Actor *source, Actor *target); + void TryToBash(Actor *source, Scriptable *tgt); + void TryToCast(Actor *source, const Point &p); + void TryToCast(Actor *source, Actor *target); + void TryToDefend(Actor *source, Actor *target); + void TryToTalk(Actor *source, Actor *target); + void TryToPick(Actor *source, Actor *tgt); + void TryToPick(Actor *source, Door *tgt); + void TryToPick(Actor *source, Container *tgt); + void TryToDisarm(Actor *source, InfoPoint *tgt); + void PerformActionOn(Actor *actor); + void ResetTargetMode(); + + // returns the default cursor fitting the targeting mode + int GetDefaultCursor() const; + //containers + int GetCursorOverContainer(Container *overContainer) const; + void HandleContainer(Container *container, Actor *actor); + //doors + int GetCursorOverDoor(Door *overDoor) const; + void HandleDoor(Door *door, Actor *actor); + //infopoints + int GetCursorOverInfoPoint(InfoPoint *overInfoPoint) const; + bool HandleActiveRegion(InfoPoint *trap, Actor *actor, Point &p); + + Point GetFormationOffset(ieDword formation, ieDword pos); + void MoveToPointFormation(Actor *actor, unsigned int pos, Point src, Point p); + /** calls MoveToPoint or RunToPoint */ + void CreateMovement(Actor *actor, const Point &p); + /** Displays a string over an object */ + void DisplayString(Scriptable* target); + /** Displays a string on screen */ + void DisplayString(const Point &p, const char *Text); + Actor *GetLastActor(); + /** changes map to the current PC */ + void ChangeMap(Actor *pc, bool forced); + /** Returns game screenshot, with or without GUI controls */ + Sprite2D* GetScreenshot( bool show_gui = 0 ); + /** Returns current area preview for saving a game */ + Sprite2D* GetPreview(); + /** Returns PC portrait for a currently running game */ + Sprite2D* GetPortraitPreview(int pcslot); + /** Sets up targeting with spells or items */ + void SetupItemUse(int slot, int header, Actor *actor, int targettype, int cnt); + /** Page is the spell type + spell level info */ + void SetupCasting(int type, int level, int slot, Actor *actor, int targettype, int cnt); + bool SetEvent(int eventType, EventHandler handler); +}; + +#endif diff --git a/project/jni/application/gemrb/src/core/GUI/Label.cpp b/project/jni/application/gemrb/src/core/GUI/Label.cpp new file mode 100644 index 000000000..4d67af4aa --- /dev/null +++ b/project/jni/application/gemrb/src/core/GUI/Label.cpp @@ -0,0 +1,152 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * + */ + +#include "GUI/Label.h" + +#include "win32def.h" + +#include "GameData.h" +#include "Interface.h" +#include "Palette.h" +#include "Variables.h" +#include "Video.h" + +Label::Label(Font* font) +{ + this->font = font; + Buffer = NULL; + useRGB = false; + ResetEventHandler( LabelOnPress ); + + Alignment = IE_FONT_ALIGN_CENTER|IE_FONT_ALIGN_MIDDLE; + palette = NULL; +} +Label::~Label() +{ + gamedata->FreePalette( palette ); + if (Buffer) { + free( Buffer ); + } +} +/** Draws the Control on the Output Display */ +void Label::Draw(unsigned short x, unsigned short y) +{ + if (!Changed && !(Owner->Flags&WF_FLOAT)) { + return; + } + Changed = false; + if (XPos == 65535) { + return; + } + if (font && Buffer) { + font->Print( Region( this->XPos + x, this->YPos + y, + this->Width, this->Height ), ( unsigned char * ) Buffer, + useRGB?palette:NULL, + Alignment | IE_FONT_SINGLE_LINE, true ); + } + + if (AnimPicture) { + int xOffs = ( Width / 2 ) - ( AnimPicture->Width / 2 ); + int yOffs = ( Height / 2 ) - ( AnimPicture->Height / 2 ); + Region r( x + XPos + xOffs, y + YPos + yOffs, (int)(AnimPicture->Width), AnimPicture->Height ); + core->GetVideoDriver()->BlitSprite( AnimPicture, x + XPos + xOffs, y + YPos + yOffs, true, &r ); + } + +} +/** This function sets the actual Label Text */ +int Label::SetText(const char* string, int /*pos*/) +{ + if (Buffer ) + free( Buffer ); + if (Alignment == IE_FONT_ALIGN_CENTER) { + if (core->HasFeature( GF_LOWER_LABEL_TEXT )) { + int len = strlen(string); + Buffer = (char *) malloc( len+1 ); + strnlwrcpy( Buffer, string, len ); + } + else { + Buffer = strdup( string ); + } + } + else { + Buffer = strdup( string ); + } + if (!palette) { + Color white = {0xff, 0xff, 0xff, 0x00}, black = {0x00, 0x00, 0x00, 0x00}; + SetColor(white, black); + } + if (Owner) { + Owner->Invalidate(); + } + return 0; +} +/** Sets the Foreground Font Color */ +void Label::SetColor(Color col, Color bac) +{ + gamedata->FreePalette( palette ); + palette = core->CreatePalette( col, bac ); + Changed = true; +} + +void Label::SetAlignment(unsigned char Alignment) +{ + this->Alignment = Alignment; + if (Alignment == IE_FONT_ALIGN_CENTER) { + if (core->HasFeature( GF_LOWER_LABEL_TEXT )) { + strlwr( Buffer ); + } + } + Changed = true; +} + +void Label::OnMouseUp(unsigned short x, unsigned short y, + unsigned short /*Button*/, unsigned short /*Mod*/) +{ + //printf( "Label::OnMouseUp\n" ); + if (( x <= Width ) && ( y <= Height )) { + if (VarName[0] != 0) { + core->GetDictionary()->SetAt( VarName, Value ); + } + if (LabelOnPress) { + RunEventHandler( LabelOnPress ); + } + } +} + +bool Label::SetEvent(int eventType, EventHandler handler) +{ + Changed = true; + + switch (eventType) { + case IE_GUI_LABEL_ON_PRESS: + LabelOnPress = handler; + break; + default: + return false; + } + + return true; +} + +/** Simply returns the pointer to the text, don't modify it! */ +const char* Label::QueryText() const +{ + return ( const char * ) Buffer; +} diff --git a/project/jni/application/gemrb/src/core/GUI/Label.h b/project/jni/application/gemrb/src/core/GUI/Label.h new file mode 100644 index 000000000..b0a841dfc --- /dev/null +++ b/project/jni/application/gemrb/src/core/GUI/Label.h @@ -0,0 +1,83 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * + */ + +/** + * @file Label.h + * Declares Label widget for displaying static texts + * @author GemRB Developement Team + */ + +#ifndef LABEL_H +#define LABEL_H + +#include "GUI/Control.h" + +#include "RGBAColor.h" +#include "exports.h" + +#include "Font.h" + +class Palette; + +// !!! Keep these synchronized with GUIDefines.py !!! +#define IE_GUI_LABEL_ON_PRESS 0x06000000 + +/** + * @class Label + * Label widget for displaying static texts in the GUI + */ + +class GEM_EXPORT Label : public Control { +public: + Label(Font* font); + ~Label(); + /** Draws the Control on the Output Display */ + void Draw(unsigned short x, unsigned short y); + /** This function sets the actual Label Text */ + int SetText(const char* string, int pos = 0); + /** Sets the Foreground Font Color */ + void SetColor(Color col, Color bac); + /** Sets the Alignment of Text */ + void SetAlignment(unsigned char Alignment); + /** Simply returns the pointer to the text, don't modify it! */ + const char* QueryText() const; + + /** Mouse Button Down */ + void OnMouseUp(unsigned short x, unsigned short y, unsigned short Button, + unsigned short Mod); + /** Set handler for specified event */ + bool SetEvent(int eventType, EventHandler handler); + /** Use the RGB Color for the Font */ + bool useRGB; + /** OnPress Scripted Event Function Name */ + EventHandler LabelOnPress; +private: // Private attributes + /** Text String Buffer */ + char* Buffer; + /** Font for Text Writing */ + Font* font; + /** Foreground & Background Colors */ + Palette* palette; + + /** Alignment Variable */ + unsigned char Alignment; +}; + +#endif diff --git a/project/jni/application/gemrb/src/core/GUI/MapControl.cpp b/project/jni/application/gemrb/src/core/GUI/MapControl.cpp new file mode 100644 index 000000000..4abd158ad --- /dev/null +++ b/project/jni/application/gemrb/src/core/GUI/MapControl.cpp @@ -0,0 +1,540 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "GUI/MapControl.h" + +#include "win32def.h" + +#include "Game.h" +#include "Interface.h" +#include "Map.h" +#include "Video.h" + +#define MAP_NO_NOTES 0 +#define MAP_VIEW_NOTES 1 +#define MAP_SET_NOTE 2 +#define MAP_REVEAL 3 + +// Ratio between pixel sizes of an Area (Big map) and a Small map + +static int MAP_DIV = 3; +static int MAP_MULT = 32; + +typedef enum {black=0, gray, violet, green, orange, red, blue, darkblue, darkgreen} colorcode; + +Color colors[]={ + { 0x00, 0x00, 0x00, 0xff }, //black + { 0x60, 0x60, 0x60, 0xff }, //gray + { 0xa0, 0x00, 0xa0, 0xff }, //violet + { 0x00, 0xff, 0x00, 0xff }, //green + { 0xff, 0xff, 0x00, 0xff }, //orange + { 0xff, 0x00, 0x00, 0xff }, //red + { 0x00, 0x00, 0xff, 0xff }, //blue + { 0x00, 0x00, 0x80, 0xff }, //darkblue + { 0x00, 0x80, 0x00, 0xff } //darkgreen +}; + +#define MAP_TO_SCREENX(x) (XWin + XPos + XCenter - ScrollX + (x)) +#define MAP_TO_SCREENY(y) (YWin + YPos + YCenter - ScrollY + (y)) +// Omit [XY]Pos, since these macros are used in OnMouseDown(x, y), and x, y is +// already relative to control [XY]Pos there +#define SCREEN_TO_MAPX(x) ((x) - XCenter + ScrollX) +#define SCREEN_TO_MAPY(y) ((y) - YCenter + ScrollY) + +#define GAME_TO_SCREENX(x) MAP_TO_SCREENX((int)((x) * MAP_DIV / MAP_MULT)) +#define GAME_TO_SCREENY(y) MAP_TO_SCREENY((int)((y) * MAP_DIV / MAP_MULT)) + +#define SCREEN_TO_GAMEX(x) (SCREEN_TO_MAPX(x) * MAP_MULT / MAP_DIV) +#define SCREEN_TO_GAMEY(y) (SCREEN_TO_MAPY(y) * MAP_MULT / MAP_DIV) + +MapControl::MapControl(void) +{ + if (core->HasFeature(GF_IWD_MAP_DIMENSIONS) ) { + MAP_DIV=4; + MAP_MULT=32; + } else { + MAP_DIV=3; + MAP_MULT=32; + } + + LinkedLabel = NULL; + ScrollX = 0; + ScrollY = 0; + NotePosX = 0; + NotePosY = 0; + mouseIsDown = false; + mouseIsDragging = false; + Changed = true; + convertToGame = true; + memset(Flag,0,sizeof(Flag) ); + + // initialize var and event callback to no-ops + VarName[0] = 0; + ResetEventHandler( MapControlOnPress ); + ResetEventHandler( MapControlOnRightPress ); + ResetEventHandler( MapControlOnDoublePress ); + + MyMap = core->GetGame()->GetCurrentArea(); + if (MyMap->SmallMap) { + MapMOS = MyMap->SmallMap; + MapMOS->acquire(); + } else + MapMOS = NULL; +} + +MapControl::~MapControl(void) +{ + Video *video = core->GetVideoDriver(); + + if (MapMOS) { + video->FreeSprite(MapMOS); + } + for(int i=0;i<8;i++) { + if (Flag[i]) { + video->FreeSprite(Flag[i]); + } + } +} + +// Draw fog on the small bitmap +void MapControl::DrawFog(unsigned short XWin, unsigned short YWin) +{ + Video *video = core->GetVideoDriver(); + + Region old_clip; + video->GetClipRect(old_clip); + + Region r( XWin + XPos, YWin + YPos, Width, Height ); + video->SetClipRect(&r); + + // FIXME: this is ugly, the knowledge of Map and ExploredMask + // sizes should be in Map.cpp + int w = MyMap->GetWidth() / 2; + int h = MyMap->GetHeight() / 2; + + for (int y = 0; y < h; y++) { + for (int x = 0; x < w; x++) { + Point p( (short) (MAP_MULT * x), (short) (MAP_MULT * y) ); + bool visible = MyMap->IsVisible( p, true ); + if (! visible) { + Region rgn = Region ( MAP_TO_SCREENX(MAP_DIV * x), MAP_TO_SCREENY(MAP_DIV * y), MAP_DIV, MAP_DIV ); + video->DrawRect( rgn, colors[black] ); + } + } + } + + video->SetClipRect(&old_clip); +} + +// To be called after changes in control's or screen geometry +void MapControl::Realize() +{ + // FIXME: ugly!! How to get area size in pixels? + //Map *map = core->GetGame()->GetCurrentMap(); + //MapWidth = map->GetWidth(); + //MapHeight = map->GetHeight(); + + if (MapMOS) { + MapWidth = (short) MapMOS->Width; + MapHeight = (short) MapMOS->Height; + } else { + MapWidth = 0; + MapHeight = 0; + } + + // FIXME: ugly hack! What is the actual viewport size? + ViewWidth = (short) (core->Width * MAP_DIV / MAP_MULT); + ViewHeight = (short) (core->Height * MAP_DIV / MAP_MULT); + + XCenter = (short) (Width - MapWidth ) / 2; + YCenter = (short) (Height - MapHeight ) / 2; + if (XCenter < 0) XCenter = 0; + if (YCenter < 0) YCenter = 0; +} + +void MapControl::RedrawMapControl(const char *VariableName, unsigned int Sum) +{ + if (strnicmp( VarName, VariableName, MAX_VARIABLE_LENGTH )) { + return; + } + Value = Sum; + Changed = true; +} + +/** Draws the Control on the Output Display */ +void MapControl::Draw(unsigned short XWin, unsigned short YWin) +{ + if (!Width || !Height) { + return; + } + if (Owner->Visible!=WINDOW_VISIBLE) { + return; + } + + if (Changed) { + Realize(); + Changed = false; + } + + // we're going to paint over labels/etc, so they need to repaint! + bool seen_this = false; + unsigned int i; + for (i = 0; i < Owner->GetControlCount(); i++) { + Control *ctrl = Owner->GetControl(i); + if (!ctrl) continue; + + // we could try working out which controls overlap, + // but the later controls are cheap to paint.. + if (ctrl == this) { seen_this = true; continue; } + if (!seen_this) continue; + + ctrl->Changed = true; + } + + Video* video = core->GetVideoDriver(); + Region r( XWin + XPos, YWin + YPos, Width, Height ); + + if (MapMOS) { + video->BlitSprite( MapMOS, MAP_TO_SCREENX(0), MAP_TO_SCREENY(0), true, &r ); + } + + if (core->FogOfWar&FOG_DRAWFOG) + DrawFog(XWin, YWin); + + Region vp = video->GetViewport(); + + vp.x = GAME_TO_SCREENX(vp.x); + vp.y = GAME_TO_SCREENY(vp.y); + vp.w = ViewWidth; + vp.h = ViewHeight; + + video->DrawRect( vp, colors[green], false, false ); + + // Draw PCs' ellipses + Game *game = core->GetGame(); + i = game->GetPartySize(true); + while (i--) { + Actor* actor = game->GetPC( i, true ); + if (MyMap->HasActor(actor) ) { + video->DrawEllipse( (short) GAME_TO_SCREENX(actor->Pos.x), (short) GAME_TO_SCREENY(actor->Pos.y), 3, 2, actor->Selected ? colors[green] : colors[darkgreen], false ); + } + } + // Draw Map notes, could be turned off in bg2 + // we use the common control value to handle it, because then we + // don't need another interface + if (Value!=MAP_NO_NOTES) { + i = MyMap -> GetMapNoteCount(); + while (i--) { + MapNote * mn = MyMap -> GetMapNote(i); + Sprite2D *anim = Flag[mn->color&7]; + Point pos = mn->Pos; + if (convertToGame) { + vp.x = GAME_TO_SCREENX(mn->Pos.x); + vp.y = GAME_TO_SCREENY(mn->Pos.y); + } else { //pst style + vp.x = MAP_TO_SCREENX(mn->Pos.x); + vp.y = MAP_TO_SCREENY(mn->Pos.y); + pos.x = pos.x * MAP_MULT / MAP_DIV; + pos.y = pos.y * MAP_MULT / MAP_DIV; + } + + //Skip unexplored map notes + bool visible = MyMap->IsVisible( pos, true ); + if (!visible) + continue; + + if (anim) { + video->BlitSprite( anim, vp.x - anim->Width/2, vp.y - anim->Height/2, true, &r ); + } else { + video->DrawEllipse( (short) vp.x, (short) vp.y, 6, 5, colors[mn->color&7], false ); + } + } + } +} + +/** Key Press Event */ +void MapControl::OnKeyPress(unsigned char Key, unsigned short /*Mod*/) +{ +#ifdef ANDROID + switch(Key) { + case 'o': + case 'p': + Control::OnKeyPress(Key, 0); + break; + } +#else +(void)Key; // unused, fool the compiler +#endif +} + +/** Key Release Event */ +void MapControl::OnKeyRelease(unsigned char Key, unsigned short Mod) +{ + switch (Key) { + case '\t': + //not GEM_TAB + printf( "TAB released\n" ); + return; + case 'f': + if (Mod & GEM_MOD_CTRL) + core->GetVideoDriver()->ToggleFullscreenMode(); + break; + default: + break; + } + if (!core->CheatEnabled()) { + return; + } +} +/** Mouse Over Event */ +void MapControl::OnMouseOver(unsigned short x, unsigned short y) +{ + if (mouseIsDown) { + ScrollX -= x - lastMouseX; + ScrollY -= y - lastMouseY; + + if (ScrollX > MapWidth - Width) + ScrollX = MapWidth - Width; + if (ScrollY > MapHeight - Height) + ScrollY = MapHeight - Height; + if (ScrollX < 0) + ScrollX = 0; + if (ScrollY < 0) + ScrollY = 0; + } + + if (mouseIsDragging) { + ViewHandle(x,y); + } + + lastMouseX = x; + lastMouseY = y; + + switch (Value) { + case MAP_REVEAL: //for farsee effect + Owner->Cursor = IE_CURSOR_CAST; + break; + case MAP_SET_NOTE: + Owner->Cursor = IE_CURSOR_GRAB; + break; + default: + Owner->Cursor = IE_CURSOR_NORMAL; + break; + } + + if (Value == MAP_VIEW_NOTES || Value == MAP_SET_NOTE || Value == MAP_REVEAL) { + Point mp; + unsigned int dist; + + if (convertToGame) { + mp.x = (short) SCREEN_TO_GAMEX(x); + mp.y = (short) SCREEN_TO_GAMEY(y); + dist = 100; + } else { + mp.x = (short) SCREEN_TO_MAPX(x); + mp.y = (short) SCREEN_TO_MAPY(y); + dist = 16; + } + int i = MyMap -> GetMapNoteCount(); + while (i--) { + MapNote * mn = MyMap -> GetMapNote(i); + if (Distance(mp, mn->Pos)SetText( mn->text ); + } + NotePosX = mn->Pos.x; + NotePosY = mn->Pos.y; + return; + } + } + NotePosX = mp.x; + NotePosY = mp.y; + } + if (LinkedLabel) { + LinkedLabel->SetText( "" ); + } +} + +/** Mouse Leave Event */ +void MapControl::OnMouseLeave(unsigned short /*x*/, unsigned short /*y*/) +{ + Owner->Cursor = IE_CURSOR_NORMAL; +} + +void MapControl::ClickHandle(unsigned short Button) +{ + core->GetDictionary()->SetAt( "MapControlX", NotePosX ); + core->GetDictionary()->SetAt( "MapControlY", NotePosY ); + switch(Button&GEM_MB_NORMAL) { + case GEM_MB_ACTION: + if (Button&GEM_MB_DOUBLECLICK) { + RunEventHandler( MapControlOnDoublePress ); + } else { + RunEventHandler( MapControlOnPress ); + } + break; + case GEM_MB_MENU: + RunEventHandler( MapControlOnRightPress ); + break; + default: + break; + } +} + +void MapControl::ViewHandle(unsigned short x, unsigned short y) +{ + short xp = (short) (SCREEN_TO_MAPX(x) - ViewWidth / 2); + short yp = (short) (SCREEN_TO_MAPY(y) - ViewHeight / 2); + + if (xp + ViewWidth > MapWidth) xp = MapWidth - ViewWidth; + if (yp + ViewHeight > MapHeight) yp = MapHeight - ViewHeight; + if (xp < 0) xp = 0; + if (yp < 0) yp = 0; + + // clear any previously scheduled moves and then do it asap, so it works while paused + unsigned int vpx = xp * MAP_MULT / MAP_DIV; + unsigned int vpy = yp * MAP_MULT / MAP_DIV; + core->timer->SetMoveViewPort( vpx, vpy, 0, false ); + core->GetVideoDriver()->MoveViewportTo( vpx, vpy ); +} + +/** Mouse Button Down */ +void MapControl::OnMouseDown(unsigned short x, unsigned short y, unsigned short Button, + unsigned short /*Mod*/) +{ + switch((unsigned char) Button) { + case GEM_MB_SCRLUP: + OnSpecialKeyPress(GEM_UP); + return; + case GEM_MB_SCRLDOWN: + OnSpecialKeyPress(GEM_DOWN); + return; + case GEM_MB_ACTION: + if (Button & GEM_MB_DOUBLECLICK) { + ClickHandle(Button); + } + break; + default: + break; + } + + mouseIsDown = true; + short xp = (short) (SCREEN_TO_GAMEX(x)); + short yp = (short) (SCREEN_TO_GAMEY(y)); + Region vp = core->GetVideoDriver()->GetViewport(); + vp.w = vp.x+ViewWidth*MAP_MULT/MAP_DIV; + vp.h = vp.y+ViewHeight*MAP_MULT/MAP_DIV; + if ((xp>vp.x) && (xpvp.y) && (yp MapWidth - Width) + ScrollX = MapWidth - Width; + if (ScrollY > MapHeight - Height) + ScrollY = MapHeight - Height; + if (ScrollX < 0) + ScrollX = 0; + if (ScrollY < 0) + ScrollY = 0; +} + +bool MapControl::SetEvent(int eventType, EventHandler handler) +{ + Changed = true; + + switch (eventType) { + case IE_GUI_MAP_ON_PRESS: + MapControlOnPress = handler; + break; + case IE_GUI_MAP_ON_RIGHT_PRESS: + MapControlOnRightPress = handler; + break; + case IE_GUI_MAP_ON_DOUBLE_PRESS: + MapControlOnDoublePress = handler; + break; + default: + return false; + } + + return true; +} diff --git a/project/jni/application/gemrb/src/core/GUI/MapControl.h b/project/jni/application/gemrb/src/core/GUI/MapControl.h new file mode 100644 index 000000000..1154bf8c1 --- /dev/null +++ b/project/jni/application/gemrb/src/core/GUI/MapControl.h @@ -0,0 +1,110 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * + */ + +/** + * @file MapControl.h + * Declares MapControl, widget for displaying current area map + */ + +class MapControl; + +#ifndef MAPCONTROL_H +#define MAPCONTROL_H + +#include "GUI/Control.h" + +#include "exports.h" +#include "Interface.h" + +// !!! Keep these synchronized with GUIDefines.py !!! +#define IE_GUI_MAP_ON_PRESS 0x09000000 +#define IE_GUI_MAP_ON_RIGHT_PRESS 0x09000005 +#define IE_GUI_MAP_ON_DOUBLE_PRESS 0x09000008 + + +/** + * @class MapControl + * Widget displaying current area map, with a viewport rectangle + * and PCs' ground circles + */ + +class GEM_EXPORT MapControl : public Control { +public: + int ScrollX, ScrollY; + int NotePosX, NotePosY; + unsigned short lastMouseX, lastMouseY; + bool mouseIsDown; + bool mouseIsDragging; + bool convertToGame; + // Small map bitmap + Sprite2D* MapMOS; + // current map + Map *MyMap; + // map flags + Sprite2D *Flag[8]; + // The MapControl can set the text of this label directly + Control *LinkedLabel; + // Size of big map (area) in pixels + short MapWidth, MapHeight; + // Size of area viewport. FIXME: hack! + short ViewWidth, ViewHeight; + short XCenter, YCenter; + EventHandler MapControlOnPress; + EventHandler MapControlOnRightPress; + EventHandler MapControlOnDoublePress; + + MapControl(void); + ~MapControl(void); + /** redraws the control after its associated variable has changed */ + void RedrawMapControl(const char *VariableName, unsigned int Sum); + /** Draws the Control on the Output Display */ + void Draw(unsigned short XWin, unsigned short YWin); + void DrawFog(unsigned short XWin, unsigned short YWin); + /** Compute parameters after changes in control's or screen geometry */ + void Realize(); + /** Sets the Text of the current control */ + int SetText(const char* /*string*/, int /*pos*/) { return 0; } + + /** Key Press Event */ + void OnKeyPress(unsigned char Key, unsigned short Mod); + /** Mouse Over Event */ + void OnMouseOver(unsigned short x, unsigned short y); + /** Mouse Leave Event */ + void OnMouseLeave(unsigned short x, unsigned short y); + /** Mouse Button Down */ + void OnMouseDown(unsigned short x, unsigned short y, unsigned short Button, + unsigned short Mod); + /** Mouse Button Up */ + void OnMouseUp(unsigned short x, unsigned short y, unsigned short Button, + unsigned short Mod); + /** Key Release Event */ + void OnKeyRelease(unsigned char Key, unsigned short Mod); + /** Special Key Press */ + void OnSpecialKeyPress(unsigned char Key); + /** Set handler for specified event */ + bool SetEvent(int eventType, EventHandler handler); +private: + /** Call event handler on click */ + void ClickHandle(unsigned short Button); + /** Move viewport */ + void ViewHandle(unsigned short x, unsigned short y); +}; + +#endif diff --git a/project/jni/application/gemrb/src/core/GUI/Progressbar.cpp b/project/jni/application/gemrb/src/core/GUI/Progressbar.cpp new file mode 100644 index 000000000..d31f58958 --- /dev/null +++ b/project/jni/application/gemrb/src/core/GUI/Progressbar.cpp @@ -0,0 +1,184 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * + */ + +#include "GUI/Progressbar.h" + +#include "win32def.h" + +#include "Interface.h" +#include "Video.h" + +#include + +Progressbar::Progressbar( unsigned short KnobStepsCount, bool Clear) +{ + BackGround = NULL; + BackGround2 = NULL; + this->Clear = Clear; + this->KnobStepsCount = KnobStepsCount; + PBarAnim = NULL; + PBarCap = NULL; + KnobXPos = KnobYPos = 0; + CapXPos = CapYPos = 0; + ResetEventHandler( EndReached ); +} + +Progressbar::~Progressbar() +{ + if (!Clear) { + return; + } + core->GetVideoDriver()->FreeSprite( BackGround ); + core->GetVideoDriver()->FreeSprite( BackGround2 ); + delete PBarAnim; + core->GetVideoDriver()->FreeSprite( PBarCap ); +} + +/** Draws the Control on the Output Display */ +void Progressbar::Draw(unsigned short x, unsigned short y) +{ + //it is unlikely that a floating window is above us, but... + if (!Changed && !(Owner->Flags&WF_FLOAT) ) { + return; + } + Changed = false; + if (XPos == 65535) { + return; + } + Sprite2D *bcksprite; + + if((Value >= 100) && KnobStepsCount && BackGround2) { + bcksprite=BackGround2; //animated progbar end stage + } + else { + bcksprite=BackGround; + } + if (bcksprite) { + Region r( x + XPos, y + YPos, Width, Height ); + core->GetVideoDriver()->BlitSprite( bcksprite, + x + XPos, y + YPos, true, &r ); + if( bcksprite==BackGround2) { + return; //done for animated progbar + } + } + + unsigned int Count; + + if(!KnobStepsCount) { + //linear progressbar (pst, iwd) + int w = BackGround2->Width; + int h = BackGround2->Height; + //this is the PST/IWD specific part + Count = Value*w/100; + Region r( x + XPos + KnobXPos, y + YPos + KnobYPos, Count, h ); + core->GetVideoDriver()->BlitSprite( BackGround2, + r.x, r.y, true, &r ); + + core->GetVideoDriver()->BlitSprite( PBarCap, + x+XPos+CapXPos+Count-PBarCap->Width, y+YPos+CapYPos, true ); + return; + } + + //animated progressbar (bg2) + Count=Value*KnobStepsCount/100; + for(unsigned int i=0; iGetFrame(i); + core->GetVideoDriver()->BlitSprite( Knob, x , y , true ); + } +} + +/** Returns the actual Progressbar Position */ +unsigned int Progressbar::GetPosition() +{ + return Value; +} + +/** Sets the actual Progressbar Position trimming to the Max and Min Values */ +void Progressbar::SetPosition(unsigned int pos) +{ + if(pos>100) pos=100; + if (Value == pos) + return; + Value = pos; + Changed = true; +} + +void Progressbar::RedrawProgressbar(const char* VariableName, int Sum) +{ + if (strnicmp( VarName, VariableName, MAX_VARIABLE_LENGTH )) { + return; + } + SetPosition((unsigned int) Sum); + if((Value==100) && Changed) + RunEventHandler( EndReached ); +} + +/** Sets the selected image */ +void Progressbar::SetImage(Sprite2D* img, Sprite2D* img2) +{ + if (BackGround && Clear) + core->GetVideoDriver()->FreeSprite( BackGround ); + BackGround = img; + if (BackGround2 && Clear) + core->GetVideoDriver()->FreeSprite( BackGround2 ); + BackGround2 = img2; + Changed = true; +} + +void Progressbar::SetBarCap(Sprite2D* img3) +{ + core->GetVideoDriver()->FreeSprite( PBarCap ); + PBarCap = img3; +} + +void Progressbar::SetAnimation(Animation *arg) +{ + delete PBarAnim; + PBarAnim = arg; +} + +void Progressbar::SetSliderPos(int x, int y, int x2, int y2) +{ + KnobXPos=x; + KnobYPos=y; + CapXPos=x2; + CapYPos=y2; +} + +/* dummy virtual function */ +int Progressbar::SetText(const char* /*string*/, int /*pos*/) +{ + return 0; +} + +bool Progressbar::SetEvent(int eventType, EventHandler handler) +{ + Changed = true; + + switch (eventType) { + case IE_GUI_PROGRESS_END_REACHED: + EndReached = handler; + break; + default: + return false; + } + + return true; +} diff --git a/project/jni/application/gemrb/src/core/GUI/Progressbar.h b/project/jni/application/gemrb/src/core/GUI/Progressbar.h new file mode 100644 index 000000000..411ae5510 --- /dev/null +++ b/project/jni/application/gemrb/src/core/GUI/Progressbar.h @@ -0,0 +1,89 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003-2005 The GemRB Project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * + */ + +/** + * @file Progressbar.h + * Declares Progressbar widget for displaying progress of loading and saving games + */ + +#ifndef PROGRESSBAR_H +#define PROGRESSBAR_H + +#include "GUI/Control.h" + +#include "exports.h" + +#include "Animation.h" +#include "Sprite2D.h" + +// !!! Keep in sync with GUIDefines.py !!! +#define IE_GUI_PROGRESS_END_REACHED 0x01000000 + + +/** + * @class Progressbar + * Widget for displaying progressbars, mainly on loading/saving screens + */ + +class GEM_EXPORT Progressbar : public Control { +public: + Progressbar(unsigned short KnobStepsCount, bool Clear = false); + ~Progressbar(); + /** Draws the Control on the Output Display */ + void Draw(unsigned short x, unsigned short y); + /** Returns the actual Progressbar Position */ + unsigned int GetPosition(); + /** Sets the actual Progressbar Position trimming to the Max and Min Values */ + void SetPosition(unsigned int pos); + /** Sets the background images */ + void SetImage(Sprite2D * img, Sprite2D * img2); + /** Sets a bam resource for progressbar */ + void SetAnimation(Animation *arg); + /** Sets a mos resource for progressbar cap */ + void SetBarCap(Sprite2D *img3); + /** Sets the mos coordinates for the progressbar filler mos/cap */ + void SetSliderPos(int x, int y, int x2, int y2); + /** Dummy function */ + int SetText(const char * string, int pos = 0); + /** Redraws a progressbar which is associated with VariableName */ + void RedrawProgressbar(const char *VariableName, int Sum); + /** Set handler for specified event */ + bool SetEvent(int eventType, EventHandler handler); + +private: // Private attributes + /** BackGround Images. If smaller than the Control Size, the image will be tiled. */ + Sprite2D * BackGround; + Sprite2D * BackGround2; //mos resource for the filling of the bar + /** Knob Steps Count */ + unsigned int KnobStepsCount; + int KnobXPos, KnobYPos; //relative coordinates for Background2 + int CapXPos, CapYPos; //relative coordinates for PBarCap + /** If true, on deletion the Progressbar will destroy the associated images */ + bool Clear; + /** The bam cycle whose frames work as a progressbar (animated progressbar) */ + Animation *PBarAnim; + /** The most for the progressbar cap (linear progressbar) */ + Sprite2D *PBarCap; +public: + /** EndReached Scripted Event Function Name */ + EventHandler EndReached; +}; + +#endif diff --git a/project/jni/application/gemrb/src/core/GUI/ScrollBar.cpp b/project/jni/application/gemrb/src/core/GUI/ScrollBar.cpp new file mode 100644 index 000000000..aea1b9833 --- /dev/null +++ b/project/jni/application/gemrb/src/core/GUI/ScrollBar.cpp @@ -0,0 +1,297 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * + */ + +#include "GUI/ScrollBar.h" + +#include "win32def.h" + +#include "Interface.h" +#include "Variables.h" +#include "Video.h" + +ScrollBar::ScrollBar(void) +{ + Pos = 0; + Value = 10; + State = 0; + ResetEventHandler( ScrollBarOnChange ); + ta = NULL; + for(int i=0;iGetVideoDriver(); + for(int i=0;iFreeSprite(Frames[i]); + } + } +} + +/** Sets a new position, relays the change to an associated textarea and calls + any existing GUI OnChange callback */ +void ScrollBar::SetPos(int NewPos) +{ + if (Pos && ( Pos == NewPos )) { + return; + } + Changed = true; + Pos = (ieWord) NewPos; + if (ta) { + TextArea* t = ( TextArea* ) ta; + t->SetRow( Pos ); + } + if (VarName[0] != 0) { + core->GetDictionary()->SetAt( VarName, Pos ); + } + RunEventHandler( ScrollBarOnChange ); +} + +/** Refreshes the ScrollBar according to a guiscript variable */ +void ScrollBar::RedrawScrollBar(const char* Variable, int Sum) +{ + if (strnicmp( VarName, Variable, MAX_VARIABLE_LENGTH )) { + return; + } + SetPos( Sum ); +} + +/** Mousewheel support */ +void ScrollBar::ScrollUp() +{ + if (Pos > 0) { + SetPos( Pos - 1 ); + } +} + +/** Mousewheel support */ +void ScrollBar::ScrollDown() +{ + if ( (ieDword) Pos + 1 < Value ) { + SetPos( Pos + 1 ); + } +} + +/** Draws the ScrollBar control */ +void ScrollBar::Draw(unsigned short x, unsigned short y) +{ + if (!Changed && !(Owner->Flags&WF_FLOAT) ) { + return; + } + Changed = false; + if (XPos == 65535) { + return; + } + int upMy = Frames[IE_GUI_SCROLLBAR_UP_UNPRESSED]->Height; + int doMy = Frames[IE_GUI_SCROLLBAR_DOWN_UNPRESSED]->Height; + unsigned int domy = (Height - doMy); + + unsigned short slmy = ( unsigned short ) + ( upMy + + ( Pos * ( ( domy - Frames[IE_GUI_SCROLLBAR_SLIDER]->Height - upMy ) / + ( double ) ( Value < 2 ? 1 : Value - 1 ) ) ) ); + unsigned short slx = ( unsigned short ) ((Width - Frames[IE_GUI_SCROLLBAR_SLIDER]->Width) / 2 ); + + if (( State & UP_PRESS ) != 0) { + core->GetVideoDriver()->BlitSprite( Frames[IE_GUI_SCROLLBAR_UP_PRESSED], + x + XPos, y + YPos, true ); + } else { + core->GetVideoDriver()->BlitSprite( Frames[IE_GUI_SCROLLBAR_UP_UNPRESSED], + x + XPos, y + YPos, true ); + } + int maxy = y + YPos + Height - + Frames[IE_GUI_SCROLLBAR_DOWN_UNPRESSED]->Height; + int stepy = Frames[IE_GUI_SCROLLBAR_TROUGH]->Height; + Region rgn( x + XPos, y + YPos + upMy, Width, domy - upMy); + for (int dy = y + YPos + upMy; dy < maxy; dy += stepy) { + core->GetVideoDriver()->BlitSprite( Frames[IE_GUI_SCROLLBAR_TROUGH], + x + XPos + ( ( Width / 2 ) - + Frames[IE_GUI_SCROLLBAR_TROUGH]->Width / 2 ), + dy, true, &rgn ); + } + if (( State & DOWN_PRESS ) != 0) { + core->GetVideoDriver()->BlitSprite( Frames[IE_GUI_SCROLLBAR_DOWN_PRESSED], + x + XPos, maxy, true ); + } else { + core->GetVideoDriver()->BlitSprite( Frames[IE_GUI_SCROLLBAR_DOWN_UNPRESSED], + x + XPos, maxy, true ); + } + core->GetVideoDriver()->BlitSprite( Frames[IE_GUI_SCROLLBAR_SLIDER], + x + XPos + slx + Frames[IE_GUI_SCROLLBAR_SLIDER]->XPos, + y + YPos + slmy + Frames[IE_GUI_SCROLLBAR_SLIDER]->YPos, + true ); +} + +/** Sets a ScrollBar GUI resource */ +void ScrollBar::SetImage(unsigned char type, Sprite2D* img) +{ + if (type >= SB_RES_COUNT) { + return; + } + if (Frames[type]) { + core->GetVideoDriver()->FreeSprite(Frames[type]); + } + Frames[type] = img; + Changed = true; +} + +/** Mouse Button Down */ +void ScrollBar::OnMouseDown(unsigned short x, unsigned short y, + unsigned short Button, unsigned short /*Mod*/) +{ + //removing the double click flag, use a more sophisticated method + //if it is needed later + Button&=GEM_MB_NORMAL; + if (Button==GEM_MB_SCRLUP) { + ScrollUp(); + return; + } + if (Button==GEM_MB_SCRLDOWN) { + ScrollDown(); + return; + } + + core->RedrawAll(); + + unsigned short upMx = (unsigned short) Frames[IE_GUI_SCROLLBAR_UP_UNPRESSED]->Width; + unsigned short upMy = (unsigned short) Frames[IE_GUI_SCROLLBAR_UP_UNPRESSED]->Height; + unsigned short domy = (unsigned short) (Height - Frames[IE_GUI_SCROLLBAR_DOWN_UNPRESSED]->Height); + unsigned short slheight = domy - upMy; + unsigned short refheight = (unsigned short) (slheight - Frames[IE_GUI_SCROLLBAR_SLIDER]->Height); + double step = refheight / (double) ( Value < 2 ? 1 : Value - 1 ); + unsigned short ymax = upMy + refheight; + unsigned short ymy = y - upMy; + unsigned short doMx = (unsigned short) Frames[IE_GUI_SCROLLBAR_DOWN_UNPRESSED]->Width; + unsigned short slMx = (unsigned short) Frames[IE_GUI_SCROLLBAR_SLIDER]->Width; + unsigned short slmy = (unsigned short) (upMy + Pos * step); + unsigned short slMy = (unsigned short) (slmy + Frames[IE_GUI_SCROLLBAR_SLIDER]->Height); + if (( x <= upMx ) && ( y <= upMy )) { + if (Pos > 0) + SetPos( Pos - 1 ); + State |= UP_PRESS; + return; + } + if (y >= domy) { + if (( x <= doMx ) && ( y <= Height )) { + if ( (ieDword) Pos + 1 < Value ) + SetPos( Pos + 1 ); + State |= DOWN_PRESS; + return; + } + } + if (y >= slmy) { + if (( x <= slMx ) && ( y <= slMy )) { + State |= SLIDER_GRAB; + return; + } + } + if (y <= upMy) { + SetPos( 0 ); + return; + } + if (y >= ymax) { + SetPos( Value - 1 ); + return; + } + unsigned short befst = ( unsigned short ) ( ymy / step ); + unsigned short aftst = befst + 1; + if (( ymy - ( befst * step ) ) < ( ( aftst * step ) - ymy )) { + SetPos( befst ); + } else { + SetPos( aftst ); + } +} + +/** Mouse Button Up */ +void ScrollBar::OnMouseUp(unsigned short /*x*/, unsigned short /*y*/, + unsigned short /*Button*/, unsigned short /*Mod*/) +{ + Changed = true; + State = 0; +} + +/** Mouse Over Event */ +void ScrollBar::OnMouseOver(unsigned short /*x*/, unsigned short y) +{ + if (( State & SLIDER_GRAB ) != 0) { + core->RedrawAll(); + unsigned short upMy =(unsigned short) Frames[IE_GUI_SCROLLBAR_UP_UNPRESSED]->Height; + unsigned short domy = (unsigned short) (Height - Frames[IE_GUI_SCROLLBAR_DOWN_UNPRESSED]->Height); + unsigned short slheight = domy - upMy; + unsigned short refheight = (unsigned short) (slheight - Frames[IE_GUI_SCROLLBAR_SLIDER]->Height); + double step = refheight / ( double ) ( Value < 2 ? 1 : Value - 1 ); + unsigned short yzero = (unsigned short) (upMy + Frames[IE_GUI_SCROLLBAR_SLIDER]->Height / 2 ); + unsigned short ymax = yzero + refheight; + unsigned short ymy = y - yzero; + if (y <= yzero) { + SetPos( 0 ); + return; + } + if (y >= ymax) { + SetPos( Value - 1 ); + return; + } + unsigned short befst = ( unsigned short ) ( ymy / step ); + unsigned short aftst = befst + 1; + if (( ymy - ( befst * step ) ) < ( ( aftst * step ) - ymy )) { + if (befst > Value ) + SetPos( befst ); + } else { + if (aftst < Value ) + SetPos( aftst ); + } + } +} + +/** Sets the Text of the current control */ +int ScrollBar::SetText(const char* /*string*/, int /*pos*/) +{ + return 0; +} + +/** Sets the Maximum Value of the ScrollBar */ +void ScrollBar::SetMax(unsigned short Max) +{ + Value = Max; + if (Max == 0) { + SetPos( 0 ); + } else if (Pos >= Max) { + SetPos( Max - 1 ); + } +} + +/** Sets the ScrollBarOnChange event (guiscript callback) */ +bool ScrollBar::SetEvent(int eventType, EventHandler handler) +{ + Changed = true; + + switch (eventType) { + case IE_GUI_SCROLLBAR_ON_CHANGE: + ScrollBarOnChange = handler; + break; + default: + return false; + } + + return true; +} diff --git a/project/jni/application/gemrb/src/core/GUI/ScrollBar.h b/project/jni/application/gemrb/src/core/GUI/ScrollBar.h new file mode 100644 index 000000000..4a2d172ac --- /dev/null +++ b/project/jni/application/gemrb/src/core/GUI/ScrollBar.h @@ -0,0 +1,103 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * + */ + +/** + * @file ScrollBar.h + * Declares ScrollBar widget for paging in long text windows. + * This does not include scales and sliders, which are of Slider class. + * @author The GemRB Project + */ + +#ifndef SCROLLBAR_H +#define SCROLLBAR_H + +#include "GUI/Control.h" +#include "GUI/TextArea.h" + +#include "exports.h" + +#include "Sprite2D.h" + +// !!! Keep these synchronized with GUIDefines.py !!! +#define IE_GUI_SCROLLBAR_ON_CHANGE 0x07000000 + +#define IE_GUI_SCROLLBAR_DEFAULT 0x00000040 // mousewheel triggers it + +#define IE_GUI_SCROLLBAR_UP_UNPRESSED 0 +#define IE_GUI_SCROLLBAR_UP_PRESSED 1 +#define IE_GUI_SCROLLBAR_DOWN_UNPRESSED 2 +#define IE_GUI_SCROLLBAR_DOWN_PRESSED 3 +#define IE_GUI_SCROLLBAR_TROUGH 4 +#define IE_GUI_SCROLLBAR_SLIDER 5 + +#define UP_PRESS 0x0001 +#define DOWN_PRESS 0x0010 +#define SLIDER_GRAB 0x0100 + +/** + * @class ScrollBar + * Widget displaying scrollbars for paging in long text windows + */ + +#define SB_RES_COUNT 6 + +class GEM_EXPORT ScrollBar : public Control { +public: + ScrollBar(void); + ~ScrollBar(void); + /**sets position, updates associated stuff */ + void SetPos(int NewPos); + void ScrollUp(); + void ScrollDown(); + /**redraws scrollbar if associated with VarName */ + void RedrawScrollBar(const char* VarName, int Sum); + /**/ + void Draw(unsigned short x, unsigned short y); +private: //Private attributes + /** Images for drawing the Scroll Bar */ + Sprite2D* Frames[SB_RES_COUNT]; + /** Cursor Position */ + unsigned short Pos; + /** Scroll Bar Status */ + unsigned short State; + /** Sets the Text of the current control */ + int SetText(const char* string, int pos = 0); +public: + void SetImage(unsigned char type, Sprite2D* img); + /** Sets the Maximum Value of the ScrollBar */ + void SetMax(unsigned short Max); + /** TextArea Associated Control */ + Control* ta; +public: // Public Events + /** Mouse Button Down */ + void OnMouseDown(unsigned short x, unsigned short y, unsigned short Button, + unsigned short Mod); + /** Mouse Button Up */ + void OnMouseUp(unsigned short x, unsigned short y, unsigned short Button, + unsigned short Mod); + /** Mouse Over Event */ + void OnMouseOver(unsigned short x, unsigned short y); + /** Set handler for specified event */ + bool SetEvent(int eventType, EventHandler handler); + /** OnChange Scripted Event Function Name */ + EventHandler ScrollBarOnChange; +}; + +#endif diff --git a/project/jni/application/gemrb/src/core/GUI/Slider.cpp b/project/jni/application/gemrb/src/core/GUI/Slider.cpp new file mode 100644 index 000000000..09812ab3c --- /dev/null +++ b/project/jni/application/gemrb/src/core/GUI/Slider.cpp @@ -0,0 +1,296 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * + */ + +#include "GUI/Slider.h" + +#include "win32def.h" + +#include "Interface.h" +#include "Variables.h" +#include "Video.h" + +#include + +Slider::Slider(short KnobXPos, short KnobYPos, short KnobStep, + unsigned short KnobStepsCount, bool Clear) +{ + this->KnobXPos = KnobXPos; + this->KnobYPos = KnobYPos; + this->KnobStep = KnobStep; + this->KnobStepsCount = KnobStepsCount; + Knob = NULL; + GrabbedKnob = NULL; + BackGround = NULL; + this->Clear = Clear; + ResetEventHandler( SliderOnChange ); + State = IE_GUI_SLIDER_KNOB; + Pos = 0; + Value = 1; +} + +Slider::~Slider() +{ + if (!Clear) { + return; + } + if (Knob) { + core->GetVideoDriver()->FreeSprite( Knob ); + } + if (GrabbedKnob) { + core->GetVideoDriver()->FreeSprite( GrabbedKnob ); + } + if (BackGround) { + core->GetVideoDriver()->FreeSprite( BackGround ); + } +} + +/** Draws the Control on the Output Display */ +void Slider::Draw(unsigned short x, unsigned short y) +{ + if (!Changed && !(Owner->Flags&WF_FLOAT) ) { + return; + } + Changed = false; + if (XPos == 65535) { + return; + } + Region r( x + XPos, y + YPos, Width, Height ); + if (BackGround) { + if (( BackGround->Width < Width ) || ( BackGround->Height < Height )) { + core->GetVideoDriver()->BlitTiled( r, BackGround, true ); + } else { + core->GetVideoDriver()->BlitSprite( BackGround, x + XPos, y + YPos, true, &r ); + } + } + switch (State) { + case IE_GUI_SLIDER_KNOB: + core->GetVideoDriver()->BlitSprite( Knob, + x + XPos + KnobXPos + ( Pos * KnobStep ), + y + YPos + KnobYPos, true ); + break; + + case IE_GUI_SLIDER_GRABBEDKNOB: + core->GetVideoDriver()->BlitSprite( GrabbedKnob, + x + XPos + KnobXPos + ( Pos * KnobStep ), + y + YPos + KnobYPos, true ); + break; + } +} + +/** Returns the actual Slider Position */ +unsigned int Slider::GetPosition() +{ + return Pos; +} + +/** Sets the actual Slider Position trimming to the Max and Min Values */ +void Slider::SetPosition(unsigned int pos) +{ + if (pos <= KnobStepsCount) { + Pos = pos; + } + if (VarName[0] != 0) { + if (!Value) + Value = 1; + core->GetDictionary()->SetAt( VarName, pos * Value ); + } + Changed = true; +} + +/** Redraws a slider which is associated with VariableName */ +void Slider::RedrawSlider(const char* VariableName, int Sum) +{ + if (strnicmp( VarName, VariableName, MAX_VARIABLE_LENGTH )) { + return; + } + if (!Value) { + Value = 1; + } + Sum /= Value; + if (Sum <= KnobStepsCount) { + Pos = Sum; + } + Changed = true; +} + +/** Sets the selected image */ +void Slider::SetImage(unsigned char type, Sprite2D* img) +{ + switch (type) { + case IE_GUI_SLIDER_KNOB: + if (Knob && Clear) + core->GetVideoDriver()->FreeSprite( Knob ); + Knob = img; + break; + + case IE_GUI_SLIDER_GRABBEDKNOB: + if (GrabbedKnob && Clear) + core->GetVideoDriver()->FreeSprite( GrabbedKnob ); + GrabbedKnob = img; + break; + + case IE_GUI_SLIDER_BACKGROUND: + if (BackGround && Clear) + core->GetVideoDriver()->FreeSprite( BackGround ); + BackGround = img; + break; + } + Changed = true; +} + +/** Mouse Button Down */ +void Slider::OnMouseDown(unsigned short x, unsigned short y, unsigned short /*Button*/, + unsigned short /*Mod*/) +{ + Changed = true; + unsigned int oldPos = Pos; + int mx = (KnobXPos + ( Pos * KnobStep ) - Knob->XPos); + int my = (KnobYPos - Knob->YPos); + int Mx = (mx + Knob->Width); + int My = (my + Knob->Height); + + if (( x >= mx ) && ( y >= my )) { + if (( x <= Mx ) && ( y <= My )) { + State = IE_GUI_SLIDER_GRABBEDKNOB; + } else { + int mx = KnobXPos; + int xmx = x - mx; + if (x < mx) { + SetPosition( 0 ); + if (oldPos != Pos) { + RunEventHandler( SliderOnChange ); + } + return; + } + int befst = xmx / KnobStep; + if (befst >= KnobStepsCount) { + SetPosition( KnobStepsCount - 1 ); + if (oldPos != Pos) { + RunEventHandler( SliderOnChange ); + } + return; + } + int aftst = befst + KnobStep; + if (( xmx - ( befst * KnobStep ) ) < + ( ( aftst * KnobStep ) - xmx )) { + SetPosition( befst ); + } else { + SetPosition( aftst ); + } + if (oldPos != Pos) { + RunEventHandler( SliderOnChange ); + } + } + } else { + int mx = KnobXPos; + int xmx = x - mx; + if (x < mx) { + SetPosition( 0 ); + if (oldPos != Pos) { + RunEventHandler( SliderOnChange ); + } + return; + } + int befst = xmx / KnobStep; + if (befst >= KnobStepsCount) { + SetPosition( KnobStepsCount - 1 ); + if (oldPos != Pos) { + RunEventHandler( SliderOnChange ); + } + return; + } + int aftst = befst + KnobStep; + if (( xmx - ( befst * KnobStep ) ) < ( ( aftst * KnobStep ) - xmx )) { + SetPosition( befst ); + } else { + SetPosition( aftst ); + } + if (oldPos != Pos) { + RunEventHandler( SliderOnChange ); + } + } +} + +/** Mouse Button Up */ +void Slider::OnMouseUp(unsigned short /*x*/, unsigned short /*y*/, unsigned short /*Button*/, + unsigned short /*Mod*/) +{ + if (State != IE_GUI_SLIDER_KNOB) { + Changed = true; + } + State = IE_GUI_SLIDER_KNOB; +} + +/** Mouse Over Event */ +void Slider::OnMouseOver(unsigned short x, unsigned short /*y*/) +{ + Changed = true; + unsigned int oldPos = Pos; + if (State == IE_GUI_SLIDER_GRABBEDKNOB) { + int mx = KnobXPos; + int xmx = x - mx; + if (x < mx) { + SetPosition( 0 ); + if (oldPos != Pos) { + RunEventHandler( SliderOnChange ); + } + return; + } + int befst = xmx / KnobStep; + if (befst >= KnobStepsCount) { + SetPosition( KnobStepsCount - 1 ); + if (oldPos != Pos) { + RunEventHandler( SliderOnChange ); + } + return; + } + short aftst = befst + KnobStep; + if (( xmx - ( befst * KnobStep ) ) < ( ( aftst * KnobStep ) - xmx )) { + SetPosition( befst ); + } else { + SetPosition( aftst ); + } + if (oldPos != Pos) { + RunEventHandler( SliderOnChange ); + } + } +} + +/** Sets the Text of the current control */ +int Slider::SetText(const char* /*string*/, int /*pos*/) +{ + return 0; +} + +/** Sets the slider change event */ +bool Slider::SetEvent(int eventType, EventHandler handler) +{ + Changed = true; + + switch (eventType) { + case IE_GUI_SLIDER_ON_CHANGE: + SliderOnChange = handler; + break; + default: + return false; + } + + return true; +} diff --git a/project/jni/application/gemrb/src/core/GUI/Slider.h b/project/jni/application/gemrb/src/core/GUI/Slider.h new file mode 100644 index 000000000..66f26ae0a --- /dev/null +++ b/project/jni/application/gemrb/src/core/GUI/Slider.h @@ -0,0 +1,106 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * + */ + +/** + * @file Slider.h + * Declares Slider widget for displaying scales and sliders for setting + * numerical values + * @author The GemRB Project + */ + +#ifndef SLIDER_H +#define SLIDER_H + +#include "GUI/Control.h" + +#include "exports.h" + +#include "Sprite2D.h" + +// !!! Keep these synchronized with GUIDefines.py !!! +#define IE_GUI_SLIDER_ON_CHANGE 0x02000000 + + +#define IE_GUI_SLIDER_KNOB 0 +#define IE_GUI_SLIDER_GRABBEDKNOB 1 +#define IE_GUI_SLIDER_BACKGROUND 2 + +/** + * @class Slider + * Widget displaying sliders or scales for inputting numerical values + * with a limited range + */ + +class GEM_EXPORT Slider : public Control { +public: + Slider(short KnobXPos, short KnobYPos, short KnobStep, unsigned short KnobStepsCount, bool Clear = false); + ~Slider(); + /** Draws the Control on the Output Display */ + void Draw(unsigned short x, unsigned short y); + /** Returns the actual Slider Position */ + unsigned int GetPosition(); + /** Sets the actual Slider Position trimming to the Max and Min Values */ + void SetPosition(unsigned int pos); + /** Sets the selected image */ + void SetImage(unsigned char type, Sprite2D * img); + /** Sets the Text of the current control */ + int SetText(const char * string, int pos = 0); + /** Sets the State of the Slider */ + void SetState(int arg) { State=(unsigned char) arg; } + /** Redraws a slider which is associated with VariableName */ + void RedrawSlider(const char *VariableName, int Sum); + +private: // Private attributes + /** BackGround Image. If smaller than the Control Size, the image will be tiled. */ + Sprite2D * BackGround; + /** Knob Image */ + Sprite2D * Knob; + /** Grabbed Knob Image */ + Sprite2D * GrabbedKnob; + /** Knob Starting X Position */ + short KnobXPos; + /** Knob Starting Y Position */ + short KnobYPos; + /** Knob Step Size */ + short KnobStep; + /** Knob Steps Count */ + unsigned short KnobStepsCount; + /** If true, on deletion the Slider will destroy the associated images */ + bool Clear; + /** Actual Knob Status */ + unsigned char State; + /** Slider Position Value */ + unsigned int Pos; +public: // Public Events + /** Mouse Button Down */ + void OnMouseDown(unsigned short x, unsigned short y, unsigned short Button, + unsigned short Mod); + /** Mouse Button Up */ + void OnMouseUp(unsigned short x, unsigned short y, unsigned short Button, + unsigned short Mod); + /** Mouse Over Event */ + void OnMouseOver(unsigned short x, unsigned short y); + /** Set handler for specified event */ + bool SetEvent(int eventType, EventHandler handler); + /** OnChange Scripted Event Function Name */ + EventHandler SliderOnChange; +}; + +#endif diff --git a/project/jni/application/gemrb/src/core/GUI/TextArea.cpp b/project/jni/application/gemrb/src/core/GUI/TextArea.cpp new file mode 100644 index 000000000..6f3d05d43 --- /dev/null +++ b/project/jni/application/gemrb/src/core/GUI/TextArea.cpp @@ -0,0 +1,958 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * + */ + +#include "GUI/TextArea.h" + +#include "GUI/GameControl.h" + +#include "win32def.h" + +#include "Audio.h" +#include "DialogHandler.h" +#include "GameData.h" +#include "ImageMgr.h" +#include "Interface.h" +#include "Palette.h" +#include "Variables.h" +#include "Video.h" +#include "Scriptable/Actor.h" + +#include +#include + +TextArea::TextArea(Color hitextcolor, Color initcolor, Color lowtextcolor) +{ + keeplines = 100; + rows = 0; + startrow = 0; + minrow = 0; + Cursor = NULL; + CurPos = 0; + CurLine = 0; + seltext = -1; + Value = 0xffffffff; + ResetEventHandler( TextAreaOnChange ); + ResetEventHandler( TextAreaOutOfText ); + PortraitResRef[0]=0; + palette = core->CreatePalette( hitextcolor, lowtextcolor ); + initpalette = core->CreatePalette( initcolor, lowtextcolor ); + Color tmp = { + hitextcolor.b, hitextcolor.g, hitextcolor.r, 0 + }; + selected = core->CreatePalette( tmp, lowtextcolor ); + tmp.r = 255; + tmp.g = 152; + tmp.b = 102; + lineselpal = core->CreatePalette( tmp, lowtextcolor ); + InternalFlags = 1; + //Drop Capitals means initials on! + core->GetDictionary()->Lookup("Drop Capitals", InternalFlags); + if (InternalFlags) { + InternalFlags = TA_INITIALS; + } +} + +TextArea::~TextArea(void) +{ + gamedata->FreePalette( palette ); + gamedata->FreePalette( initpalette ); + gamedata->FreePalette( selected ); + gamedata->FreePalette( lineselpal ); + core->GetVideoDriver()->FreeSprite( Cursor ); + for (size_t i = 0; i < lines.size(); i++) { + free( lines[i] ); + } +} + +void TextArea::RefreshSprite(const char *portrait) +{ + if (AnimPicture) { + if (!strnicmp(PortraitResRef, portrait, 8) ) { + return; + } + SetAnimPicture(NULL); + } + strnlwrcpy(PortraitResRef, portrait, 8); + if (!strnicmp(PortraitResRef, "none", 8) ) { + return; + } + ResourceHolder im(PortraitResRef); + if (im == NULL) { + return; + } + + SetAnimPicture ( im->GetSprite2D() ); +} + +void TextArea::Draw(unsigned short x, unsigned short y) +{ + /** Don't come back recursively */ + if (InternalFlags&TA_BITEMYTAIL) { + return; + } + int tx=x+XPos; + int ty=y+YPos; + Region clip( tx, ty, Width, Height ); + Video *video = core->GetVideoDriver(); + + if (Flags&IE_GUI_TEXTAREA_SPEAKER) { + if (AnimPicture) { + video->BlitSprite(AnimPicture, tx,ty, true, &clip); + clip.x+=AnimPicture->Width; + clip.w-=AnimPicture->Width; + } + } + + //this might look better in GlobalTimer + //or you might want to change the animated button to work like this + if (Flags &IE_GUI_TEXTAREA_SMOOTHSCROLL) + { + unsigned long thisTime; + + GetTime( thisTime); + if (thisTime>starttime) { + starttime = thisTime+ticks; + smooth--; + while (smooth<=0) { + smooth+=ftext->maxHeight; + if (startrowInvalidate(); + InternalFlags |= TA_BITEMYTAIL; + Owner->DrawWindow(); + InternalFlags &= ~TA_BITEMYTAIL; + } + } + + if (!Changed && !(Owner->Flags&WF_FLOAT) ) { + return; + } + Changed = false; + + if (XPos == 65535) { + return; + } + size_t linesize = lines.size(); + if (linesize == 0) { + return; + } + + //smooth vertical scrolling up + if (Flags & IE_GUI_TEXTAREA_SMOOTHSCROLL) { + clip.y+=smooth; + clip.h-=smooth; + } + + //if textarea is 'selectable' it actually means, it is a listbox + //in this case the selected value equals the line number + //if it is 'not selectable' it can still have selectable lines + //but then it is like the dialog window in the main game screen: + //the selected value is encoded into the line + if (!(Flags & IE_GUI_TEXTAREA_SELECTABLE) ) { + char* Buffer = (char *) malloc( 1 ); + Buffer[0] = 0; + int len = 0; + int lastlen = 0; + for (size_t i = 0; i < linesize; i++) { + if (strnicmp( "[s=", lines[i], 3 ) == 0) { + int tlen; + unsigned long idx, acolor, bcolor; + char* rest; + idx = strtoul( lines[i] + 3, &rest, 0 ); + if (*rest != ',') + goto notmatched; + acolor = strtoul( rest + 1, &rest, 16 ); + if (*rest != ',') + goto notmatched; + bcolor = strtoul( rest + 1, &rest, 16 ); + if (*rest != ']') + goto notmatched; + tlen = (int)(strstr( rest + 1, "[/s]" ) - rest - 1); + if (tlen < 0) + goto notmatched; + len += tlen + 23; + Buffer = (char *) realloc( Buffer, len + 2 ); + if (seltext == (int) i) { + sprintf( Buffer + lastlen, "[color=%6.6lX]%.*s[/color]", + acolor, tlen, rest + 1 ); + } else { + sprintf( Buffer + lastlen, "[color=%6.6lX]%.*s[/color]", + bcolor, tlen, rest + 1 ); + } + } else { + notmatched: + len += ( int ) strlen( lines[i] ) + 1; + Buffer = (char *) realloc( Buffer, len + 2 ); + memcpy( &Buffer[lastlen], lines[i], len - lastlen ); + } + lastlen = len; + if (i != linesize - 1) { + Buffer[lastlen - 1] = '\n'; + Buffer[lastlen] = 0; + } + } + video->SetClipRect( &clip ); + + int pos; + + if (startrow==CurLine) { + pos = CurPos; + } else { + pos = -1; + } + ftext->PrintFromLine( startrow, clip, + ( unsigned char * ) Buffer, palette, + IE_FONT_ALIGN_LEFT, finit, Cursor, pos ); + free( Buffer ); + video->SetClipRect( NULL ); + //streaming text + if (linesize>50) { + //the buffer is filled enough + return; + } + if (core->GetAudioDrv()->IsSpeaking() ) { + //the narrator is still talking + return; + } + if (RunEventHandler( TextAreaOutOfText )) { + return; + } + if (linesize==lines.size()) { + ResetEventHandler( TextAreaOutOfText ); + return; + } + AppendText("\n",-1); + return; + } + // normal scrolling textarea + int rc = 0; + int sr = startrow; + unsigned int i; + int yl; + for (i = 0; i < linesize; i++) { + if (rc + lrows[i] <= sr) { + rc += lrows[i]; + continue; + } + sr -= rc; + Palette* pal = NULL; + if (seltext == (int) i) + pal = selected; + else if (Value == i) + pal = lineselpal; + else + pal = palette; + ftext->PrintFromLine( sr, clip, + ( unsigned char * ) lines[i], pal, + IE_FONT_ALIGN_LEFT, finit, NULL ); + yl = ftext->size[1].h*(lrows[i]-sr); + clip.y+=yl; + clip.h-=yl; + break; + } + for (i++; i < linesize; i++) { + Palette* pal = NULL; + if (seltext == (int) i) + pal = selected; + else if (Value == i) + pal = lineselpal; + else + pal = palette; + ftext->Print( clip, ( unsigned char * ) lines[i], pal, + IE_FONT_ALIGN_LEFT, true ); + yl = ftext->size[1].h*lrows[i]; + clip.y+=yl; + clip.h-=yl; + + } +} +/** Sets the Scroll Bar Pointer. If 'ptr' is NULL no Scroll Bar will be linked + to this Text Area Control. */ +int TextArea::SetScrollBar(Control* ptr) +{ + int ret = Control::SetScrollBar(ptr); + CalcRowCount(); + return ret; +} + +/** Sets the Actual Text */ +int TextArea::SetText(const char* text, int pos) +{ + if (pos==0) { + if (!text[0]) { + lines.clear(); + lrows.clear(); + } + + if (lines.size() == 0) { + pos = -1; + } + } + if (pos >= ( int ) lines.size()) { + return -1; + } + int newlen = ( int ) strlen( text ); + + if (pos == -1) { + char* str = (char *) malloc( newlen + 1 ); + memcpy( str, text, newlen + 1 ); + lines.push_back( str ); + lrows.push_back( 0 ); + } else { + lines[pos] = (char *) realloc( lines[pos], newlen + 1 ); + memcpy( lines[pos], text, newlen + 1 ); + } + CurPos = newlen; + CurLine = lines.size()-1; + UpdateControls(); + return 0; +} + +void TextArea::SetMinRow(bool enable) +{ + if (enable) { + minrow = (int) lines.size(); + } else { + minrow = 0; + } + Changed = true; +} + +//drop lines scrolled out at the top. +//keeplines is the number of lines that should still be +//preserved (for scrollback history) +void TextArea::DiscardLines() +{ + if (rows<=keeplines) { + return; + } + int drop = rows-keeplines; + PopLines(drop, true); +} + +static char *note_const = NULL; +static const char inserted_crap[]="[/color][color=ffffff]"; +#define CRAPLENGTH sizeof(inserted_crap)-1 + +void TextArea::SetNoteString(const char *s) +{ + free(note_const); + if (s) { + note_const = (char *) malloc(strlen(s)+5); + sprintf(note_const, "\r\n\r\n%s", s); + } +} + +/** Appends a String to the current Text */ +int TextArea::AppendText(const char* text, int pos) +{ + int ret = 0; + if (pos >= ( int ) lines.size()) { + return -1; + } + int newlen = ( int ) strlen( text ); + + if (pos == -1) { + const char *note = NULL; + if (note_const) { + note = strstr(text,note_const); + } + char *str; + if (NULL == note) { + str = (char *) malloc( newlen +1 ); + memcpy(str, text, newlen+1); + } + else { + unsigned int notepos = (unsigned int) (note - text); + str = (char *) malloc( newlen + CRAPLENGTH+1 ); + memcpy(str,text,notepos); + memcpy(str+notepos,inserted_crap,CRAPLENGTH); + memcpy(str+notepos+CRAPLENGTH, text+notepos, newlen-notepos+1); + } + lines.push_back( str ); + lrows.push_back( 0 ); + ret =(int) (lines.size() - 1); + } else { + int mylen = ( int ) strlen( lines[pos] ); + + lines[pos] = (char *) realloc( lines[pos], mylen + newlen + 1 ); + memcpy( lines[pos]+mylen, text, newlen + 1 ); + ret = pos; + } + + //if the textarea is not a listbox, then discard scrolled out + //lines + if (Flags&IE_GUI_TEXTAREA_HISTORY) { + DiscardLines(); + } + + UpdateControls(); + return ret; +} + +/** Deletes last or first `count' lines */ +/** Probably not too optimal for many lines, but it isn't used */ +/** for many lines */ +void TextArea::PopLines(unsigned int count, bool top) +{ + if (count > lines.size()) { + count = (unsigned int) lines.size(); + } + + while (count > 0 ) { + if (top) { + int tmp = lrows.front(); + if (minrow || (startrowmaxHeight ); + else + pos = 0; + if (pos < 0) + pos = 0; + bar->SetPos( pos ); + } else { + if (Flags & IE_GUI_TEXTAREA_AUTOSCROLL) { + pos = rows - ( ( Height - 5 ) / ftext->maxHeight ); + SetRow(pos); + } + } + core->RedrawAll(); +} + +/** Sets the Fonts */ +void TextArea::SetFonts(Font* init, Font* text) +{ + finit = init; + ftext = text; + Changed = true; +} + +/** Key Press Event */ +void TextArea::OnKeyPress(unsigned char Key, unsigned short /*Mod*/) +{ + if (Flags & IE_GUI_TEXTAREA_EDITABLE) { + if (Key >= 0x20) { + Owner->Invalidate(); + Changed = true; + int len = GetRowLength(CurLine); + //printf("len: %d Before: %s\n",len, lines[CurLine]); + lines[CurLine] = (char *) realloc( lines[CurLine], len + 2 ); + for (int i = len; i > CurPos; i--) { + lines[CurLine][i] = lines[CurLine][i - 1]; + } + lines[CurLine][CurPos] = Key; + lines[CurLine][len + 1] = 0; + CurPos++; + //printf("pos: %d After: %s\n",CurPos, lines[CurLine]); + CalcRowCount(); + RunEventHandler( TextAreaOnChange ); + } + return; + } + + //Selectable=false for dialogs, rather unintuitive, but fact + if ((Flags & IE_GUI_TEXTAREA_SELECTABLE) || ( Key < '1' ) || ( Key > '9' )) + return; + GameControl *gc = core->GetGameControl(); + if (gc && (gc->GetDialogueFlags()&DF_IN_DIALOG) ) { + Changed = true; + seltext=minrow-1; + if ((unsigned int) seltext>=lines.size()) { + return; + } + for(int i=0;i=lines.size()) { + return; + } + } + while (strnicmp( lines[seltext], "[s=", 3 ) != 0 ); + } + int idx=-1; + sscanf( lines[seltext], "[s=%d,", &idx); + if (idx==-1) { + //this kills this object, don't use any more data! + gc->dialoghandler->EndDialog(); + return; + } + gc->dialoghandler->DialogChoose( idx ); + } +} + +/** Special Key Press */ +void TextArea::OnSpecialKeyPress(unsigned char Key) +{ + int len; + int i; + + if (!(Flags&IE_GUI_TEXTAREA_EDITABLE)) { + return; + } + Owner->Invalidate(); + Changed = true; + switch (Key) { + case GEM_HOME: + CurPos = 0; + CurLine = 0; + break; + case GEM_UP: + if (CurLine) { + CurLine--; + } + break; + case GEM_DOWN: + if (CurLine 0) { + CurPos--; + } else { + if (CurLine) { + CurLine--; + CurPos = GetRowLength(CurLine); + } + } + break; + case GEM_RIGHT: + len = GetRowLength(CurLine); + if (CurPos < len) { + CurPos++; + } else { + if(CurLine=len) { + //TODO: merge next line + break; + } + lines[CurLine] = (char *) realloc( lines[CurLine], len ); + for (i = CurPos; i < len; i++) { + lines[CurLine][i] = lines[CurLine][i + 1]; + } + //printf("pos: %d After: %s\n",CurPos, lines[CurLine]); + break; + case GEM_BACKSP: + len = GetRowLength(CurLine); + if (CurPos != 0) { + //printf("len: %d Before: %s\n",len, lines[CurLine]); + if (len<1) { + break; + } + lines[CurLine] = (char *) realloc( lines[CurLine], len ); + for (i = CurPos; i < len; i++) { + lines[CurLine][i - 1] = lines[CurLine][i]; + } + lines[CurLine][len - 1] = 0; + CurPos--; + //printf("pos: %d After: %s\n",CurPos, lines[CurLine]); + } else { + if (CurLine) { + //TODO: merge lines + int oldline = CurLine; + CurLine--; + int old = GetRowLength(CurLine); + //printf("len: %d Before: %s\n",old, lines[CurLine]); + //printf("len: %d Before: %s\n",len, lines[oldline]); + lines[CurLine] = (char *) realloc (lines[CurLine], len+old); + memcpy(lines[CurLine]+old, lines[oldline],len); + free(lines[oldline]); + lines[CurLine][old+len]=0; + lines.erase(lines.begin()+oldline); + lrows.erase(lrows.begin()+oldline); + CurPos = old; + //printf("pos: %d len: %d After: %s\n",CurPos, GetRowLength(CurLine), lines[CurLine]); + } + } + break; + case GEM_RETURN: + //add an empty line after CurLine + //printf("pos: %d Before: %s\n",CurPos, lines[CurLine]); + lrows.insert(lrows.begin()+CurLine, 0); + len = GetRowLength(CurLine); + //copy the text after the cursor into the new line + char *str = (char *) malloc(len-CurPos+2); + memcpy(str, lines[CurLine]+CurPos, len-CurPos+1); + str[len-CurPos+1] = 0; + lines.insert(lines.begin()+CurLine+1, str); + //truncate the current line + lines[CurLine] = (char *) realloc (lines[CurLine], CurPos+1); + lines[CurLine][CurPos]=0; + //move cursor to next line beginning + CurLine++; + CurPos=0; + //printf("len: %d After: %s\n",GetRowLength(CurLine-1), lines[CurLine-1]); + //printf("len: %d After: %s\n",GetRowLength(CurLine), lines[CurLine]); + break; + } + CalcRowCount(); + RunEventHandler( TextAreaOnChange ); +} + +/** Returns Row count */ +int TextArea::GetRowCount() +{ + return ( int ) lines.size(); +} + +int TextArea::GetRowLength(unsigned int row) +{ + if (lines.size()<=row) { + return 0; + } + //this is just roughly the line size, escape sequences need to be removed + return strlen( lines[row] ); +} + +int TextArea::GetVisibleRowCount() +{ + return (Height-5) / ftext->maxHeight; +} + +/** Returns top index */ +int TextArea::GetTopIndex() +{ + return startrow; +} + +/** Set Starting Row */ +void TextArea::SetRow(int row) +{ + if (row < rows) { + startrow = row; + } + Changed = true; +} + +void TextArea::CalcRowCount() +{ + int tr; + int w = Width; + + if (Flags&IE_GUI_TEXTAREA_SPEAKER) { + const char *portrait = NULL; + Actor *actor = NULL; + GameControl *gc = core->GetGameControl(); + if (gc) { + Scriptable *target = gc->dialoghandler->GetTarget(); + if (target && target->Type == ST_ACTOR) { + actor = (Actor *)target; + } + } + if (actor) { + portrait = actor->GetPortrait(1); + } + if (portrait) { + RefreshSprite(portrait); + } + if (AnimPicture) { + w-=AnimPicture->Width; + } + } + + rows = 0; + if (lines.size() != 0) { + for (size_t i = 0; i < lines.size(); i++) { +// rows++; + tr = 0; + int len = ( int ) strlen( lines[i] ); + char* tmp = (char *) malloc( len + 1 ); + memcpy( tmp, lines[i], len + 1 ); + ftext->SetupString( tmp, w ); + for (int p = 0; p <= len; p++) { + if (( ( unsigned char ) tmp[p] ) == '[') { + p++; + //char tag[256]; + int k = 0; + for (k = 0; k < 256; k++) { + if (tmp[p] == ']') { + //tag[k] = 0; + break; + } + p++; + //tag[k] = tmp[p++]; + } + + continue; + } + if (tmp[p] == 0) { +// if (p != len) +// rows++; + tr++; + } + } + lrows[i] = tr; + rows += tr; + free( tmp ); + } + } + + if (lines.size()) + { + if (CurLine>=lines.size()) { + CurLine=lines.size()-1; + } + w = strlen(lines[CurLine]); + if (CurPos>w) { + CurPos = w; + } + } else { + CurLine=0; + CurPos=0; + } + + if (!sb) { + return; + } + ScrollBar* bar = ( ScrollBar* ) sb; + tr = rows - Height/ftext->size[1].h + 1; + if (tr<0) { + tr = 0; + } + bar->SetMax( (ieWord) tr ); +} +/** Mouse Over Event */ +void TextArea::OnMouseOver(unsigned short /*x*/, unsigned short y) +{ + int height = ftext->maxHeight; //size[1].h; + int r = y / height; + int row = 0; + + for (size_t i = 0; i < lines.size(); i++) { + row += lrows[i]; + if (r < ( row - startrow )) { + if (seltext != (int) i) + core->RedrawAll(); + seltext = ( int ) i; + //printf("CtrlId = 0x%08lx, seltext = %d, rows = %d, row = %d, r = %d\n", ControlID, i, rows, row, r); + return; + } + } + if (seltext != -1) { + core->RedrawAll(); + } + seltext = -1; + //printf("CtrlId = 0x%08lx, seltext = %d, rows %d, row %d, r = %d\n", ControlID, seltext, rows, row, r); +} + +/** Mouse Button Up */ +void TextArea::OnMouseUp(unsigned short x, unsigned short y, unsigned short /*Button*/, + unsigned short /*Mod*/) +{ + if (( x <= Width ) && ( y <= ( Height - 5 ) ) && ( seltext != -1 )) { + Value = (unsigned int) seltext; + Changed = true; + if (strnicmp( lines[seltext], "[s=", 3 ) == 0) { + if (minrow > seltext) + return; + int idx; + sscanf( lines[seltext], "[s=%d,", &idx ); + GameControl* gc = core->GetGameControl(); + if (gc && (gc->GetDialogueFlags()&DF_IN_DIALOG) ) { + if (idx==-1) { + //this kills this object, don't use any more data! + gc->dialoghandler->EndDialog(); + return; + } + gc->dialoghandler->DialogChoose( idx ); + return; + } + } + } + + if (VarName[0] != 0) { + core->GetDictionary()->SetAt( VarName, Value ); + } + RunEventHandler( TextAreaOnChange ); +} + +/** Copies the current TextArea content to another TextArea control */ +void TextArea::CopyTo(TextArea* ta) +{ + ta->Clear(); + for (size_t i = 0; i < lines.size(); i++) { + ta->SetText( lines[i], -1 ); + } +} + +void TextArea::RedrawTextArea(const char* VariableName, unsigned int Sum) +{ + if (strnicmp( VarName, VariableName, MAX_VARIABLE_LENGTH )) { + return; + } + Value = Sum; + Changed = true; +} + +const char* TextArea::QueryText() +{ + if ( Valuegap + //minrow -2 ->npc text + while (i>=minrow-2 && i>=0) { + row+=lrows[i]; + i--; + } + row = GetVisibleRowCount()-row; + while (row>0) { + AppendText("",-1); + row--; + } +} + +void TextArea::SetPreservedRow(int arg) +{ + keeplines=arg; + Flags |= IE_GUI_TEXTAREA_HISTORY; +} + +void TextArea::Clear() +{ + for (size_t i = 0; i < lines.size(); i++) { + free( lines[i] ); + } + lines.clear(); + lrows.clear(); + rows = 0; +} + +//setting up the textarea for smooth scrolling, the first +//TEXTAREA_OUTOFTEXT callback is called automatically +void TextArea::SetupScroll(unsigned long tck) +{ + SetPreservedRow(0); + smooth = ftext->maxHeight; + startrow = 0; + ticks = tck; + //clearing the textarea + Clear(); + unsigned int i = (unsigned int) (Height/smooth); + while (i--) { + char *str = (char *) malloc(1); + str[0]=0; + lines.push_back(str); + lrows.push_back(0); + } + i = (unsigned int) lines.size(); + Flags |= IE_GUI_TEXTAREA_SMOOTHSCROLL; + GetTime( starttime ); + if (RunEventHandler( TextAreaOutOfText )) { + //event handler destructed this object? + return; + } + if (i==lines.size()) { + ResetEventHandler( TextAreaOutOfText ); + return; + } + //recalculates rows + AppendText("\n",-1); +} + +void TextArea::OnMouseDown(unsigned short /*x*/, unsigned short /*y*/, unsigned short Button, + unsigned short /*Mod*/) +{ + + ScrollBar* scrlbr = (ScrollBar*) sb; + + if (!scrlbr) { + Control *ctrl = Owner->GetScrollControl(); + if (ctrl && (ctrl->ControlType == IE_GUI_SCROLLBAR)) { + scrlbr = (ScrollBar *) ctrl; + } + } + if (scrlbr) { + switch(Button) { + case GEM_MB_SCRLUP: + scrlbr->ScrollUp(); + core->RedrawAll(); + break; + case GEM_MB_SCRLDOWN: + scrlbr->ScrollDown(); + core->RedrawAll(); + break; + } + } +} diff --git a/project/jni/application/gemrb/src/core/GUI/TextArea.h b/project/jni/application/gemrb/src/core/GUI/TextArea.h new file mode 100644 index 000000000..c861c068f --- /dev/null +++ b/project/jni/application/gemrb/src/core/GUI/TextArea.h @@ -0,0 +1,175 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * + */ + +/** + * @file TextArea.h + * Declares TextArea widget for displaying long paragraphs of text + * @author The GemRB Project + */ + +#ifndef TEXTAREA_H +#define TEXTAREA_H + +#include "GUI/Control.h" +#include "GUI/ScrollBar.h" + +#include "RGBAColor.h" +#include "exports.h" + +#include "Font.h" + +// Keep these synchronized with GUIDefines.py +// 0x05 is the control type of TextArea +#define IE_GUI_TEXTAREA_ON_CHANGE 0x05000000 +#define IE_GUI_TEXTAREA_OUT_OF_TEXT 0x05000001 + +// TextArea flags, keep these in sync too +// the control type is intentionally left out +#define IE_GUI_TEXTAREA_SELECTABLE 1 +#define IE_GUI_TEXTAREA_AUTOSCROLL 2 +#define IE_GUI_TEXTAREA_SMOOTHSCROLL 4 +#define IE_GUI_TEXTAREA_HISTORY 8 +#define IE_GUI_TEXTAREA_SPEAKER 16 +#define IE_GUI_TEXTAREA_ALT_FONT 32 //this one disables drop capitals +#define IE_GUI_TEXTAREA_EDITABLE 64 + +// internal flags +#define TA_INITIALS 1 +#define TA_BITEMYTAIL 2 + +/** + * @class TextArea + * Widget capable of displaying long paragraphs of text. + * It is usually scrolled with a ScrollBar widget + */ + +class GEM_EXPORT TextArea : public Control { +public: + TextArea(Color hitextcolor, Color initcolor, Color lowtextcolor); + ~TextArea(void); + /** global configuration */ + static void SetNoteString(const char *s); + /** Draws the Control on the Output Display */ + void Draw(unsigned short x, unsigned short y); + /** Sets the Actual Text */ + int SetText(const char* text, int pos = 0); + /** Clears the textarea */ + void Clear(); + /** Discards scrolled out lines from the textarea */ + /** preserving 'keeplines' lines for scroll back history */ + void DiscardLines(); + /** Appends a String to the current Text */ + int AppendText(const char* text, int pos = 0); + /** Deletes `count' lines (either last or top lines)*/ + void PopLines(unsigned int count, bool top = false); + /** Deletes last lines up to current 'minrow' */ + void PopMinRow() + { + PopLines((unsigned int) (lines.size()-minrow)); + } + /** adds empty lines so minrow will be the uppermost visible row */ + void PadMinRow(); + /** Sets up scrolling, tck is the scrolling speed */ + void SetupScroll(unsigned long tck); + /** Sets the Fonts */ + void SetFonts(Font* init, Font* text); + /** Returns Number of Rows */ + int GetRowCount(); + /** Returns the length of a Row */ + int GetRowLength(unsigned int row); + /** Returns Number of Visible Rows */ + int GetVisibleRowCount(); + /** Returns Starting Row */ + int GetTopIndex(); + /** Set Starting Row */ + void SetRow(int row); + /** Sets preserved lines */ + void SetPreservedRow(int arg); + /** Set Selectable */ + void SetSelectable(bool val); + /** Set Minimum Selectable Row (to the current ceiling) */ + void SetMinRow(bool enable); + /** Copies the current TextArea content to another TextArea control */ + void CopyTo(TextArea* ta); + /** Returns the selected text */ + const char* QueryText(); + /** Marks textarea for redraw with a new value */ + void RedrawTextArea(const char* VariableName, unsigned int Sum); + int SetScrollBar(Control *ptr); +private: // Private attributes + std::vector< char*> lines; + std::vector< int> lrows; + int seltext; + /** minimum selectable row */ + int minrow; + /** lines to be kept even if scrolled out */ + int keeplines; + /** vertical offset for smooth scrolling */ + int smooth; + /** timer for scrolling */ + unsigned long starttime; + /** timer ticks for scrolling (speed) */ + unsigned long ticks; + /** Number of Text Rows */ + int rows; + /** Starting Row */ + int startrow; + /** Text Colors */ + Palette* palette; + Palette* initpalette; + Palette* selected; + Palette* lineselpal; + /** a hack for smooth windows, drop capitals */ + ieDword InternalFlags; + /** Fonts */ + Font* finit, * ftext; + ieResRef PortraitResRef; + + /** Text Editing Cursor Sprite */ + Sprite2D* Cursor; + unsigned short CurPos, CurLine; + +private: //internal functions + void CalcRowCount(); + void UpdateControls(); + void RefreshSprite(const char *portrait); + +public: //Events + /** Key Press Event */ + void OnKeyPress(unsigned char Key, unsigned short Mod); + /** Special Key Press */ + void OnSpecialKeyPress(unsigned char Key); + /** Mouse Over Event */ + void OnMouseOver(unsigned short x, unsigned short y); + /** Mouse Button Up */ + void OnMouseUp(unsigned short x, unsigned short y, unsigned short Button, + unsigned short Mod); + /** Mouse button down*/ + void OnMouseDown(unsigned short x, unsigned short y, unsigned short Button, + unsigned short Mod); + /** Set handler for specified event */ + bool SetEvent(int eventType, EventHandler handler); + /** OnChange Scripted Event Function Name */ + EventHandler TextAreaOnChange; + /** OutOfText Scripted Event Function Name */ + EventHandler TextAreaOutOfText; +}; + +#endif diff --git a/project/jni/application/gemrb/src/core/GUI/TextEdit.cpp b/project/jni/application/gemrb/src/core/GUI/TextEdit.cpp new file mode 100644 index 000000000..2b6998106 --- /dev/null +++ b/project/jni/application/gemrb/src/core/GUI/TextEdit.cpp @@ -0,0 +1,231 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * + */ + + +#include "GUI/TextEdit.h" + +#include "GameData.h" +#include "Interface.h" +#include "Palette.h" +#include "Video.h" + +TextEdit::TextEdit(unsigned short maxLength, unsigned short px, unsigned short py) +{ + max = maxLength; + FontPosX = px; + FontPosY = py; + Buffer = ( unsigned char * ) malloc( max + 1 ); + font = NULL; + Cursor = NULL; + Back = NULL; + CurPos = 0; + Buffer[0] = 0; + ResetEventHandler( EditOnChange ); + ResetEventHandler( EditOnDone ); + ResetEventHandler( EditOnCancel ); + Color white = {0xff, 0xff, 0xff, 0x00}, black = {0x00, 0x00, 0x00, 0x00}; + palette = core->CreatePalette( white, black ); +} + +TextEdit::~TextEdit(void) +{ + Video *video = core->GetVideoDriver(); + gamedata->FreePalette( palette ); + free( Buffer ); + video->FreeSprite( Back ); + video->FreeSprite( Cursor ); +} + +/** Draws the Control on the Output Display */ +void TextEdit::Draw(unsigned short x, unsigned short y) +{ + if (!Changed && !(Owner->Flags&WF_FLOAT)) { + return; + } + Changed = false; + if (Back) { + core->GetVideoDriver()->BlitSprite( Back, x + XPos, y + YPos, true ); + + } + if (!font) + return; + + //The aligning of textedit fields is done by absolute positioning (FontPosX, FontPosY) + if (hasFocus) { + font->Print( Region( x + XPos + FontPosX, y + YPos + FontPosY, Width, Height ), Buffer, + palette, IE_FONT_ALIGN_LEFT | IE_FONT_ALIGN_TOP, + true, NULL, Cursor, CurPos ); + } else { + font->Print( Region( x + XPos + FontPosX, y + YPos + FontPosY, Width, Height ), Buffer, + palette, IE_FONT_ALIGN_LEFT | IE_FONT_ALIGN_TOP, true ); + } +} + +/** Set Font */ +void TextEdit::SetFont(Font* f) +{ + if (f != NULL) { + font = f; + Changed = true; + return; + } + printMessage("TextEdit","Invalid font set!\n", LIGHT_RED); +} + +Font *TextEdit::GetFont() { return font; } + +/** Set Cursor */ +void TextEdit::SetCursor(Sprite2D* cur) +{ + core->GetVideoDriver()->FreeSprite( Cursor ); + if (cur != NULL) { + Cursor = cur; + } + Changed = true; +} + +/** Set BackGround */ +void TextEdit::SetBackGround(Sprite2D* back) +{ + //if 'back' is NULL then no BackGround will be drawn + if (Back) + core->GetVideoDriver()->FreeSprite(Back); + Back = back; + Changed = true; +} + +/** Key Press Event */ +void TextEdit::OnKeyPress(unsigned char Key, unsigned short /*Mod*/) +{ + if (Key >= 0x20) { + if (Value && ( (Key<'0') || (Key>'9') ) ) + return; + Owner->Invalidate(); + Changed = true; + int len = ( int ) strlen( ( char* ) Buffer ); + if (len + 1 < max) { + for (int i = len; i > CurPos; i--) { + Buffer[i] = Buffer[i - 1]; + } + Buffer[CurPos] = Key; + Buffer[len + 1] = 0; + CurPos++; + } + RunEventHandler( EditOnChange ); + } +} +/** Special Key Press */ +void TextEdit::OnSpecialKeyPress(unsigned char Key) +{ + int len; + + Owner->Invalidate(); + Changed = true; + switch (Key) { + case GEM_HOME: + CurPos = 0; + break; + case GEM_END: + CurPos = (ieWord) strlen( (char * ) Buffer); + break; + case GEM_LEFT: + if (CurPos > 0) + CurPos--; + break; + case GEM_RIGHT: + len = ( int ) strlen( ( char * ) Buffer ); + if (CurPos < len) { + CurPos++; + } + break; + case GEM_DELETE: + len = ( int ) strlen( ( char * ) Buffer ); + if (CurPos < len) { + for (int i = CurPos; i < len; i++) { + Buffer[i] = Buffer[i + 1]; + } + } + break; + case GEM_BACKSP: + if (CurPos != 0) { + int len = ( int ) strlen( ( char* ) Buffer ); + for (int i = CurPos; i < len; i++) { + Buffer[i - 1] = Buffer[i]; + } + Buffer[len - 1] = 0; + CurPos--; + } + break; + case GEM_RETURN: + RunEventHandler( EditOnDone ); + return; + + } + RunEventHandler( EditOnChange ); +} + +/** Sets the Text of the current control */ +int TextEdit::SetText(const char* string, int /*pos*/) +{ + strncpy( ( char * ) Buffer, string, max ); + Buffer[max]=0; + CurPos = (ieWord) strlen((char *) Buffer); + if (Owner) { + Owner->Invalidate(); + } + return 0; +} + +void TextEdit::SetBufferLength(ieWord buflen) +{ + if(buflen<1) return; + if(buflen!=max) { + Buffer = (unsigned char *) realloc(Buffer, buflen+1); + max=(ieWord) buflen; + Buffer[max]=0; + } +} + +/** Simply returns the pointer to the text, don't modify it! */ +const char* TextEdit::QueryText() +{ + return ( const char * ) Buffer; +} + +bool TextEdit::SetEvent(int eventType, EventHandler handler) +{ + Changed = true; + + switch (eventType) { + case IE_GUI_EDIT_ON_CHANGE: + EditOnChange = handler; + break; + case IE_GUI_EDIT_ON_DONE: + EditOnDone = handler; + break; + case IE_GUI_EDIT_ON_CANCEL: + EditOnCancel = handler; + break; + default: + return false; + } + + return true; +} diff --git a/project/jni/application/gemrb/src/core/GUI/TextEdit.h b/project/jni/application/gemrb/src/core/GUI/TextEdit.h new file mode 100644 index 000000000..f92e91370 --- /dev/null +++ b/project/jni/application/gemrb/src/core/GUI/TextEdit.h @@ -0,0 +1,101 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * + */ + +/** + * @file TextEdit.h + * Declares TextEdit widget for displaying single line text input field + * @author The GemRB Project + */ + +#ifndef TEXTEDIT_H +#define TEXTEDIT_H + +#include "GUI/Control.h" + +#include "RGBAColor.h" +#include "exports.h" + +#include "Font.h" + +class Palette; + +// !!! Keep these synchronized with GUIDefines.py +#define IE_GUI_EDIT_ON_CHANGE 0x03000000 +#define IE_GUI_EDIT_ON_DONE 0x03000001 +#define IE_GUI_EDIT_ON_CANCEL 0x03000002 + +//this is stored in 'Value' of Control class +#define IE_GUI_EDIT_NUMBER 1 + +/** + * @class TextEdit + * Widget displaying single line text input field + */ + +class GEM_EXPORT TextEdit : public Control { +public: + TextEdit(unsigned short maxLength, unsigned short x, unsigned short y); + ~TextEdit(void); + /** Draws the Control on the Output Display */ + void Draw(unsigned short x, unsigned short y); + /** Set Font */ + void SetFont(Font* f); + Font *GetFont(); + /** Set Cursor */ + void SetCursor(Sprite2D* cur); + /** Set BackGround */ + void SetBackGround(Sprite2D* back); + /** Sets the Text of the current control */ + int SetText(const char* string, int pos = 0); + /** Sets the Text of the current control */ + const char* QueryText(); + /** Sets the buffer length */ + void SetBufferLength(ieWord buflen); +private: + /** Text Editing Cursor Sprite */ + Sprite2D* Cursor; + /** Text Font */ + Font* font; + /** Background */ + Sprite2D* Back; + /** Max Edit Text Length */ + unsigned short max; + /** Client area position */ + unsigned short FontPosX, FontPosY; + /** Text Buffer */ + unsigned char* Buffer; + /** Cursor Position */ + unsigned short CurPos; + /** Color Palette */ + Palette* palette; +public: //Events + /** Key Press Event */ + void OnKeyPress(unsigned char Key, unsigned short Mod); + /** Special Key Press */ + void OnSpecialKeyPress(unsigned char Key); + /** Set handler for specified event */ + bool SetEvent(int eventType, EventHandler handler); + /** OnChange Scripted Event Function Name */ + EventHandler EditOnChange; + EventHandler EditOnDone; + EventHandler EditOnCancel; +}; + +#endif diff --git a/project/jni/application/gemrb/src/core/GUI/Window.cpp b/project/jni/application/gemrb/src/core/GUI/Window.cpp new file mode 100644 index 000000000..ed1395aaf --- /dev/null +++ b/project/jni/application/gemrb/src/core/GUI/Window.cpp @@ -0,0 +1,442 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * + */ + +#include "GUI/Window.h" + +#include "GUI/Button.h" +#include "GUI/Control.h" +#include "GUI/MapControl.h" +#include "GUI/Progressbar.h" +#include "GUI/Slider.h" + +#include "win32def.h" + +#include "Interface.h" +#include "Video.h" + +Window::Window(unsigned short WindowID, unsigned short XPos, + unsigned short YPos, unsigned short Width, unsigned short Height) +{ + this->WindowID = WindowID; + this->XPos = XPos; + this->YPos = YPos; + this->Width = Width; + this->Height = Height; + this->BackGround = NULL; + lastC = NULL; + lastFocus = NULL; + lastMouseFocus = NULL; + lastOver = NULL; + Visible = WINDOW_INVISIBLE; + Flags = WF_CHANGED; + Cursor = IE_CURSOR_NORMAL; + DefaultControl[0] = -1; + DefaultControl[1] = -1; + ScrollControl = -1; +} + +Window::~Window() +{ + std::vector< Control*>::iterator m = Controls.begin(); + while (Controls.size() != 0) { + Control* ctrl = ( *m ); + delete ctrl; + Controls.erase( m ); + m = Controls.begin(); + } + core->GetVideoDriver()->FreeSprite( BackGround ); + BackGround = NULL; +} +/** Add a Control in the Window */ +void Window::AddControl(Control* ctrl) +{ + if (ctrl == NULL) { + return; + } + ctrl->Owner = this; + for (size_t i = 0; i < Controls.size(); i++) { + if (Controls[i]->ControlID == ctrl->ControlID) { + delete( Controls[i] ); + Controls[i] = ctrl; + Invalidate(); + return; + } + } + Controls.push_back( ctrl ); + Invalidate(); +} +/** Set the Window's BackGround Image. If 'img' is NULL, no background will be set. If the 'clean' parameter is true (default is false) the old background image will be deleted. */ +void Window::SetBackGround(Sprite2D* img, bool clean) +{ + if (clean && BackGround) { + core->GetVideoDriver()->FreeSprite( this->BackGround ); + } + BackGround = img; + Invalidate(); +} +/** This function Draws the Window on the Output Screen */ +void Window::DrawWindow() +{ + Video* video = core->GetVideoDriver(); + Region clip( XPos, YPos, Width, Height ); + //Frame && Changed + if ( (Flags & (WF_FRAME|WF_CHANGED) )== (WF_FRAME|WF_CHANGED) ) { + Region screen( 0, 0, core->Width, core->Height ); + video->SetClipRect( NULL ); + //removed this? + Color black = { 0, 0, 0, 255 }; + video->DrawRect( screen, black ); + if (core->WindowFrames[0]) + video->BlitSprite( core->WindowFrames[0], 0, 0, true ); + if (core->WindowFrames[1]) + video->BlitSprite( core->WindowFrames[1], core->Width - core->WindowFrames[1]->Width, 0, true ); + if (core->WindowFrames[2]) + video->BlitSprite( core->WindowFrames[2], (core->Width - core->WindowFrames[2]->Width) / 2, 0, true ); + if (core->WindowFrames[3]) + video->BlitSprite( core->WindowFrames[3], (core->Width - core->WindowFrames[3]->Width) / 2, core->Height - core->WindowFrames[3]->Height, true ); + } else if (clip_regions.size()) { + // clip drawing (we only do Background right now) for InvalidateForControl + for (unsigned int i = 0; i < clip_regions.size(); i++) { + Region to_clip = clip_regions[i]; + to_clip.x += XPos; + to_clip.y += YPos; + video->SetClipRect(&to_clip); + if (BackGround) { + video->BlitSprite( BackGround, XPos, YPos, true ); + } + } + } + clip_regions.clear(); + video->SetClipRect( &clip ); + //Float || Changed + if (BackGround && (Flags & (WF_FLOAT|WF_CHANGED) ) ) { + video->BlitSprite( BackGround, XPos, YPos, true ); + } + std::vector< Control*>::iterator m; + for (m = Controls.begin(); m != Controls.end(); ++m) { + ( *m )->Draw( XPos, YPos ); + } + if ( (Flags&WF_CHANGED) && (Visible == WINDOW_GRAYED) ) { + Color black = { 0, 0, 0, 128 }; + video->DrawRect(clip, black); + } + video->SetClipRect( NULL ); + Flags &= ~WF_CHANGED; +} + +/** Set window frame used to fill screen on higher resolutions*/ +void Window::SetFrame() +{ + if ( (Width < core->Width) || (Height < core->Height) ) { + Flags|=WF_FRAME; + } + Invalidate(); +} + +/** Returns the Control at X,Y Coordinates */ +Control* Window::GetControl(unsigned short x, unsigned short y, bool ignore) +{ + Control* ctrl = NULL; + + //Check if we are still on the last control + if (( lastC != NULL )) { + if (( XPos + lastC->XPos <= x ) + && ( YPos + lastC->YPos <= y ) + && ( XPos + lastC->XPos + lastC->Width >= x ) + && ( YPos + lastC->YPos + lastC->Height >= y ) + && ! lastC->IsPixelTransparent (x - XPos - lastC->XPos, y - YPos - lastC->YPos)) { + //Yes, we are on the last returned Control + return lastC; + } + } + std::vector< Control*>::const_iterator m; + for (m = Controls.begin(); m != Controls.end(); m++) { + if (ignore && (*m)->ControlID&IGNORE_CONTROL) { + continue; + } + if (( XPos + ( *m )->XPos <= x ) + && ( YPos + ( *m )->YPos <= y ) + && ( XPos + ( *m )->XPos + ( *m )->Width >= x ) + && ( YPos + ( *m )->YPos + ( *m )->Height >= y ) + && ! ( *m )->IsPixelTransparent (x - XPos - ( *m )->XPos, y - YPos - ( *m )->YPos)) { + ctrl = *m; + break; + } + } + lastC = ctrl; + return ctrl; +} + +Control* Window::GetOver() const +{ + return lastOver; +} + +Control* Window::GetFocus() const +{ + return lastFocus; +} + +Control* Window::GetMouseFocus() const +{ + return lastMouseFocus; +} + +/** Sets 'ctrl' as Focused */ +void Window::SetFocused(Control* ctrl) +{ + if (lastFocus != NULL) { + lastFocus->hasFocus = false; + lastFocus->Changed = true; + } + lastFocus = ctrl; + if (ctrl != NULL) { + lastFocus->hasFocus = true; + lastFocus->Changed = true; + } +} + +/** Sets 'ctrl' as Mouse Focused */ +void Window::SetMouseFocused(Control* ctrl) +{ + if (lastMouseFocus != NULL) { + lastMouseFocus->Changed = true; + } + lastMouseFocus = ctrl; + if (ctrl != NULL) { + lastMouseFocus->Changed = true; + } +} + +unsigned int Window::GetControlCount() const +{ + return Controls.size(); +} + +Control* Window::GetControl(unsigned short i) const +{ + if (i < Controls.size()) { + return Controls[i]; + } + return NULL; +} + +bool Window::IsValidControl(unsigned short ID, Control *ctrl) const +{ + size_t i = Controls.size(); + while (i--) { + if (Controls[i]==ctrl) { + return ctrl->ControlID==ID; + } + } + return false; +} + +void Window::DelControl(unsigned short i) +{ + if (i < Controls.size() ) { + Control *ctrl = Controls[i]; + if (ctrl==lastC) { + lastC=NULL; + } + if (ctrl==lastOver) { + lastOver=NULL; + } + if (ctrl==lastFocus) { + lastFocus=NULL; + } + if (ctrl==lastMouseFocus) { + lastMouseFocus=NULL; + } + delete ctrl; + Controls.erase(Controls.begin()+i); + } + Invalidate(); +} + +Control* Window::GetDefaultControl(unsigned int ctrltype) const +{ + if (!Controls.size()) { + return NULL; + } + if (ctrltype>=2) { + return NULL; + } + return GetControl( (ieWord) DefaultControl[ctrltype] ); +} + +Control* Window::GetScrollControl() const +{ + if (!Controls.size()) { + return NULL; + } + return GetControl( (ieWord) ScrollControl ); +} + +void Window::release(void) +{ + Visible = WINDOW_INVALID; + lastC = NULL; + lastFocus = NULL; + lastMouseFocus = NULL; + lastOver = NULL; +} + +/** Redraw all the Window */ +void Window::Invalidate() +{ + DefaultControl[0] = -1; + DefaultControl[1] = -1; + ScrollControl = -1; + for (unsigned int i = 0; i < Controls.size(); i++) { + if (!Controls[i]) { + continue; + } + Controls[i]->Changed = true; + switch (Controls[i]->ControlType) { + case IE_GUI_SCROLLBAR: + if ((ScrollControl == -1) || (Controls[i]->Flags & IE_GUI_SCROLLBAR_DEFAULT)) + ScrollControl = i; + break; + case IE_GUI_BUTTON: + if (( Controls[i]->Flags & IE_GUI_BUTTON_DEFAULT )) { + DefaultControl[0] = i; + } + if (( Controls[i]->Flags & IE_GUI_BUTTON_CANCEL )) { + DefaultControl[1] = i; + } + break; + //falling through + case IE_GUI_GAMECONTROL: + DefaultControl[0] = i; + DefaultControl[1] = i; + break; + default: ; + } + } + Flags |= WF_CHANGED; +} + +/** Redraw enough to update the specified Control */ +void Window::InvalidateForControl(Control *ctrl) { + // TODO: for this to be general-purpose, we should mark anything inside this + // region with Changed, and also do mass Invalidate() if we overlap with + // another window, but for now this just clips the *background*, see DrawWindow() + clip_regions.push_back( Region(ctrl->XPos, ctrl->YPos, ctrl->Width, ctrl->Height) ); +} + +void Window::RedrawControls(const char* VarName, unsigned int Sum) +{ + for (unsigned int i = 0; i < Controls.size(); i++) { + switch (Controls[i]->ControlType) { + case IE_GUI_MAP: + { + MapControl *mc = ( MapControl* ) (Controls[i]); + mc->RedrawMapControl( VarName, Sum ); + break; + } + case IE_GUI_BUTTON: + { + Button* bt = ( Button* ) ( Controls[i] ); + bt->RedrawButton( VarName, Sum ); + break; + } + case IE_GUI_TEXTAREA: + { + TextArea* pb = ( TextArea* ) ( Controls[i] ); + pb->RedrawTextArea( VarName, Sum ); + break; + } + case IE_GUI_PROGRESSBAR: + { + Progressbar* pb = ( Progressbar* ) ( Controls[i] ); + pb->RedrawProgressbar( VarName, Sum ); + break; + } + case IE_GUI_SLIDER: + { + Slider* sl = ( Slider* ) ( Controls[i] ); + sl->RedrawSlider( VarName, Sum ); + break; + } + case IE_GUI_SCROLLBAR: + { + ScrollBar* sb = ( ScrollBar* ) ( Controls[i] ); + sb->RedrawScrollBar( VarName, Sum ); + break; + } + } + } +} + +/** Searches for a ScrollBar and a TextArea to link them */ +void Window::Link(unsigned short SBID, unsigned short TAID) +{ + ScrollBar* sb = NULL; + TextArea* ta = NULL; + std::vector< Control*>::iterator m; + for (m = Controls.begin(); m != Controls.end(); m++) { + if (( *m )->Owner != this) + continue; + if (( *m )->ControlType == IE_GUI_SCROLLBAR) { + if (( *m )->ControlID == SBID) { + sb = ( ScrollBar * ) ( *m ); + if (ta != NULL) + break; + } + } else if (( *m )->ControlType == IE_GUI_TEXTAREA) { + if (( *m )->ControlID == TAID) { + ta = ( TextArea * ) ( *m ); + if (sb != NULL) + break; + } + } + } + if (sb && ta) { + sb->ta = ta; + ta->SetScrollBar( sb ); + } +} + +void Window::OnMouseEnter(unsigned short x, unsigned short y, Control *ctrl) +{ + lastOver = ctrl; + if (!lastOver) { + return; + } + lastOver->OnMouseEnter( x - XPos - lastOver->XPos, y - YPos - lastOver->YPos ); +} + +void Window::OnMouseLeave(unsigned short x, unsigned short y) +{ + if (!lastOver) { + return; + } + lastOver->OnMouseLeave( x - XPos - lastOver->XPos, y - YPos - lastOver->YPos ); + lastOver = NULL; +} + +void Window::OnMouseOver(unsigned short x, unsigned short y) +{ + if (!lastOver) { + return; + } + lastOver->OnMouseOver( x - XPos - lastOver->XPos, y - YPos - lastOver->YPos ); +} diff --git a/project/jni/application/gemrb/src/core/GUI/Window.h b/project/jni/application/gemrb/src/core/GUI/Window.h new file mode 100644 index 000000000..a4798ddee --- /dev/null +++ b/project/jni/application/gemrb/src/core/GUI/Window.h @@ -0,0 +1,190 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * + */ + +/** + * @file Window.h + * Declares Window, class serving as a container for Control/widget objects + * and displaying windows in GUI + * @author The GemRB Project + */ + +#ifndef WINDOW_H +#define WINDOW_H + +#include "GUI/Control.h" +#include "GUI/ScrollBar.h" +#include "GUI/TextArea.h" + +#include "exports.h" + +#include "Sprite2D.h" + +#include + +// Window Flags +#define WF_CHANGED 1 //window changed +#define WF_FRAME 2 //window has frame +#define WF_FLOAT 4 //floating window +#define WF_CHILD 8 //if invalidated, it invalidates all windows on top of it + +// Window position anchors (actually flags for WindowSetPos()) +// !!! Keep these synchronized with GUIDefines.py !!! +#define WINDOW_TOPLEFT 0x00 +#define WINDOW_CENTER 0x01 +#define WINDOW_ABSCENTER 0x02 +#define WINDOW_RELATIVE 0x04 +#define WINDOW_SCALE 0x08 +#define WINDOW_BOUNDED 0x10 + +// IE specific cursor types + +#define IE_CURSOR_INVALID -1 +#define IE_CURSOR_NORMAL 0 +#define IE_CURSOR_TAKE 2 //over pile type containers +#define IE_CURSOR_WALK 4 +#define IE_CURSOR_BLOCKED 6 +#define IE_CURSOR_USE 8 //never hardcoded +#define IE_CURSOR_WAIT 10 //hourglass +#define IE_CURSOR_ATTACK 12 +#define IE_CURSOR_SWAP 14 //dragging portraits +#define IE_CURSOR_DEFEND 16 +#define IE_CURSOR_TALK 18 +#define IE_CURSOR_CAST 20 //targeting with non weapon +#define IE_CURSOR_INFO 22 //never hardcoded +#define IE_CURSOR_LOCK 24 //locked door +#define IE_CURSOR_LOCK2 26 //locked container +#define IE_CURSOR_STAIR 28 //never hardcoded +#define IE_CURSOR_DOOR 30 //doors +#define IE_CURSOR_CHEST 32 +#define IE_CURSOR_TRAVEL 34 +#define IE_CURSOR_STEALTH 36 +#define IE_CURSOR_TRAP 38 +#define IE_CURSOR_PICK 40 //pickpocket +#define IE_CURSOR_PASS 42 //never hardcoded +#define IE_CURSOR_GRAB 44 +#define IE_CURSOR_WAY 46 //waypoint (not in PST) +#define IE_CURSOR_INFO2 46 //PST +#define IE_CURSOR_PORTAL 48 //PST +#define IE_CURSOR_STAIR2 50 //PST +#define IE_CURSOR_EXTRA 52 //PST + +#define IE_CURSOR_MASK 127 +#define IE_CURSOR_GRAY 128 +/** + * @class Window + * Class serving as a container for Control/widget objects + * and displaying windows in GUI. + */ + +class GEM_EXPORT Window { +public: + Window(unsigned short WindowID, unsigned short XPos, unsigned short YPos, + unsigned short Width, unsigned short Height); + ~Window(); + /** Set the Window's BackGround Image. + * If 'img' is NULL, no background will be set. If the 'clean' parameter is true (default is false) the old background image will be deleted. */ + void SetBackGround(Sprite2D* img, bool clean = false); + /** Add a Control in the Window */ + void AddControl(Control* ctrl); + /** This function Draws the Window on the Output Screen */ + void DrawWindow(); + /** Set window frame used to fill screen on higher resolutions*/ + void SetFrame(); + /** Returns the Control at X,Y Coordinates */ + Control* GetControl(unsigned short x, unsigned short y, bool ignore=0); + /** Returns the Control by Index */ + Control* GetControl(unsigned short i) const; + /** Returns the number of Controls */ + unsigned int GetControlCount() const; + /** Returns true if ctrl is valid and ctrl->ControlID is ID */ + bool IsValidControl(unsigned short ID, Control *ctrl) const; + /** Deletes the xth. Control */ + void DelControl(unsigned short i); + /** Returns the Default Control which may be a button/gamecontrol atm */ + Control* GetDefaultControl(unsigned int ctrltype) const; + /** Returns the Control which should get mouse scroll events */ + Control* GetScrollControl() const; + /** Sets 'ctrl' as currently under mouse */ + void SetOver(Control* ctrl); + /** Returns last control under mouse */ + Control* GetOver() const; + /** Sets 'ctrl' as Focused */ + void SetFocused(Control* ctrl); + /** Sets 'ctrl' as mouse event Focused */ + void SetMouseFocused(Control* ctrl); + /** Returns last focused control */ + Control* GetFocus() const; + /** Returns last mouse event focused control */ + Control* GetMouseFocus() const; + /** Redraw all the Window */ + void Invalidate(); + /** Redraw enough to update the specified Control */ + void InvalidateForControl(Control *ctrl); + /** Redraw controls of the same group */ + void RedrawControls(const char* VarName, unsigned int Sum); + /** Links a scrollbar to a text area */ + void Link(unsigned short SBID, unsigned short TAID); + /** Mouse entered a new control's rectangle */ + void OnMouseEnter(unsigned short x, unsigned short y, Control *ctrl); + /** Mouse left the current control */ + void OnMouseLeave(unsigned short x, unsigned short y); + /** Mouse is over the current control */ + void OnMouseOver(unsigned short x, unsigned short y); +public: //Public attributes + /** WinPack */ + char WindowPack[10]; + /** Window ID */ + unsigned short WindowID; + /** X Position */ + unsigned short XPos; + /** Y Position */ + unsigned short YPos; + /** Width */ + unsigned short Width; + /** Height */ + unsigned short Height; + /** Visible value: deleted, invisible, visible, grayed */ + signed char Visible; //-1,0,1,2 + /** Window flags: Changed, Floating, Framed, Child */ + int Flags; + int Cursor; + int DefaultControl[2]; //default enter and cancel + int ScrollControl; +private: // Private attributes + /** BackGround Image. No BackGround if this variable is NULL. */ + Sprite2D* BackGround; + /** Controls Array */ + std::vector< Control*> Controls; + /** Last Control returned by GetControl */ + Control* lastC; + /** Last Focused Control */ + Control* lastFocus; + /** Last mouse event Focused Control */ + Control* lastMouseFocus; + /** Last Control under mouse */ + Control* lastOver; + /** Regions which need to be redrawn */ + std::vector< Region> clip_regions; + +public: + void release(void); +}; + +#endif diff --git a/project/jni/application/gemrb/src/core/GUI/WorldMapControl.cpp b/project/jni/application/gemrb/src/core/GUI/WorldMapControl.cpp new file mode 100644 index 000000000..a0591b043 --- /dev/null +++ b/project/jni/application/gemrb/src/core/GUI/WorldMapControl.cpp @@ -0,0 +1,399 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "GUI/WorldMapControl.h" + +#include "win32def.h" + +#include "Game.h" +#include "GameData.h" +#include "Interface.h" +#include "Video.h" +#include "WorldMap.h" + +#define MAP_TO_SCREENX(x) XWin + XPos - ScrollX + (x) +#define MAP_TO_SCREENY(y) YWin + YPos - ScrollY + (y) + +WorldMapControl::WorldMapControl(const char *font, int direction) +{ + ScrollX = 0; + ScrollY = 0; + MouseIsDown = false; + Changed = true; + Area = NULL; + Value = direction; + Game* game = core->GetGame(); + WorldMap* worldmap = core->GetWorldMap(); + strncpy(currentArea, game->CurrentArea, 8); + int entry = core->GetAreaAlias(currentArea); + if (entry >= 0) { + WMPAreaEntry *m = worldmap->GetEntry(entry); + strncpy(currentArea, m->AreaResRef, 8); + } + + //if there is no trivial area, look harder + if (!worldmap->GetArea(currentArea, (unsigned int &) entry) && + core->HasFeature(GF_FLEXIBLE_WMAP) ) { + WMPAreaEntry *m = worldmap->FindNearestEntry(currentArea, (unsigned int &) entry); + if (m) { + strncpy(currentArea, m->AreaResRef, 8); + } + } + + //this also updates visible locations + worldmap->CalculateDistances(currentArea, Value); + + // alpha bit is unfortunately ignored + if (font[0]) { + ftext = core->GetFont(font); + } else { + ftext = NULL; + } + + // initialize label colors + Color normal = { 0xf0, 0xf0, 0xf0, 0xff }; + Color selected = { 0xf0, 0x80, 0x80, 0xff }; + Color notvisited = { 0x80, 0x80, 0xf0, 0xff }; + Color black = { 0x00, 0x00, 0x00, 0x00 }; + + pal_normal = core->CreatePalette ( normal, black ); + pal_selected = core->CreatePalette ( selected, black ); + pal_notvisited = core->CreatePalette ( notvisited, black ); + + + ResetEventHandler( WorldMapControlOnPress ); + ResetEventHandler( WorldMapControlOnEnter ); +} + +WorldMapControl::~WorldMapControl(void) +{ + //Video *video = core->GetVideoDriver(); + + gamedata->FreePalette( pal_normal ); + gamedata->FreePalette( pal_selected ); + gamedata->FreePalette( pal_notvisited ); +} + +/** Draws the Control on the Output Display */ +void WorldMapControl::Draw(unsigned short XWin, unsigned short YWin) +{ + WorldMap* worldmap = core->GetWorldMap(); + if (!Width || !Height) { + return; + } + if(!Changed) + return; + Changed = false; + Video* video = core->GetVideoDriver(); + Region r( XWin+XPos, YWin+YPos, Width, Height ); + Region clipbackup; + video->GetClipRect(clipbackup); + video->SetClipRect(&r); + video->BlitSprite( worldmap->GetMapMOS(), MAP_TO_SCREENX(0), MAP_TO_SCREENY(0), true, &r ); + + unsigned int i; + unsigned int ec = worldmap->GetEntryCount(); + for(i=0;iGetEntry(i); + if (! (m->GetAreaStatus() & WMP_ENTRY_VISIBLE)) continue; + + int xOffs = MAP_TO_SCREENX(m->X); + int yOffs = MAP_TO_SCREENY(m->Y); + Sprite2D* icon = m->GetMapIcon(worldmap->bam); + if( icon ) { + video->BlitSprite( icon, xOffs, yOffs, true, &r ); + video->FreeSprite( icon ); + } + + if (AnimPicture && !strnicmp(m->AreaResRef, currentArea, 8) ) { + core->GetVideoDriver()->BlitSprite( AnimPicture, xOffs, yOffs, true, &r ); + } + } + + // Draw WMP entry labels + if (ftext==NULL) { + video->SetClipRect(&clipbackup); + return; + } + for(i=0;iGetEntry(i); + if (! (m->GetAreaStatus() & WMP_ENTRY_VISIBLE)) continue; + Sprite2D *icon=m->GetMapIcon(worldmap->bam); + int h=0,w=0,xpos=0,ypos=0; + if (icon) { + h=icon->Height; + w=icon->Width; + xpos=icon->XPos; + ypos=icon->YPos; + video->FreeSprite( icon ); + } + + Region r2 = Region( MAP_TO_SCREENX(m->X-xpos), MAP_TO_SCREENY(m->Y-ypos), w, h ); + if (!m->GetCaption()) + continue; + + int tw = ftext->CalcStringWidth( m->GetCaption() ) + 5; + int th = ftext->maxHeight; + + Palette* text_pal = pal_normal; + + if (Area == m) { + text_pal = pal_selected; + } else { + if (! (m->GetAreaStatus() & WMP_ENTRY_VISITED)) { + text_pal = pal_notvisited; + } + } + + ftext->Print( Region( r2.x + (r2.w - tw)/2, r2.y + r2.h, tw, th ), + ( const unsigned char * ) m->GetCaption(), text_pal, 0, true ); + } + video->SetClipRect(&clipbackup); +} + +/** Key Release Event */ +void WorldMapControl::OnKeyRelease(unsigned char Key, unsigned short Mod) +{ + switch (Key) { + case 'f': + if (Mod & GEM_MOD_CTRL) + core->GetVideoDriver()->ToggleFullscreenMode(); + break; + default: + break; + } +} +void WorldMapControl::AdjustScrolling(short x, short y) +{ + WorldMap* worldmap = core->GetWorldMap(); + if (x || y) { + ScrollX += x; + ScrollY += y; + } else { + //center worldmap on current area + unsigned entry; + + WMPAreaEntry *m = worldmap->GetArea(currentArea,entry); + if (m) { + ScrollX = m->X - Width/2; + ScrollY = m->Y - Height/2; + } + } + Sprite2D *MapMOS = worldmap->GetMapMOS(); + if (ScrollX > MapMOS->Width - Width) + ScrollX = MapMOS->Width - Width; + if (ScrollY > MapMOS->Height - Height) + ScrollY = MapMOS->Height - Height; + if (ScrollX < 0) + ScrollX = 0; + if (ScrollY < 0) + ScrollY = 0; + Changed = true; + Area = NULL; +} + +/** Mouse Over Event */ +void WorldMapControl::OnMouseOver(unsigned short x, unsigned short y) +{ + WorldMap* worldmap = core->GetWorldMap(); + lastCursor = IE_CURSOR_GRAB; + + if (MouseIsDown) { + AdjustScrolling(lastMouseX-x, lastMouseY-y); + } + + lastMouseX = x; + lastMouseY = y; + + if (Value!=(ieDword) -1) { + x =(ieWord) (x + ScrollX); + y =(ieWord) (y + ScrollY); + + WMPAreaEntry *oldArea = Area; + Area = NULL; + + unsigned int i; + unsigned int ec = worldmap->GetEntryCount(); + for (i=0;iGetEntry(i); + + if ( (ae->GetAreaStatus() & WMP_ENTRY_WALKABLE)!=WMP_ENTRY_WALKABLE) { + continue; //invisible or inaccessible + } + if (!strnicmp(ae->AreaResRef, currentArea, 8) ) { + continue; //current area + } + + Sprite2D *icon=ae->GetMapIcon(worldmap->bam); + int h=0,w=0; + if (icon) { + h=icon->Height; + w=icon->Width; + core->GetVideoDriver()->FreeSprite( icon ); + } + if (ftext && ae->GetCaption()) { + int tw = ftext->CalcStringWidth( ae->GetCaption() ) + 5; + int th = ftext->maxHeight; + if(hX > x) continue; + if (ae->X + w < x) continue; + if (ae->Y > y) continue; + if (ae->Y + h < y) continue; + lastCursor = IE_CURSOR_NORMAL; + Area=ae; + if(oldArea!=ae) { + RunEventHandler(WorldMapControlOnEnter); + } + break; + } + } + + Owner->Cursor = lastCursor; +} + +/** Sets the tooltip to be displayed on the screen now */ +void WorldMapControl::DisplayTooltip() +{ + if (Area) { + int x = Owner->XPos+XPos+lastMouseX; + int y = Owner->YPos+YPos+lastMouseY-50; + core->DisplayTooltip( x, y, this ); + } else { + core->DisplayTooltip( 0, 0, NULL ); + } +} + +/** Mouse Leave Event */ +void WorldMapControl::OnMouseLeave(unsigned short /*x*/, unsigned short /*y*/) +{ + Owner->Cursor = IE_CURSOR_NORMAL; + Area = NULL; +} + +/** Mouse Button Down */ +void WorldMapControl::OnMouseDown(unsigned short x, unsigned short y, + unsigned short Button, unsigned short /*Mod*/) +{ + switch(Button) { + case GEM_MB_ACTION: + MouseIsDown = true; + lastMouseX = x; + lastMouseY = y; + break; + case GEM_MB_SCRLUP: + OnSpecialKeyPress(GEM_UP); + break; + case GEM_MB_SCRLDOWN: + OnSpecialKeyPress(GEM_DOWN); + break; + } +} +/** Mouse Button Up */ +void WorldMapControl::OnMouseUp(unsigned short /*x*/, unsigned short /*y*/, + unsigned short Button, unsigned short /*Mod*/) +{ + if (Button != GEM_MB_ACTION) { + return; + } + if (lastCursor==IE_CURSOR_NORMAL) { + RunEventHandler( WorldMapControlOnPress ); + } + MouseIsDown = false; +} + +/** Special Key Press */ +void WorldMapControl::OnSpecialKeyPress(unsigned char Key) +{ + WorldMap* worldmap = core->GetWorldMap(); + switch (Key) { + case GEM_LEFT: + ScrollX -= 64; + break; + case GEM_UP: + ScrollY -= 64; + break; + case GEM_RIGHT: + ScrollX += 64; + break; + case GEM_DOWN: + ScrollY += 64; + break; + case GEM_ALT: + printf( "ALT pressed\n" ); + break; + case GEM_TAB: + printf( "TAB pressed\n" ); + break; + } + + Sprite2D *MapMOS = worldmap->GetMapMOS(); + if (ScrollX > MapMOS->Width - Width) + ScrollX = MapMOS->Width - Width; + if (ScrollY > MapMOS->Height - Height) + ScrollY = MapMOS->Height - Height; + if (ScrollX < 0) + ScrollX = 0; + if (ScrollY < 0) + ScrollY = 0; +} + +bool WorldMapControl::SetEvent(int eventType, EventHandler handler) +{ + Changed = true; + + switch (eventType) { + case IE_GUI_WORLDMAP_ON_PRESS: + WorldMapControlOnPress = handler; + break; + case IE_GUI_MOUSE_ENTER_WORLDMAP: + WorldMapControlOnEnter = handler; + break; + default: + return false; + } + + return true; +} + +void WorldMapControl::SetColor(int which, Color color) +{ + Color black = { 0x00, 0x00, 0x00, 0x00 }; + switch (which) { + case IE_GUI_WMAP_COLOR_NORMAL: + gamedata->FreePalette( pal_normal ); + pal_normal = core->CreatePalette( color, black ); + break; + case IE_GUI_WMAP_COLOR_SELECTED: + gamedata->FreePalette( pal_selected ); + pal_selected = core->CreatePalette( color, black ); + break; + case IE_GUI_WMAP_COLOR_NOTVISITED: + gamedata->FreePalette( pal_notvisited ); + pal_notvisited = core->CreatePalette( color, black ); + break; + default: + break; + } + + Changed = true; +} diff --git a/project/jni/application/gemrb/src/core/GUI/WorldMapControl.h b/project/jni/application/gemrb/src/core/GUI/WorldMapControl.h new file mode 100644 index 000000000..36ee06007 --- /dev/null +++ b/project/jni/application/gemrb/src/core/GUI/WorldMapControl.h @@ -0,0 +1,115 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * + */ + +/** + * @file WorldMapControl.h + * Declares WorldMapControl, widget for displaying world map + */ + + +#ifndef WORLDMAPCONTROL_H +#define WORLDMAPCONTROL_H + +#include "GUI/Control.h" + +#include "exports.h" + +#include "Dialog.h" +#include "Interface.h" + +class Palette; +class WMPAreaEntry; +class WorldMapControl; + +// !!! Keep these synchronized with GUIDefines.py !!! +/** Which label color is set with SetColor() */ +#define IE_GUI_WMAP_COLOR_NORMAL 0 +#define IE_GUI_WMAP_COLOR_SELECTED 1 +#define IE_GUI_WMAP_COLOR_NOTVISITED 2 + + +/** + * @class WorldMapControl + * Widget displaying "world" map, with particular locations and possibly + * allowing travelling between areas. + */ + +#define IE_GUI_WORLDMAP_ON_PRESS 0x08000000 +#define IE_GUI_MOUSE_ENTER_WORLDMAP 0x08000002 + +class GEM_EXPORT WorldMapControl : public Control { +public: + WorldMapControl(const char *fontname, int direction); + ~WorldMapControl(void); + + /** Allows modification of the scrolling factor from outside */ + void AdjustScrolling(short x, short y); + /** Draws the Control on the Output Display */ + void Draw(unsigned short x, unsigned short y); + /** Sets the exit direction (we need this to calculate distances) */ + void SetDirection(int direction); + /** Sets the Text of the current control */ + int SetText(const char* /*string*/, int /*pos*/) { return 0; } + /** Set color for one type of area labels */ + void SetColor(int which, Color color); + int ScrollX, ScrollY; + unsigned short lastMouseX, lastMouseY; + bool MouseIsDown; + /** pointer to last pointed area */ + WMPAreaEntry *Area; + /** Set handler for specified event */ + bool SetEvent(int eventType, EventHandler handler); +private: + //font for printing area names + Font* ftext; + //mouse cursor + unsigned char lastCursor; + //current area + ieResRef currentArea; + /** Label color of a visited area */ + Palette *pal_normal; + /** Label color of a currently selected area */ + Palette *pal_selected; + /** Label color of a not yet visited area */ + Palette *pal_notvisited; + /** guiscript Event when button pressed */ + EventHandler WorldMapControlOnPress; + /** guiscript Event when mouse is over a reachable area */ + EventHandler WorldMapControlOnEnter; + + /** Mouse Over Event */ + void OnMouseOver(unsigned short x, unsigned short y); + /** Mouse Leave Event */ + void OnMouseLeave(unsigned short x, unsigned short y); + /** Mouse Button Down */ + void OnMouseDown(unsigned short x, unsigned short y, unsigned short Button, + unsigned short Mod); + /** Mouse Button Up */ + void OnMouseUp(unsigned short x, unsigned short y, unsigned short Button, + unsigned short Mod); + /** Key Release Event */ + void OnKeyRelease(unsigned char Key, unsigned short Mod); + /** Special Key Press */ + void OnSpecialKeyPress(unsigned char Key); + /** DisplayTooltip */ + void DisplayTooltip(); +}; + +#endif diff --git a/project/jni/application/gemrb/src/core/Game.cpp b/project/jni/application/gemrb/src/core/Game.cpp new file mode 100644 index 000000000..59a218593 --- /dev/null +++ b/project/jni/application/gemrb/src/core/Game.cpp @@ -0,0 +1,1816 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2004 The GemRB Project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * + */ + +// This class represents the .gam (savegame) file in the engine + +#include "Game.h" + +#include "defsounds.h" +#include "strrefs.h" +#include "win32def.h" + +#include "DisplayMessage.h" +#include "GameData.h" +#include "Interface.h" +#include "MapMgr.h" +#include "MusicMgr.h" +#include "Particles.h" +#include "ScriptEngine.h" +#include "GameScript/GameScript.h" +#include "GUI/GameControl.h" +#include "System/DataStream.h" + +#define MAX_MAPS_LOADED 1 + +Game::Game(void) : Scriptable( ST_GLOBAL ) +{ + protagonist = PM_YES; //set it to 2 for iwd/iwd2 and 0 for pst + partysize = 6; + Ticks = 0; + version = 0; + Expansion = 0; + LoadMos[0] = 0; + SelectedSingle = 1; //the PC we are looking at (inventory, shop) + PartyGold = 0; + SetScript( core->GlobalScript, 0 ); + MapIndex = -1; + Reputation = 0; + ControlStatus = 0; + CombatCounter = 0; //stored here until we know better + StateOverrideTime = 0; + StateOverrideFlag = 0; + BanterBlockTime = 0; + BanterBlockFlag = 0; + WeatherBits = 0; + crtable = NULL; + kaputz = NULL; + beasts = NULL; + mazedata = NULL; + timestop_owner = NULL; + timestop_end = 0; + event_timer = 0; + event_handler = NULL; + weather = new Particles(200); + weather->SetRegion(0, 0, core->Width, core->Height); + LastScriptUpdate = 0; + + //loading master areas + AutoTable table; + if (table.load("mastarea")) { + int i = table->GetRowCount(); + mastarea.reserve(i); + while(i--) { + char *tmp = (char *) malloc(9); + strnuprcpy (tmp,table->QueryField(i,0),8); + mastarea.push_back( tmp ); + } + } + + //loading rest/daylight switching movies (only bg2 has them) + memset(restmovies,'*',sizeof(restmovies)); + memset(daymovies,'*',sizeof(restmovies)); + memset(nightmovies,'*',sizeof(restmovies)); + if (table.load("restmov")) { + for(int i=0;i<8;i++) { + strnuprcpy(restmovies[i],table->QueryField(i,0),8); + strnuprcpy(daymovies[i],table->QueryField(i,1),8); + strnuprcpy(nightmovies[i],table->QueryField(i,2),8); + } + } + + interval = 1000/AI_UPDATE_TIME; + //FIXME:i'm not sure in this... + NoInterrupt(); +} + +Game::~Game(void) +{ + size_t i; + + delete weather; + for (i = 0; i < Maps.size(); i++) { + delete( Maps[i] ); + } + for (i = 0; i < PCs.size(); i++) { + delete ( PCs[i] ); + } + for (i = 0; i < NPCs.size(); i++) { + delete ( NPCs[i] ); + } + for (i = 0; i < mastarea.size(); i++) { + free ( mastarea[i] ); + } + + if (crtable) { + delete[] crtable; + } + + if (mazedata) { + free (mazedata); + } + if (kaputz) { + delete kaputz; + } + if (beasts) { + free (beasts); + } + i=Journals.size(); + while(i--) { + delete Journals[i]; + } + + i=savedpositions.size(); + while(i--) { + free (savedpositions[i]); + } + + i=planepositions.size(); + while(i--) { + free (planepositions[i]); + } +} + +bool IsAlive(Actor *pc) +{ + if (pc->GetStat(IE_STATE_ID)&STATE_DEAD) { + return false; + } + return true; +} + +int Game::FindPlayer(unsigned int partyID) +{ + for (unsigned int slot=0; slotInParty==partyID) { + return slot; + } + } + return -1; +} + +Actor* Game::FindPC(unsigned int partyID) +{ + for (unsigned int slot=0; slotInParty==partyID) return PCs[slot]; + } + return NULL; +} + +Actor* Game::FindPC(const char *scriptingname) +{ + for (unsigned int slot=0; slotGetScriptName(),scriptingname,32)==0 ) { + return PCs[slot]; + } + } + return NULL; +} + +Actor* Game::FindNPC(unsigned int partyID) +{ + for (unsigned int slot=0; slotInParty==partyID) return NPCs[slot]; + } + return NULL; +} + +Actor* Game::FindNPC(const char *scriptingname) +{ + for (unsigned int slot=0; slotGetScriptName(),scriptingname,32)==0 ) + { + return NPCs[slot]; + } + } + return NULL; +} + +Actor *Game::GetGlobalActorByGlobalID(ieDword globalID) +{ + unsigned int slot; + + for (slot=0; slotGetGlobalID()==globalID ) { + return PCs[slot]; + } + } + for (slot=0; slotGetGlobalID()==globalID ) { + return NPCs[slot]; + } + } + return NULL; +} + +Actor* Game::GetPC(unsigned int slot, bool onlyalive) +{ + if (slot >= PCs.size()) { + return NULL; + } + if (onlyalive) { + unsigned int i=0; + while(i= PCs.size()) { + return -1; + } + if (!PCs[slot]) { + return -1; + } + SelectActor(PCs[slot], false, SELECT_NORMAL); + if (autoFree) { + delete( PCs[slot] ); + } + std::vector< Actor*>::iterator m = PCs.begin() + slot; + PCs.erase( m ); + return 0; +} + +int Game::DelNPC(unsigned int slot, bool autoFree) +{ + if (slot >= NPCs.size()) { + return -1; + } + if (!NPCs[slot]) { + return -1; + } + if (autoFree) { + delete( NPCs[slot] ); + } + std::vector< Actor*>::iterator m = NPCs.begin() + slot; + NPCs.erase( m ); + return 0; +} + +//i'm sure this could be faster +void Game::ConsolidateParty() +{ + int max = (int) PCs.size(); + std::vector< Actor*>::const_iterator m; + for (int i=1;i<=max;) { + if (FindPlayer(i)==-1) { + + for ( m = PCs.begin(); m != PCs.end(); ++m) { + if ( (*m)->InParty>i) { + (*m)->InParty--; + } + } + } else i++; + } + for ( m = PCs.begin(); m != PCs.end(); ++m) { + (*m)->RefreshEffects(NULL); + } +} + +int Game::LeaveParty (Actor* actor) +{ + core->SetEventFlag(EF_PORTRAIT); + actor->CreateStats(); //create or update stats for leaving + actor->SetBase(IE_EXPLORE, 0); + + SelectActor(actor, false, SELECT_NORMAL); + int slot = InParty( actor ); + if (slot < 0) { + return slot; + } + std::vector< Actor*>::iterator m = PCs.begin() + slot; + PCs.erase( m ); + + ieDword id = actor->GetGlobalID(); + for ( m = PCs.begin(); m != PCs.end(); ++m) { + (*m)->PCStats->LastLeft = id; + if ( (*m)->InParty>actor->InParty) { + (*m)->InParty--; + } + } + //removing from party, but actor remains in 'game' + actor->SetPersistent(0); + NPCs.push_back( actor ); + + if (core->HasFeature( GF_HAS_DPLAYER )) { + actor->SetScript( "", SCR_DEFAULT ); + } + actor->SetBase( IE_EA, EA_NEUTRAL ); + return ( int ) NPCs.size() - 1; +} + +//determines if startpos.2da has rotation rows (it cannot have tutorial line) +bool Game::DetermineStartPosType(const TableMgr *strta) +{ + if ((strta->GetRowCount()>=6) && !stricmp(strta->GetRowName(4),"START_ROT" ) ) + { + return true; + } + return false; +} + +#define PMODE_COUNT 3 + +void Game::InitActorPos(Actor *actor) +{ + //start.2da row labels + const char *mode[PMODE_COUNT] = { "NORMAL", "TUTORIAL", "EXPANSION" }; + + unsigned int ip = (unsigned int) (actor->InParty-1); + AutoTable start("start"); + AutoTable strta("startpos"); + // 0 - single player, 1 - tutorial, 2 - expansion + ieDword playmode = 0; + core->GetDictionary()->Lookup( "PlayMode", playmode ); + + //Sometimes playmode is set to -1 (in pregenerate) + //normally execution shouldn't ever come here, but it actually does + //preventing problems by defaulting to the regular entry points + if (playmode>PMODE_COUNT) { + playmode = 0; + } + const char *xpos = start->QueryField(mode[playmode],"XPOS"); + const char *ypos = start->QueryField(mode[playmode],"YPOS"); + const char *area = start->QueryField(mode[playmode],"AREA"); + const char *rot = start->QueryField(mode[playmode],"ROT"); + + actor->Pos.x = actor->Destination.x = (short) atoi( strta->QueryField( strta->GetRowIndex(xpos), ip ) ); + actor->Pos.y = actor->Destination.y = (short) atoi( strta->QueryField( strta->GetRowIndex(ypos), ip ) ); + actor->SetOrientation( atoi( strta->QueryField( strta->GetRowIndex(rot), ip) ), false ); + + strta.load("startare"); + strnlwrcpy(actor->Area, strta->QueryField( strta->GetRowIndex(area), 0 ), 8 ); +} + +int Game::JoinParty(Actor* actor, int join) +{ + core->SetEventFlag(EF_PORTRAIT); + actor->CreateStats(); //create stats if they didn't exist yet + actor->InitButtons(actor->GetStat(IE_CLASS), false); //init actor's buttons + actor->SetBase(IE_EXPLORE, 1); + if (join&JP_INITPOS) { + InitActorPos(actor); + } + int slot = InParty( actor ); + if (slot != -1) { + return slot; + } + size_t size = PCs.size(); + //set the lastjoined trigger + + if (join&JP_JOIN) { + //update kit abilities of actor + actor->ApplyKit(false); + //update the quickslots + actor->ReinitQuickSlots(); + //set the joining date + actor->PCStats->JoinDate = GameTime; + if (size) { + ieDword id = actor->GetGlobalID(); + for (size_t i=0;iPCStats->LastJoined = id; + } + } else { + Reputation = actor->GetStat(IE_REPUTATION); + } + } + slot = InStore( actor ); + if (slot >= 0) { + std::vector< Actor*>::iterator m = NPCs.begin() + slot; + NPCs.erase( m ); + } + + + PCs.push_back( actor ); + if (!actor->InParty) { + actor->InParty = (ieByte) (size+1); + } + + if (join&(JP_INITPOS|JP_SELECT)) { + actor->Selected = 0; // don't confuse SelectActor! + SelectActor(actor,true, SELECT_NORMAL); + } + + return ( int ) size; +} + +int Game::GetPartySize(bool onlyalive) const +{ + if (onlyalive) { + int count = 0; + for (unsigned int i = 0; i < PCs.size(); i++) { + if (!IsAlive(PCs[i])) { + continue; + } + count++; + } + return count; + } + return (int) PCs.size(); +} + +/* sends the hotkey trigger to all selected actors */ +void Game::SetHotKey(unsigned long Key) +{ + std::vector< Actor*>::const_iterator m; + + for ( m = selected.begin(); m != selected.end(); ++m) { + Actor *actor = *m; + + if (actor->IsSelected()) { + actor->HotKey = (ieDword) Key; + } + } +} + +bool Game::SelectPCSingle(int index) +{ + Actor* actor = FindPC( index ); + if (!actor || ! actor->ValidTarget( GA_NO_HIDDEN )) + return false; + + SelectedSingle = index; + return true; +} + +int Game::GetSelectedPCSingle() const +{ + return SelectedSingle; +} + +/* + * SelectActor() - handle (de)selecting actors. + * If selection was changed, runs "SelectionChanged" handler + * + * actor - either specific actor, or NULL for all + * select - whether actor(s) should be selected or deselected + * flags: + * SELECT_REPLACE - if true, deselect all other actors when selecting one + * SELECT_QUIET - do not run handler if selection was changed. Used for + * nested calls to SelectActor() + */ + +bool Game::SelectActor(Actor* actor, bool select, unsigned flags) +{ + std::vector< Actor*>::iterator m; + + // actor was not specified, which means all selectables should be (de)selected + if (! actor) { + for ( m = selected.begin(); m != selected.end(); ++m) { + (*m)->Select( false ); + (*m)->SetOver( false ); + } + selected.clear(); + + if (select) { + area->SelectActors(); +/* + for ( m = PCs.begin(); m != PCs.end(); ++m) { + if (! *m) { + continue; + } + SelectActor( *m, true, SELECT_QUIET ); + } +*/ + } + + if (! (flags & SELECT_QUIET)) { + core->SetEventFlag(EF_SELECTION); + } + Infravision(); + return true; + } + + // actor was specified, so we will work with him + if (select) { + if (! actor->ValidTarget( GA_SELECT | GA_NO_DEAD )) + return false; + + // deselect all actors first when exclusive + if (flags & SELECT_REPLACE) { + if (selected.size() == 1 && actor->IsSelected()) { + assert(selected[0] == actor); + // already the only selected actor + return true; + } + SelectActor( NULL, false, SELECT_QUIET ); + } else if (actor->IsSelected()) { + // already selected + return true; + } + + actor->Select( true ); + assert(actor->IsSelected()); + selected.push_back( actor ); + } else { + if (!actor->IsSelected()) { + // already not selected + return true; + + /*for ( m = selected.begin(); m != selected.end(); ++m) { + assert((*m) != actor); + } + return true;*/ + } + for ( m = selected.begin(); m != selected.end(); ++m) { + if ((*m) == actor) { + selected.erase( m ); + break; + } + } + actor->Select( false ); + assert(!actor->IsSelected()); + } + + if (! (flags & SELECT_QUIET)) { + core->SetEventFlag(EF_SELECTION); + } + Infravision(); + return true; +} + +// Gets average party level, of onlyalive is true, then counts only living PCs +int Game::GetPartyLevel(bool onlyalive) const +{ + int count = 0; + for (unsigned int i = 0; iGetStat(IE_STATE_ID)&STATE_DEAD) { + continue; + } + } + count += PCs[i]->GetXPLevel(0); + } + return count; +} + +// Returns map structure (ARE) if it is already loaded in memory +int Game::FindMap(const char *ResRef) +{ + int index = (int) Maps.size(); + while (index--) { + Map *map=Maps[index]; + if (strnicmp(ResRef, map->GetScriptName(), 8) == 0) { + return index; + } + } + return -1; +} + +Map* Game::GetMap(unsigned int index) const +{ + if (index >= Maps.size()) { + return NULL; + } + return Maps[index]; +} + +Map *Game::GetMap(const char *areaname, bool change) +{ + int index = LoadMap(areaname, change); + if (index >= 0) { + if (change) { + MapIndex = index; + area = GetMap(index); + memcpy (CurrentArea, areaname, 8); + area->SetupAmbients(); + //change the tileset if needed + area->ChangeMap(IsDay()); + ChangeSong(false, true); + Infravision(); + return area; + } + return GetMap(index); + } + return NULL; +} + +bool Game::MasterArea(const char *area) +{ + unsigned int i=(int) mastarea.size(); + while(i--) { + if (strnicmp(mastarea[i], area, 8) ) { + return true; + } + } + return false; +} + +void Game::SetMasterArea(const char *area) +{ + if (MasterArea(area) ) return; + char *tmp = (char *) malloc(9); + strnlwrcpy (tmp,area,8); + mastarea.push_back(tmp); +} + +int Game::AddMap(Map* map) +{ + if (MasterArea(map->GetScriptName()) ) { + Maps.insert(Maps.begin(), 1, map); + MapIndex++; + return 0; + } + unsigned int i = (unsigned int) Maps.size(); + Maps.push_back( map ); + return i; +} + +int Game::DelMap(unsigned int index, int forced) +{ +//this function should archive the area, and remove it only if the area +//contains no active actors (combat, partymembers, etc) + if (index >= Maps.size()) { + return -1; + } + Map *map = Maps[index]; + + if (MapIndex==(int) index) { //can't remove current map in any case + const char *name = map->GetScriptName(); + memcpy(AnotherArea, name, sizeof(AnotherArea) ); + return -1; + } + + + if (!map) { //this shouldn't happen, i guess + printMessage("Game","Erased NULL Map\n",YELLOW); + Maps.erase( Maps.begin()+index); + if (MapIndex>(int) index) { + MapIndex--; + } + return 1; + } + + if (forced || (Maps.size()>MAX_MAPS_LOADED) ) + { + //keep at least one master + const char *name = map->GetScriptName(); + if (MasterArea(name)) { + if(!AnotherArea[0]) { + memcpy(AnotherArea, name, sizeof(AnotherArea)); + if (!forced) { + return -1; + } + } + } + //this check must be the last, because + //after PurgeActors you cannot keep the + //area in memory + //Or the queues should be regenerated! + if (!map->CanFree()) + { + return 1; + } + //remove map from memory + core->SwapoutArea(Maps[index]); + delete( Maps[index] ); + Maps.erase( Maps.begin()+index); + //current map will be decreased + if (MapIndex>(int) index) { + MapIndex--; + } + return 1; + } + //didn't remove the map + return 0; +} + +/* Loads an area */ +int Game::LoadMap(const char* ResRef, bool loadscreen) +{ + unsigned int i; + Map *newMap; + PluginHolder mM(IE_ARE_CLASS_ID); + + //this shouldn't happen + if (!mM) { + return -1; + } + + int index = FindMap(ResRef); + if (index>=0) { + return index; + } + + bool hide = false; + if (loadscreen) { + hide = core->HideGCWindow(); + core->GetGUIScriptEngine()->RunFunction("LoadScreen", "StartLoadScreen"); + core->GetGUIScriptEngine()->RunFunction("LoadScreen", "SetLoadScreen"); + } + DataStream* ds = gamedata->GetResource( ResRef, IE_ARE_CLASS_ID ); + if (!ds) { + goto failedload; + } + if(!mM->Open( ds, true )) { + goto failedload; + } + newMap = mM->GetMap(ResRef, IsDay()); + if (!newMap) { + goto failedload; + } + core->LoadProgress(100); + + for (i = 0; i < PCs.size(); i++) { + if (stricmp( PCs[i]->Area, ResRef ) == 0) { + newMap->AddActor( PCs[i] ); + } + } + for (i = 0; i < NPCs.size(); i++) { + if (stricmp( NPCs[i]->Area, ResRef ) == 0) { + newMap->AddActor( NPCs[i] ); + } + } + if (hide) { + core->UnhideGCWindow(); + } + return AddMap( newMap ); +failedload: + if (hide) + core->UnhideGCWindow(); + core->LoadProgress(100); + return -1; +} + +int Game::AddNPC(Actor* npc) +{ + int slot = InStore( npc ); //already an npc + if (slot != -1) { + return slot; + } + slot = InParty( npc ); + if (slot != -1) { + return -1; + } //can't add as npc already in party + npc->SetPersistent(0); + NPCs.push_back( npc ); + + return (int) NPCs.size() - 1; +} + +Actor* Game::GetNPC(unsigned int Index) +{ + if (Index >= NPCs.size()) { + return NULL; + } + return NPCs[Index]; +} + +void Game::SwapPCs(unsigned int Index1, unsigned int Index2) +{ + if (Index1 >= PCs.size()) { + return; + } + + if (Index2 >= PCs.size()) { + return; + } + int tmp = PCs[Index1]->InParty; + PCs[Index1]->InParty = PCs[Index2]->InParty; + PCs[Index2]->InParty = tmp; + //signal a change of the portrait window + core->SetEventFlag(EF_PORTRAIT | EF_SELECTION); +} + +void Game::DeleteJournalEntry(ieStrRef strref) +{ + size_t i=Journals.size(); + while(i--) { + if ((Journals[i]->Text==strref) || (strref==(ieStrRef) -1) ) { + delete Journals[i]; + Journals.erase(Journals.begin()+i); + } + } +} + +void Game::DeleteJournalGroup(int Group) +{ + size_t i=Journals.size(); + while(i--) { + if (Journals[i]->Group==(ieByte) Group) { + delete Journals[i]; + Journals.erase(Journals.begin()+i); + } + } +} +/* returns true if it modified or added a journal entry */ +bool Game::AddJournalEntry(ieStrRef strref, int Section, int Group) +{ + GAMJournalEntry *je = FindJournalEntry(strref); + if (je) { + //don't set this entry again in the same section + if (je->Section==Section) { + return false; + } + if ((Section == IE_GAM_QUEST_DONE) && Group) { + //removing all of this group and adding a new entry + DeleteJournalGroup(Group); + } else { + //modifying existing entry + je->Section = (ieByte) Section; + je->Group = (ieByte) Group; + ieDword chapter = 0; + locals->Lookup("CHAPTER", chapter); + je->Chapter = (ieByte) chapter; + je->GameTime = GameTime; + return true; + } + } + je = new GAMJournalEntry; + je->GameTime = GameTime; + ieDword chapter = 0; + locals->Lookup("CHAPTER", chapter); + je->Chapter = (ieByte) chapter; + je->unknown09 = 0; + je->Section = (ieByte) Section; + je->Group = (ieByte) Group; + je->Text = strref; + + Journals.push_back( je ); + return true; +} + +void Game::AddJournalEntry(GAMJournalEntry* entry) +{ + Journals.push_back( entry ); +} + +unsigned int Game::GetJournalCount() const +{ + return (unsigned int) Journals.size(); +} + +GAMJournalEntry* Game::FindJournalEntry(ieStrRef strref) +{ + unsigned int Index = (unsigned int) Journals.size(); + while(Index--) { + GAMJournalEntry *ret = Journals[Index]; + + if (ret->Text==strref) { + return ret; + } + } + + return NULL; +} + +GAMJournalEntry* Game::GetJournalEntry(unsigned int Index) +{ + if (Index >= Journals.size()) { + return NULL; + } + return Journals[Index]; +} + +unsigned int Game::GetSavedLocationCount() const +{ + return (unsigned int) savedpositions.size(); +} + +void Game::ClearSavedLocations() +{ + size_t i=savedpositions.size(); + while(i--) { + delete savedpositions[i]; + } + savedpositions.clear(); +} + +GAMLocationEntry* Game::GetSavedLocationEntry(unsigned int i) +{ + size_t current = savedpositions.size(); + if (i>=current) { + if (i>PCs.size()) { + return NULL; + } + savedpositions.resize(i+1); + while(current<=i) { + savedpositions[current++]=(GAMLocationEntry *) calloc(1, sizeof(GAMLocationEntry) ); + } + } + return savedpositions[i]; +} + +unsigned int Game::GetPlaneLocationCount() const +{ + return (unsigned int) planepositions.size(); +} + +void Game::ClearPlaneLocations() +{ + size_t i=planepositions.size(); + while(i--) { + delete planepositions[i]; + } + planepositions.clear(); +} + +GAMLocationEntry* Game::GetPlaneLocationEntry(unsigned int i) +{ + size_t current = planepositions.size(); + if (i>=current) { + if (i>PCs.size()) { + return NULL; + } + planepositions.resize(i+1); + while(current<=i) { + planepositions[current++]=(GAMLocationEntry *) calloc(1, sizeof(GAMLocationEntry) ); + } + } + return planepositions[i]; +} + +char *Game::GetFamiliar(unsigned int Index) +{ + return Familiars[Index]; +} + +//reading the challenge rating table for iwd2 (only when needed) +void Game::LoadCRTable() +{ + AutoTable table("moncrate"); + if (table.ok()) { + int maxrow = table->GetRowCount()-1; + crtable = new CRRow[MAX_LEVEL]; + for(int i=0;iGetColumnCount(row)-1; + for(int j=0;jQueryField(row,col) ); + } + } + } +} + +int Game::GetXPFromCR(int cr) +{ + if (!crtable) LoadCRTable(); + if (crtable) { + int level = GetPartyLevel(true); + if (cr>=MAX_CRLEVEL) { + cr=MAX_CRLEVEL-1; + } + printf("Challenge Rating: %d, party level: %d ", cr, level); + return crtable[level][cr]; + } + printMessage("Game","Cannot find moncrate.2da!\n", LIGHT_RED); + return 0; +} + +void Game::ShareXP(int xp, int flags) +{ + int individual; + + if (flags&SX_CR) { + xp = GetXPFromCR(xp); + } + + if (flags&SX_DIVIDE) { + int PartySize = GetPartySize(true); //party size, only alive + if (PartySize<1) { + return; + } + individual = xp / PartySize; + } else { + individual = xp; + } + + if (!individual) { + return; + } + + if (xp>0) { + displaymsg->DisplayConstantStringValue( STR_GOTXP, 0xbcefbc, (ieDword) xp); //you have gained ... xp + } else { + displaymsg->DisplayConstantStringValue( STR_LOSTXP, 0xbcefbc, (ieDword) -xp); //you have lost ... xp + } + for (unsigned int i=0; iGetStat(IE_STATE_ID)&STATE_DEAD) { + continue; + } + PCs[i]->AddExperience(individual); + } +} + +bool Game::EveryoneStopped() const +{ + for (unsigned int i=0; iGetNextStep() ) return false; + } + return true; +} + +//canmove=true: if some PC can't move (or hostile), then this returns false +bool Game::EveryoneNearPoint(Map *area, const Point &p, int flags) const +{ + for (unsigned int i=0; iSelected) { + continue; + } + } + if (PCs[i]->GetStat(IE_STATE_ID)&STATE_DEAD) { + continue; + } + if (flags&ENP_CANMOVE) { + //someone is uncontrollable, can't move + if (PCs[i]->GetStat(IE_EA)>EA_GOODCUTOFF) { + return false; + } + + if (PCs[i]->GetStat(IE_STATE_ID)&STATE_CANTMOVE) { + return false; + } + } + if (PCs[i]->GetCurrentArea()!=area) { + return false; + } + if (Distance(p,PCs[i])>MAX_TRAVELING_DISTANCE) { + return false; + } + } + return true; +} + +//called when someone died +void Game::PartyMemberDied(Actor *actor) +{ + //this could be null, in some extreme cases... + Map *area = actor->GetCurrentArea(); + + for (unsigned int i=0; iGetStat(IE_STATE_ID)&STATE_DEAD) { + continue; + } + if (PCs[i]->GetCurrentArea()!=area) { + continue; + } + PCs[i]->ReactToDeath(actor->GetScriptName()); + } +} + +//reports if someone died +int Game::PartyMemberDied() const +{ + for (unsigned int i=0; iGetInternalFlag()&IF_JUSTDIED) { + return i; + } + } + return -1; +} + +void Game::IncrementChapter() +{ + //chapter first set to 0 (prologue) + ieDword chapter = (ieDword) -1; + locals->Lookup("CHAPTER",chapter); + locals->SetAt("CHAPTER",chapter+1); + //clear statistics + for (unsigned int i=0; iPCStats->IncrementChapter(); + } +} + +void Game::SetReputation(ieDword r) +{ + if (r<10) r=10; + else if (r>200) r=200; + if (Reputation>r) { + displaymsg->DisplayConstantStringValue(STR_LOSTREP,0xc0c000,(Reputation-r)/10); + } else if (ReputationDisplayConstantStringValue(STR_GOTREP,0xc0c000,(r-Reputation)/10); + } + Reputation = r; + for (unsigned int i=0; iSetBase(IE_REPUTATION, Reputation); + } +} + +void Game::SetControlStatus(int value, int mode) +{ + switch(mode) { + case BM_OR: ControlStatus|=value; break; + case BM_NAND: ControlStatus&=~value; break; + case BM_SET: ControlStatus=value; break; + case BM_AND: ControlStatus&=value; break; + case BM_XOR: ControlStatus^=value; break; + } + core->SetEventFlag(EF_CONTROL); +} + +void Game::AddGold(ieDword add) +{ + ieDword old; + + if (!add) { + return; + } + old = PartyGold; + PartyGold += add; + if (oldDisplayConstantStringValue( STR_GOTGOLD, 0xc0c000, PartyGold-old); + } else { + displaymsg->DisplayConstantStringValue( STR_LOSTGOLD, 0xc0c000, old-PartyGold); + } +} + +//later this could be more complicated +void Game::AdvanceTime(ieDword add) +{ + ieDword h = GameTime/(300*AI_UPDATE_TIME); + GameTime+=add; + if (h!=GameTime/(300*AI_UPDATE_TIME)) { + //asking for a new weather when the hour changes + WeatherBits&=~WB_HASWEATHER; + } + Ticks+=add*interval; + //change the tileset if needed + Map *map = GetCurrentArea(); + if (map && map->ChangeMap(IsDay())) { + //play the daylight transition movie appropriate for the area + //it is needed to play only when the area truly changed its tileset + //this is signalled by ChangeMap + int areatype = (area->AreaType&(AT_FOREST|AT_CITY|AT_DUNGEON))>>3; + ieResRef *res; + + printMessage("Game","Switching DayLight\n",GREEN); + if (IsDay()) { + res=&nightmovies[areatype]; + } else { + res=&daymovies[areatype]; + } + if (*res[0]!='*') { + core->PlayMovie(*res); + } + } +} + +//returns true if there are excess players in the team +bool Game::PartyOverflow() const +{ + GameControl *gc = core->GetGameControl(); + if (!gc) { + return false; + } + //don't start this screen when the gui is busy + if (gc->GetDialogueFlags() & (DF_IN_DIALOG|DF_IN_CONTAINER|DF_FREEZE_SCRIPTS) ) { + return false; + } + if (!partysize) { + return false; + } + return (PCs.size()>partysize); +} + +bool Game::PCInCombat(Actor* actor) const +{ + if (!CombatCounter) { + return false; + } + + if (actor->LastTarget) { + return true; + } + if (AttackersOf(actor->GetGlobalID(), actor->GetCurrentArea())) { + return true; + } + return false; +} + +bool Game::AnyPCInCombat() const +{ + if (!CombatCounter) { + return false; + } + + for (unsigned int i=0; iGetStat(IE_STATE_ID)&STATE_NOSAVE) { + if (area->INISpawn) { + area->INISpawn->RespawnNameless(); + } + } + return false; + } + // if protagonist died + if (protagonist==PM_YES) { + if (PCs[0]->GetStat(IE_STATE_ID)&STATE_NOSAVE) { + return true; + } + return false; + } + //protagonist == 2 + for (unsigned int i=0; iGetStat(IE_STATE_ID)&STATE_NOSAVE) ) { + return false; + } + } + return true; +} + +//runs all area scripts + +void Game::UpdateScripts() +{ + ExecuteScript( 1 ); + ProcessActions(false); + size_t idx; + + bool PartyAttack = false; + + for (idx=0;idxUpdateScripts(); + size_t acnt=Attackers.size(); + while(acnt--) { + Actor *actor = Maps[idx]->GetActorByGlobalID(Attackers[acnt]); + if (actor) { + if ( !Maps[idx]->GetActorByGlobalID(actor->LastTarget) ) { + //Actor's target left area + OutAttack(Attackers[acnt]); + continue; + } else { + //each attacker handles their own round initiation + actor->InitRound(GameTime); + if (actor->InParty) { + PartyAttack = true; + } + } + } else { + //Attacker is gone from area + OutAttack(Attackers[acnt]); + } + } + } + + if (PartyAttack) { + //ChangeSong will set the battlesong only if CombatCounter is nonzero + CombatCounter=150; + ChangeSong(false, true); + } else { + if (CombatCounter) { + CombatCounter--; + //Change song if combatcounter went down to 0 + if (!CombatCounter) { + ChangeSong(false, false); + } + } + } + + if (StateOverrideTime) + StateOverrideTime--; + if (BanterBlockTime) + BanterBlockTime--; + + if (Maps.size()>MAX_MAPS_LOADED) { + idx = Maps.size(); + + //starting from 0, so we see the most recent master area first + for(unsigned int i=0;iGetMusicMgr()->IsPlaying()) { + ChangeSong(false,false); + } + + //this is used only for the death delay so far + if (event_handler) { + if (!event_timer) { + event_handler->call(); + event_handler = NULL; + } + event_timer--; + } + + if (EveryoneDead()) { + //don't check it any more + protagonist = PM_NO; + core->GetGUIScriptEngine()->RunFunction("GUIWORLD", "DeathWindow"); + return; + } + + if (PartyOverflow()) { + partysize = 0; + core->GetGUIScriptEngine()->RunFunction("GUIWORLD", "OpenReformPartyWindow"); + return; + } +} + +void Game::SetTimedEvent(EventHandler func, int count) +{ + event_timer = count; + event_handler = func; +} + +void Game::SetProtagonistMode(int mode) +{ + protagonist = mode; +} + +void Game::SetPartySize(int size) +{ + // 0 size means no party size control + if (size<0) { + return; + } + partysize = (size_t) size; +} + +//Get the area dependent rest movie +ieResRef *Game::GetDream(Map *area) +{ + //select dream based on area + int daynight = IsDay(); + if (area->Dream[daynight][0]) { + return area->Dream+daynight; + } + int dream = (area->AreaType&(AT_FOREST|AT_CITY|AT_DUNGEON))>>3; + return restmovies+dream; +} + +//Start dream cutscenes for player1 +void Game::PlayerDream() +{ + Scriptable *Sender = GetPC(0,true); + if (!Sender) return; + + GameScript* gs = new GameScript( "player1d", Sender,0,0 ); + gs->Update(); + delete( gs ); +} + +//noareacheck = no random encounters +//dream = 0 - based on area non-0 - select from list +//-1 no dream +//hp is how much hp the rest will heal +void Game::RestParty(int checks, int dream, int hp) +{ + if (!(checks&REST_NOMOVE) ) { + if (!EveryoneStopped()) { + return; + } + } + Actor *leader = GetPC(0, true); + if (!leader) { + return; + } + + Map *area = leader->GetCurrentArea(); + //we let them rest if someone is paralyzed, but the others gather around + if (!(checks&REST_NOSCATTER) ) { + if (!EveryoneNearPoint( area, leader->Pos, 0 ) ) { + //party too scattered + displaymsg->DisplayConstantString( STR_SCATTERED, 0xff0000 ); + return; + } + } + + if (!(checks&REST_NOCRITTER) ) { + //don't allow resting while in combat + if (AnyPCInCombat()) { + displaymsg->DisplayConstantString( STR_CANTRESTMONS, 0xff0000 ); + return; + } + //don't allow resting if hostiles are nearby + if (area->AnyEnemyNearPoint(leader->Pos)) { + displaymsg->DisplayConstantString( STR_CANTRESTMONS, 0xff0000 ); + return; + } + } + + //rest check, if PartyRested should be set, area should return true + //area should advance gametime too (so partial rest is possible) + int hours = 8; + if (!(checks&REST_NOAREA) ) { + //you cannot rest here + if (area->AreaFlags&1) { + displaymsg->DisplayConstantString( STR_MAYNOTREST, 0xff0000 ); + return; + } + //you may not rest here, find an inn + if (!(area->AreaType&(AT_OUTDOOR|AT_FOREST|AT_DUNGEON|AT_CAN_REST) )) + { + displaymsg->DisplayConstantString( STR_MAYNOTREST, 0xff0000 ); + return; + } + //area encounters + if(area->Rest( leader->Pos, 8, (GameTime/AI_UPDATE_TIME)%7200/3600) ) { + return; + } + } + AdvanceTime(2400*AI_UPDATE_TIME); + + int i = GetPartySize(true); // party size, only alive + + while (i--) { + Actor *tar = GetPC(i, true); + tar->ClearPath(); + tar->ClearActions(); + tar->SetModal(MS_NONE, 0); + //if hp = 0, then healing will be complete + tar->Heal(hp); + //removes fatigue, recharges spells + tar->Rest(0); + tar->PartyRested(); + } + + //movie and cutscene dreams + if (dream>=0) { + //cutscene dreams + if (gamedata->Exists("player1d",IE_BCS_CLASS_ID, true)) + PlayerDream(); + + //select dream based on area + ieResRef *movie; + if (dream==0 || dream>7) { + movie = GetDream(area); + } else { + movie = restmovies+dream; + } + if (*movie[0]!='*') { + core->PlayMovie(*movie); + } + } + + //set partyrested flags + PartyRested(); + area->PartyRested(); + core->SetEventFlag(EF_ACTION); + + //restindex will be -1 in the case of PST + //FIXME: I don't quite see why we can't sumply use the same strings.2da entry + //It seems we could reduce complexity here, and free up 2-3 string slots too + int restindex = displaymsg->GetStringReference(STR_REST); + int strindex; + char* tmpstr = NULL; + + core->GetTokenDictionary()->SetAtCopy("HOUR", hours); + if (restindex != -1) { + strindex = displaymsg->GetStringReference(STR_HOURS); + } else { + strindex = displaymsg->GetStringReference(STR_PST_HOURS); + restindex = displaymsg->GetStringReference(STR_PST_REST); + } + + //this would be bad + if (strindex == -1 || restindex == -1) return; + tmpstr = core->GetString(strindex, 0); + //as would this + if (!tmpstr) return; + + core->GetTokenDictionary()->SetAtCopy("DURATION", tmpstr); + core->FreeString(tmpstr); + displaymsg->DisplayString(restindex, 0xffffff, 0); +} + +//timestop effect +void Game::TimeStop(Actor* owner, ieDword end) +{ + timestop_owner=owner; + timestop_end=GameTime+end; +} + +//recalculate the party's infravision state +void Game::Infravision() +{ + hasInfra = false; + Map *map = GetCurrentArea(); + if (!map) return; + for(size_t i=0;iGetCurrentArea()!=map) continue; + //Group infravision overrides this??? + if (!actor->Selected) continue; + if (actor->GetStat(IE_STATE_ID) & STATE_INFRA) { + hasInfra = true; + return; + } + } +} + +//returns the colour which should be applied onto the whole game area viewport +//this is based on timestop, dream area, weather, daytime + +static const Color TimeStopTint={0xe0,0xe0,0xe0,0x20}; //greyscale +static const Color DreamTint={0xf0,0xe0,0xd0,0x10}; //light brown scale +static const Color NightTint={0x80,0x80,0xe0,0x40}; //dark, bluish +static const Color DuskTint={0xe0,0x80,0x80,0x40}; //dark, reddish +static const Color FogTint={0xff,0xff,0xff,0x40}; //whitish +static const Color DarkTint={0x80,0x80,0xe0,0x10}; //slightly dark bluish + +const Color *Game::GetGlobalTint() const +{ + if (timestop_end>GameTime) { + return &TimeStopTint; + } + Map *map = GetCurrentArea(); + if (!map) return NULL; + if (map->AreaFlags&AF_DREAM) { + return &DreamTint; + } + if ((map->AreaType&(AT_OUTDOOR|AT_DAYNIGHT|AT_EXTENDED_NIGHT)) == (AT_OUTDOOR|AT_DAYNIGHT) ) { + //get daytime colour + ieDword daynight = ((GameTime/AI_UPDATE_TIME)%7200/300); + if (daynight<2 || daynight>22) { + return &NightTint; + } + if (daynight>20 || daynight<4) { + return &DuskTint; + } + } + if ((map->AreaType&(AT_OUTDOOR|AT_WEATHER)) == (AT_OUTDOOR|AT_WEATHER)) { + //get weather tint + if (WeatherBits&WB_RAIN) { + return &DarkTint; + } + if (WeatherBits&WB_FOG) { + return &FogTint; + } + } + + return NULL; +} + +bool Game::IsDay() +{ + ieDword daynight = ((GameTime/AI_UPDATE_TIME)%7200/300); + if(daynight<4 || daynight>20) { + return false; + } + return true; +} + +void Game::InAttack(ieDword globalID) +{ + std::vector< ieDword>::const_iterator idx; + + for(idx=Attackers.begin(); idx!=Attackers.end();idx++) { + if (*idx==globalID) return; + } + Attackers.push_back(globalID); +} + +void Game::OutAttack(ieDword globalID) +{ + std::vector< ieDword>::iterator idx; + + for(idx=Attackers.begin(); idx!=Attackers.end();idx++) { + if (*idx==globalID) { + Attackers.erase(idx); + break; + } + } +} + +void Game::ChangeSong(bool always, bool force) +{ + int Song; + + if (CombatCounter) { + //battlesong + Song = SONG_BATTLE; + } else { + //will select SONG_DAY or SONG_NIGHT + Song = (GameTime/AI_UPDATE_TIME)%7200/3600; + } + //area may override the song played (stick in battlemusic) + //always transition gracefully with ChangeSong + //force just means, we schedule the song for later, if currently + //is playing + area->PlayAreaSong( Song, always, force ); +} + +int Game::AttackersOf(ieDword globalID, Map *area) const +{ + if (!area) { + return 0; + } + std::vector< ieDword>::const_iterator idx; + + int cnt = 0; + for(idx=Attackers.begin(); idx!=Attackers.end();idx++) { + Actor * actor = area->GetActorByGlobalID(*idx); + if (actor) { + if (actor->LastTarget==globalID) { + cnt++; + } + } + } + return cnt; +} + +/* this method redraws weather. If update is false, +then the weather particles won't change (game paused) +*/ +void Game::DrawWeather(const Region &screen, bool update) +{ + if (!weather) { + return; + } + if (!area->HasWeather()) { + return; + } + + weather->Draw( screen ); + if (!update) { + return; + } + + if (!(WeatherBits & (WB_RAIN|WB_SNOW)) ) { + if (weather->GetPhase() == P_GROW) { + weather->SetPhase(P_FADE); + } + } + //if (GameTime&1) { + int drawn = weather->Update(); + if (drawn) { + WeatherBits &= ~WB_START; + } + //} + + if (WeatherBits&WB_HASWEATHER) { + return; + } + StartRainOrSnow(true, area->GetWeather()); +} + +/* sets the weather type */ +void Game::StartRainOrSnow(bool conditional, int w) +{ + if (conditional && (w & (WB_RAIN|WB_SNOW)) ) { + if (WeatherBits & (WB_RAIN | WB_SNOW) ) + return; + } + // whatever was responsible for calling this, we now have some set weather + WeatherBits = w | WB_HASWEATHER; + if (w & WB_LIGHTNING) { + if (WeatherBits&WB_START) { + //already raining + if (GameTime&1) { + core->PlaySound(DS_LIGHTNING1); + } else { + core->PlaySound(DS_LIGHTNING2); + } + } else { + //start raining (far) + core->PlaySound(DS_LIGHTNING3); + } + } + if (w&WB_SNOW) { + core->PlaySound(DS_SNOW); + weather->SetType(SP_TYPE_POINT, SP_PATH_FLIT, SP_SPAWN_SOME); + weather->SetPhase(P_GROW); + weather->SetColor(SPARK_COLOR_WHITE); + return; + } + if (w&WB_RAIN) { + core->PlaySound(DS_RAIN); + weather->SetType(SP_TYPE_LINE, SP_PATH_RAIN, SP_SPAWN_SOME); + weather->SetPhase(P_GROW); + weather->SetColor(SPARK_COLOR_STONE); + return; + } + weather->SetPhase(P_FADE); +} + +void Game::SetExpansion(ieDword value) +{ + if (Expansion>=value) { + return; + } + Expansion = value; + + switch(Expansion) { + default: + core->SetEventFlag(EF_EXPANSION); + break; + //TODO: move this hardcoded hack to the scripts + case 5: + core->GetDictionary()->SetAt( "PlayMode", 2 ); + + int i = GetPartySize(false); + while(i--) { + Actor *actor = GetPC(i, false); + InitActorPos(actor); + } + } +} + +void Game::DebugDump() +{ + size_t idx; + + printf("Currently loaded areas:\n"); + for(idx=0;idxGetScriptName()); + } + printf("Current area: %s Previous area: %s\n", CurrentArea, PreviousArea); + printf("Global script: %s\n", Scripts[0]->GetName()); + printf("CombatCounter: %d\n", (int) CombatCounter); + printf("Attackers count: %d\n", (int) Attackers.size()); + for(idx=0;idxLastTarget); + printf("Name: %s Attacking : %s\n", actor->ShortName, whom?whom->ShortName:"???"); + } + + printf("Party size: %d\n", (int) PCs.size()); + for(idx=0;idxShortName, actor->InParty, actor->Selected?"x":"-"); + } +} + +Actor *Game::GetActorByGlobalID(ieDword globalID) +{ + size_t mc = GetLoadedMapCount(); + while(mc--) { + Map *map = GetMap(mc); + Actor *actor = map->GetActorByGlobalID(globalID); + if (actor) return actor; + } + return GetGlobalActorByGlobalID(globalID); +} + diff --git a/project/jni/application/gemrb/src/core/Game.h b/project/jni/application/gemrb/src/core/Game.h new file mode 100644 index 000000000..1e4f9b9ea --- /dev/null +++ b/project/jni/application/gemrb/src/core/Game.h @@ -0,0 +1,424 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * + */ + +/** + * @file Game.h + * Declares Game class, object representing current game state. + * @author The GemRB Project + */ + + +class Game; + +#ifndef GAME_H +#define GAME_H + +#include "exports.h" +#include "ie_types.h" + +#include "Callback.h" +#include "Map.h" +#include "Variables.h" +#include "Scriptable/Actor.h" + +#include + +class Particles; + +//the size of the bestiary register +#define BESTIARY_SIZE 260 + +//ShareXP flags +#define SX_DIVIDE 1 //divide XP among team members +#define SX_CR 2 //use challenge rating resolution + +//joinparty flags +#define JP_JOIN 1 //refresh join time +#define JP_INITPOS 2 //init startpos +#define JP_SELECT 4 //select the actor after joining + +//protagonist mode +#define PM_NO 0 //no death checks +#define PM_YES 1 //if protagonist dies, game over +#define PM_TEAM 2 //if team dies, game over + +// Flags bits for SelectActor() +// !!! Keep these synchronized with GUIDefines.py !!! +#define SELECT_NORMAL 0x00 +#define SELECT_REPLACE 0x01 // when selecting actor, deselect all others +#define SELECT_QUIET 0x02 // do not run handler when changing selection + +// Flags bits for EveryoneNearPoint() +#define ENP_CANMOVE 1 // also check if the PC can move +#define ENP_ONLYSELECT 2 // check only selected PC + +// GUI Control Status flags (saved in game) +#define CS_PARTY_AI 1 //enable party AI +#define CS_MEDIUM 2 //medium dialog +#define CS_LARGE 6 //large dialog, both bits set +#define CS_DIALOGSIZEMASK 6 +#define CS_DIALOG 8 //dialog is running +#define CS_HIDEGUI 16 //hide all gui +#define CS_ACTION 32 //hide action pane +#define CS_PORTRAIT 64 //hide portrait pane +#define CS_MAPNOTES 128 //hide mapnotes + +//Weather bits +#define WB_NORMAL 0 +#define WB_RAIN 1 +#define WB_SNOW 2 +#define WB_FOG 3 +#define WB_MASK 7 +#define WB_LIGHTNING 8 +#define WB_HASWEATHER 0x40 +#define WB_START 0x80 + +//Rest flags +#define REST_NOAREA 1 //no area check +#define REST_NOSCATTER 2 //no scatter check +#define REST_NOMOVE 4 //no movement check +#define REST_NOCRITTER 8 //no hostiles check + +//Song types (hardcoded) +#define SONG_DAY 0 +#define SONG_NIGHT 1 +#define SONG_BATTLE 3 + +/** + * @struct PCStruct + * Information about party member. + */ + +struct PCStruct { + ieWord Selected; + ieWord PartyOrder; + ieDword OffsetToCRE; + ieDword CRESize; + ieResRef CREResRef; + ieDword Orientation; + ieResRef Area; + ieWord XPos; + ieWord YPos; + ieWord ViewXPos; + ieWord ViewYPos; + ieWord ModalState; + ieWord Happiness; + ieDword Interact[MAX_INTERACT]; + ieWord QuickWeaponSlot[MAX_QUICKWEAPONSLOT]; + ieWord QuickWeaponHeader[MAX_QUICKWEAPONSLOT]; + ieResRef QuickSpellResRef[MAX_QSLOTS]; + ieWord QuickItemSlot[MAX_QUICKITEMSLOT]; + ieWord QuickItemHeader[MAX_QUICKITEMSLOT]; + char Name[32]; + ieDword TalkCount; + ieByte QSlots[GUIBT_COUNT]; + ieByte QuickSpellClass[MAX_QSLOTS]; +}; + +#define IE_GAM_JOURNAL 0 +#define IE_GAM_QUEST_UNSOLVED 1 +#define IE_GAM_QUEST_DONE 2 +#define IE_GAM_JOURNAL_USER 3 + +/** + * @struct GAMJournalEntry + * Single entry in a journal + */ + +struct GAMJournalEntry { + ieStrRef Text; + ieDword GameTime; // in game time seconds + ieByte Chapter; + ieByte unknown09; + ieByte Section; + ieByte Group; // this is a GemRB extension +}; + +// Saved location of party member. +struct GAMLocationEntry { + ieResRef AreaResRef; + Point Pos; +}; + +#define MAX_CRLEVEL 32 + +typedef int CRRow[MAX_CRLEVEL]; + +/** + * @class Game + * Object representing current game state, mostly party. + */ + +class GEM_EXPORT Game : public Scriptable { +public: + Game(void); + ~Game(void); +private: + std::vector< Actor*> PCs; + std::vector< Actor*> NPCs; + std::vector< Map*> Maps; + std::vector< GAMJournalEntry*> Journals; + std::vector< GAMLocationEntry*> savedpositions; + std::vector< GAMLocationEntry*> planepositions; + std::vector< char*> mastarea; + std::vector< ieDword> Attackers; + CRRow *crtable; + ieResRef restmovies[8]; + ieResRef daymovies[8]; + ieResRef nightmovies[8]; + int MapIndex; +public: + std::vector< Actor*> selected; + int version; + Variables* kaputz; + ieByte* beasts; + ieByte* mazedata; //only in PST + ieResRef Familiars[9]; + ieDword CombatCounter; + ieDword StateOverrideFlag, StateOverrideTime; + ieDword BanterBlockFlag, BanterBlockTime; + + /** Index of PC selected in non-walking environment (shops, inventory...) */ + int SelectedSingle; + /** 0 if the protagonist's death doesn't cause game over */ + /** 1 if the protagonist's death causes game over */ + /** 2 if no check is needed (pst) */ + int protagonist; + /** if party size exceeds this amount, a callback will be called */ + size_t partysize; + ieDword Ticks; + ieDword interval; // 1000/AI_UPDATE (a tenth of a round in ms) + ieDword GameTime; + ieDword LastScriptUpdate; // GameTime at which UpdateScripts last ran + ieDword RealTime; + ieWord WhichFormation; + ieWord Formations[5]; + ieDword PartyGold; + ieWord NpcInParty; + ieWord WeatherBits; + ieDword Unknown48; //still unknown + ieDword Reputation; + ieDword ControlStatus; // used in bg2, iwd (where you can switch panes off) + ieDword Expansion; // mostly used by BG2. IWD games set it to 3 on newgame + ieResRef AnotherArea; + ieResRef CurrentArea; + ieResRef PreviousArea; //move here if the worldmap exit is illegal? + ieResRef LoadMos; + Actor *timestop_owner; + ieDword timestop_end; + Particles *weather; + int event_timer; + EventHandler event_handler; //like in Control + bool hasInfra; +private: + /** reads the challenge rating table */ + void LoadCRTable(); +public: + /** Returns the PC's slot count for partyID */ + int FindPlayer(unsigned int partyID); + /** Returns actor by slot */ + Actor* GetPC(unsigned int slot, bool onlyalive); + /** Finds an actor in party by party ID, returns Actor, if not there, returns NULL*/ + Actor* FindPC(unsigned int partyID); + Actor* FindNPC(unsigned int partyID); + /** Finds a global actor by global ID */ + Actor* GetGlobalActorByGlobalID(ieDword globalID); + /** Finds an actor in party, returns slot, if not there, returns -1*/ + int InParty(Actor* pc) const; + /** Finds an actor in store, returns slot, if not there, returns -1*/ + int InStore(Actor* pc) const; + /** Finds an actor in party by scripting name*/ + Actor* FindPC(const char *deathvar); + /** Finds an actor in store by scripting name*/ + Actor* FindNPC(const char *deathvar); + /** Sets the area and position of the actor to the starting position */ + void InitActorPos(Actor *actor); + /** Joins party */ + int JoinParty(Actor* pc, int join=JP_JOIN); + /** Return current party size */ + int GetPartySize(bool onlyalive) const; + /** Returns the npcs count */ + int GetNPCCount() const { return (int)NPCs.size(); } + /** Sends the hotkey trigger to all selected pcs */ + void SetHotKey(unsigned long Key); + /** Select PC for non-walking environment (shops, inventory, ...) */ + bool SelectPCSingle(int index); + /** Get index of selected PC for non-walking env (shops, inventory, ...) */ + int GetSelectedPCSingle() const; + /** (De)selects actor. */ + bool SelectActor( Actor* actor, bool select, unsigned flags ); + + /** Return current party level count for xp calculations */ + int GetPartyLevel(bool onlyalive) const; + /** Reassigns inparty numbers, call it after party creation */ + void ConsolidateParty(); + /** Removes actor from party (if in there) */ + int LeaveParty(Actor* pc); + /** Returns slot*/ + int DelPC(unsigned int slot, bool autoFree = false); + int DelNPC(unsigned int slot, bool autoFree = false); + /** Returns map in index */ + Map* GetMap(unsigned int index) const; + /** Returns a map from area name, loads it if needed + * use it for the biggest safety, change = true will change the current map */ + Map* GetMap(const char *areaname, bool change); + /** Returns slot of the map if found */ + int FindMap(const char *ResRef); + int AddMap(Map* map); + /** Determine if area is master area*/ + bool MasterArea(const char *area); + /** Dynamically adding an area to master areas*/ + void SetMasterArea(const char *area); + /** Returns slot of the map, if it was already loaded, + * don't load it again, set changepf == true, + * if you want to change the pathfinder too. */ + int LoadMap(const char* ResRef, bool loadscreen); + int DelMap(unsigned int index, int forced = 0); + int AddNPC(Actor* npc); + Actor* GetNPC(unsigned int Index); + void SwapPCs(unsigned int Index1, unsigned int Index2); + bool IsDay(); + void InAttack(ieDword globalID); + void OutAttack(ieDword globalID); + int AttackersOf(ieDword globalID, Map *area) const; + + //journal entries + /** Deletes one or all journal entries if strref is -1 */ + void DeleteJournalEntry(ieStrRef strref); + /** Delete entries of the same group */ + void DeleteJournalGroup(int Group); + /** Adds a journal entry from dialog data. + * Time and chapter are calculated on the fly + * Returns false if the entry already exists */ + bool AddJournalEntry(ieStrRef strref, int section, int group); + /** Adds a journal entry while loading the .gam structure */ + void AddJournalEntry(GAMJournalEntry* entry); + unsigned int GetJournalCount() const; + GAMJournalEntry* FindJournalEntry(ieStrRef strref); + GAMJournalEntry* GetJournalEntry(unsigned int Index); + + //saved locations + unsigned int GetSavedLocationCount() const; + void ClearSavedLocations(); + GAMLocationEntry* GetSavedLocationEntry(unsigned int Index); + + //plane locations + unsigned int GetPlaneLocationCount() const; + void ClearPlaneLocations(); + GAMLocationEntry* GetPlaneLocationEntry(unsigned int Index); + + char *GetFamiliar(unsigned int Index); + + bool IsBeastKnown(unsigned int Index) const { + if (!beasts) { + return false; + } + if (Index>=BESTIARY_SIZE) { + return false; + } + return beasts[Index] != 0; + } + void SetBeastKnown(unsigned int Index) { + if (!beasts) { + return; + } + if (Index>=BESTIARY_SIZE) { + return; + } + beasts[Index] = 1; + } + ieWord GetFormation() const { + if (WhichFormation>4) { + return 0; + } + return Formations[WhichFormation]; + } + size_t GetAttackerCount() const { + return Attackers.size(); + } + + /** converts challenge rating to xp */ + int GetXPFromCR(int cr); + /** shares XP among all party members */ + void ShareXP(int XP, int flags); + /** returns true if we should start the party overflow window */ + bool PartyOverflow() const; + /** returns true if actor is an attacker or being attacked */ + bool PCInCombat(Actor *actor) const; + /** returns true if any pc is attacker or being attacked */ + bool AnyPCInCombat() const; + /** returns true if the party death condition is true */ + bool EveryoneDead() const; + /** returns true if no one moves */ + bool EveryoneStopped() const; + bool EveryoneNearPoint(Map *map, const Point &p, int flags) const; + /** returns true if a PC just died */ + int PartyMemberDied() const; + /** a party member just died now */ + void PartyMemberDied(Actor *); + /** Increments chapter variable and refreshes kill stats */ + void IncrementChapter(); + /** Sets party reputation */ + void SetReputation(ieDword r); + /** Sets the gamescreen control status (pane states, dialog textarea size) */ + void SetControlStatus(int value, int operation); + /** Sets party size (1-32000) */ + void SetPartySize(int value); + /** Sets a guiscript function to happen after x AI cycles have elapsed */ + void SetTimedEvent(EventHandler func, int count); + /** Sets protagonist mode to 0-none,1-protagonist,2-team */ + void SetProtagonistMode(int value); + void StartRainOrSnow(bool conditional, int weather); + size_t GetLoadedMapCount() const { return Maps.size(); } + /** Adds or removes gold */ + void AddGold(ieDword add); + /** Adds ticks to game time */ + void AdvanceTime(ieDword add); + /** Runs the script engine on the global script and the area scripts + areas run scripts on door, infopoint, container, actors too */ + void UpdateScripts(); + /** runs area functionality, sets partyrested trigger */ + void RestParty(int checks, int dream, int hp); + /** timestop effect initiated by actor */ + void TimeStop(Actor *actor, ieDword end); + /** gets the colour which should be applied over the game area, + may return NULL */ + const Color *GetGlobalTint() const; + /** returns true if party has infravision */ + bool PartyHasInfravision() const { return hasInfra; } + /** draw weather */ + void DrawWeather(const Region &screen, bool update); + /** updates current area music */ + void ChangeSong(bool always = true, bool force = true); + /** sets expansion mode */ + void SetExpansion(ieDword value); + /** Dumps information about the object */ + void DebugDump(); + /** Finds an actor by global ID */ + Actor *GetActorByGlobalID(ieDword objectID); + /** updates the infravision info */ + void Infravision(); +private: + bool DetermineStartPosType(const TableMgr *strta); + ieResRef *GetDream(Map *area); + void PlayerDream(); +}; + +#endif // ! GAME_H diff --git a/project/jni/application/gemrb/src/core/GameData.cpp b/project/jni/application/gemrb/src/core/GameData.cpp new file mode 100644 index 000000000..f8fb2d6e6 --- /dev/null +++ b/project/jni/application/gemrb/src/core/GameData.cpp @@ -0,0 +1,490 @@ +/* GemRB - Infinity Engine Emulator +* Copyright (C) 2003-2005 The GemRB Project +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* as published by the Free Software Foundation; either version 2 +* of the License, or (at your option) any later version. + +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. + +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +* +* +*/ + +#include "GameData.h" + +#include "ActorMgr.h" +#include "AnimationMgr.h" +#include "Cache.h" +#include "Effect.h" +#include "EffectMgr.h" +#include "Factory.h" +#include "Game.h" +#include "ImageFactory.h" +#include "ImageMgr.h" +#include "Interface.h" +#include "Item.h" +#include "ItemMgr.h" +#include "ResourceDesc.h" +#include "Spell.h" +#include "SpellMgr.h" +#include "Scriptable/Actor.h" +#include "System/FileStream.h" + +#include + +static void ReleaseItem(void *poi) +{ + delete ((Item *) poi); +} + +static void ReleaseSpell(void *poi) +{ + delete ((Spell *) poi); +} + +static void ReleaseEffect(void *poi) +{ + delete ((Effect *) poi); +} + +static void ReleasePalette(void *poi) +{ + //we allow nulls, but we shouldn't release them + if (!poi) return; + //as long as palette has its own refcount, this should be Release + ((Palette *) poi)->Release(); +} + +GEM_EXPORT GameData* gamedata; + +GameData::GameData() +{ + factory = new Factory(); +} + +GameData::~GameData() +{ + delete factory; +} + +void GameData::ClearCaches() +{ + ItemCache.RemoveAll(ReleaseItem); + SpellCache.RemoveAll(ReleaseSpell); + EffectCache.RemoveAll(ReleaseEffect); + PaletteCache.RemoveAll(ReleasePalette); +} + +Actor *GameData::GetCreature(const char* ResRef, unsigned int PartySlot) +{ + DataStream* ds = GetResource( ResRef, IE_CRE_CLASS_ID ); + if (!ds) + return 0; + + PluginHolder actormgr(IE_CRE_CLASS_ID); + if (!actormgr->Open( ds, true )) { + return 0; + } + Actor* actor = actormgr->GetActor(PartySlot); + return actor; +} + +int GameData::LoadCreature(const char* ResRef, unsigned int PartySlot, bool character, int VersionOverride) +{ + DataStream *stream; + + Actor* actor; + if (character) { + char nPath[_MAX_PATH], fName[16]; + snprintf( fName, sizeof(fName), "%s.chr", ResRef); + PathJoin( nPath, core->GamePath, "characters", fName, NULL ); + FileStream *fs = new FileStream(); + fs -> Open( nPath, true ); + stream = (DataStream *) fs; + PluginHolder actormgr(IE_CRE_CLASS_ID); + if (!actormgr->Open( stream, true )) { + return -1; + } + actor = actormgr->GetActor(PartySlot); + } else { + actor = GetCreature(ResRef, PartySlot); + } + + if ( !actor ) { + return -1; + } + + if (VersionOverride != -1) { + actor->version = VersionOverride; + } + + //both fields are of length 9, make this sure! + memcpy(actor->Area, core->GetGame()->CurrentArea, sizeof(actor->Area) ); + if (actor->BaseStats[IE_STATE_ID] & STATE_DEAD) { + actor->SetStance( IE_ANI_TWITCH ); + } else { + actor->SetStance( IE_ANI_AWAKE ); + } + actor->SetOrientation( 0, false ); + + if ( PartySlot != 0 ) { + return core->GetGame()->JoinParty( actor, JP_JOIN|JP_INITPOS ); + } + else { + return core->GetGame()->AddNPC( actor ); + } +} + +/** Loads a 2DA Table, returns -1 on error or the Table Index on success */ +int GameData::LoadTable(const ieResRef ResRef) +{ + int ind = GetTableIndex( ResRef ); + if (ind != -1) { + tables[ind].refcount++; + return ind; + } + //printf("(%s) Table not found... Loading from file\n", ResRef); + DataStream* str = GetResource( ResRef, IE_2DA_CLASS_ID ); + if (!str) { + return -1; + } + PluginHolder tm(IE_2DA_CLASS_ID); + if (!tm) { + delete str; + return -1; + } + if (!tm->Open( str, true )) { + return -1; + } + Table t; + t.refcount = 1; + strncpy( t.ResRef, ResRef, 8 ); + t.tm = tm; + ind = -1; + for (size_t i = 0; i < tables.size(); i++) { + if (tables[i].refcount == 0) { + ind = ( int ) i; + break; + } + } + if (ind != -1) { + tables[ind] = t; + return ind; + } + tables.push_back( t ); + return ( int ) tables.size() - 1; +} +/** Gets the index of a loaded table, returns -1 on error */ +int GameData::GetTableIndex(const char* ResRef) const +{ + for (size_t i = 0; i < tables.size(); i++) { + if (tables[i].refcount == 0) + continue; + if (strnicmp( tables[i].ResRef, ResRef, 8 ) == 0) + return ( int ) i; + } + return -1; +} +/** Gets a Loaded Table by its index, returns NULL on error */ +Holder GameData::GetTable(unsigned int index) const +{ + if (index >= tables.size()) { + return NULL; + } + if (tables[index].refcount == 0) { + return NULL; + } + return tables[index].tm; +} + +/** Frees a Loaded Table, returns false on error, true on success */ +bool GameData::DelTable(unsigned int index) +{ + if (index==0xffffffff) { + tables.clear(); + return true; + } + if (index >= tables.size()) { + return false; + } + if (tables[index].refcount == 0) { + return false; + } + tables[index].refcount--; + if (tables[index].refcount == 0) + if (tables[index].tm) + tables[index].tm.release(); + return true; +} + +Palette *GameData::GetPalette(const ieResRef resname) +{ + Palette *palette = (Palette *) PaletteCache.GetResource(resname); + if (palette) { + return palette; + } + //additional hack for allowing NULL's + if (PaletteCache.RefCount(resname)!=-1) { + return NULL; + } + ResourceHolder im(resname); + if (im == NULL) { + PaletteCache.SetAt(resname, NULL); + return NULL; + } + + palette = new Palette(); + im->GetPalette(256,palette->col); + palette->named=true; + PaletteCache.SetAt(resname, (void *) palette); + return palette; +} + +void GameData::FreePalette(Palette *&pal, const ieResRef name) +{ + int res; + + if (!pal) { + return; + } + if (!name || !name[0]) { + if(pal->named) { + printf("Palette is supposed to be named, but got no name!\n"); + abort(); + } else { + pal->Release(); + pal=NULL; + } + return; + } + if (!pal->named) { + printf("Unnamed palette, it should be %s!\n", name); + abort(); + } + res=PaletteCache.DecRef((void *) pal, name, true); + if (res<0) { + printMessage( "Core", "Corrupted Palette cache encountered (reference count went below zero), ", LIGHT_RED ); + printf( "Palette name is: %.8s\n", name); + abort(); + } + if (!res) { + pal->Release(); + } + pal = NULL; +} + +Item* GameData::GetItem(const ieResRef resname) +{ + Item *item = (Item *) ItemCache.GetResource(resname); + if (item) { + return item; + } + DataStream* str = GetResource( resname, IE_ITM_CLASS_ID ); + PluginHolder sm(IE_ITM_CLASS_ID); + if (!sm) { + delete ( str ); + return NULL; + } + if (!sm->Open( str, true )) { + return NULL; + } + + item = new Item(); + //this is required for storing the 'source' + strnlwrcpy(item->Name, resname, 8); + sm->GetItem( item ); + if (item == NULL) { + return NULL; + } + + ItemCache.SetAt(resname, (void *) item); + return item; +} + +//you can supply name for faster access +void GameData::FreeItem(Item const *itm, const ieResRef name, bool free) +{ + int res; + + res=ItemCache.DecRef((void *) itm, name, free); + if (res<0) { + printMessage( "Core", "Corrupted Item cache encountered (reference count went below zero), ", LIGHT_RED ); + printf( "Item name is: %.8s\n", name); + abort(); + } + if (res) return; + if (free) delete itm; +} + +Spell* GameData::GetSpell(const ieResRef resname, bool silent) +{ + Spell *spell = (Spell *) SpellCache.GetResource(resname); + if (spell) { + return spell; + } + DataStream* str = GetResource( resname, IE_SPL_CLASS_ID, silent ); + PluginHolder sm(IE_SPL_CLASS_ID); + if (!sm) { + delete ( str ); + return NULL; + } + if (!sm->Open( str, true )) { + return NULL; + } + + spell = new Spell(); + //this is required for storing the 'source' + strnlwrcpy(spell->Name, resname, 8); + sm->GetSpell( spell, silent ); + if (spell == NULL) { + return NULL; + } + + SpellCache.SetAt(resname, (void *) spell); + return spell; +} + +void GameData::FreeSpell(Spell *spl, const ieResRef name, bool free) +{ + int res; + + res=SpellCache.DecRef((void *) spl, name, free); + if (res<0) { + printMessage( "Core", "Corrupted Spell cache encountered (reference count went below zero), ", LIGHT_RED ); + printf( "Spell name is: %.8s or %.8s\n", name, spl->Name); + abort(); + } + if (res) return; + if (free) delete spl; +} + +Effect* GameData::GetEffect(const ieResRef resname) +{ + Effect *effect = (Effect *) EffectCache.GetResource(resname); + if (effect) { + return effect; + } + DataStream* str = GetResource( resname, IE_EFF_CLASS_ID ); + PluginHolder em(IE_EFF_CLASS_ID); + if (!em) { + delete ( str ); + return NULL; + } + if (!em->Open( str, true )) { + return NULL; + } + + effect = em->GetEffect(new Effect() ); + if (effect == NULL) { + return NULL; + } + + EffectCache.SetAt(resname, (void *) effect); + return effect; +} + +void GameData::FreeEffect(Effect *eff, const ieResRef name, bool free) +{ + int res; + + res=EffectCache.DecRef((void *) eff, name, free); + if (res<0) { + printMessage( "Core", "Corrupted Effect cache encountered (reference count went below zero), ", LIGHT_RED ); + printf( "Effect name is: %.8s\n", name); + abort(); + } + if (res) return; + if (free) delete eff; +} + +//if the default setup doesn't fit for an animation +//create a vvc for it! +ScriptedAnimation* GameData::GetScriptedAnimation( const char *effect, bool doublehint) +{ + ScriptedAnimation *ret = NULL; + + if (Exists( effect, IE_VVC_CLASS_ID ) ) { + DataStream *ds = GetResource( effect, IE_VVC_CLASS_ID ); + ret = new ScriptedAnimation(ds, true); + } else { + AnimationFactory *af = (AnimationFactory *) + GetFactoryResource( effect, IE_BAM_CLASS_ID, IE_NORMAL ); + if (af) { + ret = new ScriptedAnimation(); + ret->LoadAnimationFactory( af, doublehint?2:0); + } + } + if (ret) { + strnlwrcpy(ret->ResName, effect, 8); + } + return ret; +} + +// Return single BAM frame as a sprite. Use if you want one frame only, +// otherwise it's not efficient +Sprite2D* GameData::GetBAMSprite(const ieResRef ResRef, int cycle, int frame) +{ + Sprite2D *tspr; + AnimationFactory* af = ( AnimationFactory* ) + GetFactoryResource( ResRef, IE_BAM_CLASS_ID, IE_NORMAL ); + if (!af) return 0; + if (cycle == -1) + tspr = af->GetFrameWithoutCycle( (unsigned short) frame ); + else + tspr = af->GetFrame( (unsigned short) frame, (unsigned char) cycle ); + return tspr; +} + +void* GameData::GetFactoryResource(const char* resname, SClass_ID type, + unsigned char mode, bool silent) +{ + int fobjindex = factory->IsLoaded(resname,type); + // already cached + if ( fobjindex != -1) + return factory->GetFactoryObject( fobjindex ); + + // empty resref + if (!strcmp(resname, "")) + return NULL; + + switch (type) { + case IE_BAM_CLASS_ID: + { + DataStream* ret = GetResource( resname, type, silent ); + if (ret) { + PluginHolder ani(IE_BAM_CLASS_ID); + if (!ani) + return NULL; + ani->Open( ret, true ); + AnimationFactory* af = ani->GetAnimationFactory( resname, mode ); + factory->AddFactoryObject( af ); + return af; + } + return NULL; + } + case IE_BMP_CLASS_ID: + { + ResourceHolder img(resname); + if (img) { + ImageFactory* fact = img->GetImageFactory( resname ); + factory->AddFactoryObject( fact ); + return fact; + } + + return NULL; + } + default: + printf( "\n" ); + printMessage( "KEYImporter", " ", WHITE ); + printf( "%s files are not supported.\n", core->TypeExt( type ) ); + return NULL; + } +} diff --git a/project/jni/application/gemrb/src/core/GameData.h b/project/jni/application/gemrb/src/core/GameData.h new file mode 100644 index 000000000..e61a65eb0 --- /dev/null +++ b/project/jni/application/gemrb/src/core/GameData.h @@ -0,0 +1,121 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * + */ + +#ifndef GAMEDATA_H +#define GAMEDATA_H + +#include "SClassID.h" +#include "exports.h" +#include "ie_types.h" + +#include "Cache.h" +#include "Holder.h" +#include "ResourceManager.h" + +class Actor; +struct Effect; +class Factory; +class Item; +class Palette; +class ScriptedAnimation; +class Spell; +class Sprite2D; +class TableMgr; + +struct Table { + Holder tm; + char ResRef[8]; + unsigned int refcount; +}; + +class GEM_EXPORT GameData : public ResourceManager +{ +public: + GameData(); + ~GameData(); + + void ClearCaches(); + + /** Returns actor */ + Actor *GetCreature(const char *ResRef, unsigned int PartySlot=0); + /** Returns a PC index, by loading a creature */ + int LoadCreature(const char *ResRef, unsigned int PartySlot, bool character=false, int VersionOverride=-1); + + + // 2DA table functions. + // (See also the AutoTable class) + + /** Loads a 2DA Table, returns -1 on error or the Table Index on success */ + int LoadTable(const char * ResRef); + /** Gets the index of a loaded table, returns -1 on error */ + int GetTableIndex(const char * ResRef) const; + /** Gets a Loaded Table by its index, returns NULL on error */ + Holder GetTable(unsigned int index) const; + /** Frees a Loaded Table, returns false on error, true on success */ + bool DelTable(unsigned int index); + + Palette* GetPalette(const ieResRef resname); + void FreePalette(Palette *&pal, const ieResRef name=NULL); + + Item* GetItem(const ieResRef resname); + void FreeItem(Item const *itm, const ieResRef name, bool free=false); + Spell* GetSpell(const ieResRef resname, bool silent=false); + void FreeSpell(Spell *spl, const ieResRef name, bool free=false); + Effect* GetEffect(const ieResRef resname); + void FreeEffect(Effect *eff, const ieResRef name, bool free=false); + + /** creates a vvc/bam animation object at point */ + ScriptedAnimation* GetScriptedAnimation( const char *ResRef, bool doublehint); + + /** returns a single sprite (not cached) from a BAM resource */ + Sprite2D* GetBAMSprite(const ieResRef ResRef, int cycle, int frame); + + /** returns factory resource, currently works only with animations */ + void* GetFactoryResource(const char* resname, SClass_ID type, + unsigned char mode = IE_NORMAL, bool silent=false); +private: + Cache ItemCache; + Cache SpellCache; + Cache EffectCache; + Cache PaletteCache; + Factory* factory; + std::vector tables; +}; + +extern GEM_EXPORT GameData * gamedata; + +template +class ResourceHolder : public Holder +{ +public: + ResourceHolder() + { + } + ResourceHolder(const char* resname) + : Holder(static_cast(gamedata->GetResource(resname,&T::ID))) + { + } + ResourceHolder(const char* resname, const ResourceManager& manager, bool silent = false) + : Holder(static_cast(manager.GetResource(resname,&T::ID,silent))) + { + } +}; + +#endif diff --git a/project/jni/application/gemrb/src/core/GameScript/Actions.cpp b/project/jni/application/gemrb/src/core/GameScript/Actions.cpp new file mode 100644 index 000000000..2a9284883 --- /dev/null +++ b/project/jni/application/gemrb/src/core/GameScript/Actions.cpp @@ -0,0 +1,7109 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003-2007 The GemRB Project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * + */ + +#include "GameScript/GameScript.h" + +#include "GameScript/GSUtils.h" +#include "GameScript/Matching.h" + +#include "win32def.h" + +#include "AmbientMgr.h" +#include "Audio.h" +#include "DataFileMgr.h" +#include "DialogHandler.h" +#include "DisplayMessage.h" +#include "Game.h" +#include "GameData.h" +#include "Item.h" +#include "Map.h" +#include "MusicMgr.h" +#include "SaveGameIterator.h" +#include "ScriptEngine.h" +#include "TileMap.h" +#include "Video.h" +#include "WorldMap.h" +#include "GUI/GameControl.h" + +//------------------------------------------------------------ +// Action Functions +//------------------------------------------------------------- + +void GameScript::SetExtendedNight(Scriptable* Sender, Action* parameters) +{ + Map *map=Sender->GetCurrentArea(); + //sets the 'can rest other' bit + if (parameters->int0Parameter) { + map->AreaType|=AT_EXTENDED_NIGHT; + } else { + map->AreaType&=~AT_EXTENDED_NIGHT; + } +} + +void GameScript::SetAreaRestFlag(Scriptable* Sender, Action* parameters) +{ + Map *map=Sender->GetCurrentArea(); + //sets the 'can rest other' bit + if (parameters->int0Parameter) { + map->AreaType|=AT_CAN_REST; + } else { + map->AreaType&=~AT_CAN_REST; + } +} + +void GameScript::AddAreaFlag(Scriptable* Sender, Action* parameters) +{ + Map *map=Sender->GetCurrentArea(); + map->AreaFlags|=parameters->int0Parameter; +} + +void GameScript::RemoveAreaFlag(Scriptable* Sender, Action* parameters) +{ + Map *map=Sender->GetCurrentArea(); + map->AreaFlags&=~parameters->int0Parameter; +} + +void GameScript::SetAreaFlags(Scriptable* Sender, Action* parameters) +{ + Map *map=Sender->GetCurrentArea(); + ieDword value = map->AreaFlags; + HandleBitMod( value, parameters->int0Parameter, parameters->int1Parameter); + map->AreaFlags=value; +} + +void GameScript::AddAreaType(Scriptable* Sender, Action* parameters) +{ + Map *map=Sender->GetCurrentArea(); + map->AreaType|=parameters->int0Parameter; +} + +void GameScript::RemoveAreaType(Scriptable* Sender, Action* parameters) +{ + Map *map=Sender->GetCurrentArea(); + map->AreaType&=~parameters->int0Parameter; +} + +void GameScript::NoActionAtAll(Scriptable* /*Sender*/, Action* /*parameters*/) +{ + //thats all :) +} + +// this action stops modal actions, so... +void GameScript::NoAction(Scriptable* Sender, Action* /*parameters*/) +{ + if (Sender->Type!=ST_ACTOR) { + return; + } + Actor *actor = (Actor *) Sender; + actor->SetModal( MS_NONE); +} + +void GameScript::SG(Scriptable* Sender, Action* parameters) +{ + SetVariable( Sender, parameters->string0Parameter, "GLOBAL", parameters->int0Parameter ); +} + +void GameScript::SetGlobal(Scriptable* Sender, Action* parameters) +{ + SetVariable( Sender, parameters->string0Parameter, parameters->int0Parameter ); +} + +void GameScript::SetGlobalRandom(Scriptable* Sender, Action* parameters) +{ + int max=parameters->int1Parameter-parameters->int0Parameter+1; + if (max>0) { + SetVariable( Sender, parameters->string0Parameter, RandomNumValue%max+parameters->int0Parameter ); + } else { + SetVariable( Sender, parameters->string0Parameter, 0); + } +} + +void GameScript::StartTimer(Scriptable* Sender, Action* parameters) +{ + Sender->StartTimer(parameters->int0Parameter, parameters->int1Parameter); +} + +void GameScript::StartRandomTimer(Scriptable* Sender, Action* parameters) +{ + ieDword value = core->Roll(1, parameters->int2Parameter-parameters->int1Parameter, parameters->int2Parameter-1); + Sender->StartTimer(parameters->int0Parameter, value); +} + +void GameScript::SetGlobalTimer(Scriptable* Sender, Action* parameters) +{ + ieDword mytime; + + mytime=core->GetGame()->GameTime; //gametime (should increase it) + SetVariable( Sender, parameters->string0Parameter, + parameters->int0Parameter*AI_UPDATE_TIME + mytime); +} + +void GameScript::SetGlobalTimerRandom(Scriptable* Sender, Action* parameters) +{ + ieDword mytime; + int random; + + //This works both ways in the original engine + if (parameters->int1Parameter>parameters->int0Parameter) { + random = parameters->int1Parameter-parameters->int0Parameter+1; + //random cannot be 0, its minimal value is 1 + random = RandomNumValue % random + parameters->int0Parameter; + } else { + random = parameters->int0Parameter-parameters->int1Parameter+1; + random = RandomNumValue % random + parameters->int1Parameter; + } + mytime=core->GetGame()->GameTime; //gametime (should increase it) + SetVariable( Sender, parameters->string0Parameter, random*AI_UPDATE_TIME + mytime); +} + +void GameScript::SetGlobalTimerOnce(Scriptable* Sender, Action* parameters) +{ + ieDword mytime = CheckVariable( Sender, parameters->string0Parameter ); + if (mytime != 0) { + return; + } + mytime=core->GetGame()->GameTime; //gametime (should increase it) + SetVariable( Sender, parameters->string0Parameter, + parameters->int0Parameter*AI_UPDATE_TIME + mytime); +} + +void GameScript::RealSetGlobalTimer(Scriptable* Sender, Action* parameters) +{ + ieDword mytime=core->GetGame()->RealTime; + + SetVariable( Sender, parameters->string0Parameter, + parameters->int0Parameter + mytime); +} + +void GameScript::ChangeAllegiance(Scriptable* Sender, Action* parameters) +{ + Scriptable *scr = Sender; + if (parameters->objects[1]) { + scr=GetActorFromObject( Sender, parameters->objects[1] ); + } + if (!scr || scr->Type != ST_ACTOR) { + return; + } + Actor* actor = ( Actor* ) scr; + actor->SetBase( IE_EA, parameters->int0Parameter ); +} + +void GameScript::ChangeGeneral(Scriptable* Sender, Action* parameters) +{ + Scriptable *scr = Sender; + if (parameters->objects[1]) { + scr=GetActorFromObject( Sender, parameters->objects[1] ); + } + if (!scr || scr->Type != ST_ACTOR) { + return; + } + Actor* actor = ( Actor* ) scr; + actor->SetBase( IE_GENERAL, parameters->int0Parameter ); +} + +void GameScript::ChangeRace(Scriptable* Sender, Action* parameters) +{ + Scriptable *scr = Sender; + if (parameters->objects[1]) { + scr=GetActorFromObject( Sender, parameters->objects[1] ); + } + if (!scr || scr->Type != ST_ACTOR) { + return; + } + Actor* actor = ( Actor* ) scr; + actor->SetBase( IE_RACE, parameters->int0Parameter ); +} + +void GameScript::ChangeClass(Scriptable* Sender, Action* parameters) +{ + Scriptable *scr = Sender; + if (parameters->objects[1]) { + scr=GetActorFromObject( Sender, parameters->objects[1] ); + } + if (!scr || scr->Type != ST_ACTOR) { + return; + } + Actor* actor = ( Actor* ) scr; + actor->SetBase( IE_CLASS, parameters->int0Parameter ); +} + +void GameScript::SetNamelessClass(Scriptable* /*Sender*/, Action* parameters) +{ + //same as Protagonist + Actor* actor = core->GetGame()->GetPC(0, false); + actor->SetBase( IE_CLASS, parameters->int0Parameter ); +} + +void GameScript::SetNamelessDisguise(Scriptable* Sender, Action* parameters) +{ + SetVariable(Sender, "APPEARANCE", "GLOBAL", parameters->int0Parameter); + core->SetEventFlag(EF_UPDATEANIM); +} + +void GameScript::ChangeSpecifics(Scriptable* Sender, Action* parameters) +{ + Scriptable *scr = Sender; + if (parameters->objects[1]) { + scr=GetActorFromObject( Sender, parameters->objects[1] ); + } + if (!scr || scr->Type != ST_ACTOR) { + return; + } + Actor* actor = ( Actor* ) scr; + actor->SetBase( IE_SPECIFIC, parameters->int0Parameter ); +} + +void GameScript::PermanentStatChange(Scriptable* Sender, Action* parameters) +{ + Scriptable *scr = Sender; + if (parameters->objects[1]) { + scr=GetActorFromObject( Sender, parameters->objects[1] ); + } + if (!scr || scr->Type != ST_ACTOR) { + return; + } + Actor* actor = ( Actor* ) scr; + ieDword value; + switch (parameters->int1Parameter) { + case 1: + value = actor->GetBase(parameters->int0Parameter); + value-= parameters->int2Parameter; + break; + case 2: + value = actor->GetBase(parameters->int0Parameter); + value+= parameters->int2Parameter; + break; + case 3: + default: //no idea what happens + value = parameters->int2Parameter; + break; + } + actor->SetBase( parameters->int0Parameter, value); +} + +void GameScript::ChangeStat(Scriptable* Sender, Action* parameters) +{ + Scriptable *scr = Sender; + if (parameters->objects[1]) { + scr=GetActorFromObject( Sender, parameters->objects[1] ); + } + if (!scr || scr->Type != ST_ACTOR) { + return; + } + Actor* actor = ( Actor* ) scr; + ieDword value = parameters->int1Parameter; + if (parameters->int2Parameter==1) { + value+=actor->GetBase(parameters->int0Parameter); + } + actor->SetBase( parameters->int0Parameter, value); +} + +void GameScript::ChangeStatGlobal(Scriptable* Sender, Action* parameters) +{ + Scriptable *scr = Sender; + if (parameters->objects[1]) { + scr=GetActorFromObject( Sender, parameters->objects[1] ); + } + if (!scr || scr->Type != ST_ACTOR) { + return; + } + ieDword value = (ieDword) CheckVariable( Sender, parameters->string0Parameter, parameters->string1Parameter ); + Actor* actor = ( Actor* ) scr; + if (parameters->int1Parameter==1) { + value+=actor->GetBase(parameters->int0Parameter); + } + actor->SetBase( parameters->int0Parameter, value); +} + +void GameScript::ChangeGender(Scriptable* Sender, Action* parameters) +{ + Scriptable *scr = Sender; + if (parameters->objects[1]) { + scr=GetActorFromObject( Sender, parameters->objects[1] ); + } + if (!scr || scr->Type != ST_ACTOR) { + return; + } + Actor* actor = ( Actor* ) scr; + actor->SetBase( IE_SEX, parameters->int0Parameter ); +} + +void GameScript::ChangeAlignment(Scriptable* Sender, Action* parameters) +{ + Scriptable *scr = Sender; + if (parameters->objects[1]) { + scr=GetActorFromObject( Sender, parameters->objects[1] ); + } + if (!scr || scr->Type != ST_ACTOR) { + return; + } + Actor* actor = ( Actor* ) scr; + actor->SetBase( IE_ALIGNMENT, parameters->int0Parameter ); +} + +void GameScript::SetFaction(Scriptable* Sender, Action* parameters) +{ + Scriptable *scr = Sender; + if (parameters->objects[1]) { + scr=GetActorFromObject( Sender, parameters->objects[1] ); + } + if (!scr || scr->Type != ST_ACTOR) { + return; + } + Actor* actor = ( Actor* ) scr; + actor->SetBase( IE_FACTION, parameters->int0Parameter ); +} + +void GameScript::SetHP(Scriptable* Sender, Action* parameters) +{ + Scriptable *scr = Sender; + if (parameters->objects[1]) { + scr=GetActorFromObject( Sender, parameters->objects[1] ); + } + if (!scr || scr->Type != ST_ACTOR) { + return; + } + Actor* actor = ( Actor* ) scr; + actor->SetBase( IE_HITPOINTS, parameters->int0Parameter ); +} + +void GameScript::SetHPPercent(Scriptable* Sender, Action* parameters) +{ + Scriptable *scr = Sender; + if (parameters->objects[1]) { + scr=GetActorFromObject( Sender, parameters->objects[1] ); + } + if (!scr || scr->Type != ST_ACTOR) { + return; + } + Actor* actor = ( Actor* ) scr; + actor->NewBase( IE_HITPOINTS, parameters->int0Parameter, MOD_PERCENT); +} + +void GameScript::AddHP(Scriptable* Sender, Action* parameters) +{ + Scriptable *scr = Sender; + if (parameters->objects[1]) { + scr=GetActorFromObject( Sender, parameters->objects[1] ); + } + if (!scr || scr->Type != ST_ACTOR) { + return; + } + Actor* actor = ( Actor* ) scr; + actor->NewBase(IE_HITPOINTS, parameters->int0Parameter, MOD_ADDITIVE); +} + +//this works on an object (pst) +//but can also work on actor itself (gemrb) +void GameScript::SetTeam(Scriptable* Sender, Action* parameters) +{ + Scriptable *scr = Sender; + if (parameters->objects[1]) { + scr=GetActorFromObject( Sender, parameters->objects[1] ); + } + if (!scr || scr->Type != ST_ACTOR) { + return; + } + Actor* actor = ( Actor* ) scr; + actor->SetBase( IE_TEAM, parameters->int0Parameter ); +} + +//this works on an object (gemrb) +//or on Myself if object isn't given (iwd2) +void GameScript::SetTeamBit(Scriptable* Sender, Action* parameters) +{ + Scriptable *scr = Sender; + if (parameters->objects[1]) { + scr=GetActorFromObject( Sender, parameters->objects[1] ); + } + if (!scr || scr->Type != ST_ACTOR) { + return; + } + Actor* actor = ( Actor* ) scr; + if (parameters->int1Parameter) { + actor->SetBase( IE_TEAM, actor->GetStat(IE_TEAM) | parameters->int0Parameter ); + } else { + actor->SetBase( IE_TEAM, actor->GetStat(IE_TEAM) & ~parameters->int0Parameter ); + } +} + +void GameScript::TriggerActivation(Scriptable* Sender, Action* parameters) +{ + Scriptable* ip; + + if (!parameters->objects[1]) { + ip=Sender; + } else { + ip = Sender->GetCurrentArea()->TMap->GetInfoPoint(parameters->objects[1]->objectName); + } + if (!ip || (ip->Type!=ST_TRIGGER && ip->Type!=ST_TRAVEL && ip->Type!=ST_PROXIMITY)) { + printf("Script error: No Trigger Named \"%s\"\n", parameters->objects[1]->objectName); + return; + } + InfoPoint *trigger = (InfoPoint *) ip; + if ( parameters->int0Parameter != 0 ) { + trigger->Flags &= ~TRAP_DEACTIVATED; + } else { + trigger->Flags |= TRAP_DEACTIVATED; + } +} + +void GameScript::FadeToColor(Scriptable* Sender, Action* parameters) +{ + core->timer->SetFadeToColor( parameters->pointParameter.x ); +// Sender->SetWait( parameters->pointParameter.x ); + Sender->ReleaseCurrentAction(); // todo, blocking? +} + +void GameScript::FadeFromColor(Scriptable* Sender, Action* parameters) +{ + core->timer->SetFadeFromColor( parameters->pointParameter.x ); +// Sender->SetWait( parameters->pointParameter.x ); + Sender->ReleaseCurrentAction(); // todo, blocking? +} + +void GameScript::FadeToAndFromColor(Scriptable* Sender, Action* parameters) +{ + core->timer->SetFadeToColor( parameters->pointParameter.x ); + core->timer->SetFadeFromColor( parameters->pointParameter.x ); +// Sender->SetWait( parameters->pointParameter.x<<1 ); //multiply by 2 + Sender->ReleaseCurrentAction(); // todo, blocking? +} + +void GameScript::JumpToPoint(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type != ST_ACTOR) { + return; + } + Actor* ab = ( Actor* ) Sender; + ab->SetPosition( parameters->pointParameter, true ); +} + +void GameScript::JumpToPointInstant(Scriptable* Sender, Action* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar || tar->Type != ST_ACTOR) { + return; + } + Actor* ab = ( Actor* ) tar; + ab->SetPosition( parameters->pointParameter, true ); +} + +/** instant jump to location saved in stats */ +/** default subject is the current actor */ +void GameScript::JumpToSavedLocation(Scriptable* Sender, Action* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar) { + tar = Sender; + } + if (tar->Type != ST_ACTOR) { + return; + } + Actor *actor = (Actor *) tar; + Point p((short) actor->GetStat(IE_SAVEDXPOS), (short) actor->GetStat(IE_SAVEDYPOS) ); + actor->SetPosition(p, true ); + actor->SetOrientation( actor->GetStat(IE_SAVEDFACE), false ); +} + +void GameScript::JumpToObject(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type != ST_ACTOR) { + return; + } + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + + if (!tar) { + return; + } + const Map *map = tar->GetCurrentArea(); + + if (map) { + if (parameters->string0Parameter[0]) { + CreateVisualEffectCore(Sender, Sender->Pos, parameters->string0Parameter, 0); + } + MoveBetweenAreasCore( (Actor *) Sender, map->GetScriptName(), tar->Pos, -1, true); + } +} + +void GameScript::TeleportParty(Scriptable* /*Sender*/, Action* parameters) +{ + Game *game = core->GetGame(); + int i = game->GetPartySize(false); + while (i--) { + Actor *tar = game->GetPC(i, false); + MoveBetweenAreasCore( tar, parameters->string1Parameter, + parameters->pointParameter, -1, true); + } +} + +//5 is the ToB value, but it might be useful to have multiple expansions +void GameScript::MoveToExpansion(Scriptable* Sender, Action* parameters) +{ + Game *game = core->GetGame(); + + if (!parameters->int0Parameter) { + parameters->int0Parameter = 5; + } + game->SetExpansion(parameters->int0Parameter); + Sender->ReleaseCurrentAction(); +} + +//add some animation effects too? +void GameScript::ExitPocketPlane(Scriptable* /*Sender*/, Action* /*parameters*/) +{ + Game *game = core->GetGame(); + for (int i = 0; i < game->GetPartySize(false); i++) { + Actor* act = game->GetPC( i, false ); + if (act) { + if (game->GetPlaneLocationCount() <= (unsigned int)i) { + // what are we meant to do here? + printf("argh, couldn't restore party member %d!", i + 1); + continue; + } + GAMLocationEntry *gle = game->GetPlaneLocationEntry(i); + MoveBetweenAreasCore(act, gle->AreaResRef, gle->Pos, -1, true); + } + } + + // don't clear locations! +} + +//moves pcs and npcs from an area to another area +void GameScript::MoveGlobalsTo(Scriptable* /*Sender*/, Action* parameters) +{ + Game *game = core->GetGame(); + int i = game->GetPartySize(false); + while (i--) { + Actor *tar = game->GetPC(i, false); + //if the actor isn't in the area, we don't care + if (strnicmp(tar->Area, parameters->string0Parameter,8) ) { + continue; + } + MoveBetweenAreasCore( tar, parameters->string1Parameter, + parameters->pointParameter, -1, true); + } + i = game->GetNPCCount(); + while (i--) { + Actor *tar = game->GetNPC(i); + //if the actor isn't in the area, we don't care + if (strnicmp(tar->Area, parameters->string0Parameter,8) ) { + continue; + } + MoveBetweenAreasCore( tar, parameters->string1Parameter, + parameters->pointParameter, -1, true); + } +} + +void GameScript::MoveGlobal(Scriptable* Sender, Action* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar || tar->Type != ST_ACTOR) { + return; + } + + MoveBetweenAreasCore( (Actor *) tar, parameters->string0Parameter, + parameters->pointParameter, -1, true); +} + +//we also allow moving to door, container +void GameScript::MoveGlobalObject(Scriptable* Sender, Action* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar || tar->Type != ST_ACTOR) { + return; + } + Scriptable* to = GetActorFromObject( Sender, parameters->objects[2] ); + if (!to) { + return; + } + const Map *map = to->GetCurrentArea(); + + if (map) { + MoveBetweenAreasCore( (Actor *) tar, map->GetScriptName(), + to->Pos, -1, true); + } +} + +void GameScript::MoveGlobalObjectOffScreen(Scriptable* Sender, Action* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar || tar->Type != ST_ACTOR) { + return; + } + Scriptable* to = GetActorFromObject( Sender, parameters->objects[2] ); + if (!to) { + return; + } + MoveBetweenAreasCore( (Actor *) tar, parameters->string0Parameter, + to->Pos, -1, false); +} + +//don't use offset from Sender +void GameScript::CreateCreature(Scriptable* Sender, Action* parameters) +{ + CreateCreatureCore( Sender, parameters, CC_CHECK_IMPASSABLE|CC_CHECK_OVERLAP|CC_SCRIPTNAME ); +} + +//another highly redundant action +void GameScript::CreateCreatureDoor(Scriptable* Sender, Action* parameters) +{ + //we hack this to death + strcpy(parameters->string1Parameter, "SPDIMNDR"); + CreateCreatureCore( Sender, parameters, CC_CHECK_IMPASSABLE|CC_CHECK_OVERLAP | CC_PLAY_ANIM ); +} + +//another highly redundant action +void GameScript::CreateCreatureObjectDoor(Scriptable* Sender, Action* parameters) +{ + //we hack this to death + strcpy(parameters->string1Parameter, "SPDIMNDR"); + CreateCreatureCore( Sender, parameters, CC_OFFSET | CC_CHECK_IMPASSABLE|CC_CHECK_OVERLAP | CC_PLAY_ANIM ); +} + +//don't use offset from Sender +void GameScript::CreateCreatureImpassable(Scriptable* Sender, Action* parameters) +{ + CreateCreatureCore( Sender, parameters, CC_CHECK_OVERLAP ); +} + +void GameScript::CreateCreatureImpassableAllowOverlap(Scriptable* Sender, Action* parameters) +{ + CreateCreatureCore( Sender, parameters, 0 ); +} + +//use offset from Sender +void GameScript::CreateCreatureAtFeet(Scriptable* Sender, Action* parameters) +{ + CreateCreatureCore( Sender, parameters, CC_OFFSET | CC_CHECK_IMPASSABLE | CC_CHECK_OVERLAP); +} + +void GameScript::CreateCreatureOffScreen(Scriptable* Sender, Action* parameters) +{ + CreateCreatureCore( Sender, parameters, CC_OFFSCREEN | CC_CHECK_IMPASSABLE | CC_CHECK_OVERLAP ); +} + +//creates copy at actor, plays animation +void GameScript::CreateCreatureObjectCopy(Scriptable* Sender, Action* parameters) +{ + CreateCreatureCore( Sender, parameters, CC_OBJECT | CC_CHECK_IMPASSABLE | CC_CHECK_OVERLAP | CC_COPY | CC_PLAY_ANIM ); +} + +//creates copy at absolute point +void GameScript::CreateCreatureCopyPoint(Scriptable* Sender, Action* parameters) +{ + CreateCreatureCore( Sender, parameters, CC_CHECK_IMPASSABLE | CC_CHECK_OVERLAP | CC_COPY | CC_PLAY_ANIM ); +} + +//this is the same, object + offset +//using this for simple createcreatureobject, (0 offsets) +//createcreatureobjecteffect may have animation +void GameScript::CreateCreatureObjectOffset(Scriptable* Sender, Action* parameters) +{ + CreateCreatureCore( Sender, parameters, CC_OBJECT | CC_CHECK_IMPASSABLE | CC_CHECK_OVERLAP | CC_PLAY_ANIM); +} + +void GameScript::CreateCreatureObjectOffScreen(Scriptable* Sender, Action* parameters) +{ + CreateCreatureCore( Sender, parameters, CC_OFFSCREEN | CC_OBJECT | CC_CHECK_IMPASSABLE | CC_CHECK_OVERLAP ); +} + +//I think this simply removes the cursor and hides the gui without disabling scripts +//See Interface::SetCutSceneMode +void GameScript::SetCursorState(Scriptable* /*Sender*/, Action* parameters) +{ + int active = parameters->int0Parameter; + + Game *game = core->GetGame(); + if (active) { + game->ControlStatus |= CS_HIDEGUI; + } else { + game->ControlStatus &= ~CS_HIDEGUI; + } + core->SetEventFlag(EF_CONTROL); + core->GetVideoDriver()->SetMouseEnabled(!active); +} + +void GameScript::StartCutSceneMode(Scriptable* /*Sender*/, Action* /*parameters*/) +{ + core->SetCutSceneMode( true ); +} + +void GameScript::EndCutSceneMode(Scriptable* /*Sender*/, Action* /*parameters*/) +{ + core->SetCutSceneMode( false ); +} + +void GameScript::StartCutScene(Scriptable* Sender, Action* parameters) +{ + GameScript* gs = new GameScript( parameters->string0Parameter, Sender ); + gs->EvaluateAllBlocks(); + delete( gs ); +} + +void GameScript::CutSceneID(Scriptable* /*Sender*/, Action* /*parameters*/) +{ + // shouldn't get called + printMessage("GameScript","CutSceneID was called!\n",YELLOW); +} + +void GameScript::Enemy(Scriptable* Sender, Action* /*parameters*/) +{ + if (Sender->Type != ST_ACTOR) { + return; + } + Actor* actor = ( Actor* ) Sender; + actor->SetBase( IE_EA, EA_ENEMY ); +} + +void GameScript::Ally(Scriptable* Sender, Action* /*parameters*/) +{ + if (Sender->Type != ST_ACTOR) { + return; + } + Actor* actor = ( Actor* ) Sender; + actor->SetBase( IE_EA, EA_ALLY ); +} + +/** GemRB extension: you can replace baldur.bcs */ +void GameScript::ChangeAIScript(Scriptable* Sender, Action* parameters) +{ + if (parameters->int0Parameter>7) { + return; + } + if (Sender->Type!=ST_ACTOR && parameters->int0Parameter) { + return; + } + Sender->SetScript( parameters->string0Parameter, parameters->int0Parameter, false ); +} + +void GameScript::ForceAIScript(Scriptable* Sender, Action* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar || tar->Type != ST_ACTOR) { + return; + } + Actor* actor = ( Actor* ) tar; + //changeaiscript clears the queue, i believe + // actor->ClearActions(); + actor->SetScript( parameters->string0Parameter, parameters->int0Parameter, false ); +} + +void GameScript::SetPlayerSound(Scriptable* Sender, Action* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar || tar->Type != ST_ACTOR) { + return; + } + Actor* actor = ( Actor* ) tar; + actor->StrRefs[parameters->int0Parameter]=parameters->int1Parameter; +} + +//this one works only on real actors, they got constants +void GameScript::VerbalConstantHead(Scriptable* Sender, Action* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar || tar->Type != ST_ACTOR) { + return; + } + DisplayStringCore( tar, parameters->int0Parameter, DS_HEAD|DS_CONSOLE|DS_CONST); +} + +void GameScript::VerbalConstant(Scriptable* Sender, Action* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar || tar->Type != ST_ACTOR) { + return; + } + DisplayStringCore( tar, parameters->int0Parameter, DS_CONSOLE|DS_CONST); +} + +//bg2 - variable +void GameScript::SaveLocation(Scriptable* Sender, Action* parameters) +{ + ieDword value = parameters->pointParameter.asDword(); + if (!parameters->string0Parameter[0]) { + strcpy(parameters->string0Parameter,"LOCALSsavedlocation"); + } + SetVariable(Sender, parameters->string0Parameter, value); +} + +//PST:has parameters, IWD2: no params +void GameScript::SetSavedLocation(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type!=ST_ACTOR) { + return; + } + Actor *actor = (Actor *) Sender; + //iwd2 + if (parameters->pointParameter.isnull()) { + actor->SetBase(IE_SAVEDXPOS, actor->Pos.x); + actor->SetBase(IE_SAVEDYPOS, actor->Pos.y); + actor->SetBase(IE_SAVEDFACE, actor->GetOrientation()); + return; + } + //pst + actor->SetBase(IE_SAVEDXPOS, parameters->pointParameter.x); + actor->SetBase(IE_SAVEDYPOS, parameters->pointParameter.y); + actor->SetBase(IE_SAVEDFACE, parameters->int0Parameter); +} +//IWD2, sets the homepoint int0,int1,int2 +void GameScript::SetSavedLocationPoint(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type!=ST_ACTOR) { + return; + } + Actor *actor = (Actor *) Sender; + actor->SetBase(IE_SAVEDXPOS, parameters->int0Parameter); + actor->SetBase(IE_SAVEDYPOS, parameters->int1Parameter); + actor->SetBase(IE_SAVEDFACE, parameters->int2Parameter); +} +//IWD2, sets the homepoint P +void GameScript::SetStartPos(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type!=ST_ACTOR) { + return; + } + Actor *actor = (Actor *) Sender; + actor->SetBase(IE_SAVEDXPOS, parameters->pointParameter.x); + actor->SetBase(IE_SAVEDYPOS, parameters->pointParameter.y); + actor->SetBase(IE_SAVEDFACE, parameters->int0Parameter); +} + +void GameScript::SaveObjectLocation(Scriptable* Sender, Action* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar) { + return; + } + ieDword value = tar->Pos.asDword(); + if (!parameters->string0Parameter[0]) { + strcpy(parameters->string0Parameter,"LOCALSsavedlocation"); + } + SetVariable(Sender, parameters->string0Parameter, value); +} + +/** you may omit the string0Parameter, in this case this will be a */ +/** CreateCreatureAtSavedLocation */ +void GameScript::CreateCreatureAtLocation(Scriptable* Sender, Action* parameters) +{ + if (!parameters->string0Parameter[0]) { + strcpy(parameters->string0Parameter,"LOCALSsavedlocation"); + } + ieDword value = CheckVariable(Sender, parameters->string0Parameter); + parameters->pointParameter.y = (ieWord) (value & 0xffff); + parameters->pointParameter.x = (ieWord) (value >> 16); + CreateCreatureCore(Sender, parameters, CC_CHECK_IMPASSABLE|CC_STRING1); +} + +void GameScript::WaitRandom(Scriptable* Sender, Action* parameters) +{ + if (!Sender->CurrentActionState) { + int width = parameters->int1Parameter-parameters->int0Parameter; + if (width<2) { + width = parameters->int0Parameter; + } else { + width = rand() % width + parameters->int0Parameter; + } + Sender->CurrentActionState = width * AI_UPDATE_TIME; + } else { + Sender->CurrentActionState--; + } + + if (!Sender->CurrentActionState) { + Sender->ReleaseCurrentAction(); + } + + assert(Sender->CurrentActionState >= 0); +} + +void GameScript::Wait(Scriptable* Sender, Action* parameters) +{ + if (!Sender->CurrentActionState) { + Sender->CurrentActionState = parameters->int0Parameter * AI_UPDATE_TIME; + } else { + Sender->CurrentActionState--; + } + + if (!Sender->CurrentActionState) { + Sender->ReleaseCurrentAction(); + } + + assert(Sender->CurrentActionState >= 0); +} + +void GameScript::SmallWait(Scriptable* Sender, Action* parameters) +{ + if (!Sender->CurrentActionState) { + Sender->CurrentActionState = parameters->int0Parameter; + } else { + Sender->CurrentActionState--; + } + + if (!Sender->CurrentActionState) { + Sender->ReleaseCurrentAction(); + } + + assert(Sender->CurrentActionState >= 0); +} + +void GameScript::SmallWaitRandom(Scriptable* Sender, Action* parameters) +{ + if (!Sender->CurrentActionState) { + int random = parameters->int1Parameter - parameters->int0Parameter; + if (random<1) { + random = 1; + } + Sender->CurrentActionState = rand() % random + parameters->int0Parameter; + } else { + Sender->CurrentActionState--; + } + + if (!Sender->CurrentActionState) { + Sender->ReleaseCurrentAction(); + } + + assert(Sender->CurrentActionState >= 0); +} + +void GameScript::MoveViewPoint(Scriptable* Sender, Action* parameters) +{ + core->timer->SetMoveViewPort( parameters->pointParameter.x, parameters->pointParameter.y, parameters->int0Parameter<<1, true ); + Sender->SetWait(1); // todo, blocking? + Sender->ReleaseCurrentAction(); // todo, blocking? +} + +void GameScript::MoveViewObject(Scriptable* Sender, Action* parameters) +{ + Scriptable * scr = GetActorFromObject( Sender, parameters->objects[1]); + if (!scr) { + Sender->ReleaseCurrentAction(); + return; + } + core->timer->SetMoveViewPort( scr->Pos.x, scr->Pos.y, parameters->int0Parameter<<1, true ); + Sender->SetWait(1); // todo, blocking? + Sender->ReleaseCurrentAction(); // todo, blocking? +} + +void GameScript::AddWayPoint(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type != ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + Actor* actor = ( Actor* ) Sender; + actor->AddWayPoint( parameters->pointParameter ); + // this is marked as AF_BLOCKING (and indeed AddWayPoint causes moves), + // but this probably needs more thought + Sender->ReleaseCurrentAction(); +} + +void GameScript::MoveToPointNoRecticle(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type != ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + Actor *actor = ( Actor* ) Sender; + if (!actor->InMove() || actor->Destination != parameters->pointParameter) { + actor->WalkTo( parameters->pointParameter, IF_NORECTICLE, 0 ); + } + if (!actor->InMove()) { + // we should probably instead keep retrying until we reach dest + Sender->ReleaseCurrentAction(); + } +} + +void GameScript::MoveToPointNoInterrupt(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type != ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + Actor* actor = ( Actor* ) Sender; + if (!actor->InMove() || actor->Destination != parameters->pointParameter) { + actor->WalkTo( parameters->pointParameter, IF_NOINT, 0 ); + } + // should we always force IF_NOINT here? + if (!actor->InMove()) { + // we should probably instead keep retrying until we reach dest + actor->Interrupt(); + Sender->ReleaseCurrentAction(); + } +} + +void GameScript::RunToPointNoRecticle(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type != ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + Actor* actor = ( Actor* ) Sender; + if (!actor->InMove() || actor->Destination != parameters->pointParameter) { + actor->WalkTo( parameters->pointParameter, IF_NORECTICLE|IF_RUNNING, 0 ); + } + if (!actor->InMove()) { + // we should probably instead keep retrying until we reach dest + Sender->ReleaseCurrentAction(); + } +} + +void GameScript::RunToPoint(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type != ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + Actor* actor = ( Actor* ) Sender; + if (!actor->InMove() || actor->Destination != parameters->pointParameter) { + actor->WalkTo( parameters->pointParameter, IF_RUNNING, 0 ); + } + if (!actor->InMove()) { + // we should probably instead keep retrying until we reach dest + Sender->ReleaseCurrentAction(); + } +} + +//movetopoint until timer is down or target reached +void GameScript::TimedMoveToPoint(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type != ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + if (parameters->int0Parameter<=0) { + Sender->ReleaseCurrentAction(); + return; + } + Actor *actor = (Actor *) Sender; + + if (!actor->InMove() || actor->Destination != parameters->pointParameter) { + actor->WalkTo( parameters->pointParameter, parameters->int1Parameter,0 ); + } + + //hopefully this hack will prevent lockups + if (!actor->InMove()) { + // we should probably instead keep retrying until we reach dest + Sender->ReleaseCurrentAction(); + return; + } + + //repeat movement... + if (parameters->int0Parameter>0) { + Action *newaction = ParamCopyNoOverride(parameters); + newaction->int0Parameter--; + actor->AddActionInFront(newaction); + Sender->SetWait(1); + } + + Sender->ReleaseCurrentAction(); +} + +void GameScript::MoveToPoint(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type != ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + Actor* actor = ( Actor* ) Sender; + //WalkTo could release the current action, so we need this + ieDword tmp = (ieDword) parameters->int0Parameter; + //InMove can clear destination, so we need to save it + Point dest = actor->Destination; + + // try the actual move, if we are not already moving there + if (!actor->InMove() || actor->Destination != parameters->pointParameter) { + actor->WalkTo( parameters->pointParameter, 0, tmp ); + dest = actor->Destination; + } + + // give up if we can't move there (no path was found) + if (!actor->InMove()) { + // we should probably instead keep retrying until we reach dest + Sender->ReleaseCurrentAction(); + } + + if (tmp) { + if (!actor->InMove()) { + //can't reach target, movement failed + //we have to use tmp-1 because the distance required might be 0, + //so in GoNearAndRetry we add 1 to distance + if (Distance(dest,actor)>tmp-1) { + //to prevent deadlocks, we free the action + //which caused MoveToPoint in the first place + Sender->PopNextAction(); + } + } + } +} + +//bg2, jumps to saved location in variable +void GameScript::MoveToSavedLocation(Scriptable* Sender, Action* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar) { + tar = Sender; + } + if (tar->Type != ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + + Point p; + Actor* actor = ( Actor* ) tar; + ieDword value = (ieDword) CheckVariable( Sender, parameters->string0Parameter ); + p.fromDword(value); + actor->SetPosition(p, true ); + Sender->ReleaseCurrentAction(); +} +/** iwd2 returntosavedlocation (with stats) */ +/** pst returntosavedplace */ +/** use Sender as default subject */ +void GameScript::ReturnToSavedLocation(Scriptable* Sender, Action* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1], GA_NO_DEAD ); + if (!tar) { + tar = Sender; + } + if (tar->Type != ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + + Actor* actor = ( Actor* ) tar; + Point p((short) actor->GetBase(IE_SAVEDXPOS),(short) actor->GetBase(IE_SAVEDYPOS) ); + if (p.isnull()) { + Sender->ReleaseCurrentAction(); + return; + } + if (!actor->InMove() || actor->Destination != p) { + actor->WalkTo( p, 0, 0 ); + } + if (!actor->InMove()) { + // we should probably instead keep retrying until we reach dest + Sender->ReleaseCurrentAction(); + } +} + +//PST +void GameScript::RunToSavedLocation(Scriptable* Sender, Action* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1], GA_NO_DEAD ); + if (!tar) { + tar = Sender; + } + if (tar->Type != ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + + Actor* actor = ( Actor* ) tar; + Point p((short) actor->GetBase(IE_SAVEDXPOS),(short) actor->GetBase(IE_SAVEDYPOS) ); + if (p.isnull()) { + Sender->ReleaseCurrentAction(); + return; + } + if (!actor->InMove() || actor->Destination != p) { + actor->WalkTo( p, IF_RUNNING, 0 ); + } + if (!actor->InMove()) { + // we should probably instead keep retrying until we reach dest + Sender->ReleaseCurrentAction(); + } +} + +//iwd2 +void GameScript::ReturnToSavedLocationDelete(Scriptable* Sender, Action* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1], GA_NO_DEAD ); + if (!tar) { + tar = Sender; + } + if (tar->Type != ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + + Actor* actor = ( Actor* ) tar; + Point p((short) actor->GetBase(IE_SAVEDXPOS),(short) actor->GetBase(IE_SAVEDYPOS) ); + actor->SetBase(IE_SAVEDXPOS,0); + actor->SetBase(IE_SAVEDYPOS,0); + if (p.isnull()) { + Sender->ReleaseCurrentAction(); + return; + } + if (!actor->InMove() || actor->Destination != p) { + actor->WalkTo( p, 0, 0 ); + } + //what else? + if (!actor->InMove()) { + // we should probably instead keep retrying until we reach dest + Sender->ReleaseCurrentAction(); + } +} + +void GameScript::MoveToObjectNoInterrupt(Scriptable* Sender, Action* parameters) +{ + MoveToObjectCore(Sender, parameters, IF_NOINT, false); +} + +void GameScript::RunToObject(Scriptable* Sender, Action* parameters) +{ + MoveToObjectCore(Sender, parameters, IF_RUNNING, false); +} + +void GameScript::MoveToObject(Scriptable* Sender, Action* parameters) +{ + MoveToObjectCore(Sender, parameters, 0, false); +} + +void GameScript::MoveToObjectUntilSee(Scriptable* Sender, Action* parameters) +{ + MoveToObjectCore(Sender, parameters, 0, true); +} + +void GameScript::MoveToObjectFollow(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type != ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + Scriptable* target = GetStoredActorFromObject( Sender, parameters->objects[1] ); + if (!target) { + Sender->ReleaseCurrentAction(); + return; + } + Actor* actor = ( Actor* ) Sender; + //follow leader from a distance of 5 + //could also follow the leader with a point offset + if (target->Type==ST_ACTOR) { + actor->SetLeader( (Actor *) target, 5); + } + MoveNearerTo(Sender, target, MAX_OPERATING_DISTANCE); +} + +void GameScript::StorePartyLocation(Scriptable* /*Sender*/, Action* /*parameters*/) +{ + Game *game = core->GetGame(); + for (int i = 0; i < game->GetPartySize(false); i++) { + Actor* act = game->GetPC( i, false ); + GAMLocationEntry *gle = game->GetSavedLocationEntry(i); + if (act && gle) { + gle->Pos = act->Pos; + memcpy(gle->AreaResRef, act->Area, 9); + } + } +} + +void GameScript::RestorePartyLocation(Scriptable* /*Sender*/, Action* /*parameters*/) +{ + Game *game = core->GetGame(); + for (int i = 0; i < game->GetPartySize(false); i++) { + Actor* act = game->GetPC( i, false ); + if (act) { + if (game->GetSavedLocationCount() <= (unsigned int)i) { + // what are we meant to do here? + printf("argh, couldn't restore party member %d!", i + 1); + continue; + } + GAMLocationEntry *gle = game->GetSavedLocationEntry(i); + MoveBetweenAreasCore(act, gle->AreaResRef, gle->Pos, -1, true); + } + } + + // presumably this is correct + game->ClearSavedLocations(); +} + +void GameScript::MoveToCenterOfScreen(Scriptable* Sender, Action* /*parameters*/) +{ + if (Sender->Type != ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + Region vp = core->GetVideoDriver()->GetViewport(); + Actor* actor = ( Actor* ) Sender; + Point p((short) (vp.x+vp.w/2), (short) (vp.y+vp.h/2) ); + if (!actor->InMove() || actor->Destination != p) { + actor->WalkTo( p, IF_NOINT, 0 ); + } + if (!actor->InMove()) { + // we should probably instead keep retrying until we reach dest + Sender->ReleaseCurrentAction(); + } +} + +void GameScript::MoveToOffset(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type != ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + Actor* actor = ( Actor* ) Sender; + Point p(Sender->Pos.x+parameters->pointParameter.x, Sender->Pos.y+parameters->pointParameter.y); + if (!actor->InMove() || actor->Destination != p) { + actor->WalkTo( p, 0, 0 ); + } + if (!actor->InMove()) { + // we should probably instead keep retrying until we reach dest + Sender->ReleaseCurrentAction(); + } +} + +void GameScript::RunAwayFrom(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type != ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + if (Sender->GetInternalFlag()&IF_STOPATTACK) { + Sender->ReleaseCurrentAction(); + return; + } + Actor* actor = ( Actor* ) Sender; + Scriptable* tar = GetStoredActorFromObject( Sender, parameters->objects[1] ); + if (!tar) { + Sender->ReleaseCurrentAction(); + return; + } + //TODO: actor could use travel areas + // we should be using int0Parameter for the timing here, not distance + if (!actor->InMove()) { + // we should make sure our existing walk is a 'run away', or fix moving/path code + actor->RunAwayFrom( tar->Pos, parameters->int0Parameter, false); + } + + //repeat movement... + if (parameters->int0Parameter>0) { + Action *newaction = ParamCopyNoOverride(parameters); + newaction->int0Parameter--; + actor->AddActionInFront(newaction); + Sender->SetWait(1); + } + + Sender->ReleaseCurrentAction(); +} + +void GameScript::RunAwayFromNoLeaveArea(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type != ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + if (Sender->GetInternalFlag()&IF_STOPATTACK) { + Sender->ReleaseCurrentAction(); + return; + } + Actor* actor = ( Actor* ) Sender; + Scriptable* tar = GetStoredActorFromObject( Sender, parameters->objects[1] ); + if (!tar) { + Sender->ReleaseCurrentAction(); + return; + } + // we should be using int0Parameter for the timing here, not distance + if (!actor->InMove()) { + // we should make sure our existing walk is a 'run away', or fix moving/path code + actor->RunAwayFrom( tar->Pos, parameters->int0Parameter, false); + } + + //repeat movement... + if (parameters->int0Parameter>0) { + Action *newaction = ParamCopyNoOverride(parameters); + newaction->int0Parameter--; + actor->AddActionInFront(newaction); + Sender->SetWait(1); + } + + Sender->ReleaseCurrentAction(); +} + +void GameScript::RunAwayFromNoInterrupt(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type != ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + //i believe being dead still interrupts this action + if (Sender->GetInternalFlag()&IF_STOPATTACK) { + Sender->ReleaseCurrentAction(); + return; + } + Actor* actor = ( Actor* ) Sender; + Scriptable* tar = GetStoredActorFromObject( Sender, parameters->objects[1] ); + if (!tar) { + Sender->ReleaseCurrentAction(); + return; + } + //actor->InternalFlags|=IF_NOINT; + actor->NoInterrupt(); + // we should be using int0Parameter for the timing here, not distance + if (!actor->InMove()) { + // we should make sure our existing walk is a 'run away', or fix moving/path code + actor->RunAwayFrom( tar->Pos, parameters->int0Parameter, false); + } + + //repeat movement... + if (parameters->int0Parameter>0) { + Action *newaction = ParamCopyNoOverride(parameters); + newaction->int0Parameter--; + actor->AddActionInFront(newaction); + Sender->SetWait(1); + } else { + actor->Interrupt(); + } + + Sender->ReleaseCurrentAction(); +} + +void GameScript::RunAwayFromPoint(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type != ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + if (Sender->GetInternalFlag()&IF_STOPATTACK) { + Sender->ReleaseCurrentAction(); + return; + } + Actor* actor = ( Actor* ) Sender; + // we should be using int0Parameter for the timing here, not distance? + if (!actor->InMove()) { + // we should make sure our existing walk is a 'run away', or fix moving/path code + actor->RunAwayFrom( parameters->pointParameter, parameters->int0Parameter, false); + } + + //repeat movement... + if (parameters->int0Parameter>0) { + Action *newaction = ParamCopyNoOverride(parameters); + newaction->int0Parameter--; + actor->AddActionInFront(newaction); + Sender->SetWait(1); + } + + Sender->ReleaseCurrentAction(); +} + +void GameScript::DisplayStringNoName(Scriptable* Sender, Action* parameters) +{ + Scriptable* target = GetActorFromObject( Sender, parameters->objects[1]); + if (!target) { + target=Sender; + } + if (Sender->Type==ST_ACTOR) { + DisplayStringCore( target, parameters->int0Parameter, DS_CONSOLE|DS_NONAME); + } else { + DisplayStringCore( target, parameters->int0Parameter, DS_AREA|DS_NONAME); + } +} + +void GameScript::DisplayStringNoNameHead(Scriptable* Sender, Action* parameters) +{ + Scriptable* target = GetActorFromObject( Sender, parameters->objects[1] ); + if (!target) { + target=Sender; + } + + DisplayStringCore( target, parameters->int0Parameter, DS_HEAD|DS_CONSOLE|DS_NONAME); +} + +//display message over current script owner +void GameScript::DisplayMessage(Scriptable* Sender, Action* parameters) +{ + DisplayStringCore(Sender, parameters->int0Parameter, DS_CONSOLE ); +} + +//float message over target +void GameScript::DisplayStringHead(Scriptable* Sender, Action* parameters) +{ + Scriptable* target = GetActorFromObject( Sender, parameters->objects[1] ); + if (!target) { + target=Sender; + printf("DisplayStringHead/FloatMessage got no target, assuming Sender!\n"); + } + + DisplayStringCore(target, parameters->int0Parameter, DS_CONSOLE|DS_HEAD|DS_SPEECH ); +} + +void GameScript::KillFloatMessage(Scriptable* Sender, Action* parameters) +{ + Scriptable* target = GetActorFromObject( Sender, parameters->objects[1] ); + if (!target) { + target=Sender; + } + target->DisplayHeadText(NULL); +} + +void GameScript::DisplayStringHeadOwner(Scriptable* /*Sender*/, Action* parameters) +{ + Game *game=core->GetGame(); + + int i = game->GetPartySize(true); + while(i--) { + Actor *actor = game->GetPC(i, true); + if (actor->inventory.HasItem(parameters->string0Parameter,parameters->int0Parameter) ) { + DisplayStringCore(actor, parameters->int0Parameter, DS_CONSOLE|DS_HEAD ); + } + } +} + +void GameScript::FloatMessageFixed(Scriptable* Sender, Action* parameters) +{ + Scriptable* target = GetActorFromObject( Sender, parameters->objects[1] ); + if (!target) { + target=Sender; + printf("DisplayStringHead/FloatMessage got no target, assuming Sender!\n"); + } + + DisplayStringCore(target, parameters->int0Parameter, DS_CONSOLE|DS_HEAD); +} + +void GameScript::FloatMessageFixedRnd(Scriptable* Sender, Action* parameters) +{ + Scriptable* target = GetActorFromObject( Sender, parameters->objects[1] ); + if (!target) { + target=Sender; + printf("DisplayStringHead/FloatMessage got no target, assuming Sender!\n"); + } + + SrcVector *rndstr=LoadSrc(parameters->string0Parameter); + if (!rndstr) { + printMessage("GameScript","Cannot display resource!",LIGHT_RED); + return; + } + DisplayStringCore(target, rndstr->at(rand()%rndstr->size()), DS_CONSOLE|DS_HEAD); + FreeSrc(rndstr, parameters->string0Parameter); +} + +void GameScript::FloatMessageRnd(Scriptable* Sender, Action* parameters) +{ + Scriptable* target = GetActorFromObject( Sender, parameters->objects[1] ); + if (!target) { + target=Sender; + printf("DisplayStringHead/FloatMessage got no target, assuming Sender!\n"); + } + + SrcVector *rndstr=LoadSrc(parameters->string0Parameter); + if (!rndstr) { + printMessage("GameScript","Cannot display resource!",LIGHT_RED); + return; + } + DisplayStringCore(target, rndstr->at(rand()%rndstr->size()), DS_CONSOLE|DS_HEAD); + FreeSrc(rndstr, parameters->string0Parameter); +} + +//apparently this should not display over head (for actors) +void GameScript::DisplayString(Scriptable* Sender, Action* parameters) +{ + Scriptable* target = GetActorFromObject( Sender, parameters->objects[1]); + if (!target) { + target=Sender; + } + if (Sender->Type==ST_ACTOR) { + DisplayStringCore( target, parameters->int0Parameter, DS_CONSOLE); + } else { + DisplayStringCore( target, parameters->int0Parameter, DS_AREA); + } +} + +//DisplayStringHead, but wait until done +void GameScript::DisplayStringWait(Scriptable* Sender, Action* parameters) +{ + if (Sender->CurrentActionState) { + // TODO: should probably store the actual time and wait for that, + // rather than this hack + if (!core->GetAudioDrv()->IsSpeaking()) { + Sender->ReleaseCurrentAction(); + } + return; + } + Scriptable* target = GetActorFromObject( Sender, parameters->objects[1]); + if (!target) { + target=Sender; + } + DisplayStringCore( target, parameters->int0Parameter, DS_CONSOLE|DS_WAIT|DS_SPEECH|DS_HEAD); + Sender->CurrentActionState = 1; +} + +void GameScript::ForceFacing(Scriptable* Sender, Action* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar || tar->Type!=ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + Actor *actor = (Actor *) tar; + actor->SetOrientation(parameters->int0Parameter, false); +} + +/* A -1 means random facing? */ +void GameScript::Face(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type != ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + Actor* actor = ( Actor* ) Sender; + if (parameters->int0Parameter==-1) { + actor->SetOrientation(core->Roll(1,MAX_ORIENT,-1), false); + } else { + actor->SetOrientation(parameters->int0Parameter, false); + } + actor->SetWait( 1 ); + Sender->ReleaseCurrentAction(); // todo, blocking? +} + +void GameScript::FaceObject(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type != ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + Scriptable* target = GetActorFromObject( Sender, parameters->objects[1] ); + if (!target) { + Sender->ReleaseCurrentAction(); + return; + } + Actor* actor = ( Actor* ) Sender; + actor->SetOrientation( GetOrient( target->Pos, actor->Pos ), false); + actor->SetWait( 1 ); + Sender->ReleaseCurrentAction(); // todo, blocking? +} + +void GameScript::FaceSavedLocation(Scriptable* Sender, Action* parameters) +{ + Scriptable* target = GetActorFromObject( Sender, parameters->objects[1] ); + if (!target || target->Type!=ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + Actor* actor = ( Actor* ) target; + ieDword value; + if (!parameters->string0Parameter[0]) { + strcpy(parameters->string0Parameter,"LOCALSsavedlocation"); + } + value = (ieDword) CheckVariable( target, parameters->string0Parameter ); + Point p; + p.fromDword(value); + + actor->SetOrientation ( GetOrient( p, actor->Pos ), false); + actor->SetWait( 1 ); + Sender->ReleaseCurrentAction(); // todo, blocking? +} + +//pst and bg2 can play a song designated by index +//actually pst has some extra params not currently implemented +//switchplaylist implements fade by simply scheduling the next +//music after the currently running one +//FIXME: This code is similar to PlayAreaSong, consider refactoring +void GameScript::StartSong(Scriptable* /*Sender*/, Action* parameters) +{ + //the force play logic should be handled by SwitchPlayList + bool force; + char* poi = core->GetMusicPlaylist( parameters->int0Parameter ); + if (!poi) return; + + //if parameter is force, force the music, otherwise just schedule it for next + if (parameters->int1Parameter==1) { + force = true; + } else { + force = false; + } + int ret = core->GetMusicMgr()->SwitchPlayList( poi, force ); + if (ret) { + *poi = '*'; + } + if (parameters->int0Parameter == SONG_BATTLE) { + core->GetGame()->CombatCounter = 150; + } +} + +//starts the current area music (songtype is in int0Parameter) +//PlayAreaSong will set the CombatCounter to 150 if +//it is battlemusic (the Counter will tick back to 0) +void GameScript::StartMusic(Scriptable* Sender, Action* parameters) +{ + //don't break on bad values + if (parameters->int0Parameter>10) return; + Map *map = Sender->GetCurrentArea(); + if (!map) return; + bool force, restart; + + switch (parameters->int1Parameter) { + case 1: //force switch + force = true; + restart = true; + break; + case 3: //force switch, but wait for previous music to end gracefully + force = false; + restart = true; + default: + force = false; + restart = false; + break; + } + map->PlayAreaSong(parameters->int0Parameter, restart, force); +} + +void GameScript::StartCombatCounter(Scriptable* Sender, Action* /*parameters*/) +{ + Map *map = Sender->GetCurrentArea(); + if (!map) return; + map->PlayAreaSong(3, 1, 1); +} + +/*iwd2 can set an areasong slot*/ +void GameScript::SetMusic(Scriptable* Sender, Action* parameters) +{ + //iwd2 allows setting all 10 slots, though, there is no evidence they are used + if (parameters->int0Parameter>10) return; + Map *map = Sender->GetCurrentArea(); + if (!map) return; + map->SongHeader.SongList[parameters->int0Parameter]=parameters->int1Parameter; +} + +//optional integer parameter (isSpeech) +void GameScript::PlaySound(Scriptable* Sender, Action* parameters) +{ + printf( "PlaySound(%s)\n", parameters->string0Parameter ); + core->GetAudioDrv()->Play( parameters->string0Parameter, Sender->Pos.x, + Sender->Pos.y, parameters->int0Parameter ? GEM_SND_SPEECH : 0 ); +} + +void GameScript::PlaySoundPoint(Scriptable* /*Sender*/, Action* parameters) +{ + printf( "PlaySound(%s)\n", parameters->string0Parameter ); + core->GetAudioDrv()->Play( parameters->string0Parameter, parameters->pointParameter.x, parameters->pointParameter.y ); +} + +void GameScript::PlaySoundNotRanged(Scriptable* /*Sender*/, Action* parameters) +{ + printf( "PlaySound(%s)\n", parameters->string0Parameter ); + core->GetAudioDrv()->Play( parameters->string0Parameter, 0, 0); +} + +void GameScript::Continue(Scriptable* /*Sender*/, Action* /*parameters*/) +{ +} + +// creates area vvc at position of object +void GameScript::CreateVisualEffectObject(Scriptable* Sender, Action* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar) { + return; + } + CreateVisualEffectCore(tar, tar->Pos, parameters->string0Parameter, parameters->int0Parameter); +} + +// creates sticky vvc on actor or normal animation on object +void GameScript::CreateVisualEffectObjectSticky(Scriptable* Sender, Action* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar) { + return; + } + if (tar->Type==ST_ACTOR) { + CreateVisualEffectCore((Actor *) tar, parameters->string0Parameter, parameters->int0Parameter); + } else { + CreateVisualEffectCore(tar, tar->Pos, parameters->string0Parameter, parameters->int0Parameter); + } +} + +// creates area effect at point +void GameScript::CreateVisualEffect(Scriptable* Sender, Action* parameters) +{ + CreateVisualEffectCore(Sender, parameters->pointParameter, parameters->string0Parameter, parameters->int0Parameter); +} + +void GameScript::DestroySelf(Scriptable* Sender, Action* /*parameters*/) +{ + if (Sender->Type != ST_ACTOR) { + return; + } + Sender->ClearActions(); + Actor* actor = ( Actor* ) Sender; + actor->DestroySelf(); + //actor->InternalFlags |= IF_CLEANUP; +} + +void GameScript::ScreenShake(Scriptable* Sender, Action* parameters) +{ + if (parameters->int1Parameter) { //IWD2 has a different profile + core->timer->SetScreenShake( parameters->int1Parameter, + parameters->int2Parameter, parameters->int0Parameter ); + } else { + core->timer->SetScreenShake( parameters->pointParameter.x, + parameters->pointParameter.y, parameters->int0Parameter ); + } + Sender->SetWait( parameters->int0Parameter ); + Sender->ReleaseCurrentAction(); // todo, blocking? +} + +void GameScript::UnhideGUI(Scriptable* /*Sender*/, Action* /*parameters*/) +{ + Game* game = core->GetGame(); + game->SetControlStatus(CS_HIDEGUI, BM_NAND); +} + +void GameScript::HideGUI(Scriptable* /*Sender*/, Action* /*parameters*/) +{ + Game* game = core->GetGame(); + game->SetControlStatus(CS_HIDEGUI, BM_OR); +} + +void GameScript::LockScroll(Scriptable* /*Sender*/, Action* /*parameters*/) +{ + GameControl* gc = core->GetGameControl(); + if (gc) { + gc->SetScreenFlags(SF_LOCKSCROLL, BM_OR); + } +} + +void GameScript::UnlockScroll(Scriptable* /*Sender*/, Action* /*parameters*/) +{ + GameControl* gc = core->GetGameControl(); + if (gc) { + gc->SetScreenFlags(SF_LOCKSCROLL, BM_NAND); + } +} + +//no string, increase talkcount, no interrupt +void GameScript::Dialogue(Scriptable* Sender, Action* parameters) +{ + BeginDialog( Sender, parameters, BD_SOURCE | BD_TALKCOUNT | BD_CHECKDIST ); +} + +void GameScript::DialogueForceInterrupt(Scriptable* Sender, Action* parameters) +{ + BeginDialog( Sender, parameters, BD_SOURCE | BD_TALKCOUNT | BD_INTERRUPT ); +} + +// not in IESDP but this one should affect ambients +void GameScript::SoundActivate(Scriptable* /*Sender*/, Action* parameters) +{ + AmbientMgr * ambientmgr = core->GetAudioDrv()->GetAmbientMgr(); + if (parameters->int0Parameter) { + ambientmgr->activate(parameters->objects[1]->objectName); + } else { + ambientmgr->deactivate(parameters->objects[1]->objectName); + } +} + +// according to IESDP this action is about animations +void GameScript::AmbientActivate(Scriptable* Sender, Action* parameters) +{ + AreaAnimation* anim = Sender->GetCurrentArea( )->GetAnimation( parameters->string0Parameter); + if (!anim) { + anim = Sender->GetCurrentArea( )->GetAnimation( parameters->objects[1]->objectName ); + } + if (!anim) { + printf( "Script error: No Animation Named \"%s\" or \"%s\"\n", + parameters->string0Parameter,parameters->objects[1]->objectName ); + return; + } + if (parameters->int0Parameter) { + anim->Flags |= A_ANI_ACTIVE; + } else { + anim->Flags &= ~A_ANI_ACTIVE; + } +} + +void GameScript::ChangeTileState(Scriptable* Sender, Action* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1], GA_NO_DEAD ); + if (!tar) { + return; + } + if (tar->Type != ST_DOOR) { + return; + } + Door* door = ( Door* ) tar; + int state = parameters->int0Parameter; + if(door) { + door->ToggleTiles(state); /* default is false for playsound */ + } +} + +void GameScript::StaticStart(Scriptable* Sender, Action* parameters) +{ + AreaAnimation *anim = Sender->GetCurrentArea()->GetAnimation(parameters->objects[1]->objectName); + if (!anim) { + printf( "Script error: No Animation Named \"%s\"\n", + parameters->objects[1]->objectName ); + return; + } + anim->Flags &=~A_ANI_PLAYONCE; +} + +void GameScript::StaticStop(Scriptable* Sender, Action* parameters) +{ + AreaAnimation *anim = Sender->GetCurrentArea()->GetAnimation(parameters->objects[1]->objectName); + if (!anim) { + printf( "Script error: No Animation Named \"%s\"\n", + parameters->objects[1]->objectName ); + return; + } + anim->Flags |= A_ANI_PLAYONCE; +} + +void GameScript::StaticPalette(Scriptable* Sender, Action* parameters) +{ + AreaAnimation *anim = Sender->GetCurrentArea()->GetAnimation(parameters->objects[1]->objectName); + if (!anim) { + printf( "Script error: No Animation Named \"%s\"\n", + parameters->objects[1]->objectName ); + return; + } + anim->SetPalette( parameters->string0Parameter ); +} + +//this is a special case of PlaySequence (with wait time, not for area anims) +void GameScript::PlaySequenceTimed(Scriptable* Sender, Action* parameters) +{ + Scriptable* tar; + if (parameters->objects[1]) { + tar = GetActorFromObject( Sender, parameters->objects[1] ); + } else { + tar=Sender; + } + if (!tar || tar->Type != ST_ACTOR) { + return; + } + Actor* actor = ( Actor* ) tar; + actor->SetStance( parameters->int0Parameter ); + int delay = parameters->int1Parameter || 1; + actor->SetWait( delay ); +} + +//waitanimation: waiting while animation of target is of a certain type +void GameScript::WaitAnimation(Scriptable* Sender, Action* parameters) +{ + Scriptable *tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar) { + tar=Sender; + } + if (tar->Type != ST_ACTOR) { + return; + } + Actor* actor = ( Actor* ) tar; + if (actor->GetStance()!=parameters->int0Parameter) { + Sender->ReleaseCurrentAction(); + return; + } +} + +// PlaySequence without object parameter defaults to Sender +void GameScript::PlaySequence(Scriptable* Sender, Action* parameters) +{ + Scriptable* tar; + if (parameters->objects[1]) { + tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar) { + //could be an animation + AreaAnimation* anim = Sender->GetCurrentArea( )->GetAnimation( parameters->objects[1]->objectName); + if (anim) { + //set animation's cycle to parameters->int0Parameter; + anim->sequence=parameters->int0Parameter; + anim->frame=0; + //what else to be done??? + anim->InitAnimation(); + } + return; + } + + } else { + tar = Sender; + } + if (tar->Type != ST_ACTOR) { + return; + } + Actor* actor = ( Actor* ) tar; + actor->SetStance( parameters->int0Parameter ); +} + +void GameScript::SetDialogue(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type != ST_ACTOR) { + return; + } + Actor* target = ( Actor* ) Sender; + target->SetDialog( parameters->string0Parameter ); +} + +void GameScript::ChangeDialogue(Scriptable* Sender, Action* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar) { + return; + } + if (tar->Type != ST_ACTOR) { + return; + } + Actor* target = ( Actor* ) tar; + target->SetDialog( parameters->string0Parameter ); +} + +//string0, no interrupt, talkcount increased +void GameScript::StartDialogue(Scriptable* Sender, Action* parameters) +{ + BeginDialog( Sender, parameters, BD_STRING0 | BD_TALKCOUNT | BD_SETDIALOG ); +} + +//string0, no interrupt, talkcount increased, don't set default +//optionally item name is used +void GameScript::StartDialogueOverride(Scriptable* Sender, Action* parameters) +{ + int flags = BD_STRING0 | BD_TALKCOUNT; + + if (parameters->int2Parameter) { + flags|=BD_ITEM; + } + BeginDialog( Sender, parameters, flags ); +} + +//string0, no interrupt, talkcount increased, don't set default +//optionally item name is used +void GameScript::StartDialogueOverrideInterrupt(Scriptable* Sender, + Action* parameters) +{ + int flags = BD_STRING0 | BD_TALKCOUNT | BD_INTERRUPT; + + if (parameters->int2Parameter) { + flags|=BD_ITEM; + } + BeginDialog( Sender, parameters, flags ); +} + +//start talking to oneself +void GameScript::PlayerDialogue(Scriptable* Sender, Action* parameters) +{ + BeginDialog( Sender, parameters, BD_RESERVED | BD_OWN ); +} + +//we hijack this action for the player initiated dialogue +void GameScript::NIDSpecial1(Scriptable* Sender, Action* parameters) +{ + BeginDialog( Sender, parameters, BD_INTERRUPT | BD_TARGET /*| BD_NUMERIC*/ | BD_TALKCOUNT | BD_CHECKDIST ); +} + +void GameScript::NIDSpecial2(Scriptable* Sender, Action* /*parameters*/) +{ + if (Sender->Type != ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + Game *game=core->GetGame(); + if (!game->EveryoneStopped() ) { + //wait for a while + Sender->SetWait( 1 * AI_UPDATE_TIME ); + return; + } + Actor *actor = (Actor *) Sender; + if (!game->EveryoneNearPoint(actor->GetCurrentArea(), actor->Pos, true) ) { + //we abort the command, everyone should be here + Sender->ReleaseCurrentAction(); + return; + } + //travel direction passed to guiscript + int direction = Sender->GetCurrentArea()->WhichEdge(actor->Pos); + printf("Travel direction returned: %d\n", direction); + if (direction==-1) { + Sender->ReleaseCurrentAction(); + return; + } + core->GetDictionary()->SetAt("Travel", (ieDword) direction); + core->GetGUIScriptEngine()->RunFunction( "GUIMA", "OpenWorldMapWindow" ); + //sorry, i have absolutely no idea when i should do this :) + Sender->ReleaseCurrentAction(); +} + +void GameScript::StartDialogueInterrupt(Scriptable* Sender, Action* parameters) +{ + BeginDialog( Sender, parameters, + BD_STRING0 | BD_INTERRUPT | BD_TALKCOUNT | BD_SETDIALOG ); +} + +//No string, flags:0 +void GameScript::StartDialogueNoSet(Scriptable* Sender, Action* parameters) +{ + BeginDialog( Sender, parameters, BD_TALKCOUNT | BD_SOURCE ); +} + +void GameScript::StartDialogueNoSetInterrupt(Scriptable* Sender, + Action* parameters) +{ + BeginDialog( Sender, parameters, BD_TALKCOUNT | BD_SOURCE | BD_INTERRUPT ); +} + +//no talkcount, using banter dialogs +//probably banter dialogs are random, like rumours! +//no, they aren't, but they increase interactcount +void GameScript::Interact(Scriptable* Sender, Action* parameters) +{ + BeginDialog( Sender, parameters, BD_INTERACT | BD_NOEMPTY ); +} + +static unsigned int FindNearPoint(Scriptable* Sender, Point *&p1, Point *&p2) +{ + unsigned int distance1 = Distance(*p1, Sender); + unsigned int distance2 = Distance(*p2, Sender); + if (distance1 <= distance2) { + return distance1; + } else { + Point *tmp = p1; + p1 = p2; + p2 = tmp; + return distance2; + } +} + +//this is an immediate action without checking Sender +void GameScript::DetectSecretDoor(Scriptable* Sender, Action* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1], GA_NO_DEAD ); + if (!tar) { + return; + } + if (tar->Type != ST_DOOR) { + return; + } + Door* door = ( Door* ) tar; + if (door->Flags & DOOR_SECRET) { + door->Flags |= DOOR_FOUND; + } +} + +//this is an immediate action without checking Sender +void GameScript::Lock(Scriptable* Sender, Action* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar) { + return; + } + switch (tar->Type) { + case ST_DOOR: + ((Door *)tar)->SetDoorLocked(true, true); + break; + case ST_CONTAINER: + ((Container *)tar)->SetContainerLocked(true); + break; + default: + return; + } +} + +void GameScript::Unlock(Scriptable* Sender, Action* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar) { + return; + } + switch (tar->Type) { + case ST_DOOR: + ((Door *)tar)->SetDoorLocked(false, true); + break; + case ST_CONTAINER: + ((Container *)tar)->SetContainerLocked(false); + break; + default: + return; + } +} + +void GameScript::SetDoorLocked(Scriptable* Sender, Action* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar) { + return; + } + if (tar->Type != ST_DOOR) { + return; + } + Door* door = ( Door* ) tar; + door->SetDoorLocked( parameters->int0Parameter!=0, false); +} + +void GameScript::SetDoorFlag(Scriptable* Sender, Action* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar) { + return; + } + if (tar->Type != ST_DOOR) { + return; + } + Door* door = ( Door* ) tar; + ieDword flag = parameters->int0Parameter; + + //these are special flags + if (flag&DOOR_LOCKED) { + flag&=~DOOR_LOCKED; + door->SetDoorLocked(parameters->int1Parameter!=0, false); + } + if (flag&DOOR_OPEN) { + flag&=~DOOR_OPEN; + door->SetDoorOpen(parameters->int1Parameter!=0, false, 0); + } + + if (parameters->int1Parameter) { + door->Flags|=flag; + } else { + door->Flags&=~flag; + } +} + +void GameScript::RemoveTraps(Scriptable* Sender, Action* parameters) +{ + //only actors may try to pick a lock + if (Sender->Type != ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar) { + Sender->ReleaseCurrentAction(); + return; + } + unsigned int distance; + Point *p, *otherp; + Door *door = NULL; + Container *container = NULL; + InfoPoint *trigger = NULL; + ScriptableType type = tar->Type; + ieDword flags; + + switch (type) { + case ST_DOOR: + door = ( Door* ) tar; + if (door->IsOpen()) { + //door is already open + Sender->ReleaseCurrentAction(); + return; + } + p = door->toOpen; + otherp = door->toOpen+1; + distance = FindNearPoint( Sender, p, otherp); + flags = door->Trapped && door->TrapDetected; + break; + case ST_CONTAINER: + container = (Container *) tar; + p = &container->Pos; + otherp = p; + distance = Distance(*p, Sender); + flags = container->Trapped && container->TrapDetected; + break; + case ST_PROXIMITY: + trigger = (InfoPoint *) tar; + // this point is incorrect! will cause actor to enter trap + // need to find a point using trigger->outline + p = &trigger->Pos; + otherp = p; + distance = Distance(tar, Sender); + flags = trigger->Trapped && trigger->TrapDetected && trigger->CanDetectTrap(); + break; + default: + Sender->ReleaseCurrentAction(); + return; + } + Actor * actor = (Actor *) Sender; + actor->SetOrientation( GetOrient( *otherp, actor->Pos ), false); + if (distance <= MAX_OPERATING_DISTANCE) { + if (flags) { + switch(type) { + case ST_DOOR: + door->TryDisarm(actor); + break; + case ST_CONTAINER: + container->TryDisarm(actor); + break; + case ST_PROXIMITY: + trigger->TryDisarm(actor); + break; + default: + //not gonna happen! + assert(false); + } + } else { + //no trap here + //displaymsg->DisplayString(STR_NOT_TRAPPED); + } + } else { + MoveNearerTo(Sender, *p, MAX_OPERATING_DISTANCE,0); + return; + } + Sender->SetWait(1); + Sender->ReleaseCurrentAction(); +} + +void GameScript::PickLock(Scriptable* Sender, Action* parameters) +{ + //only actors may try to pick a lock + if (Sender->Type != ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar) { + Sender->ReleaseCurrentAction(); + return; + } + unsigned int distance; + Point *p, *otherp; + Door *door = NULL; + Container *container = NULL; + ScriptableType type = tar->Type; + ieDword flags; + + switch (type) { + case ST_DOOR: + door = ( Door* ) tar; + if (door->IsOpen()) { + //door is already open + Sender->ReleaseCurrentAction(); + return; + } + p = door->toOpen; + otherp = door->toOpen+1; + distance = FindNearPoint( Sender, p, otherp); + flags = door->Flags&DOOR_LOCKED; + break; + case ST_CONTAINER: + container = (Container *) tar; + p = &container->Pos; + otherp = p; + distance = Distance(*p, Sender); + flags = container->Flags&CONT_LOCKED; + break; + default: + Sender->ReleaseCurrentAction(); + return; + } + Actor * actor = (Actor *) Sender; + actor->SetOrientation( GetOrient( *otherp, actor->Pos ), false); + if (distance <= MAX_OPERATING_DISTANCE) { + if (flags) { + if (type==ST_DOOR) { + door->TryPickLock(actor); + } else { + container->TryPickLock(actor); + } + } else { + //notlocked + //displaymsg->DisplayString(STR_NOT_LOCKED); + } + } else { + MoveNearerTo(Sender, *p, MAX_OPERATING_DISTANCE,0); + return; + } + Sender->SetWait(1); + Sender->ReleaseCurrentAction(); +} + +void GameScript::OpenDoor(Scriptable* Sender, Action* parameters) { + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar) { + return; + } + if (tar->Type != ST_DOOR) { + return; + } + Door* door = ( Door* ) tar; + // no idea if this is right, or whether OpenDoor/CloseDoor should allow opening + // of all doors, or some doors, or whether it should still check for non-actors + if (Sender->Type == ST_ACTOR) { + Actor *actor = (Actor *)Sender; + actor->SetModal(MS_NONE); + if (!door->TryUnlock(actor)) { + return; + } + } + //if not an actor opens, it don't play sound + door->SetDoorOpen( true, (Sender->Type == ST_ACTOR), 0 ); + Sender->ReleaseCurrentAction(); +} + +void GameScript::CloseDoor(Scriptable* Sender, Action* parameters) { + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar) { + return; + } + if (tar->Type != ST_DOOR) { + return; + } + Door* door = ( Door* ) tar; + // see comments in OpenDoor above + if (Sender->Type == ST_ACTOR) { + Actor *actor = (Actor *)Sender; + if (!door->TryUnlock(actor)) { + return; + } + } + //if not an actor closes, it don't play sound + door->SetDoorOpen( false, (Sender->Type == ST_ACTOR), 0 ); + Sender->ReleaseCurrentAction(); +} + +void GameScript::ToggleDoor(Scriptable* Sender, Action* /*parameters*/) +{ + if (Sender->Type != ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + Actor *actor = (Actor *) Sender; + actor->SetModal(MS_NONE); + + Door* door = actor->GetCurrentArea()->GetDoorByGlobalID(actor->TargetDoor); + if (!door) { + Sender->ReleaseCurrentAction(); + return; + } + unsigned int distance; + Point *p = door->toOpen; + Point *otherp = door->toOpen+1; + distance = FindNearPoint( Sender, p, otherp); + if (distance <= MAX_OPERATING_DISTANCE) { + actor->SetOrientation( GetOrient( *otherp, actor->Pos ), false); + if (!door->TryUnlock(actor)) { + displaymsg->DisplayConstantString(STR_DOORLOCKED,0xd7d7be,door); + //playsound unsuccessful opening of door + if(door->IsOpen()) + core->PlaySound(DS_CLOSE_FAIL); + else + core->PlaySound(DS_OPEN_FAIL); + Sender->ReleaseCurrentAction(); + actor->TargetDoor = 0; + return; //don't open door + } + + // should we be triggering the trap on close? + door->TriggerTrap(0, actor->GetGlobalID()); + door->SetDoorOpen( !door->IsOpen(), true, actor->GetGlobalID() ); + } else { + MoveNearerTo(Sender, *p, MAX_OPERATING_DISTANCE,0); + return; + } + Sender->SetWait(1); + Sender->ReleaseCurrentAction(); + actor->TargetDoor = 0; +} + +void GameScript::ContainerEnable(Scriptable* Sender, Action* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar || tar->Type!=ST_CONTAINER) { + return; + } + Container *cnt = (Container *) tar; + if (parameters->int0Parameter) { + cnt->Flags&=~CONT_DISABLED; + } else { + cnt->Flags|=CONT_DISABLED; + } +} + +void GameScript::MoveBetweenAreas(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type != ST_ACTOR) { + return; + } + if (parameters->string1Parameter[0]) { + CreateVisualEffectCore(Sender, Sender->Pos, parameters->string1Parameter, 0); + } + MoveBetweenAreasCore((Actor *) Sender, parameters->string0Parameter, + parameters->pointParameter, parameters->int0Parameter, true); +} + +//spell is depleted, casting time is calculated, interruptible +//FIXME The caster must meet the level requirements as set in the spell file +void GameScript::Spell(Scriptable* Sender, Action* parameters) +{ + ieResRef spellres; + + //resolve spellname + if (!ResolveSpellName( spellres, parameters) ) { + Sender->ReleaseCurrentAction(); + return; + } + + //if target was set, fire spell + if (Sender->LastTarget) { + Sender->CastSpellEnd( spellres ); + Sender->ReleaseCurrentAction(); + return; + } + + //the target was converted to a point + if(!Sender->LastTargetPos.isempty()) { + Sender->CastSpellPointEnd( spellres ); + Sender->ReleaseCurrentAction(); + return; + } + + //parse target + int seeflag; + unsigned int dist = GetSpellDistance(spellres, Sender); + if (dist == 0xffffffff) { + seeflag = 0; + } else { + seeflag = GA_NO_DEAD; + } + + Scriptable* tar = GetStoredActorFromObject( Sender, parameters->objects[1], seeflag ); + if (!tar) { + Sender->ReleaseCurrentAction(); + return; + } + + if(Sender->Type==ST_ACTOR) { + Actor *act = (Actor *) Sender; + + //move near to target + if (dist != 0xfffffff) { + if (PersonalDistance(tar, Sender) > dist) { + MoveNearerTo(Sender,tar,dist); + return; + } + } + + //face target + if (tar != Sender) { + act->SetOrientation( GetOrient( tar->Pos, act->Pos ), false ); + } + + //stop doing anything else + act->SetModal(MS_NONE); + } + int duration = Sender->CastSpell( spellres, tar, true ); + if (duration != -1) Sender->SetWait(duration); + + //if target was set, feed action back + if (!Sender->LastTarget && Sender->LastTargetPos.isempty()) { + Sender->ReleaseCurrentAction(); + } +} + +//spell is depleted, casting time is calculated, interruptible +//FIXME The caster must meet the level requirements as set in the spell file +void GameScript::SpellPoint(Scriptable* Sender, Action* parameters) +{ + ieResRef spellres; + + //resolve spellname + if (!ResolveSpellName( spellres, parameters) ) { + Sender->ReleaseCurrentAction(); + return; + } + + //if target was set, fire spell + if (!Sender->LastTargetPos.isempty()) { + Sender->CastSpellPointEnd( spellres ); + Sender->ReleaseCurrentAction(); + return; + } + + if(Sender->Type==ST_ACTOR) { + unsigned int dist = GetSpellDistance(spellres, Sender); + + Actor *act = (Actor *) Sender; + //move near to target + if (PersonalDistance(parameters->pointParameter, Sender) > dist) { + MoveNearerTo(Sender,parameters->pointParameter,dist, 0); + return; + } + + //face target + act->SetOrientation( GetOrient( parameters->pointParameter, act->Pos ), false ); + //stop doing anything else + act->SetModal(MS_NONE); + } + + int duration = Sender->CastSpellPoint( spellres, parameters->pointParameter, true ); + if (duration != -1) Sender->SetWait(duration); + + //if target was set, feed action back + if (!Sender->LastTarget && Sender->LastTargetPos.isempty()) { + Sender->ReleaseCurrentAction(); + } +} + +//spell is not depleted (doesn't need to be memorised or known) +//casting time is calculated, interruptible +//FIXME The caster must meet the level requirements as set in the spell file +void GameScript::SpellNoDec(Scriptable* Sender, Action* parameters) +{ + ieResRef spellres; + + //resolve spellname + if (!ResolveSpellName( spellres, parameters) ) { + Sender->ReleaseCurrentAction(); + return; + } + + //if target was set, fire spell + if (Sender->LastTarget) { + Sender->CastSpellEnd( spellres ); + Sender->ReleaseCurrentAction(); + return; + } + + //the target was converted to a point + if(!Sender->LastTargetPos.isempty()) { + Sender->CastSpellPointEnd( spellres ); + Sender->ReleaseCurrentAction(); + return; + } + + //parse target + Scriptable* tar = GetStoredActorFromObject( Sender, parameters->objects[1] ); + if (!tar) { + Sender->ReleaseCurrentAction(); + return; + } + + //face target + if (Sender->Type==ST_ACTOR) { + Actor *act = (Actor *) Sender; + if (tar != Sender) { + act->SetOrientation( GetOrient( tar->Pos, act->Pos ), false ); + } + + //stop doing anything else + act->SetModal(MS_NONE); + } + int duration = Sender->CastSpell( spellres, tar, false ); + if (duration != -1) Sender->SetWait(duration); + + //if target was set, feed action back + if (!Sender->LastTarget && Sender->LastTargetPos.isempty()) { + Sender->ReleaseCurrentAction(); + } +} + +//spell is not depleted (doesn't need to be memorised or known) +//casting time is calculated, interruptible +//FIXME The caster must meet the level requirements as set in the spell file +void GameScript::SpellPointNoDec(Scriptable* Sender, Action* parameters) +{ + ieResRef spellres; + + //resolve spellname + if (!ResolveSpellName( spellres, parameters) ) { + Sender->ReleaseCurrentAction(); + return; + } + + //if target was set, fire spell + if (!Sender->LastTargetPos.isempty()) { + Sender->CastSpellPointEnd( spellres ); + Sender->ReleaseCurrentAction(); + return; + } + + //face target + if (Sender->Type==ST_ACTOR) { + Actor *act = (Actor *) Sender; + act->SetOrientation( GetOrient( parameters->pointParameter, act->Pos ), false ); + + //stop doing anything else + act->SetModal(MS_NONE); + } + + int duration = Sender->CastSpellPoint( spellres, parameters->pointParameter, false ); + if (duration != -1) Sender->SetWait(duration); + + //if target was set, feed action back + if (Sender->LastTargetPos.isempty()) { + Sender->ReleaseCurrentAction(); + } +} + +//spell is not depleted (doesn't need to be memorised or known) +//casting time is calculated, not interruptable +//FIXME The caster must meet the level requirements as set in the spell file +void GameScript::ForceSpell(Scriptable* Sender, Action* parameters) +{ + ieResRef spellres; + + //resolve spellname + if (!ResolveSpellName( spellres, parameters) ) { + Sender->ReleaseCurrentAction(); + return; + } + + //if target was set, fire spell + if (Sender->LastTarget) { + Sender->CastSpellEnd( spellres ); + Sender->ReleaseCurrentAction(); + return; + } + + //the target was converted to a point + if(!Sender->LastTargetPos.isempty()) { + Sender->CastSpellPointEnd( spellres ); + Sender->ReleaseCurrentAction(); + return; + } + + //parse target + Scriptable* tar = GetStoredActorFromObject( Sender, parameters->objects[1] ); + if (!tar) { + Sender->ReleaseCurrentAction(); + return; + } + + //face target + if (Sender->Type==ST_ACTOR) { + Actor *act = (Actor *) Sender; + if (tar != Sender) { + act->SetOrientation( GetOrient( tar->Pos, act->Pos ), false ); + } + + //stop doing anything else + act->SetModal(MS_NONE); + } + int duration = Sender->CastSpell (spellres, tar, false); + if (duration != -1) Sender->SetWait(duration); + + //if target was set, feed action back + if (!Sender->LastTarget && Sender->LastTargetPos.isempty()) { + Sender->ReleaseCurrentAction(); + } +} + +//spell is not depleted (doesn't need to be memorised or known) +//casting time is calculated, not interruptable +//FIXME The caster must meet the level requirements as set in the spell file +void GameScript::ForceSpellPoint(Scriptable* Sender, Action* parameters) +{ + ieResRef spellres; + + if (!ResolveSpellName( spellres, parameters) ) { + Sender->ReleaseCurrentAction(); + return; + } + + //if target was set, fire spell + if (!Sender->LastTargetPos.isempty()) { + Sender->CastSpellPointEnd( spellres ); + Sender->ReleaseCurrentAction(); + return; + } + + //face target + if (Sender->Type==ST_ACTOR) { + Actor *act = (Actor *) Sender; + act->SetOrientation( GetOrient( parameters->pointParameter, act->Pos ), false ); + + //stop doing anything else + act->SetModal(MS_NONE); + } + + int duration = Sender->CastSpellPoint (spellres, parameters->pointParameter, false); + if (duration != -1) Sender->SetWait(duration); + + //if target was set, feed action back + if (Sender->LastTargetPos.isempty()) { + Sender->ReleaseCurrentAction(); + } +} + +//ForceSpell with zero casting time +//zero casting time, no depletion, not interruptable +//FIXME The caster must meet the level requirements as set in the spell file +//FIXME The spell level is taken as parameter2 in some cases +void GameScript::ReallyForceSpell(Scriptable* Sender, Action* parameters) +{ + ieResRef spellres; + + if (!ResolveSpellName( spellres, parameters) ) { + Sender->ReleaseCurrentAction(); + return; + } + + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar) { + Sender->ReleaseCurrentAction(); + return; + } + if (Sender->Type == ST_ACTOR) { + Actor *actor = (Actor *) Sender; + if (tar != Sender) { + actor->SetOrientation( GetOrient( tar->Pos, actor->Pos ), false ); + } + actor->SetStance (IE_ANI_CONJURE); + } + Sender->CastSpell (spellres, tar, false, true); + if (tar->Type==ST_ACTOR) { + Sender->CastSpellEnd(spellres); + } else { + Sender->CastSpellPointEnd(spellres); + } + Sender->ReleaseCurrentAction(); +} + +//ForceSpellPoint with zero casting time +//zero casting time, no depletion (finish casting at point), not interruptable +//no CFB +//FIXME The caster must meet the level requirements as set in the spell file +void GameScript::ReallyForceSpellPoint(Scriptable* Sender, Action* parameters) +{ + ieResRef spellres; + + if (!ResolveSpellName( spellres, parameters) ) { + Sender->ReleaseCurrentAction(); + return; + } + + //Sender->LastTargetPos=parameters->pointParameter; + if (Sender->Type == ST_ACTOR) { + if (Sender->GetInternalFlag()&IF_STOPATTACK) { + Sender->ReleaseCurrentAction(); + return; + } + Actor *actor = (Actor *) Sender; + actor->SetOrientation( GetOrient( parameters->pointParameter, actor->Pos ), false ); + actor->SetStance (IE_ANI_CONJURE); + } + Sender->CastSpellPoint (spellres, parameters->pointParameter, false, true); + Sender->CastSpellPointEnd(spellres); + Sender->ReleaseCurrentAction(); +} + +// this differs from ReallyForceSpell that this one allows dead Sender casting +// zero casting time, no depletion +void GameScript::ReallyForceSpellDead(Scriptable* Sender, Action* parameters) +{ + ieResRef spellres; + + if (!ResolveSpellName( spellres, parameters) ) { + Sender->ReleaseCurrentAction(); + return; + } + + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar) { + Sender->ReleaseCurrentAction(); + return; + } + Sender->LastTargetPos=parameters->pointParameter; + /* + if (Sender->Type == ST_ACTOR) { + Actor *actor = (Actor *) Sender; + //the dead don't wiggle their fingers + //actor->SetStance (IE_ANI_CONJURE); + } + */ + Sender->CastSpell (spellres, tar, false, true); + if (tar->Type==ST_ACTOR) { + Sender->CastSpellEnd(spellres); + } else { + Sender->CastSpellPointEnd(spellres); + } + Sender->ReleaseCurrentAction(); +} + +void GameScript::Deactivate(Scriptable* Sender, Action* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar) { + return; + } + if (tar->Type != ST_ACTOR) { + return; + } + tar->Hide(); +} + +void GameScript::MakeGlobal(Scriptable* Sender, Action* /*parameters*/) +{ + if (Sender->Type != ST_ACTOR) { + return; + } + Actor* act = ( Actor* ) Sender; + core->GetGame()->AddNPC( act ); +} + +void GameScript::UnMakeGlobal(Scriptable* Sender, Action* /*parameters*/) +{ + if (Sender->Type != ST_ACTOR) { + return; + } + Actor* act = ( Actor* ) Sender; + int slot; + slot = core->GetGame()->InStore( act ); + if (slot >= 0) { + core->GetGame()->DelNPC( slot ); + } +} + +//this apparently doesn't check the gold, thus could be used from non actors +void GameScript::GivePartyGoldGlobal(Scriptable* Sender, Action* parameters) +{ + ieDword gold = (ieDword) CheckVariable( Sender, parameters->string0Parameter, parameters->string1Parameter ); + if (Sender->Type == ST_ACTOR) { + Actor* act = ( Actor* ) Sender; + ieDword mygold = act->GetStat(IE_GOLD); + if (mygold < gold) { + gold = mygold; + } + //will get saved, not adjusted + act->SetBase(IE_GOLD, act->GetBase(IE_GOLD)-gold); + } + core->GetGame()->AddGold(gold); +} + +void GameScript::CreatePartyGold(Scriptable* /*Sender*/, Action* parameters) +{ + core->GetGame()->AddGold(parameters->int0Parameter); +} + +void GameScript::GivePartyGold(Scriptable* Sender, Action* parameters) +{ + ieDword gold = (ieDword) parameters->int0Parameter; + if (Sender->Type == ST_ACTOR) { + Actor* act = ( Actor* ) Sender; + ieDword mygold = act->GetStat(IE_GOLD); + if (mygold < gold) { + gold = mygold; + } + //will get saved, not adjusted + act->SetBase(IE_GOLD, act->GetBase(IE_GOLD)-gold); + } + core->GetGame()->AddGold(gold); +} + +void GameScript::DestroyPartyGold(Scriptable* /*Sender*/, Action* parameters) +{ + int gold = core->GetGame()->PartyGold; + if (gold>parameters->int0Parameter) { + gold=parameters->int0Parameter; + } + core->GetGame()->AddGold(-gold); +} + +void GameScript::TakePartyGold(Scriptable* Sender, Action* parameters) +{ + ieDword gold = core->GetGame()->PartyGold; + if (gold>(ieDword) parameters->int0Parameter) { + gold=(ieDword) parameters->int0Parameter; + } + core->GetGame()->AddGold((ieDword) -(int) gold); + if (Sender->Type == ST_ACTOR) { + Actor* act = ( Actor* ) Sender; + act->SetBase(IE_GOLD, act->GetBase(IE_GOLD)+gold); + } +} + +void GameScript::AddXPObject(Scriptable* Sender, Action* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar) { + return; + } + if (tar->Type != ST_ACTOR) { + return; + } + Actor* actor = ( Actor* ) tar; + int xp = parameters->int0Parameter; + if (displaymsg->HasStringReference(STR_GOTQUESTXP)) { + core->GetTokenDictionary()->SetAtCopy("EXPERIENCEAMOUNT", xp); + displaymsg->DisplayConstantStringName(STR_GOTQUESTXP, 0xbcefbc, actor); + } else { + displaymsg->DisplayConstantStringValue(STR_GOTXP, 0xbcefbc, (ieDword)xp); + } + actor->AddExperience(xp); +} + +void GameScript::AddXP2DA(Scriptable* /*Sender*/, Action* parameters) +{ + AutoTable xptable; + + if (core->HasFeature(GF_HAS_EXPTABLE) ) { + xptable.load("exptable"); + } else { + xptable.load("xplist"); + } + + if (parameters->int0Parameter>0) { + displaymsg->DisplayString(parameters->int0Parameter, 0x40f0f000,IE_STR_SOUND); + } + if (!xptable) { + printMessage("GameScript","Can't perform ADDXP2DA",LIGHT_RED); + return; + } + const char * xpvalue = xptable->QueryField( parameters->string0Parameter, "0" ); //level is unused + + if ( xpvalue[0]=='P' && xpvalue[1]=='_') { + //divide party xp + core->GetGame()->ShareXP(atoi(xpvalue+2), SX_DIVIDE ); + } else { + //give xp everyone + core->GetGame()->ShareXP(atoi(xpvalue), 0 ); + } +} + +void GameScript::AddExperienceParty(Scriptable* /*Sender*/, Action* parameters) +{ + core->GetGame()->ShareXP(parameters->int0Parameter, SX_DIVIDE); +} + +//this needs moncrate.2da, but otherwise independent from GF_CHALLENGERATING +void GameScript::AddExperiencePartyCR(Scriptable* /*Sender*/, Action* parameters) +{ + core->GetGame()->ShareXP(parameters->int0Parameter, SX_DIVIDE|SX_CR); +} + +void GameScript::AddExperiencePartyGlobal(Scriptable* Sender, Action* parameters) +{ + ieDword xp = CheckVariable( Sender, parameters->string0Parameter, parameters->string1Parameter ); + core->GetGame()->ShareXP(xp, SX_DIVIDE); +} + +void GameScript::SetMoraleAI(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type != ST_ACTOR) { + return; + } + Actor* act = ( Actor* ) Sender; + act->SetBase(IE_MORALE, parameters->int0Parameter); +} + +void GameScript::IncMoraleAI(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type != ST_ACTOR) { + return; + } + Actor* act = ( Actor* ) Sender; + act->SetBase(IE_MORALE, parameters->int0Parameter+act->GetBase(IE_MORALE) ); +} + +void GameScript::MoraleSet(Scriptable* Sender, Action* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar) { + return; + } + if (tar->Type != ST_ACTOR) { + return; + } + Actor* act = ( Actor* ) tar; + act->SetBase(IE_MORALEBREAK, parameters->int0Parameter); +} + +void GameScript::MoraleInc(Scriptable* Sender, Action* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar) { + return; + } + if (tar->Type != ST_ACTOR) { + return; + } + Actor* act = ( Actor* ) tar; + act->SetBase(IE_MORALEBREAK, act->GetBase(IE_MORALEBREAK)+parameters->int0Parameter); +} + +void GameScript::MoraleDec(Scriptable* Sender, Action* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar) { + return; + } + if (tar->Type != ST_ACTOR) { + return; + } + Actor* act = ( Actor* ) tar; + act->SetBase(IE_MORALEBREAK, act->GetBase(IE_MORALEBREAK)-parameters->int0Parameter); +} + +void GameScript::JoinParty(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type != ST_ACTOR) { + return; + } + /* calling this, so it is simpler to change */ + /* i'm not sure this is required here at all */ + SetBeenInPartyFlags(Sender, parameters); + Actor* act = ( Actor* ) Sender; + act->SetBase( IE_EA, EA_PC ); + if (core->HasFeature( GF_HAS_DPLAYER )) { + /* we must reset various existing scripts */ + act->SetScript( "DEFAULT", AI_SCRIPT_LEVEL, true ); + act->SetScript( "", SCR_RACE, true ); + act->SetScript( "", SCR_GENERAL, true ); + act->SetScript( "DPLAYER2", SCR_DEFAULT, false ); + } + AutoTable pdtable("pdialog"); + if (pdtable) { + const char* scriptname = act->GetScriptName(); + ieResRef resref; + //set dialog only if we got a row + if (pdtable->GetRowIndex( scriptname ) != -1) { + strnlwrcpy(resref, pdtable->QueryField( scriptname, "JOIN_DIALOG_FILE"),8); + act->SetDialog( resref ); + } + } + core->GetGame()->JoinParty( act, JP_JOIN ); + //core->GetGUIScriptEngine()->RunFunction( "GUICommonWindows", "UpdatePortraitWindow" ); +} + +void GameScript::LeaveParty(Scriptable* Sender, Action* /*parameters*/) +{ + if (Sender->Type != ST_ACTOR) { + return; + } + Actor* act = ( Actor* ) Sender; + core->GetGame()->LeaveParty( act ); + //core->GetGUIScriptEngine()->RunFunction( "GUICommonWindows", "UpdatePortraitWindow" ); +} + +//HideCreature hides only the visuals of a creature +//(feet circle and avatar) +//the scripts of the creature are still running +//iwd2 stores this flag in the MC field +void GameScript::HideCreature(Scriptable* Sender, Action* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar || tar->Type != ST_ACTOR) { + return; + } + Actor* actor = ( Actor* ) tar; + actor->BaseStats[IE_AVATARREMOVAL]=parameters->int0Parameter; +} + +//i have absolutely no idea why this is needed when we have HideCreature +void GameScript::ForceHide(Scriptable* Sender, Action* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar) { + tar=Sender; + } + if (tar->Type != ST_ACTOR) { + return; + } + Actor* actor = ( Actor* ) tar; + actor->BaseStats[IE_AVATARREMOVAL]=1; +} + +void GameScript::Activate(Scriptable* Sender, Action* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar || tar->Type != ST_ACTOR) { + return; + } + // Deactivate hides, so this should unhide.. + //tar->Activate(); + tar->Unhide(); +} + +void GameScript::ForceLeaveAreaLUA(Scriptable* Sender, Action* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar || tar->Type != ST_ACTOR) { + return; + } + Actor* actor = ( Actor* ) tar; + //the LoadMos ResRef may be empty + strncpy(core->GetGame()->LoadMos, parameters->string1Parameter,8); + MoveBetweenAreasCore( actor, parameters->string0Parameter, parameters->pointParameter, parameters->int0Parameter, true); +} + +void GameScript::LeaveAreaLUA(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type != ST_ACTOR) { + return; + } + Actor* actor = ( Actor* ) Sender; + //the LoadMos ResRef may be empty + strncpy(core->GetGame()->LoadMos, parameters->string1Parameter,8); + MoveBetweenAreasCore( actor, parameters->string0Parameter, parameters->pointParameter, parameters->int0Parameter, true); +} + +//this is a blocking action, because we have to move to the Entry +void GameScript::LeaveAreaLUAEntry(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type != ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + Game *game = core->GetGame(); + strncpy(game->LoadMos, parameters->string1Parameter,8); + Point p = GetEntryPoint(parameters->string0Parameter, parameters->string1Parameter); + if (p.isempty()) { + Sender->ReleaseCurrentAction(); + return; + } + parameters->pointParameter=p; + strcpy(parameters->string1Parameter, ""); + LeaveAreaLUA(Sender, parameters); + Sender->ReleaseCurrentAction(); +} + +void GameScript::LeaveAreaLUAPanic(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type != ST_ACTOR) { + return; + } + Actor* actor = ( Actor* ) Sender; + strncpy(core->GetGame()->LoadMos, parameters->string1Parameter,8); + MoveBetweenAreasCore( actor, parameters->string0Parameter, parameters->pointParameter, parameters->int0Parameter, true); +} + +//this is a blocking action, because we have to move to the Entry +void GameScript::LeaveAreaLUAPanicEntry(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type != ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + Game *game = core->GetGame(); + strncpy(game->LoadMos, parameters->string1Parameter,8); + Point p = GetEntryPoint(parameters->string0Parameter, parameters->string1Parameter); + if (p.isempty()) { + Sender->ReleaseCurrentAction(); + return; + } + parameters->pointParameter=p; + strcpy(parameters->string1Parameter, ""); + LeaveAreaLUAPanic(Sender, parameters); + Sender->ReleaseCurrentAction(); +} + +void GameScript::SetToken(Scriptable* /*Sender*/, Action* parameters) +{ + //SetAt takes a newly created reference (no need of free/copy) + char * str = core->GetString( parameters->int0Parameter); + core->GetTokenDictionary()->SetAt( parameters->string1Parameter, str); +} + +//Assigns a numeric variable to the token +void GameScript::SetTokenGlobal(Scriptable* Sender, Action* parameters) +{ + ieDword value = CheckVariable( Sender, parameters->string0Parameter ); + //using SetAtCopy because we need a copy of the value + core->GetTokenDictionary()->SetAtCopy( parameters->string1Parameter, value ); +} + +//Assigns the target object's name (not scriptname) to the token +void GameScript::SetTokenObject(Scriptable* Sender, Action* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar || tar->Type != ST_ACTOR) { + return; + } + Actor* actor = ( Actor* ) tar; + core->GetTokenDictionary()->SetAtCopy( parameters->string0Parameter, actor->GetName(0) ); +} + +void GameScript::PlayDead(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type != ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + Actor* actor = ( Actor* ) Sender; + actor->CurrentActionInterruptable = false; + if (Sender->CurrentActionState == 0) { + // TODO: what if parameter is 0? see orphan2 + Sender->CurrentActionState = parameters->int0Parameter; + actor->SetStance( IE_ANI_DIE ); + } else { + actor->CurrentActionState--; + if (Sender->CurrentActionState == 0) { + actor->SetStance( IE_ANI_GET_UP ); + Sender->ReleaseCurrentAction(); + } + } +} + +void GameScript::PlayDeadInterruptable(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type != ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + Actor* actor = ( Actor* ) Sender; + if (Sender->CurrentActionState == 0) { + // TODO: what if parameter is 0? see orphan2 + Sender->CurrentActionState = parameters->int0Parameter; + actor->SetStance( IE_ANI_DIE ); + } else { + actor->CurrentActionState--; + if (Sender->CurrentActionState == 0) { + actor->SetStance( IE_ANI_GET_UP ); + Sender->ReleaseCurrentAction(); + } + } +} + +/* this may not be correct, just a placeholder you can fix */ +void GameScript::Swing(Scriptable* Sender, Action* /*parameters*/) +{ + if (Sender->Type != ST_ACTOR) { + return; + } + Actor* actor = ( Actor* ) Sender; + actor->SetStance( IE_ANI_ATTACK ); + actor->SetWait( 1 ); +} + +/* this may not be correct, just a placeholder you can fix */ +void GameScript::SwingOnce(Scriptable* Sender, Action* /*parameters*/) +{ + if (Sender->Type != ST_ACTOR) { + return; + } + Actor* actor = ( Actor* ) Sender; + actor->SetStance( IE_ANI_ATTACK ); + actor->SetWait( 1 ); +} + +void GameScript::Recoil(Scriptable* Sender, Action* /*parameters*/) +{ + if (Sender->Type != ST_ACTOR) { + return; + } + Actor* actor = ( Actor* ) Sender; + actor->SetStance( IE_ANI_DAMAGE ); + actor->SetWait( 1 ); +} + +void GameScript::AnkhegEmerge(Scriptable* Sender, Action* /*parameters*/) +{ + if (Sender->Type != ST_ACTOR) { + return; + } + Actor* actor = ( Actor* ) Sender; + if (actor->GetStance()!=IE_ANI_EMERGE) { + actor->SetStance( IE_ANI_EMERGE ); + actor->SetWait( 1 ); + } +} + +void GameScript::AnkhegHide(Scriptable* Sender, Action* /*parameters*/) +{ + if (Sender->Type != ST_ACTOR) { + return; + } + Actor* actor = ( Actor* ) Sender; + if (actor->GetStance()!=IE_ANI_HIDE) { + actor->SetStance( IE_ANI_HIDE ); + actor->SetWait( 1 ); + } +} + +void GameScript::GlobalSetGlobal(Scriptable* Sender, Action* parameters) +{ + ieDword value = CheckVariable( Sender, parameters->string0Parameter ); + SetVariable( Sender, parameters->string1Parameter, value ); +} + +/* adding the second variable to the first, they must be GLOBAL */ +void GameScript::AddGlobals(Scriptable* Sender, Action* parameters) +{ + ieDword value1 = CheckVariable( Sender, parameters->string0Parameter, "GLOBAL"); + ieDword value2 = CheckVariable( Sender, parameters->string1Parameter, "GLOBAL"); + SetVariable( Sender, parameters->string0Parameter, "GLOBAL", value1 + value2 ); +} + +/* adding the second variable to the first, they could be area or locals */ +void GameScript::GlobalAddGlobal(Scriptable* Sender, Action* parameters) +{ + ieDword value1 = CheckVariable( Sender, + parameters->string0Parameter ); + ieDword value2 = CheckVariable( Sender, + parameters->string1Parameter ); + SetVariable( Sender, parameters->string0Parameter, value1 + value2 ); +} + +/* adding the number to the global, they could be area or locals */ +void GameScript::IncrementGlobal(Scriptable* Sender, Action* parameters) +{ + ieDword value = CheckVariable( Sender, parameters->string0Parameter ); + SetVariable( Sender, parameters->string0Parameter, + value + parameters->int0Parameter ); +} + +/* adding the number to the global ONLY if the first global is zero */ +void GameScript::IncrementGlobalOnce(Scriptable* Sender, Action* parameters) +{ + ieDword value = CheckVariable( Sender, parameters->string0Parameter ); + if (value != 0) { + return; + } + value = CheckVariable( Sender, parameters->string1Parameter ); + SetVariable( Sender, parameters->string1Parameter, + value + parameters->int0Parameter ); +} + +void GameScript::GlobalSubGlobal(Scriptable* Sender, Action* parameters) +{ + ieDword value1 = CheckVariable( Sender, + parameters->string0Parameter ); + ieDword value2 = CheckVariable( Sender, + parameters->string1Parameter ); + SetVariable( Sender, parameters->string0Parameter, value1 - value2 ); +} + +void GameScript::GlobalAndGlobal(Scriptable* Sender, Action* parameters) +{ + ieDword value1 = CheckVariable( Sender, + parameters->string0Parameter ); + ieDword value2 = CheckVariable( Sender, + parameters->string1Parameter ); + SetVariable( Sender, parameters->string0Parameter, value1 && value2 ); +} + +void GameScript::GlobalOrGlobal(Scriptable* Sender, Action* parameters) +{ + ieDword value1 = CheckVariable( Sender, + parameters->string0Parameter ); + ieDword value2 = CheckVariable( Sender, + parameters->string1Parameter ); + SetVariable( Sender, parameters->string0Parameter, value1 || value2 ); +} + +void GameScript::GlobalBOrGlobal(Scriptable* Sender, Action* parameters) +{ + ieDword value1 = CheckVariable( Sender, + parameters->string0Parameter ); + ieDword value2 = CheckVariable( Sender, + parameters->string1Parameter ); + SetVariable( Sender, parameters->string0Parameter, value1 | value2 ); +} + +void GameScript::GlobalBAndGlobal(Scriptable* Sender, Action* parameters) +{ + ieDword value1 = CheckVariable( Sender, + parameters->string0Parameter ); + ieDword value2 = CheckVariable( Sender, + parameters->string1Parameter ); + SetVariable( Sender, parameters->string0Parameter, value1 & value2 ); +} + +void GameScript::GlobalXorGlobal(Scriptable* Sender, Action* parameters) +{ + ieDword value1 = CheckVariable( Sender, + parameters->string0Parameter ); + ieDword value2 = CheckVariable( Sender, + parameters->string1Parameter ); + SetVariable( Sender, parameters->string0Parameter, value1 ^ value2 ); +} + +void GameScript::GlobalBOr(Scriptable* Sender, Action* parameters) +{ + ieDword value1 = CheckVariable( Sender, + parameters->string0Parameter ); + SetVariable( Sender, parameters->string0Parameter, + value1 | parameters->int0Parameter ); +} + +void GameScript::GlobalBAnd(Scriptable* Sender, Action* parameters) +{ + ieDword value1 = CheckVariable( Sender, + parameters->string0Parameter ); + SetVariable( Sender, parameters->string0Parameter, + value1 & parameters->int0Parameter ); +} + +void GameScript::GlobalXor(Scriptable* Sender, Action* parameters) +{ + ieDword value1 = CheckVariable( Sender, + parameters->string0Parameter ); + SetVariable( Sender, parameters->string0Parameter, + value1 ^ parameters->int0Parameter ); +} + +void GameScript::GlobalMax(Scriptable* Sender, Action* parameters) +{ + long value1 = CheckVariable( Sender, parameters->string0Parameter ); + if (value1 > parameters->int0Parameter) { + SetVariable( Sender, parameters->string0Parameter, value1 ); + } +} + +void GameScript::GlobalMin(Scriptable* Sender, Action* parameters) +{ + long value1 = CheckVariable( Sender, parameters->string0Parameter ); + if (value1 < parameters->int0Parameter) { + SetVariable( Sender, parameters->string0Parameter, value1 ); + } +} + +void GameScript::BitClear(Scriptable* Sender, Action* parameters) +{ + ieDword value1 = CheckVariable( Sender, + parameters->string0Parameter ); + SetVariable( Sender, parameters->string0Parameter, + value1 & ~parameters->int0Parameter ); +} + +void GameScript::GlobalShL(Scriptable* Sender, Action* parameters) +{ + ieDword value1 = CheckVariable( Sender, + parameters->string0Parameter ); + ieDword value2 = parameters->int0Parameter; + if (value2 > 31) { + value1 = 0; + } else { + value1 <<= value2; + } + SetVariable( Sender, parameters->string0Parameter, value1 ); +} + +void GameScript::GlobalShR(Scriptable* Sender, Action* parameters) +{ + ieDword value1 = CheckVariable( Sender, + parameters->string0Parameter ); + ieDword value2 = parameters->int0Parameter; + if (value2 > 31) { + value1 = 0; + } else { + value1 >>= value2; + } + SetVariable( Sender, parameters->string0Parameter, value1 ); +} + +void GameScript::GlobalMaxGlobal(Scriptable* Sender, Action* parameters) +{ + ieDword value1 = CheckVariable( Sender, parameters->string0Parameter ); + ieDword value2 = CheckVariable( Sender, parameters->string1Parameter ); + if (value1 < value2) { + SetVariable( Sender, parameters->string0Parameter, value2 ); + } +} + +void GameScript::GlobalMinGlobal(Scriptable* Sender, Action* parameters) +{ + ieDword value1 = CheckVariable( Sender, parameters->string0Parameter ); + ieDword value2 = CheckVariable( Sender, parameters->string1Parameter ); + if (value1 > value2) { + SetVariable( Sender, parameters->string0Parameter, value2 ); + } +} + +void GameScript::GlobalShLGlobal(Scriptable* Sender, Action* parameters) +{ + ieDword value1 = CheckVariable( Sender, parameters->string0Parameter ); + ieDword value2 = CheckVariable( Sender, parameters->string1Parameter ); + if (value2 > 31) { + value1 = 0; + } else { + value1 <<= value2; + } + SetVariable( Sender, parameters->string0Parameter, value1 ); +} +void GameScript::GlobalShRGlobal(Scriptable* Sender, Action* parameters) +{ + ieDword value1 = CheckVariable( Sender, parameters->string0Parameter ); + ieDword value2 = CheckVariable( Sender, parameters->string1Parameter ); + if (value2 > 31) { + value1 = 0; + } else { + value1 >>= value2; + } + SetVariable( Sender, parameters->string0Parameter, value1 ); +} + +void GameScript::ClearAllActions(Scriptable* Sender, Action* /*parameters*/) +{ + Actor *except = NULL; + if (Sender->Type==ST_ACTOR) { + except = (Actor *) Sender; + } + Map *map = Sender->GetCurrentArea(); + ieDword gametime = core->GetGame()->GameTime; + int i = map->GetActorCount(true); + while(i--) { + Actor* act = map->GetActor(i,true); + if (act && act!=except) { + if (!act->ValidTarget(GA_NO_DEAD) ) { + continue; + } + //Do we need this??? + if (!act->Schedule(gametime, false) ) { + continue; + } + act->ClearActions(); + act->ClearPath(); + act->SetModal(MS_NONE); + } + } +} + +void GameScript::ClearActions(Scriptable* Sender, Action* parameters) +{ + Scriptable* tar = Sender; + if (parameters->objects[1]) { + tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar) { + printMessage("GameScript","Couldn't find target for ClearActions!",YELLOW); + parameters->objects[1]->Dump(); + return; + } + } + tar->ClearActions(); + if (tar->Type==ST_ACTOR) { + Actor* act = (Actor *) tar; + act->ClearPath(); + //not sure about this + //act->SetModal(MS_NONE); + } +} + +void GameScript::SetNumTimesTalkedTo(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type != ST_ACTOR) { + return; + } + Actor* actor = ( Actor* ) Sender; + actor->TalkCount = parameters->int0Parameter; +} + +void GameScript::StartMovie(Scriptable* Sender, Action* parameters) +{ + core->PlayMovie( parameters->string0Parameter ); + Sender->ReleaseCurrentAction(); // should this be blocking? +} + +void GameScript::SetLeavePartyDialogFile(Scriptable* Sender, Action* /*parameters*/) +{ + if (Sender->Type != ST_ACTOR) { + return; + } + AutoTable pdtable("pdialog"); + Actor* act = ( Actor* ) Sender; + const char* scriptingname = act->GetScriptName(); + act->SetDialog( pdtable->QueryField( scriptingname, "POST_DIALOG_FILE" ) ); +} + +void GameScript::TextScreen(Scriptable* Sender, Action* parameters) +{ + strnlwrcpy(core->GetGame()->LoadMos, parameters->string0Parameter,8); + core->GetGUIScriptEngine()->RunFunction( "TextScreen", "StartTextScreen" ); + core->GetVideoDriver()->SetMouseEnabled(true); + Sender->SetWait(1); + Sender->ReleaseCurrentAction(); // should this be blocking? +} + +void GameScript::IncrementChapter(Scriptable* Sender, Action* parameters) +{ + TextScreen(Sender, parameters); // textscreen will release blocking for us + core->GetGame()->IncrementChapter(); +} + +void GameScript::SetCriticalPathObject(Scriptable* Sender, Action* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar || tar->Type != ST_ACTOR) { + return; + } + Actor* actor = ( Actor* ) tar; + if (parameters->int0Parameter) { + actor->SetMCFlag(MC_PLOT_CRITICAL, BM_OR); + } else { + actor->SetMCFlag(MC_PLOT_CRITICAL, BM_NAND); + } +} + +void GameScript::SetBeenInPartyFlags(Scriptable* Sender, Action* /*parameters*/) +{ + if (Sender->Type != ST_ACTOR) { + return; + } + Actor* actor = ( Actor* ) Sender; + //it is bit 15 of the multi-class flags (confirmed) + actor->SetMCFlag(MC_BEENINPARTY, BM_OR); +} + +/*iwd2 sets the high MC bits this way*/ +void GameScript::SetCreatureAreaFlag(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type != ST_ACTOR) { + return; + } + Actor* actor = ( Actor* ) Sender; + actor->SetMCFlag(parameters->int0Parameter, parameters->int1Parameter); +} + +//this will be a global change, fixme if it should be local +void GameScript::SetTextColor(Scriptable* /*Sender*/, Action* parameters) +{ + Color c; + memcpy(&c,¶meters->int0Parameter,4); + core->SetInfoTextColor(c); +} + +void GameScript::BitGlobal(Scriptable* Sender, Action* parameters) +{ + ieDword value = CheckVariable(Sender, parameters->string0Parameter ); + HandleBitMod( value, parameters->int0Parameter, parameters->int1Parameter); + SetVariable(Sender, parameters->string0Parameter, value); +} + +void GameScript::GlobalBitGlobal(Scriptable* Sender, Action* parameters) +{ + ieDword value1 = CheckVariable(Sender, parameters->string0Parameter ); + ieDword value2 = CheckVariable(Sender, parameters->string1Parameter ); + HandleBitMod( value1, value2, parameters->int1Parameter); + SetVariable(Sender, parameters->string0Parameter, value1); +} + +void GameScript::SetVisualRange(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type != ST_ACTOR) { + return; + } + Actor* actor = ( Actor* ) Sender; + actor->SetBase(IE_VISUALRANGE,parameters->int0Parameter); +} + +void GameScript::MakeUnselectable(Scriptable* Sender, Action* parameters) +{ + Sender->UnselectableTimer=parameters->int0Parameter; + + //update color + if (Sender->Type != ST_ACTOR) { + return; + } + Actor* actor = ( Actor* ) Sender; + if (parameters->int0Parameter) { + // flags may be wrong + core->GetGame()->SelectActor(actor, false, SELECT_QUIET); + } + + actor->SetCircleSize(); +} + +void GameScript::Debug(Scriptable* /*Sender*/, Action* parameters) +{ + InDebug=parameters->int0Parameter; + printMessage("GameScript",parameters->string0Parameter,YELLOW); +} + +void GameScript::IncrementProficiency(Scriptable* Sender, Action* parameters) +{ + unsigned int idx = parameters->int0Parameter; + if (idx>31) { + return; + } + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar) { + return; + } + if (tar->Type != ST_ACTOR) { + return; + } + Actor* target = ( Actor* ) tar; + //start of the proficiency stats + target->SetBase(IE_PROFICIENCYBASTARDSWORD+idx, + target->GetBase(IE_PROFICIENCYBASTARDSWORD+idx)+parameters->int1Parameter); +} + +void GameScript::IncrementExtraProficiency(Scriptable* Sender, Action* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar) { + return; + } + if (tar->Type != ST_ACTOR) { + return; + } + Actor* target = ( Actor* ) tar; + target->SetBase(IE_FREESLOTS, target->GetBase(IE_FREESLOTS)+parameters->int0Parameter); +} + +//the third parameter is a GemRB extension +void GameScript::AddJournalEntry(Scriptable* /*Sender*/, Action* parameters) +{ + core->GetGame()->AddJournalEntry(parameters->int0Parameter, parameters->int1Parameter, parameters->int2Parameter); +} + +void GameScript::SetQuestDone(Scriptable* /*Sender*/, Action* parameters) +{ + Game *game = core->GetGame(); + game->DeleteJournalEntry(parameters->int0Parameter); + game->AddJournalEntry(parameters->int0Parameter, IE_GAM_QUEST_DONE, parameters->int2Parameter); + +} + +void GameScript::RemoveJournalEntry(Scriptable* /*Sender*/, Action* parameters) +{ + core->GetGame()->DeleteJournalEntry(parameters->int0Parameter); +} + +void GameScript::SetInternal(Scriptable* Sender, Action* parameters) +{ + unsigned int idx = parameters->int0Parameter; + if (idx>15) { + return; + } + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar) { + return; + } + if (tar->Type != ST_ACTOR) { + return; + } + Actor* target = ( Actor* ) tar; + //start of the internal stats + target->SetBase(IE_INTERNAL_0+idx, parameters->int1Parameter); +} + +void GameScript::IncInternal(Scriptable* Sender, Action* parameters) +{ + unsigned int idx = parameters->int0Parameter; + if (idx>15) { + return; + } + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar || tar->Type != ST_ACTOR) { + return; + } + Actor* target = ( Actor* ) tar; + //start of the internal stats + target->SetBase(IE_INTERNAL_0+idx, + target->GetBase(IE_INTERNAL_0+idx)+parameters->int1Parameter); +} + +void GameScript::DestroyAllEquipment(Scriptable* Sender, Action* /*parameters*/) +{ + Inventory *inv=NULL; + + switch (Sender->Type) { + case ST_ACTOR: + inv = &(((Actor *) Sender)->inventory); + break; + case ST_CONTAINER: + inv = &(((Container *) Sender)->inventory); + break; + default:; + } + if (inv) { + inv->DestroyItem("",0,(ieDword) ~0); //destroy any and all + } +} + +void GameScript::DestroyItem(Scriptable* Sender, Action* parameters) +{ + Inventory *inv=NULL; + + switch (Sender->Type) { + case ST_ACTOR: + inv = &(((Actor *) Sender)->inventory); + break; + case ST_CONTAINER: + inv = &(((Container *) Sender)->inventory); + break; + default:; + } + if (inv) { + inv->DestroyItem(parameters->string0Parameter,0,1); //destroy one (even indestructible?) + } +} + +//negative destroygold creates gold +void GameScript::DestroyGold(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type!=ST_ACTOR) + return; + Actor *act=(Actor *) Sender; + int max=(int) act->GetStat(IE_GOLD); + if (parameters->int0Parameter != 0) { + if (max>parameters->int0Parameter) { + max=parameters->int0Parameter; + } + } + act->SetBase(IE_GOLD, act->GetBase(IE_GOLD)-max); +} + +void GameScript::DestroyPartyItem(Scriptable* /*Sender*/, Action* parameters) +{ + Game *game = core->GetGame(); + int i = game->GetPartySize(false); + ieDword count; + if (parameters->int0Parameter) { + count=0; + } else { + count=1; + } + while (i--) { + Inventory *inv = &(game->GetPC( i,false )->inventory); + int res=inv->DestroyItem(parameters->string0Parameter,0,count); + if ( (count == 1) && res) { + break; + } + } +} + +/* this is a gemrb extension */ +void GameScript::DestroyPartyItemNum(Scriptable* /*Sender*/, Action* parameters) +{ + Game *game = core->GetGame(); + int i = game->GetPartySize(false); + ieDword count; + count = parameters->int0Parameter; + while (i--) { + Inventory *inv = &(game->GetPC( i,false )->inventory); + count -= inv->DestroyItem(parameters->string0Parameter,0,count); + if (!count ) { + break; + } + } +} + +void GameScript::DestroyAllDestructableEquipment(Scriptable* Sender, Action* /*parameters*/) +{ + Inventory *inv=NULL; + + switch (Sender->Type) { + case ST_ACTOR: + inv = &(((Actor *) Sender)->inventory); + break; + case ST_CONTAINER: + inv = &(((Container *) Sender)->inventory); + break; + default:; + } + if (inv) { + inv->DestroyItem("", IE_INV_ITEM_DESTRUCTIBLE, (ieDword) ~0); + } +} + +void GameScript::SetApparentName(Scriptable* Sender, Action* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar || tar->Type != ST_ACTOR) { + return; + } + Actor* target = ( Actor* ) tar; + target->SetName(parameters->int0Parameter,1); +} + +void GameScript::SetRegularName(Scriptable* Sender, Action* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar || tar->Type != ST_ACTOR) { + return; + } + Actor* target = ( Actor* ) tar; + target->SetName(parameters->int0Parameter,2); +} + +/** this is a gemrb extension */ +void GameScript::UnloadArea(Scriptable* /*Sender*/, Action* parameters) +{ + int map=core->GetGame()->FindMap(parameters->string0Parameter); + if (map>=0) { + core->GetGame()->DelMap(map, parameters->int0Parameter); + } +} + +static EffectRef fx_death_ref={"Death", NULL, -1}; +void GameScript::Kill(Scriptable* Sender, Action* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar || tar->Type != ST_ACTOR) { + return; + } + Actor* target = ( Actor* ) tar; + Effect *fx = EffectQueue::CreateEffect(fx_death_ref, 0, 0, FX_DURATION_INSTANT_PERMANENT); + target->fxqueue.AddEffect(fx, false); + delete fx; +} + +void GameScript::SetGabber(Scriptable* Sender, Action* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar || tar->Type != ST_ACTOR) { + return; + } + GameControl* gc = core->GetGameControl(); + if (gc->GetDialogueFlags()&DF_IN_DIALOG) { + gc->dialoghandler->speakerID = tar->GetGlobalID(); + } else { + printMessage("GameScript","Can't set gabber!",YELLOW); + } +} + +void GameScript::ReputationSet(Scriptable* /*Sender*/, Action* parameters) +{ + core->GetGame()->SetReputation(parameters->int0Parameter*10); +} + +void GameScript::ReputationInc(Scriptable* /*Sender*/, Action* parameters) +{ + Game *game = core->GetGame(); + game->SetReputation( (int) game->Reputation + parameters->int0Parameter*10); +} + +void GameScript::FullHeal(Scriptable* Sender, Action* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar || tar->Type != ST_ACTOR) { + return; + } + Actor *scr = (Actor *) tar; + //0 means full healing + //Heal() might contain curing of some conditions + //if FullHeal doesn't do that, replace this with a SetBase + //fullhealex might still be the curing action + scr->Heal(0); +} + +void GameScript::RemovePaladinHood(Scriptable* Sender, Action* /*parameters*/) +{ + if (Sender->Type!=ST_ACTOR) { + return; + } + Actor *act = (Actor *) Sender; + act->ApplyKit(true); + act->SetMCFlag(MC_FALLEN_PALADIN, BM_OR); + if (act->InParty) displaymsg->DisplayConstantStringName(STR_PALADIN_FALL, 0xbcefbc, act); +} + +void GameScript::RemoveRangerHood(Scriptable* Sender, Action* /*parameters*/) +{ + if (Sender->Type!=ST_ACTOR) { + return; + } + Actor *act = (Actor *) Sender; + act->ApplyKit(true); + act->SetMCFlag(MC_FALLEN_RANGER, BM_OR); + if (act->InParty) displaymsg->DisplayConstantStringName(STR_RANGER_FALL, 0xbcefbc, act); +} + +void GameScript::RegainPaladinHood(Scriptable* Sender, Action* /*parameters*/) +{ + if (Sender->Type!=ST_ACTOR) { + return; + } + Actor *act = (Actor *) Sender; + act->SetMCFlag(MC_FALLEN_PALADIN, BM_NAND); + act->ApplyKit(false); +} + +void GameScript::RegainRangerHood(Scriptable* Sender, Action* /*parameters*/) +{ + if (Sender->Type!=ST_ACTOR) { + return; + } + Actor *act = (Actor *) Sender; + act->SetMCFlag(MC_FALLEN_RANGER, BM_NAND); + act->ApplyKit(false); +} + +//transfering item from Sender to target, target must be an actor +//if target can't get it, it will be dropped at its feet +//a container or an actor can take an item from someone +void GameScript::GetItem(Scriptable* Sender, Action* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + MoveItemCore(tar, Sender, parameters->string0Parameter,0,0); +} + +//getting one single item +void GameScript::TakePartyItem(Scriptable* Sender, Action* parameters) +{ + Game *game=core->GetGame(); + int i=game->GetPartySize(false); + while (i--) { + int res=MoveItemCore(game->GetPC(i,false), Sender, parameters->string0Parameter,0,IE_INV_ITEM_UNSTEALABLE); + if (res!=MIC_NOITEM) return; + } +} + +//getting x single item +void GameScript::TakePartyItemNum(Scriptable* Sender, Action* parameters) +{ + int count = parameters->int0Parameter; + Game *game=core->GetGame(); + int i=game->GetPartySize(false); + while (i--) { + int res=MoveItemCore(game->GetPC(i,false), Sender, parameters->string0Parameter,0, IE_INV_ITEM_UNSTEALABLE); + if (res == MIC_GOTITEM) { + i++; + count--; + } + if (!count) return; + } +} + +void GameScript::TakePartyItemRange(Scriptable* Sender, Action* parameters) +{ + Game *game=core->GetGame(); + int i=game->GetPartySize(false); + while (i--) { + Actor *ac = game->GetPC(i,false); + if (Distance(Sender, ac)string0Parameter,0,IE_INV_ITEM_UNSTEALABLE)==MIC_GOTITEM) { } + } + } +} + +void GameScript::TakePartyItemAll(Scriptable* Sender, Action* parameters) +{ + Game *game=core->GetGame(); + int i=game->GetPartySize(false); + while (i--) { + while (MoveItemCore(game->GetPC(i,false), Sender, parameters->string0Parameter,0, IE_INV_ITEM_UNSTEALABLE)==MIC_GOTITEM) { } + } +} + +//an actor can 'give' an item to a container or another actor +void GameScript::GiveItem(Scriptable *Sender, Action* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + MoveItemCore(Sender, tar, parameters->string0Parameter,0,0); +} + +//this action creates an item in a container or a creature +//if there is an object it works as GiveItemCreate +//otherwise it creates the item on the Sender +void GameScript::CreateItem(Scriptable *Sender, Action* parameters) +{ + Scriptable* tar; + if (parameters->objects[1]) { + tar = GetActorFromObject( Sender, parameters->objects[1] ); + } else { + tar = Sender; + } + if (!tar) + return; + Inventory *myinv; + + switch(tar->Type) { + case ST_ACTOR: + myinv = &((Actor *) tar)->inventory; + break; + case ST_CONTAINER: + myinv = &((Container *) tar)->inventory; + break; + default: + return; + } + + CREItem *item = new CREItem(); + CreateItemCore(item, parameters->string0Parameter, parameters->int0Parameter, parameters->int1Parameter, parameters->int2Parameter); + if (tar->Type==ST_CONTAINER) { + myinv->AddItem(item); + } else { + if ( ASI_SUCCESS != myinv->AddSlotItem(item, SLOT_ONLYINVENTORY)) { + Map *map=tar->GetCurrentArea(); + // drop it at my feet + map->AddItemToLocation(tar->Pos, item); + if (((Actor *)tar)->InParty) displaymsg->DisplayConstantString(STR_INVFULL_ITEMDROP, 0xbcefbc); + } else { + if (((Actor *)tar)->InParty) displaymsg->DisplayConstantString(STR_GOTITEM, 0xbcefbc); + } + } +} + +void GameScript::CreateItemNumGlobal(Scriptable *Sender, Action* parameters) +{ + Inventory *myinv; + + switch(Sender->Type) { + case ST_ACTOR: + myinv = &((Actor *) Sender)->inventory; + break; + case ST_CONTAINER: + myinv = &((Container *) Sender)->inventory; + break; + default: + return; + } + int value = CheckVariable( Sender, parameters->string0Parameter ); + CREItem *item = new CREItem(); + CreateItemCore(item, parameters->string1Parameter, value, 0, 0); + if (Sender->Type==ST_CONTAINER) { + myinv->AddItem(item); + } else { + if ( ASI_SUCCESS != myinv->AddSlotItem(item, SLOT_ONLYINVENTORY)) { + Map *map=Sender->GetCurrentArea(); + // drop it at my feet + map->AddItemToLocation(Sender->Pos, item); + if (((Actor *)Sender)->InParty) displaymsg->DisplayConstantString(STR_INVFULL_ITEMDROP, 0xbcefbc); + } else { + if (((Actor *)Sender)->InParty) displaymsg->DisplayConstantString(STR_GOTITEM, 0xbcefbc); + } + } +} + +void GameScript::TakeItemReplace(Scriptable *Sender, Action* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar || tar->Type != ST_ACTOR) { + return; + } + + Actor *scr = (Actor *) tar; + CREItem *item; + int slot = scr->inventory.RemoveItem(parameters->string1Parameter, IE_INV_ITEM_UNDROPPABLE, &item); + if (!item) { + item = new CREItem(); + } + CreateItemCore(item, parameters->string0Parameter, -1, 0, 0); + if (ASI_SUCCESS != scr->inventory.AddSlotItem(item,slot)) { + Map *map = scr->GetCurrentArea(); + map->AddItemToLocation(Sender->Pos, item); + } +} + +//same as equipitem, but with additional slots parameter, and object to perform action +void GameScript::XEquipItem(Scriptable *Sender, Action* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + + if (!tar || tar->Type!=ST_ACTOR) { + return; + } + Actor *actor = (Actor *) tar; + int slot = actor->inventory.FindItem(parameters->string0Parameter, 0); + if (slot<0) { + return; + } + actor->inventory.EquipItem(slot); + actor->ReinitQuickSlots(); +} + +//GemRB extension: if int1Parameter is nonzero, don't destroy existing items +void GameScript::FillSlot(Scriptable *Sender, Action* parameters) +{ + if (Sender->Type!=ST_ACTOR) { + return; + } + + CREItem *tmp = NULL; + Actor *actor = (Actor *) Sender; + int slot = parameters->int0Parameter; + + //free up target slot + tmp = actor->inventory.RemoveItem(slot); + + actor->inventory.TryEquipAll(slot); + + if (tmp) { + if (actor->inventory.HasItemInSlot("",slot) ) { + slot = SLOT_ONLYINVENTORY; + } + + //reequip original item + if(actor->inventory.AddSlotItem(tmp, slot)!=ASI_SUCCESS) { + delete tmp; + } + } +} + +//iwd2 also has a flag for unequip (it might collide with original!) +void GameScript::EquipItem(Scriptable *Sender, Action* parameters) +{ + if (Sender->Type!=ST_ACTOR) { + return; + } + Actor *actor = (Actor *) Sender; + int slot = actor->inventory.FindItem(parameters->string0Parameter, IE_INV_ITEM_UNDROPPABLE); + if (slot<0) { + return; + } + + int slot2; + + if (parameters->int0Parameter) { + //unequip item, and move it to the inventory + slot2 = SLOT_ONLYINVENTORY; + } else { + //equip item if possible + slot2 = SLOT_AUTOEQUIP; + } + CREItem *si = actor->inventory.RemoveItem(slot); + if (si) { + if (actor->inventory.AddSlotItem(si, slot2)==ASI_FAILED) { + Map *map = Sender->GetCurrentArea(); + if (map) { + //drop item at the feet of the character instead of destroying it + map->AddItemToLocation(Sender->Pos, si); + } else { + delete si; + } + } + } + actor->ReinitQuickSlots(); +} + +void GameScript::DropItem(Scriptable *Sender, Action* parameters) +{ + if (Sender->Type!=ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + if (Distance(parameters->pointParameter, Sender) > 10) { + MoveNearerTo(Sender, parameters->pointParameter, 10,0); + return; + } + Actor *scr = (Actor *) Sender; + Map *map = Sender->GetCurrentArea(); + + if (parameters->string0Parameter[0]) { + //dropping location isn't exactly our place, this is why i didn't use a simple DropItem + scr->inventory.DropItemAtLocation(parameters->string0Parameter, +0, map, parameters->pointParameter); + } else { + //this should be converted from scripting slot to physical slot + scr->inventory.DropItemAtLocation(parameters->int0Parameter, 0, map, parameters->pointParameter); + } + + Sender->ReleaseCurrentAction(); +} + +void GameScript::DropInventory(Scriptable *Sender, Action* /*parameters*/) +{ + if (Sender->Type!=ST_ACTOR) { + return; + } + Actor *scr = (Actor *) Sender; + scr->DropItem("",0); +} + +//this should work on containers! +//using the same code for DropInventoryEXExclude +void GameScript::DropInventoryEX(Scriptable *Sender, Action* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar) { + return; + } + Inventory *inv = NULL; + switch (Sender->Type) { + case ST_ACTOR: + inv = &(((Actor *) tar)->inventory); + break; + case ST_CONTAINER: + inv = &(((Container *) tar)->inventory); + break; + default:; + } + if (inv) { + int x = inv->GetSlotCount(); + Map *area = tar->GetCurrentArea(); + while(x--) { + if (parameters->string0Parameter[0]) { + const char *resref = inv->GetSlotItem(x)->ItemResRef; + if (!strnicmp(parameters->string0Parameter, resref, 8)) { + continue; + } + } + inv->DropItemAtLocation(x, 0, area, tar->Pos); + } + } +} + +void GameScript::GivePartyAllEquipment(Scriptable *Sender, Action* /*parameters*/) +{ + if (Sender->Type!=ST_ACTOR) { + return; + } + Game *game = core->GetGame(); + // pick the first actor first + for (int i = 0; i < game->GetPartySize(false); i++) { + Actor *tar = game->GetPC(i,false); + //don't try to give self, it would be an infinite loop + if (tar==(Actor *) Sender) + continue; + while(MoveItemCore(Sender, tar, "",0,0)!=MIC_NOITEM) { } + } +} + +//This is unsure, Plunder could be just handling ground piles and not dead actors +void GameScript::Plunder(Scriptable *Sender, Action* parameters) +{ + if (Sender->Type!=ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + Scriptable* tar = GetStoredActorFromObject( Sender, parameters->objects[1] ); + if (!tar) { + Sender->ReleaseCurrentAction(); + return; + } + + //you must be joking + if (tar==Sender) { + Sender->ReleaseCurrentAction(); + return; + } + + if (tar->Type == ST_ACTOR) { + Actor *scr = (Actor *) tar; + //can plunder only dead actors + if (! (scr->BaseStats[IE_STATE_ID]&STATE_DEAD) ) { + Sender->ReleaseCurrentAction(); + return; + } + } + if (PersonalDistance(Sender, tar)>MAX_OPERATING_DISTANCE ) { + MoveNearerTo(Sender, tar->Pos, MAX_OPERATING_DISTANCE,0); + return; + } + //move all movable item from the target to the Sender + //the rest will be dropped at the feet of Sender + while(MoveItemCore(tar, Sender, "",0,0)!=MIC_NOITEM) { } + Sender->ReleaseCurrentAction(); +} + +void GameScript::MoveInventory(Scriptable *Sender, Action* parameters) +{ + Scriptable* src = GetActorFromObject( Sender, parameters->objects[1] ); + if (!src || src->Type!=ST_ACTOR) { + return; + } + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[2] ); + if (!tar || tar->Type!=ST_ACTOR) { + return; + } + //don't try to move to self, it would create infinite loop + if (src==tar) + return; + //move all movable item from the target to the Sender + //the rest will be dropped at the feet of Sender + while(MoveItemCore(src, tar, "",0,0)!=MIC_NOITEM) { } +} + +void GameScript::PickPockets(Scriptable *Sender, Action* parameters) +{ + if (Sender->Type!=ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + Scriptable* tar = GetStoredActorFromObject( Sender, parameters->objects[1] ); + if (!tar || tar->Type!=ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + Actor *snd = (Actor *) Sender; + Actor *scr = (Actor *) tar; + //for PP one must go REALLY close + Map *map=Sender->GetCurrentArea(); + if (!map) { + Sender->ReleaseCurrentAction(); + return; + } + + if (PersonalDistance(Sender, tar)>10 ) { + MoveNearerTo(Sender, tar, 10); + return; + } + + if (scr->GetStat(IE_EA)>EA_EVILCUTOFF) { + displaymsg->DisplayConstantString(STR_PICKPOCKET_EVIL,0xffffff); + Sender->ReleaseCurrentAction(); + return; + } + + int skill = snd->GetStat(IE_PICKPOCKET); + int tgt = scr->GetStat(IE_PICKPOCKET); + //the original engine has no random here + if (tgt != 255) { + skill -= tgt; + //if you want original behaviour: remove this + skill += core->Roll(1,100, snd->GetStat(IE_LUCK) ); + } else { + skill = 0; + } + //and change this 50 to 0. + if (skill<50) { + //noticed attempt + displaymsg->DisplayConstantString(STR_PICKPOCKET_FAIL,0xffffff); + if (core->HasFeature(GF_STEAL_IS_ATTACK) ) { + tar->LastAttacker = snd->GetGlobalID(); + } else { + //pickpocket failed trigger + tar->LastOpenFailed = snd->GetGlobalID(); + } + Sender->ReleaseCurrentAction(); + return; + } + + int ret = MIC_NOITEM; + if ((RandomNumValue&3) || (scr->GetStat(IE_GOLD)<=0) ) { + int slot = scr->inventory.FindStealableItem(); + if (slot) { + CREItem *item = scr->inventory.RemoveItem(slot); + ret = snd->inventory.AddSlotItem(item, SLOT_ONLYINVENTORY); + if (ret!=ASI_SUCCESS) { + map->AddItemToLocation(snd->Pos, item); + ret = MIC_FULL; + } + } + } + + if (ret==MIC_NOITEM) { + int money=0; + //go for money too + if (scr->GetStat(IE_GOLD)>0) { + money=RandomNumValue%(scr->GetStat(IE_GOLD)+1); + } + if (!money) { + //no stuff to steal + displaymsg->DisplayConstantString(STR_PICKPOCKET_NONE,0xffffff); + Sender->ReleaseCurrentAction(); + return; + } + CREItem *item = new CREItem(); + CreateItemCore(item, core->GoldResRef, money, 0, 0); + if ( ASI_SUCCESS == snd->inventory.AddSlotItem(item, SLOT_ONLYINVENTORY)) { + scr->SetBase(IE_GOLD,scr->GetBase(IE_GOLD)-money); + } else { + // drop it at my feet + map->AddItemToLocation(Sender->Pos, item); + if (((Actor *)Sender)->InParty) displaymsg->DisplayConstantString(STR_INVFULL_ITEMDROP, 0xbcefbc); + Sender->ReleaseCurrentAction(); + return; + } + } + + displaymsg->DisplayConstantString(STR_PICKPOCKET_DONE,0xffffff); + DisplayStringCore(snd, VB_PP_SUCC, DS_CONSOLE|DS_CONST ); + Sender->ReleaseCurrentAction(); +} + +void GameScript::TakeItemList(Scriptable * Sender, Action* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar || tar->Type!=ST_ACTOR) { + return; + } + AutoTable tab(parameters->string0Parameter); + if (!tab) { + return; + } + + int rows = tab->GetRowCount(); + for (int i=0;iQueryField(i,0), 0, IE_INV_ITEM_UNSTEALABLE); + } +} + +void GameScript::TakeItemListParty(Scriptable * Sender, Action* parameters) +{ + AutoTable tab(parameters->string0Parameter); + if (!tab) { + return; + } + Game *game = core->GetGame(); + int rows = tab->GetRowCount(); + for (int i=0;iGetPartySize(false); + while (j--) { + Actor *tar = game->GetPC(j, false); + MoveItemCore(tar, Sender, tab->QueryField(i,0), 0, IE_INV_ITEM_UNSTEALABLE); + } + } +} + +void GameScript::TakeItemListPartyNum(Scriptable * Sender, Action* parameters) +{ + AutoTable tab(parameters->string0Parameter); + if (!tab) { + return; + } + Game *game = core->GetGame(); + int rows = tab->GetRowCount(); + int count = parameters->int0Parameter; + for (int i=0;iGetPartySize(false); + while (j--) { + Actor *tar = game->GetPC(j, false); + int res=MoveItemCore(tar, Sender, tab->QueryField(i,0), 0, IE_INV_ITEM_UNSTEALABLE); + if (res==MIC_GOTITEM) { + j++; + count--; + } + if (!count) break; + } + } + if (count == 1) { + // grant the default table item to the Sender in regular games + Action *params = new Action(true); + sprintf(params->string0Parameter, "%s", tab->QueryField(9999,9999)); + CreateItem(Sender, params); + delete params; + } +} + +//bg2 +void GameScript::SetRestEncounterProbabilityDay(Scriptable* Sender, Action* parameters) +{ + Map *map=Sender->GetCurrentArea(); + map->RestHeader.DayChance = (ieWord) parameters->int0Parameter; +} + +void GameScript::SetRestEncounterProbabilityNight(Scriptable* Sender, Action* parameters) +{ + Map *map=Sender->GetCurrentArea(); + map->RestHeader.NightChance = (ieWord) parameters->int0Parameter; +} + +//iwd +void GameScript::SetRestEncounterChance(Scriptable * Sender, Action* parameters) +{ + Map *map=Sender->GetCurrentArea(); + map->RestHeader.DayChance = (ieWord) parameters->int0Parameter; + map->RestHeader.NightChance = (ieWord) parameters->int1Parameter; +} + +//easily hardcoded end sequence +void GameScript::EndCredits(Scriptable* /*Sender*/, Action* /*parameters*/) +{ + core->PlayMovie("credits"); +} + +//easily hardcoded end sequence +void GameScript::ExpansionEndCredits(Scriptable* /*Sender*/, Action* /*parameters*/) +{ + core->PlayMovie("ecredit"); +} + +//always quits game, but based on game it can play end animation, or display +//death text, etc +//this covers: +//QuitGame (play two of 3 movies in PST, display death screen with strref) +//EndGame (display death screen with strref) +void GameScript::QuitGame(Scriptable* Sender, Action* parameters) +{ + ClearAllActions(Sender, parameters); + core->GetDictionary()->SetAt("QuitGame1", (ieDword) parameters->int0Parameter); + core->GetDictionary()->SetAt("QuitGame2", (ieDword) parameters->int1Parameter); + core->GetDictionary()->SetAt("QuitGame3", (ieDword) parameters->int2Parameter); + core->SetNextScript("QuitGame"); +} + +void GameScript::StopMoving(Scriptable* Sender, Action* /*parameters*/) +{ + if (Sender->Type!=ST_ACTOR) { + return; + } + Actor *actor = (Actor *) Sender; + actor->ClearPath(); +} + +void GameScript::ApplyDamage(Scriptable* Sender, Action* parameters) +{ + Actor *damagee; + Actor *damager; + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar || tar->Type!=ST_ACTOR) { + return; + } + damagee = (Actor *) tar; + if (Sender->Type==ST_ACTOR) { + damager=(Actor *) Sender; + } else { + damager=damagee; + } + damagee->Damage(parameters->int0Parameter, parameters->int1Parameter, damager); +} + +void GameScript::ApplyDamagePercent(Scriptable* Sender, Action* parameters) +{ + Actor *damagee; + Actor *damager; + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar || tar->Type!=ST_ACTOR) { + return; + } + damagee = (Actor *) tar; + if (Sender->Type==ST_ACTOR) { + damager=(Actor *) Sender; + } else { + damager=damagee; + } + damagee->Damage(damagee->GetBase(IE_HITPOINTS)*parameters->int0Parameter/100, parameters->int1Parameter, damager); +} + +void GameScript::Damage(Scriptable* Sender, Action* parameters) +{ + Actor *damagee; + Actor *damager; + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar || tar->Type!=ST_ACTOR) { + return; + } + damagee = (Actor *) tar; + if (Sender->Type==ST_ACTOR) { + damager=(Actor *) Sender; + } else { + damager=damagee; + } + int damage = damagee->LuckyRoll( (parameters->int1Parameter>>12)&15, (parameters->int1Parameter>>4)&255, parameters->int1Parameter&15, 0, 1, damager); + int type=MOD_ADDITIVE; + switch(parameters->int0Parameter) { + case 2: //raise + damage=-damage; + break; + case 3: //set + type=MOD_ABSOLUTE; + break; + case 4: // + type=MOD_PERCENT; + break; + } + damagee->Damage( damage, type, damager ); +} +/* +void GameScript::SetHomeLocation(Scriptable* Sender, Action* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar || tar->Type!=ST_ACTOR) { + return; + } + Movable *movable = (Movable *) tar; //not actor, though it is the only moveable + movable->Destination = parameters->pointParameter; + //no movement should be started here, i think +} +*/ + +void GameScript::SetMasterArea(Scriptable* /*Sender*/, Action* parameters) +{ + core->GetGame()->SetMasterArea(parameters->string0Parameter); +} + +void GameScript::Berserk(Scriptable* Sender, Action* /*parameters*/) +{ + if (Sender->Type!=ST_ACTOR) { + return; + } + Actor *act = (Actor *) Sender; + act->SetBaseBit(IE_STATE_ID, STATE_BERSERK, true); + act->Panic(NULL, PANIC_BERSERK); +} + +void GameScript::Panic(Scriptable* Sender, Action* /*parameters*/) +{ + if (Sender->Type!=ST_ACTOR) { + return; + } + Actor *act = (Actor *) Sender; + act->Panic(NULL, PANIC_RANDOMWALK); +} + +/* as of now: removes panic and berserk */ +void GameScript::Calm(Scriptable* Sender, Action* /*parameters*/) +{ + if (Sender->Type!=ST_ACTOR) { + return; + } + Actor *act = (Actor *) Sender; + act->SetBaseBit(IE_STATE_ID, STATE_BERSERK|STATE_PANIC, false); +} + +void GameScript::RevealAreaOnMap(Scriptable* /*Sender*/, Action* parameters) +{ + WorldMap *worldmap = core->GetWorldMap(); + if (!worldmap) { + printf("Can't find worldmap!\n"); + abort(); + } + // WMP_ENTRY_ADJACENT because otherwise revealed bg2 areas are unreachable from city gates + worldmap->SetAreaStatus(parameters->string0Parameter, WMP_ENTRY_VISIBLE|WMP_ENTRY_ADJACENT, BM_OR); + displaymsg->DisplayConstantString(STR_WORLDMAPCHANGE, 0xc8ffc8); +} + +void GameScript::HideAreaOnMap( Scriptable* /*Sender*/, Action* parameters) +{ + WorldMap *worldmap = core->GetWorldMap(); + if (!worldmap) { + printf("Can't find worldmap!\n"); + abort(); + } + // WMP_ENTRY_ADJACENT because otherwise revealed bg2 areas are unreachable from city gates + worldmap->SetAreaStatus(parameters->string0Parameter, WMP_ENTRY_VISIBLE|WMP_ENTRY_ADJACENT, BM_NAND); +} + +void GameScript::SendTrigger(Scriptable* Sender, Action* parameters) +{ + Scriptable *tar = GetActorFromObject( Sender, parameters->objects[1], GA_NO_DEAD ); + if (!tar) { + return; + } + tar->TriggerID=parameters->int0Parameter; +} + +void GameScript::Shout( Scriptable* Sender, Action* parameters) +{ + if (Sender->Type!=ST_ACTOR) { + return; + } + //according to IESDP silenced creatures cannot use shout + Actor *actor = (Actor *) Sender; + if (actor->GetStat( IE_STATE_ID) & STATE_SILENCED) { + return; + } + Map *map=Sender->GetCurrentArea(); + //max. shouting distance, please adjust it if you know better + map->Shout(actor, parameters->int0Parameter, MAX_TRAVELING_DISTANCE); +} + +void GameScript::GlobalShout( Scriptable* Sender, Action* parameters) +{ + if (Sender->Type!=ST_ACTOR) { + return; + } + //according to IESDP silenced creatures cannot use shout + Actor *actor = (Actor *) Sender; + if (actor->GetStat( IE_STATE_ID) & STATE_SILENCED) { + return; + } + Map *map=Sender->GetCurrentArea(); + // 0 means unlimited shout distance + map->Shout(actor, parameters->int0Parameter, 0); +} + +void GameScript::Help( Scriptable* Sender, Action* /*parameters*/) +{ + if (Sender->Type!=ST_ACTOR) { + return; + } + Map *map=Sender->GetCurrentArea(); + map->Shout((Actor *) Sender, 0, 40); +} + +void GameScript::GiveOrder(Scriptable* Sender, Action* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (tar) { + tar->LastOrderer = Sender->GetGlobalID(); + tar->LastOrder = parameters->int0Parameter; + } +} + +void GameScript::AddMapnote( Scriptable* Sender, Action* parameters) +{ + Map *map=Sender->GetCurrentArea(); + char *str = core->GetString( parameters->int0Parameter, 0); + map->AddMapNote(parameters->pointParameter, parameters->int1Parameter, str, parameters->int0Parameter); +} + +void GameScript::RemoveMapnote( Scriptable* Sender, Action* parameters) +{ + Map *map=Sender->GetCurrentArea(); + map->RemoveMapNote(parameters->pointParameter); +} + +void GameScript::AttackOneRound( Scriptable* Sender, Action* parameters) +{ + if (Sender->Type != ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + //using auto target! + Scriptable* tar; + /*if (!parameters->objects[1]) { + GameControl *gc = core->GetGameControl(); + tar = gc->GetTarget(); + } else {*/ + tar = GetStoredActorFromObject( Sender, parameters->objects[1], GA_NO_DEAD ); + /*}*/ + if (!tar || (tar->Type != ST_ACTOR && tar->Type !=ST_DOOR && tar->Type !=ST_CONTAINER) ) { + Sender->ReleaseCurrentAction(); + return; + } + + //actor is already incapable of attack + if (Sender->GetInternalFlag()&IF_STOPATTACK) { + Sender->ReleaseCurrentAction(); + return; + } + + if (!Sender->CurrentActionState) { + Sender->CurrentActionState = core->Time.round_size; + } + + AttackCore(Sender, tar, 0); + + if (Sender->CurrentActionState == 1) { + //this is the LastDisarmFailed field, but this is an actor + //Sender->LastTarget = 0; + Sender->ReleaseCurrentAction(); + } else { + Sender->CurrentActionState--; + } +} + +void GameScript::RunningAttackNoSound( Scriptable* Sender, Action* parameters) +{ + if (Sender->Type != ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + //using auto target! + Scriptable* tar; + /*if (!parameters->objects[1]) { + GameControl *gc = core->GetGameControl(); + tar = gc->GetTarget(); + } else {*/ + tar = GetStoredActorFromObject( Sender, parameters->objects[1], GA_NO_DEAD ); + /*}*/ + if (!tar || (tar->Type != ST_ACTOR && tar->Type !=ST_DOOR && tar->Type !=ST_CONTAINER) ) { + Sender->ReleaseCurrentAction(); + return; + } + + //actor is already incapable of attack + if (Sender->GetInternalFlag()&IF_STOPATTACK) { + Sender->ReleaseCurrentAction(); + return; + } + + AttackCore(Sender, tar, AC_NO_SOUND|AC_RUNNING); +} + +void GameScript::AttackNoSound( Scriptable* Sender, Action* parameters) +{ + if (Sender->Type != ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + //using auto target! + Scriptable* tar; + /*if (!parameters->objects[1]) { + GameControl *gc = core->GetGameControl(); + tar = gc->GetTarget(); + } else {*/ + tar = GetStoredActorFromObject( Sender, parameters->objects[1], GA_NO_DEAD ); + /*}*/ + if (!tar || (tar->Type != ST_ACTOR && tar->Type !=ST_DOOR && tar->Type !=ST_CONTAINER) ) { + Sender->ReleaseCurrentAction(); + return; + } + + //actor is already incapable of attack + if (Sender->GetInternalFlag()&IF_STOPATTACK) { + Sender->ReleaseCurrentAction(); + return; + } + + AttackCore(Sender, tar, AC_NO_SOUND); +} + +void GameScript::RunningAttack( Scriptable* Sender, Action* parameters) +{ + if (Sender->Type != ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + //using auto target! + Scriptable* tar; + /*if (!parameters->objects[1]) { + GameControl *gc = core->GetGameControl(); + tar = gc->GetTarget(); + } else {*/ + tar = GetStoredActorFromObject( Sender, parameters->objects[1], GA_NO_DEAD ); + /*}*/ + if (!tar || (tar->Type != ST_ACTOR && tar->Type !=ST_DOOR && tar->Type !=ST_CONTAINER) ) { + Sender->ReleaseCurrentAction(); + return; + } + + //actor is already incapable of attack + if (Sender->GetInternalFlag()&IF_STOPATTACK) { + Sender->ReleaseCurrentAction(); + return; + } + + AttackCore(Sender, tar, AC_RUNNING); +} + +void GameScript::Attack( Scriptable* Sender, Action* parameters) +{ + if (Sender->Type != ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + //using auto target! + Scriptable* tar; + tar = GetStoredActorFromObject( Sender, parameters->objects[1], GA_NO_DEAD ); + + if (!tar || (tar->Type != ST_ACTOR && tar->Type !=ST_DOOR && tar->Type !=ST_CONTAINER) || tar == Sender) { + Sender->ReleaseCurrentAction(); + return; + } + + //actor is already incapable of attack + if (Sender->GetInternalFlag()&IF_STOPATTACK) { + Sender->ReleaseCurrentAction(); + return; + } + + AttackCore(Sender, tar, 0); +} + +void GameScript::ForceAttack( Scriptable* Sender, Action* parameters) +{ + Scriptable* scr = GetActorFromObject( Sender, parameters->objects[1], GA_NO_DEAD ); + if (!scr || scr->Type != ST_ACTOR) { + return; + } + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[2], GA_NO_DEAD ); + if (!tar || (tar->Type != ST_ACTOR && tar->Type !=ST_DOOR && tar->Type !=ST_CONTAINER) ) { + return; + } + //this is a hack, we use a gui variable for our own hideous reasons? + if (tar->Type==ST_ACTOR) { + GameControl *gc = core->GetGameControl(); + if (gc) { + //saving the target object ID from the gui variable + char Tmp[40]; + strncpy(Tmp,"NIDSpecial3()",sizeof(Tmp) ); + scr->AddAction( GenerateActionDirect(Tmp, (Actor *) tar) ); + } + } else { + char Tmp[80]; + snprintf(Tmp, sizeof(Tmp), "BashDoor(%s)", tar->GetScriptName()); + scr->AddAction ( GenerateAction(Tmp) ); + } +} + +void GameScript::AttackReevaluate( Scriptable* Sender, Action* parameters) +{ + if (Sender->Type != ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + + if (!Sender->CurrentActionState) { + Sender->CurrentActionState = parameters->int0Parameter; + // TODO: reevaluate target (set CurrentActionTarget to 0) if we are not actively in combat + } + + Scriptable* tar; + tar = GetStoredActorFromObject( Sender, parameters->objects[1], GA_NO_DEAD ); + if (!tar || (tar->Type != ST_ACTOR && tar->Type !=ST_DOOR && tar->Type !=ST_CONTAINER) ) { + Sender->ReleaseCurrentAction(); + return; + } + + //actor is already incapable of attack + if (Sender->GetInternalFlag()&IF_STOPATTACK) { + Sender->ReleaseCurrentAction(); + return; + } + + AttackCore(Sender, tar, 0); + + Sender->CurrentActionState--; +} + +void GameScript::Explore( Scriptable* Sender, Action* /*parameters*/) +{ + Sender->GetCurrentArea( )->Explore(-1); +} + +void GameScript::UndoExplore( Scriptable* Sender, Action* /*parameters*/) +{ + Sender->GetCurrentArea( )->Explore(0); +} + +void GameScript::ExploreMapChunk( Scriptable* Sender, Action* parameters) +{ + Map *map = Sender->GetCurrentArea(); + /* + There is a mode flag in int1Parameter, but i don't know what is it, + our implementation uses it for LOS=1, or no LOS=0 + ExploreMapChunk will reveal both visibility/explored map, but the + visibility will fade in the next update cycle (which is quite frequent) + */ + map->ExploreMapChunk(parameters->pointParameter, parameters->int0Parameter, parameters->int1Parameter); +} + +void GameScript::StartStore( Scriptable* Sender, Action* parameters) +{ + if (core->GetCurrentStore() ) { + return; + } + core->SetCurrentStore( parameters->string0Parameter, Sender->GetGlobalID()); + core->SetEventFlag(EF_OPENSTORE); + //sorry, i have absolutely no idea when i should do this :) + Sender->ReleaseCurrentAction(); +} + +//The integer parameter is a GemRB extension, if set to 1, the player +//gains experience for learning the spell +void GameScript::AddSpecialAbility( Scriptable* Sender, Action* parameters) +{ + if (Sender->Type != ST_ACTOR) { + return; + } + Actor *actor = (Actor *) Sender; + actor->LearnSpell (parameters->string0Parameter, parameters->int0Parameter|LS_MEMO|LS_LEARN); + core->SetEventFlag(EF_ACTION); +} + +//actually this just depletes a spell, doesn't remove it from the book +//GemRB extension: the first/second int parameter can also make it removed +//from the spell memorization schedule (also from the spellbook) +void GameScript::RemoveSpell( Scriptable* Sender, Action* parameters) +{ + ieResRef spellres; + int type; + + if (Sender->Type!=ST_ACTOR) { + return; + } + if (!ResolveSpellName( spellres, parameters) ) { + return; + } + Actor *actor = (Actor *) Sender; + if (parameters->string0Parameter[0]) { + type = parameters->int1Parameter; + } else { + type = parameters->int0Parameter; + } + if (type==2) { + //remove spell from both book and memorization + actor->spellbook.RemoveSpell(spellres); + return; + } + //type == 1 remove spell only from memorization + //type == 0 original behaviour: deplete only + actor->spellbook.UnmemorizeSpell(spellres, type); +} + +void GameScript::SetScriptName( Scriptable* Sender, Action* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar || tar->Type!=ST_ACTOR) { + return; + } + tar->SetScriptName(parameters->string0Parameter); +} + +//iwd2 +//advance time with a constant +//This is in seconds according to IESDP +void GameScript::AdvanceTime(Scriptable* /*Sender*/, Action* parameters) +{ + core->GetGame()->AdvanceTime(parameters->int0Parameter*1000/AI_UPDATE_TIME); +} + +//advance at least one day, then stop at next day/dusk/night/morning +//oops, not TimeODay is used but Time (this means we got hours) +//i'm not sure if we should add a whole day either, needs more research +void GameScript::DayNight(Scriptable* /*Sender*/, Action* parameters) +{ + // first, calculate the current number of hours. + int padding = ((core->GetGame()->GameTime / AI_UPDATE_TIME) % 7200) / 300; + // then, calculate the offset (in hours) required to take us to the desired hour. + padding = (24 + parameters->int0Parameter - padding) % 24; + // then, advance one day (7200), plus the desired number of hours. + core->GetGame()->AdvanceTime(AI_UPDATE_TIME*(7200 + padding*300)); +} + +//implement pst style parameters: +//suggested dream - unused +//if suggested dream is 0, then area flags determine the 'movie' +//hp - number of hps healed +//renting - crashes pst, we simply ignore it +void GameScript::RestParty(Scriptable* Sender, Action* parameters) +{ + Game *game = core->GetGame(); + game->RestParty(REST_NOAREA|REST_NOMOVE|REST_NOCRITTER, parameters->int0Parameter, parameters->int1Parameter); + Sender->ReleaseCurrentAction(); +} + +//doesn't advance game time, just refreshes spells of target +//this is a non-blocking action +void GameScript::Rest(Scriptable* Sender, Action* /*parameters*/) +{ + if (Sender->Type!=ST_ACTOR) { + return; + } + Actor *actor = (Actor *) Sender; + actor->spellbook.ChargeAllSpells(); + //check if this should be a full heal + actor->Heal(0); + actor->fxqueue.RemoveExpiredEffects(0xffffffff); +} + +//doesn't advance game time (unsure), just refreshes spells of target +void GameScript::RestNoSpells(Scriptable* Sender, Action* /*parameters*/) +{ + if (Sender->Type!=ST_ACTOR) { + return; + } + Actor *actor = (Actor *) Sender; + //check if this should be a full heal + actor->Heal(0); + actor->fxqueue.RemoveExpiredEffects(0xffffffff); +} + +//this is most likely advances time +void GameScript::RestUntilHealed(Scriptable* Sender, Action* /*parameters*/) +{ + if (Sender->Type!=ST_ACTOR) { + return; + } + Actor *actor = (Actor *) Sender; + actor->Heal(1); + //not sure if this should remove timed effects + //more like execute them hour by hour :> +} + +//iwd2 +//removes all delayed/duration/semi permanent effects (like a ctrl-r) +void GameScript::ClearPartyEffects(Scriptable* /*Sender*/, Action* /*parameters*/) +{ + Game *game = core->GetGame(); + int i = game->GetPartySize(false); + while (i--) { + Actor *tar = game->GetPC(i, false); + tar->fxqueue.RemoveExpiredEffects(0xffffffff); + } +} + +//iwd2 removes effects from a single sprite +void GameScript::ClearSpriteEffects(Scriptable* Sender, Action* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar || tar->Type!=ST_ACTOR) { + return; + } + Actor *actor = (Actor *) tar; + actor->fxqueue.RemoveExpiredEffects(0xffffffff); +} + +//IWD2 special, can mark only actors, hope it is enough +void GameScript::MarkObject(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type != ST_ACTOR) { + return; + } + //unsure, could mark dead objects? + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1], GA_NO_DEAD ); + if (!tar || tar->Type!=ST_ACTOR) { + return; + } + Actor *actor = (Actor *) Sender; + actor->LastMarked = tar->GetGlobalID(); + //if this doesn't modify LastSeen, then remove this line + actor->LastSeen = actor->LastMarked; +} + +void GameScript::MarkSpellAndObject(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type != ST_ACTOR) { + return; + } + Actor *me = (Actor *) Sender; + if (me->LastMarkedSpell) { + return; + } + + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1]); + Actor *actor = NULL; + if (tar->Type == ST_ACTOR) { + actor = (Actor *) tar; + } + + int flags = parameters->int0Parameter; + if (!(flags & MSO_IGNORE_NULL) && !actor) { + return; + } + if (!(flags & MSO_IGNORE_INVALID) && actor && actor->InvalidSpellTarget() ) { + return; + } + if (!(flags & MSO_IGNORE_SEE) && actor && !CanSee(Sender, actor, true, 0) ) { + return; + } + int len = strlen(parameters->string0Parameter); + // + if (len&3) { + return; + } + len/=4; + int max = len; + int pos; + if (flags & MSO_RANDOM_SPELL) { + pos = core->Roll(1,len,0); + } else { + pos = 0; + } + while(len--) { + char spl[5]; + + memcpy(spl, parameters->string0Parameter+pos*4, 4); + spl[4]=0; + int splnum = atoi(spl); + + if (!(flags & MSO_IGNORE_HAVE) && !me->spellbook.HaveSpell(splnum, 0) ) { + goto end_mso_loop; + } + int range; + if ((flags & MSO_IGNORE_RANGE) || !actor) { + range = 0; + } else { + range = Distance(me, actor); + } + if (!(flags & MSO_IGNORE_INVALID) && actor->InvalidSpellTarget(splnum, me, range)) { + goto end_mso_loop; + } + //mark spell and target + me->LastMarkedSpell = splnum; + me->LastMarked = actor->GetGlobalID(); + break; +end_mso_loop: + pos++; + if (pos==max) { + pos = 0; + } + } +} + +void GameScript::ForceMarkedSpell(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type != ST_ACTOR) { + return; + } + Actor *actor = (Actor *) Sender; + actor->LastMarkedSpell = parameters->int0Parameter; +} + +void GameScript::SetMarkedSpell(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type != ST_ACTOR) { + return; + } + Actor *actor = (Actor *) Sender; + if (parameters->int0Parameter) { + if (actor->LastMarkedSpell) { + return; + } + if (!actor->spellbook.HaveSpell(parameters->int0Parameter, 0) ) { + return; + } + } + + //TODO: check if spell exists (not really important) + actor->LastMarkedSpell = parameters->int0Parameter; + return; +} + +void GameScript::SetDialogueRange(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type != ST_ACTOR) { + return; + } + Actor *actor = (Actor *) Sender; + actor->SetBase( IE_DIALOGRANGE, parameters->int0Parameter ); +} + +void GameScript::SetGlobalTint(Scriptable* /*Sender*/, Action* parameters) +{ + core->GetVideoDriver()->SetFadeColor(parameters->int0Parameter, parameters->int1Parameter, parameters->int2Parameter); +} + +void GameScript::SetArmourLevel(Scriptable* Sender, Action* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar || tar->Type!=ST_ACTOR) { + return; + } + Actor *actor = (Actor *) Sender; + actor->SetBase( IE_ARMOR_TYPE, parameters->int0Parameter ); +} + +void GameScript::RandomWalk(Scriptable* Sender, Action* /*parameters*/) +{ + if (Sender->Type != ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + Actor* actor = ( Actor* ) Sender; + actor->RandomWalk( true, false ); + Sender->ReleaseCurrentAction(); +} + +void GameScript::RandomRun(Scriptable* Sender, Action* /*parameters*/) +{ + if (Sender->Type != ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + Actor* actor = ( Actor* ) Sender; + actor->RandomWalk( true, true ); + Sender->ReleaseCurrentAction(); +} + +void GameScript::RandomWalkContinuous(Scriptable* Sender, Action* /*parameters*/) +{ + if (Sender->Type != ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + Actor* actor = ( Actor* ) Sender; + actor->RandomWalk( false, false ); +} + +void GameScript::RandomFly(Scriptable* Sender, Action* /*parameters*/) +{ + if (Sender->Type != ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + Actor* actor = ( Actor* ) Sender; + int x = rand()&31; + if (x<10) { + actor->SetOrientation(actor->GetOrientation()-1, false); + } else if (x>20) { + actor->SetOrientation(actor->GetOrientation()+1, false); + } + //fly in this direction for 5 steps + actor->MoveLine(5, GL_PASS, actor->GetOrientation() ); + //readding the action to the end of the queue + //Sender->AddAction( parameters ); + //Sender->ReleaseCurrentAction(); +} + +//UseContainer uses the predefined target (like Nidspecial1 dialog hack) +void GameScript::UseContainer(Scriptable* Sender, Action* /*parameters*/) +{ + if (Sender->Type != ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + Actor *actor = (Actor *)Sender; + Container *container = core->GetCurrentContainer(); + if (!container) { + printMessage("GameScript","No container selected!", YELLOW); + Sender->ReleaseCurrentAction(); + return; + } + + ieDword distance = PersonalDistance(Sender, container); + ieDword needed = MAX_OPERATING_DISTANCE; + if (container->Type==IE_CONTAINER_PILE) { + needed = 0; // less than a search square (width) + } + if (distance<=needed) + { + //check if the container is unlocked + if (!container->TryUnlock(actor)) { + //playsound can't open container + //display string, etc + displaymsg->DisplayConstantString(STR_CONTLOCKED,0xd7d7be,container); + Sender->ReleaseCurrentAction(); + return; + } + Actor *actor = (Actor *)Sender; + actor->SetModal(MS_NONE); + container->TriggerTrap(0, actor->GetGlobalID()); + core->SetCurrentContainer(actor, container, true); + Sender->ReleaseCurrentAction(); + return; + } + MoveNearerTo(Sender, container, needed); +} + +//call the usecontainer action in target (not used) +void GameScript::ForceUseContainer(Scriptable* Sender, Action* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar || tar->Type != ST_ACTOR) { + Sender->ReleaseCurrentAction(); //why blocking??? + return; + } + char Tmp[256]; + sprintf( Tmp, "UseContainer()"); + Action *newaction = GenerateAction(Tmp); + tar->AddActionInFront(newaction); + Sender->ReleaseCurrentAction(); //why blocking??? +} + +//these actions directly manipulate a game variable (as the original engine) +void GameScript::SetMazeEasier(Scriptable* Sender, Action* /*parameters*/) +{ + int value = CheckVariable( Sender, "MAZEDIFFICULTY","GLOBAL"); + if (value>0) { + SetVariable(Sender, "MAZEDIFFICULTY", "GLOBAL", value-1); + } +} + +void GameScript::SetMazeHarder(Scriptable* Sender, Action* /*parameters*/) +{ + int value = CheckVariable( Sender, "MAZEDIFFICULTY","GLOBAL"); + if (value<2) { + SetVariable(Sender, "MAZEDIFFICULTY", "GLOBAL", value+1); + } +} + +void GameScript::StartRainNow(Scriptable* /*Sender*/, Action* /*parameters*/) +{ + core->GetGame()->StartRainOrSnow( false, WB_RAIN|WB_LIGHTNING); +} + +void GameScript::Weather(Scriptable* /*Sender*/, Action* parameters) +{ + Game *game = core->GetGame(); + switch(parameters->int0Parameter & WB_FOG) { + case WB_NORMAL: + game->StartRainOrSnow( false, 0); + break; + case WB_RAIN: + game->StartRainOrSnow( true, WB_RAIN|WB_LIGHTNING); + break; + case WB_SNOW: + game->StartRainOrSnow( true, WB_SNOW); + break; + case WB_FOG: + game->StartRainOrSnow( true, WB_FOG); + break; + } +} + +void GameScript::CopyGroundPilesTo(Scriptable* Sender, Action* parameters) +{ + Map *map = Sender->GetCurrentArea(); + Map *othermap = core->GetGame()->GetMap( parameters->string0Parameter, false ); + if (!othermap) { + return; + } + map->CopyGroundPiles( othermap, parameters->pointParameter ); +} + +//iwd specific +void GameScript::PlayBardSong(Scriptable* Sender, Action* /*parameters*/) +{ + if (Sender->Type!=ST_ACTOR) { + return; + } + //actually this one must use int0Parameter to set a bardsong + Actor *actor = (Actor *) Sender; + actor->SetModal( MS_BATTLESONG); +} + +void GameScript::BattleSong(Scriptable* Sender, Action* /*parameters*/) +{ + if (Sender->Type!=ST_ACTOR) { + return; + } + Actor *actor = (Actor *) Sender; + actor->SetModal( MS_BATTLESONG); +} + +void GameScript::FindTraps(Scriptable* Sender, Action* /*parameters*/) +{ + if (Sender->Type!=ST_ACTOR) { + return; + } + Actor *actor = (Actor *) Sender; + actor->SetModal( MS_DETECTTRAPS); +} + +void GameScript::Hide(Scriptable* Sender, Action* /*parameters*/) +{ + if (Sender->Type!=ST_ACTOR) { + return; + } + Actor *actor = (Actor *) Sender; + + if (actor->TryToHide()) { + actor->SetModal(MS_STEALTH); + } + //TODO: expiry isn't instant (skill based transition?) + +} + +void GameScript::Turn(Scriptable* Sender, Action* /*parameters*/) +{ + if (Sender->Type!=ST_ACTOR) { + return; + } + Actor *actor = (Actor *) Sender; + + if (actor->Modified[IE_DISABLEDBUTTON] & (1<GetStat(IE_TURNUNDEADLEVEL); + if (skill < 1) return; + + actor->SetModal(MS_TURNUNDEAD); + +} + +void GameScript::TurnAMT(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type!=ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + Actor *actor = (Actor *) Sender; + actor->SetOrientation(actor->GetOrientation()+parameters->int0Parameter, true); + actor->SetWait( 1 ); + Sender->ReleaseCurrentAction(); // todo, blocking? +} + +void GameScript::RandomTurn(Scriptable* Sender, Action* /*parameters*/) +{ + if (Sender->Type!=ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + Actor *actor = (Actor *) Sender; + actor->SetOrientation(rand() % MAX_ORIENT, true); + actor->SetWait( 1 ); + Sender->ReleaseCurrentAction(); // todo, blocking? +} + +void GameScript::AttachTransitionToDoor(Scriptable* Sender, Action* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar || tar->Type != ST_DOOR) { + return; + } + Door* door = ( Door* ) tar; + strnspccpy(door->LinkedInfo, parameters->string0Parameter, 32); +} + +/*getting a handle of a temporary actor resource to copy its selected attributes*/ +void GameScript::ChangeAnimation(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type!=ST_ACTOR) { + return; + } + ChangeAnimationCore((Actor *) Sender, parameters->string0Parameter,1); +} + +void GameScript::ChangeAnimationNoEffect(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type!=ST_ACTOR) { + return; + } + ChangeAnimationCore((Actor *) Sender, parameters->string0Parameter,0); +} + +void GameScript::Polymorph(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type!=ST_ACTOR) { + return; + } + Actor *act = (Actor *) Sender; + act->SetBase(IE_ANIMATION_ID, parameters->int0Parameter); +} + +void GameScript::PolymorphCopy(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type!=ST_ACTOR) { + return; + } + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar || tar->Type!=ST_ACTOR) { + return; + } + PolymorphCopyCore((Actor *) tar, (Actor *) Sender, false); +} + +/* according to IESDP this only copies the animation ID */ +void GameScript::PolymorphCopyBase(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type!=ST_ACTOR) { + return; + } + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar || tar->Type!=ST_ACTOR) { + return; + } + Actor *act = (Actor *) Sender; + Actor *actor = (Actor *) tar; + act->SetBase(IE_ANIMATION_ID, actor->GetBase(IE_ANIMATION_ID) ); +} + +void GameScript::ExportParty(Scriptable* /*Sender*/, Action* parameters) +{ + char FileName[_MAX_PATH]; + + Game *game = core->GetGame(); + int i = game->GetPartySize(false); + while (i--) { + Actor *actor = game->GetPC(i, false); + snprintf(FileName,_MAX_PATH,"%s%d",parameters->string0Parameter,i+1); + core->WriteCharacter(FileName, actor); + } + displaymsg->DisplayConstantString(STR_EXPORTED, 0xbcefbc); +} + +void GameScript::SaveGame(Scriptable* /*Sender*/, Action* parameters) +{ + if (core->HasFeature(GF_STRREF_SAVEGAME)) { + const char *basename = "Auto-Save"; + AutoTable tab("savegame"); + if (tab) { + basename = tab->QueryDefault(); + } + char * str = core->GetString( parameters->int0Parameter, IE_STR_STRREFOFF); + char FolderName[_MAX_PATH]; + snprintf (FolderName, sizeof(FolderName), "%s - %s", basename, str); + core->FreeString( str ); + + core->GetSaveGameIterator()->CreateSaveGame(core->GetSaveGameIterator()->GetSaveGame(FolderName), FolderName); + } else { + core->GetSaveGameIterator()->CreateSaveGame(parameters->int0Parameter); + } +} + +/*EscapeAreaMove(S:Area*,I:X*,I:Y*,I:Face*)*/ +void GameScript::EscapeArea(Scriptable* Sender, Action* parameters) +{ + if (InDebug&ID_ACTIONS) { + printf("EscapeArea/EscapeAreaMove\n"); + } + if (Sender->Type!=ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + Map *map = Sender->GetCurrentArea(); + if (!map) { + Sender->ReleaseCurrentAction(); + return; + } + + Point p = Sender->Pos; + map->TMap->AdjustNearestTravel(p); + + if (parameters->string0Parameter[0]) { + Point q((short) parameters->int0Parameter, (short) parameters->int1Parameter); + EscapeAreaCore( Sender, p, parameters->string0Parameter, q, 0, parameters->int2Parameter ); + } else { + EscapeAreaCore( Sender, p, parameters->string0Parameter, p, EA_DESTROY, parameters->int0Parameter ); + } + //EscapeAreaCore will do its ReleaseCurrentAction + //Sender->ReleaseCurrentAction(); +} + +void GameScript::EscapeAreaNoSee(Scriptable* Sender, Action* parameters) +{ + if (InDebug&ID_ACTIONS) { + printf("EscapeAreaNoSee\n"); + } + if (Sender->Type!=ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + Map *map = Sender->GetCurrentArea(); + if (!map) { + Sender->ReleaseCurrentAction(); + return; + } + + Point p = Sender->Pos; + map->TMap->AdjustNearestTravel(p); + + if (parameters->string0Parameter[0]) { + Point q((short) parameters->int0Parameter, (short) parameters->int1Parameter); + EscapeAreaCore( Sender, p, parameters->string0Parameter, q, 0, parameters->int2Parameter ); + } else { + EscapeAreaCore( Sender, p, parameters->string0Parameter, p, EA_DESTROY|EA_NOSEE, parameters->int0Parameter ); + } + //EscapeAreaCore will do its ReleaseCurrentAction + //Sender->ReleaseCurrentAction(); +} + +void GameScript::EscapeAreaDestroy(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type!=ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + Map *map = Sender->GetCurrentArea(); + if (!map) { + Sender->ReleaseCurrentAction(); + return; + } + + //find nearest exit + Point p = Sender->Pos; + map->TMap->AdjustNearestTravel(p); + //EscapeAreaCore will do its ReleaseCurrentAction + EscapeAreaCore( Sender, p, parameters->string0Parameter, p, EA_DESTROY, parameters->int0Parameter ); +} + +/*EscapeAreaObjectMove(S:Area*,I:X*,I:Y*,I:Face*)*/ +void GameScript::EscapeAreaObject(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type!=ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + Map *map = Sender->GetCurrentArea(); + if (!map) { + Sender->ReleaseCurrentAction(); + return; + } + + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar) { + Sender->ReleaseCurrentAction(); + return; + } + Point p = tar->Pos; + if (parameters->string0Parameter[0]) { + Point q((short) parameters->int0Parameter, (short) parameters->int1Parameter); + EscapeAreaCore( Sender, p, parameters->string0Parameter, q, 0, parameters->int2Parameter ); + } else { + EscapeAreaCore( Sender, p, 0, p, EA_DESTROY, parameters->int0Parameter ); + } + //EscapeAreaCore will do its ReleaseCurrentAction +} + +//This one doesn't require the object to be seen? +//We don't have that feature yet, so this is the same as EscapeAreaObject +void GameScript::EscapeAreaObjectNoSee(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type!=ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + Map *map = Sender->GetCurrentArea(); + if (!map) { + Sender->ReleaseCurrentAction(); + return; + } + + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar) { + Sender->ReleaseCurrentAction(); + return; + } + Point p = tar->Pos; + Sender->SetWait(parameters->int0Parameter); + if (parameters->string0Parameter[0]) { + Point q((short) parameters->int0Parameter, (short) parameters->int1Parameter); + EscapeAreaCore( Sender, p, parameters->string0Parameter, q, 0, parameters->int2Parameter ); + } else { + EscapeAreaCore( Sender, p, 0, p, EA_DESTROY|EA_NOSEE, parameters->int0Parameter ); + } + //EscapeAreaCore will do its ReleaseCurrentAction +} + +//takes first fitting item from container at feet, doesn't seem to be working in the original engines +void GameScript::PickUpItem(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type!=ST_ACTOR) { + return; + } + Actor *scr = (Actor *) Sender; + Map *map = scr->GetCurrentArea(); + Container *c = map->GetPile(scr->Pos); + if (!c) { //this shouldn't happen, but lets prepare for the worst + return; + } + + //the following part is coming from GUISCript.cpp with trivial changes + int Slot = c->inventory.FindItem(parameters->string0Parameter, 0); + if (Slot<0) { + return; + } + int res = core->CanMoveItem(c->inventory.GetSlotItem(Slot) ); + if (!res) { //cannot move + return; + } + CREItem *item = c->RemoveItem(Slot,0); + if (!item) { + return; + } + if (res!=-1 && scr->InParty) { //it is gold and we got the party pool! + goto item_is_gold; + } + res = scr->inventory.AddSlotItem(item, SLOT_ONLYINVENTORY); + if (res !=ASI_SUCCESS) { //putting it back + c->AddItem(item); + } + return; +item_is_gold: //we take gold! + if (scr->InParty) { + core->GetGame()->PartyGold += res; + //if you want message here then use + //core->GetGame()->AddGold(res); + } else { + scr->SetBase( IE_GOLD, scr->GetBase(IE_GOLD) + res ); + } + delete item; +} + +void GameScript::ChangeStoreMarkup(Scriptable* /*Sender*/, Action* parameters) +{ + bool has_current = false; + ieResRef current; + ieDword owner; + + Store *store = core->GetCurrentStore(); + if (!store) { + store = core->SetCurrentStore(parameters->string0Parameter, 0); + } else { + if (strnicmp(store->Name, parameters->string0Parameter, 8) ) { + //not the current store, we need some dirty hack + has_current = true; + strnlwrcpy(current, store->Name, 8); + owner = store->GetOwnerID(); + } + } + store->BuyMarkup = parameters->int0Parameter; + store->SellMarkup = parameters->int1Parameter; + //additional markup, is this depreciation??? + store->DepreciationRate = parameters->int2Parameter; + if (has_current) { + //setting back old store (this will save our current store) + core->SetCurrentStore(current, owner); + } +} + +void GameScript::SetEncounterProbability(Scriptable* /*Sender*/, Action* parameters) +{ + WorldMap *wmap = core->GetWorldMap(parameters->string0Parameter); + if (!wmap) { + //no such starting area + return; + } + WMPAreaLink *link = wmap->GetLink(parameters->string0Parameter, parameters->string1Parameter); + if (!link) { + return; + } + link->EncounterChance = parameters->int0Parameter; +} + +void GameScript::SpawnPtActivate(Scriptable* Sender, Action* parameters) +{ + if (parameters->objects[1]) { + Map *map = Sender->GetCurrentArea(); + Spawn *spawn = map->GetSpawn(parameters->objects[1]->objectName); + if (spawn) { + spawn->Enabled = 1; + } + } +} + +void GameScript::SpawnPtDeactivate(Scriptable* Sender, Action* parameters) +{ + if (parameters->objects[1]) { + Map *map = Sender->GetCurrentArea(); + Spawn *spawn = map->GetSpawn(parameters->objects[1]->objectName); + if (spawn) { + spawn->Enabled = 0; + } + } +} + +void GameScript::SpawnPtSpawn(Scriptable* Sender, Action* parameters) +{ + if (parameters->objects[1]) { + Map *map = Sender->GetCurrentArea(); + Spawn *spawn = map->GetSpawn(parameters->objects[1]->objectName); + if (spawn) { + spawn->Enabled = 1; //??? maybe use an unconditionality flag + map->TriggerSpawn(spawn); + } + } +} + +void GameScript::ApplySpell(Scriptable* Sender, Action* parameters) +{ + ieResRef spellres; + + if (!ResolveSpellName( spellres, parameters) ) { + return; + } + + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar) { + return; + } + if (tar->Type==ST_ACTOR) { + //apply spell on target +/* + Actor *owner; + + if (Sender->Type==ST_ACTOR) { + owner = (Actor *) Sender; + } else { + owner = (Actor *) tar; + } +*/ + //core->ApplySpell(spellres, (Actor *) tar, owner, parameters->int1Parameter); + core->ApplySpell(spellres, (Actor *) tar, Sender, parameters->int1Parameter); + } else { + //no idea about this one +/* + Actor *owner; + + if (Sender->Type==ST_ACTOR) { + owner = (Actor *) Sender; + } else { + owner = NULL; + } +*/ + //apply spell on point + Point d; + GetPositionFromScriptable(tar, d, false); + //core->ApplySpellPoint(spellres, tar->GetCurrentArea(), d, owner, parameters->int1Parameter); + core->ApplySpellPoint(spellres, tar->GetCurrentArea(), d, Sender, parameters->int1Parameter); + } +} + +void GameScript::ApplySpellPoint(Scriptable* Sender, Action* parameters) +{ + ieResRef spellres; + Actor *owner; + + if (!ResolveSpellName( spellres, parameters) ) { + return; + } + + if (Sender->Type==ST_ACTOR) { + owner = (Actor *) Sender; + } else { + owner = NULL; + } + core->ApplySpellPoint(spellres, Sender->GetCurrentArea(), parameters->pointParameter, owner, parameters->int1Parameter); +} + +//this is a gemrb extension +//sets a variable to the stat value +void GameScript::GetStat(Scriptable* Sender, Action* parameters) +{ + ieDword value; + + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar || tar->Type!=ST_ACTOR) { + value = 0; + } else { + Actor* actor = ( Actor* ) tar; + value = actor->GetStat( parameters->int0Parameter ); + } + SetVariable( Sender, parameters->string0Parameter, value ); +} + +void GameScript::BreakInstants(Scriptable* Sender, Action* /*parameters*/) +{ + //don't do anything, apparently the point of this action is to + //delay the execution of further actions to the next AI cycle + Sender->SetWait(1); + Sender->ReleaseCurrentAction(); // this doesn't really need to block +} + +//an interesting improvement would be to pause game for a given duration +void GameScript::PauseGame(Scriptable* Sender, Action* /*parameters*/) +{ + GameControl *gc = core->GetGameControl(); + if (gc) { + gc->SetDialogueFlags(DF_FREEZE_SCRIPTS, BM_OR); + displaymsg->DisplayConstantString(STR_SCRIPTPAUSED,0xff0000); + } + // releasing this action allows actions to continue executing, + // so we force a wait + Sender->SetWait(1); + Sender->ReleaseCurrentAction(); // does this need to block? +} + +void GameScript::SetNoOneOnTrigger(Scriptable* Sender, Action* parameters) +{ + Scriptable* ip; + + if (!parameters->objects[1]) { + ip=Sender; + } else { + ip = Sender->GetCurrentArea()->TMap->GetInfoPoint(parameters->objects[1]->objectName); + } + if (!ip || (ip->Type!=ST_TRIGGER && ip->Type!=ST_TRAVEL && ip->Type!=ST_PROXIMITY)) { + printf("Script error: No Trigger Named \"%s\"\n", parameters->objects[1]->objectName); + return; + } + ip->LastEntered = 0; + ip->LastTrigger = 0; + ip->LastTriggerObject = 0; +} + +void GameScript::UseDoor(Scriptable* Sender, Action* parameters) +{ + GameControl *gc = core->GetGameControl(); + if (!gc) { + Sender->ReleaseCurrentAction(); + return; + } + + gc->ResetTargetMode(); + OpenDoor(Sender, parameters); + + Sender->ReleaseCurrentAction(); // this is blocking, OpenDoor is not +} + +//this will force bashing the door +void GameScript::BashDoor(Scriptable* Sender, Action* parameters) +{ + GameControl *gc = core->GetGameControl(); + if (!gc) { + Sender->ReleaseCurrentAction(); + return; + } + if (Sender->Type != ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + + Scriptable *target = GetActorFromObject(Sender, parameters->objects[1]); + TileMap *tmap = Sender->GetCurrentArea()->TMap; + Door *door = NULL; + Container *container = NULL; + Point pos; + if (target->Type == ST_DOOR) { + // FIXME: actually it chooses from two possible points + pos = target->Pos; + door = tmap->GetDoorByPosition(pos); + } else if(target->Type == ST_CONTAINER) { + pos = target->Pos; + container = tmap->GetContainerByPosition(pos); + } else { + Sender->ReleaseCurrentAction(); + return; + } + + // TODO: "sets a field in the door/container to 1" + + if (SquaredPersonalDistance(pos, Sender) > MAX_OPERATING_DISTANCE*MAX_OPERATING_DISTANCE) { + MoveNearerTo(Sender, pos, MAX_OPERATING_DISTANCE, 0); + return; + } + + gc->SetTargetMode(TARGET_MODE_ATTACK); //for bashing doors too + + // try to bash it + if (door) { + door->TryBashLock((Actor *) Sender); + } else if (container) { + container->TryBashLock((Actor *) Sender); + } + + Sender->ReleaseCurrentAction(); +} + +//pst action +void GameScript::ActivatePortalCursor(Scriptable* Sender, Action* parameters) +{ + Scriptable* ip; + + if (!parameters->objects[1]) { + ip=Sender; + } else { + ip = Sender->GetCurrentArea()->TMap->GetInfoPoint(parameters->objects[1]->objectName); + } + if (!ip) { + return; + } + if (ip->Type!=ST_PROXIMITY && ip->Type!=ST_TRAVEL) { + return; + } + InfoPoint *tar = (InfoPoint *) ip; + if (parameters->int0Parameter) { + tar->Trapped|=PORTAL_CURSOR; + } else { + tar->Trapped&=~PORTAL_CURSOR; + } +} + +//pst action +void GameScript::EnablePortalTravel(Scriptable* Sender, Action* parameters) +{ + Scriptable* ip; + + if (!parameters->objects[1]) { + ip=Sender; + } else { + ip = Sender->GetCurrentArea()->TMap->GetInfoPoint(parameters->objects[1]->objectName); + } + if (!ip) { + return; + } + if (ip->Type!=ST_PROXIMITY && ip->Type!=ST_TRAVEL) { + return; + } + InfoPoint *tar = (InfoPoint *) ip; + if (parameters->int0Parameter) { + tar->Trapped|=PORTAL_TRAVEL; + } else { + tar->Trapped&=~PORTAL_TRAVEL; + } +} + +//unhardcoded iwd action (for the forge entrance change) +void GameScript::ChangeDestination(Scriptable* Sender, Action* parameters) +{ + InfoPoint *ip = Sender->GetCurrentArea()->TMap->GetInfoPoint(parameters->objects[1]->objectName); + if (ip && (ip->Type==ST_TRAVEL) ) { + strnlwrcpy(ip->Destination, parameters->string0Parameter, 32); + } +} + +void GameScript::MoveCursorPoint(Scriptable* /*Sender*/, Action* parameters) +{ + core->GetVideoDriver()->MoveMouse(parameters->pointParameter.x, parameters->pointParameter.y); +} + +//false means, no talk +void GameScript::DialogueInterrupt(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type!=ST_ACTOR) { + return; + } + Actor* actor = ( Actor* ) Sender; + if ( parameters->int0Parameter != 0 ) { + actor->SetMCFlag(MC_NO_TALK, BM_NAND); + } else { + actor->SetMCFlag(MC_NO_TALK, BM_OR); + } +} + +void GameScript::EquipMostDamagingMelee(Scriptable* Sender, Action* /*parameters*/) +{ + if (Sender->Type!=ST_ACTOR) { + return; + } + Actor* actor = ( Actor* ) Sender; + actor->inventory.EquipBestWeapon(EQUIP_MELEE); +} + +void GameScript::EquipRanged(Scriptable* Sender, Action* /*parameters*/) +{ + if (Sender->Type!=ST_ACTOR) { + return; + } + Actor* actor = ( Actor* ) Sender; + actor->inventory.EquipBestWeapon(EQUIP_RANGED); +} + +//will equip best weapon regardless of range considerations +void GameScript::EquipWeapon(Scriptable* Sender, Action* /*parameters*/) +{ + if (Sender->Type!=ST_ACTOR) { + return; + } + Actor* actor = ( Actor* ) Sender; + actor->inventory.EquipBestWeapon(EQUIP_MELEE|EQUIP_RANGED); +} + +void GameScript::SetBestWeapon(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type!=ST_ACTOR) { + return; + } + + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar || tar->Type!=ST_ACTOR) { + return; + } + Actor* actor = ( Actor* ) Sender; + + Actor *target = (Actor *) tar; + if (PersonalDistance(actor,target)>(unsigned int) parameters->int0Parameter) { + actor->inventory.EquipBestWeapon(EQUIP_RANGED); + } else { + actor->inventory.EquipBestWeapon(EQUIP_MELEE); + } +} + +void GameScript::FakeEffectExpiryCheck(Scriptable* Sender, Action* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar || tar->Type!=ST_ACTOR) { + return; + } + Actor *target = (Actor *) tar; + target->fxqueue.RemoveExpiredEffects(parameters->int0Parameter); +} + +void GameScript::SetInterrupt(Scriptable* Sender, Action* parameters) +{ + if (parameters->int0Parameter) { + Sender->Interrupt(); + } else { + Sender->NoInterrupt(); + } +} + +void GameScript::SelectWeaponAbility(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type!=ST_ACTOR) { + return; + } + Actor *scr = (Actor *) Sender; + int slot = parameters->int0Parameter; + int wslot = scr->inventory.GetWeaponSlot(); + //weapon + if (core->QuerySlotType(slot)&SLOT_WEAPON) { + slot-=wslot; + if (slot<0 || slot>=MAX_QUICKWEAPONSLOT) { + return; + } + scr->SetEquippedQuickSlot(slot, parameters->int1Parameter); + return; + } + //quick item + wslot = scr->inventory.GetQuickSlot(); + if (core->QuerySlotType(slot)&SLOT_ITEM) { + slot-=wslot; + if (slot<0 || slot>=MAX_QUICKITEMSLOT) { + return; + } + if (scr->PCStats) { + scr->PCStats->QuickItemHeaders[slot]=(ieWord) parameters->int1Parameter; + } + } +} + +void GameScript::UseItem(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type!=ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + Scriptable* tar = GetStoredActorFromObject( Sender, parameters->objects[1] ); + if (!tar) { + Sender->ReleaseCurrentAction(); + return; + } + Actor *act = (Actor *) Sender; + int Slot; + ieDword header, flags; + ieResRef itemres; + + if (parameters->string0Parameter[0]) { + Slot = act->inventory.FindItem(parameters->string0Parameter, 0); + //this IS in the original game code (ability) + header = parameters->int0Parameter; + flags = parameters->int1Parameter; + } else { + Slot = parameters->int0Parameter; + //this is actually not in the original game code + header = parameters->int1Parameter; + flags = parameters->int2Parameter; + } + + if (Slot == -1) { + Sender->ReleaseCurrentAction(); + return; + } + + if (!ResolveItemName( itemres, act, Slot) ) { + Sender->ReleaseCurrentAction(); + return; + } + + unsigned int dist = GetItemDistance(itemres, header); + + if (PersonalDistance(tar->Pos, Sender) > dist) { + MoveNearerTo(Sender, tar, dist); + return; + } + + act->UseItem(Slot, header, tar, flags); + Sender->ReleaseCurrentAction(); +} + +void GameScript::UseItemPoint(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type!=ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + + Actor *act = (Actor *) Sender; + int Slot; + ieDword header; + ieResRef itemres; + ieDword flags; + + if (parameters->string0Parameter[0]) { + Slot = act->inventory.FindItem(parameters->string0Parameter, 0); + //this IS in the original game code (ability) + header = parameters->int0Parameter; + flags = parameters->int1Parameter; + } else { + Slot = parameters->int0Parameter; + //this is actually not in the original game code + header = parameters->int1Parameter; + flags = parameters->int2Parameter; + } + + if (Slot == -1) { + Sender->ReleaseCurrentAction(); + return; + } + + if (!ResolveItemName( itemres, act, Slot) ) { + Sender->ReleaseCurrentAction(); + return; + } + + unsigned int dist = GetItemDistance(itemres, header); + + if (PersonalDistance(parameters->pointParameter, Sender) > dist) { + MoveNearerTo(Sender, parameters->pointParameter, dist, 0); + return; + } + + act->UseItemPoint(Slot, header, parameters->pointParameter, flags); + Sender->ReleaseCurrentAction(); +} + +//addfeat will be able to remove feats too +//(the second int parameter is a bitmode) +void GameScript::AddFeat(Scriptable* Sender, Action* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar || tar->Type!=ST_ACTOR) { + return; + } + Actor *actor = (Actor *)tar; + actor->SetFeat(parameters->int0Parameter, parameters->int1Parameter); +} + +void GameScript::MatchHP(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type!=ST_ACTOR) { + return; + } + Actor *scr = (Actor *) Sender; + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar || tar->Type!=ST_ACTOR) { + return; + } + Actor *actor = (Actor *)tar; + switch (parameters->int0Parameter) { + case 1: //sadly the hpflags are not the same as stats + actor->SetBase(IE_HITPOINTS,scr->GetBase(IE_HITPOINTS)); + break; + case 0: + actor->SetBase(IE_MAXHITPOINTS, scr->GetBase(IE_MAXHITPOINTS)); + break; + default: //this is gemrb extension + actor->SetBase(parameters->int0Parameter, scr->GetBase(parameters->int0Parameter)); + break; + } +} + +void GameScript::ChangeColor(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type!=ST_ACTOR) { + return; + } + Actor *scr = (Actor *) Sender; + ieDword stat = parameters->int0Parameter; + if (stat<9 || stat>14) { + return; + } + stat += IE_COLORS - 9; + scr->SetBase(stat, (scr->GetBase(stat)&~255)|(parameters->int1Parameter&255)); +} + +//removes previous kit, adds new +void GameScript::AddKit(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type!=ST_ACTOR) { + return; + } + Actor *scr = (Actor *) Sender; + //remove previous kit stuff + scr->ApplyKit(true); + //this adds the current level abilities + scr->SetBase(IE_KIT, parameters->int0Parameter); + scr->ApplyKit(false); +} + +//doesn't remove old kit +void GameScript::AddSuperKit(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type!=ST_ACTOR) { + return; + } + Actor *scr = (Actor *) Sender; + scr->SetBase(IE_KIT, parameters->int0Parameter); + scr->ApplyKit(false); +} + +void GameScript::SetSelection(Scriptable* /*Sender*/, Action* parameters) +{ + GameControl *gc = core->GetGameControl(); + if (!gc) { + return; + } + gc->SelectActor(parameters->int0Parameter, parameters->int1Parameter); +} + +//this action is weird in the original game, because it overwrites ALL +//IDS stats. +//in this version, if a stat is set to 0, it won't change +//it will alter only the main IDS stats +void GameScript::ChangeAIType(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type!=ST_ACTOR) { + return; + } + Object *ob = parameters->objects[1]; + if (!ob) { + return; + } + Actor *scr = (Actor *) Sender; + for (int i=0;iobjectFields[i]; + if (!val) continue; + if (!strnicmp(ObjectIDSTableNames[i],"ea",8)) { + scr->SetBase(IE_EA, val); + continue; + } + if (!strnicmp(ObjectIDSTableNames[i],"general",8)) { + scr->SetBase(IE_GENERAL, val); + continue; + } + if (!strnicmp(ObjectIDSTableNames[i],"race",8)) { + scr->SetBase(IE_RACE, val); + continue; + } + if (!strnicmp(ObjectIDSTableNames[i],"class",8)) { + scr->SetBase(IE_CLASS, val); + continue; + } + if (!strnicmp(ObjectIDSTableNames[i],"gender",8)) { + scr->SetBase(IE_SEX, val); + continue; + } + if (!strnicmp(ObjectIDSTableNames[i],"specific",8)) { + scr->SetBase(IE_SPECIFIC, val); + continue; + } + if (!strnicmp(ObjectIDSTableNames[i],"align",8)) { + scr->SetBase(IE_ALIGNMENT, val); + continue; + } + } +} + +//same as MoveToPoint, but not blocking +void GameScript::Leader(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type != ST_ACTOR) { + return; + } + + char Tmp[256]; + + snprintf(Tmp, 256, "MoveToPoint([%d.%d])", parameters->pointParameter.x, parameters->pointParameter.y); + Action *newact = GenerateAction(Tmp); + Sender->AddAction(newact); +} + +//same as MoveToPointNoRecticle, but not blocking +void GameScript::Follow(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type != ST_ACTOR) { + return; + } + + char Tmp[256]; + + snprintf(Tmp, 256, "MoveToPointNoRecticle([%d.%d])", parameters->pointParameter.x, parameters->pointParameter.y); + Action *newact = GenerateAction(Tmp); + Sender->AddAction(newact); +} + +void GameScript::FollowCreature(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type!=ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + + Scriptable* tar = GetStoredActorFromObject( Sender, parameters->objects[1] ); + if (!tar || tar->Type!=ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + Actor *scr = (Actor *)Sender; + Actor *actor = (Actor *)tar; + scr->LastFollowed = actor->GetGlobalID(); + scr->FollowOffset.empty(); + if (!scr->InMove() || scr->Destination != actor->Pos) { + scr->WalkTo(actor->Pos, 0, 1); + } +} + +void GameScript::RunFollow(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type!=ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + + Scriptable* tar = GetStoredActorFromObject( Sender, parameters->objects[1] ); + if (!tar || tar->Type!=ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + Actor *scr = (Actor *)Sender; + Actor *actor = (Actor *)tar; + scr->LastFollowed = actor->GetGlobalID(); + scr->FollowOffset.empty(); + if (!scr->InMove() || scr->Destination != actor->Pos) { + scr->WalkTo(actor->Pos, IF_RUNNING, 1); + } +} + +void GameScript::ProtectPoint(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type!=ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + Actor *scr = (Actor *)Sender; + if (!scr->InMove() || scr->Destination != parameters->pointParameter) { + scr->WalkTo( parameters->pointParameter, 0, 1 ); + } + // we should handle 'Protect' here rather than just unblocking + Sender->ReleaseCurrentAction(); +} + +void GameScript::ProtectObject(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type!=ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + + Scriptable* tar = GetStoredActorFromObject( Sender, parameters->objects[1] ); + if (!tar || tar->Type!=ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + Actor *scr = (Actor *)Sender; + Actor *actor = (Actor *)tar; + scr->LastFollowed = actor->GetGlobalID(); + scr->LastProtected = actor->GetGlobalID(); + //not exactly range + scr->FollowOffset.x = parameters->int0Parameter; + scr->FollowOffset.y = parameters->int0Parameter; + if (!scr->InMove() || scr->Destination != tar->Pos) { + scr->WalkTo( tar->Pos, 0, MAX_OPERATING_DISTANCE ); + } + // we should handle 'Protect' here rather than just unblocking + Sender->ReleaseCurrentAction(); +} + +//keeps following the object in formation +void GameScript::FollowObjectFormation(Scriptable* Sender, Action* parameters) +{ + GameControl *gc = core->GetGameControl(); + if (!gc) { + Sender->ReleaseCurrentAction(); + return; + } + if (Sender->Type!=ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + + Scriptable* tar = GetStoredActorFromObject( Sender, parameters->objects[1] ); + if (!tar || tar->Type!=ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + Actor *scr = (Actor *)Sender; + Actor *actor = (Actor *)tar; + scr->LastFollowed = actor->GetGlobalID(); + ieDword formation = parameters->int0Parameter; + ieDword pos = parameters->int1Parameter; + scr->FollowOffset = gc->GetFormationOffset(formation, pos); + if (!scr->InMove() || scr->Destination != tar->Pos) { + scr->WalkTo( tar->Pos, 0, 1 ); + } + Sender->ReleaseCurrentAction(); +} + +//walks to a specific offset of target (quite like movetoobject) +void GameScript::Formation(Scriptable* Sender, Action* parameters) +{ + GameControl *gc = core->GetGameControl(); + if (!gc) { + Sender->ReleaseCurrentAction(); + return; + } + if (Sender->Type!=ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + Scriptable* tar = GetStoredActorFromObject( Sender, parameters->objects[1] ); + if (!tar) { + Sender->ReleaseCurrentAction(); + return; + } + Actor *scr = (Actor *)Sender; + ieDword formation = parameters->int0Parameter; + ieDword pos = parameters->int1Parameter; + Point FollowOffset = gc->GetFormationOffset(formation, pos); + FollowOffset.x+=tar->Pos.x; + FollowOffset.y+=tar->Pos.y; + if (!scr->InMove() || scr->Destination != FollowOffset) { + scr->WalkTo( FollowOffset, 0, 1 ); + } +} + +void GameScript::TransformItem(Scriptable* Sender, Action* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar || tar->Type!=ST_ACTOR) { + return; + } + TransformItemCore((Actor *)tar, parameters, true); +} + +void GameScript::TransformPartyItem(Scriptable* /*Sender*/, Action* parameters) +{ + Game *game = core->GetGame(); + int i = game->GetPartySize(false); + while (i--) { + Actor *tar = game->GetPC(i, false); + TransformItemCore(tar, parameters, true); + } +} + +void GameScript::TransformItemAll(Scriptable* Sender, Action* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar || tar->Type!=ST_ACTOR) { + return; + } + TransformItemCore((Actor *)tar, parameters, false); +} + +void GameScript::TransformPartyItemAll(Scriptable* /*Sender*/, Action* parameters) +{ + Game *game = core->GetGame(); + int i = game->GetPartySize(false); + while (i--) { + Actor *tar = game->GetPC(i, false); + TransformItemCore(tar, parameters, false); + } +} + +void GameScript::GeneratePartyMember(Scriptable* /*Sender*/, Action* parameters) +{ + AutoTable pcs("bios"); + if (!pcs) { + return; + } + const char* string = pcs->QueryField( parameters->int0Parameter, 0 ); + int pos = gamedata->LoadCreature(string,0,false); + if (pos<0) { + return; + } + Actor *actor = core->GetGame()->GetNPC(pos); + if (!actor) { + return; + } + actor->SetOrientation(parameters->int1Parameter, false); + actor->MoveTo(parameters->pointParameter); +} + +void GameScript::EnableFogDither(Scriptable* /*Sender*/, Action* /*parameters*/) +{ + core->FogOfWar|=FOG_DRAWFOG; +} + +void GameScript::DisableFogDither(Scriptable* /*Sender*/, Action* /*parameters*/) +{ + core->FogOfWar&=~FOG_DRAWFOG; +} + +void DeleteAllSpriteCovers() +{ + Game *game = core->GetGame(); + int i = game->GetPartySize(false); + while (i--) { + Selectable *tar = (Selectable *) game->GetPC(i, false); + tar->SetSpriteCover(NULL); + } +} + +void GameScript::EnableSpriteDither(Scriptable* /*Sender*/, Action* /*parameters*/) +{ + core->FogOfWar&=~FOG_DITHERSPRITES; + DeleteAllSpriteCovers(); +} + +void GameScript::DisableSpriteDither(Scriptable* /*Sender*/, Action* /*parameters*/) +{ + core->FogOfWar|=~FOG_DITHERSPRITES; + DeleteAllSpriteCovers(); +} + +//the PST crew apparently loved hardcoding stuff +ieResRef RebusResRef={"DABUS1"}; + +void GameScript::FloatRebus(Scriptable* Sender, Action* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar || tar->Type!=ST_ACTOR) { + return; + } + Actor *actor = (Actor *)tar; + RebusResRef[5]=(char) core->Roll(1,5,'0'); + ScriptedAnimation *vvc = gamedata->GetScriptedAnimation(RebusResRef, 0); + if (vvc) { + //setting the height + vvc->ZPos=actor->size*20; + vvc->PlayOnce(); + //maybe this needs setting up some time + vvc->SetDefaultDuration(20); + actor->AddVVCell(vvc); + } +} + +void GameScript::IncrementKillStat(Scriptable* Sender, Action* parameters) +{ + DataFileMgr * ini = core->GetBeastsINI(); + if (!ini) { + return; + } + char key[5]; + sprintf(key,"%d", parameters->int0Parameter); + const char *variable = ini->GetKeyAsString( key, "killvar", NULL ); + if (!variable) { + return; + } + ieDword value = CheckVariable( Sender, variable, "GLOBAL" ) + 1; + SetVariable( Sender, variable, "GLOBAL", value ); +} + +void GameScript::SpellCastEffect(Scriptable* Sender, Action* parameters) +{ + Scriptable* src = GetActorFromObject( Sender, parameters->objects[1] ); + if (!src) { + return; + } + //TODO: finish this +} + +//this action plays a vvc animation over target +//we simply apply the appropriate opcode on the target (see iwdopcodes) +//the list of vvcs is in iwdshtab.2da +EffectRef fx_iwd_visual_spell_hit_ref={"IWDVisualSpellHit",NULL,-1}; + +void GameScript::SpellHitEffectSprite(Scriptable* Sender, Action* parameters) +{ + Scriptable* src = GetActorFromObject( Sender, parameters->objects[1] ); + if (!src) { + return; + } + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[2] ); + if (!tar || tar->Type!=ST_ACTOR) { + return; + } + int opcode = EffectQueue::ResolveEffect(fx_iwd_visual_spell_hit_ref); + Effect *fx = core->GetEffect(opcode); + if (!fx) { + //invalid effect name didn't resolve to opcode + return; + } + + //vvc type + fx->Parameter2 = parameters->int0Parameter; + //height (not sure if this is in the opcode, but seems acceptable) + fx->Parameter1 = parameters->int1Parameter; + fx->Probability1=100; + fx->TimingMode=FX_DURATION_INSTANT_PERMANENT_AFTER_BONUSES; + core->ApplyEffect(fx, (Actor *) tar, src); +} + +void GameScript::SpellHitEffectPoint(Scriptable* Sender, Action* parameters) +{ + Scriptable* src = GetActorFromObject( Sender, parameters->objects[1] ); + if (!src) { + return; + } + + int opcode = EffectQueue::ResolveEffect(fx_iwd_visual_spell_hit_ref); + Effect *fx = core->GetEffect(opcode); + if (!fx) { + //invalid effect name didn't resolve to opcode + return; + } + + //vvc type + fx->Parameter2 = parameters->int0Parameter; + //height (not sure if this is in the opcode, but seems acceptable) + fx->Parameter1 = parameters->int1Parameter; + fx->Probability1=100; + fx->TimingMode=FX_DURATION_INSTANT_PERMANENT_AFTER_BONUSES; + fx->PosX=parameters->pointParameter.x; + fx->PosY=parameters->pointParameter.y; + core->ApplyEffect(fx, NULL, src); +} + + +void GameScript::ClickLButtonObject(Scriptable* Sender, Action* parameters) +{ + Scriptable *tar = GetActorFromObject(Sender, parameters->objects[1] ); + if (!tar) { + Sender->ReleaseCurrentAction(); // this is blocking for some reason? + return; + } + ClickCore(Sender, tar->Pos, GEM_MB_ACTION, parameters->int0Parameter); +} + +void GameScript::ClickLButtonPoint(Scriptable* Sender, Action* parameters) +{ + ClickCore(Sender, parameters->pointParameter, GEM_MB_ACTION, parameters->int0Parameter); +} + +void GameScript::ClickRButtonObject(Scriptable* Sender, Action* parameters) +{ + Scriptable *tar = GetActorFromObject(Sender, parameters->objects[1] ); + if (!tar) { + Sender->ReleaseCurrentAction(); // this is blocking for some reason? + return; + } + ClickCore(Sender, tar->Pos, GEM_MB_MENU, parameters->int0Parameter); +} + +void GameScript::ClickRButtonPoint(Scriptable* Sender, Action* parameters) +{ + ClickCore(Sender, parameters->pointParameter, GEM_MB_MENU, parameters->int0Parameter); +} + +void GameScript::DoubleClickLButtonObject(Scriptable* Sender, Action* parameters) +{ + Scriptable *tar = GetActorFromObject(Sender, parameters->objects[1] ); + if (!tar) { + Sender->ReleaseCurrentAction(); // this is blocking for some reason? + return; + } + ClickCore(Sender, tar->Pos, GEM_MB_ACTION|GEM_MB_DOUBLECLICK, parameters->int0Parameter); +} + +void GameScript::DoubleClickLButtonPoint(Scriptable* Sender, Action* parameters) +{ + ClickCore(Sender, parameters->pointParameter, GEM_MB_ACTION|GEM_MB_DOUBLECLICK, parameters->int0Parameter); +} + +void GameScript::DoubleClickRButtonObject(Scriptable* Sender, Action* parameters) +{ + Scriptable *tar = GetActorFromObject(Sender, parameters->objects[1] ); + if (!tar) { + Sender->ReleaseCurrentAction(); // this is blocking for some reason? + return; + } + ClickCore(Sender, tar->Pos, GEM_MB_MENU|GEM_MB_DOUBLECLICK, parameters->int0Parameter); +} + +void GameScript::DoubleClickRButtonPoint(Scriptable* Sender, Action* parameters) +{ + ClickCore(Sender, parameters->pointParameter, GEM_MB_MENU|GEM_MB_DOUBLECLICK, parameters->int0Parameter); +} + +//Picks 5 lines from wish.2da +//Gets the 5 values (column is int0parameter) from the table. +//Sets the five wishpowerNN to 1, while resets the rest to 0. +//TODO: investigate what happens with * values +void GameScript::SetupWish(Scriptable* Sender, Action* parameters) +{ + SetupWishCore(Sender, parameters->int0Parameter, parameters->int1Parameter); +} + +//The same as the previous action, except that the column parameter comes from +//the target object's wisdom directly (this action is not used in the original) +void GameScript::SetupWishObject(Scriptable* Sender, Action* parameters) +{ + Scriptable *tar = GetActorFromObject(Sender, parameters->objects[1] ); + if (!tar || tar->Type!=ST_ACTOR) { + return; + } + SetupWishCore(Sender, ((Actor *)tar)->GetStat(IE_WIS), parameters->int0Parameter); +} + +//GemRB specific action +//Sets up multiple tokens randomly (one per 2da row) +//the row label column sets the token names +void GameScript::SetToken2DA(Scriptable* /*Sender*/, Action* parameters) +{ + int count; + int i,j; + ieVariable tokenname; + + AutoTable tm(parameters->string0Parameter); + if (!tm) { + printStatus( "ERROR", LIGHT_RED ); + printf( "Cannot find %s.2da.\n", parameters->string0Parameter); + return; + } + + count = tm->GetRowCount(); + for(i=0;iRoll(1,tm->GetColumnCount(i),-1); + strnuprcpy(tokenname, tm->GetRowName(i), 32); + core->GetTokenDictionary()->SetAtCopy( tokenname, tm->QueryField(i, j) ); + } +} + +//this is a gemrb extension for scriptable tracks +void GameScript::SetTrackString(Scriptable* Sender, Action* parameters) +{ + Map *map = Sender->GetCurrentArea(); + if (!map) return; + map->SetTrackString(parameters->int0Parameter, parameters->int1Parameter, parameters->int2Parameter); +} + +void GameScript::StateOverrideFlag(Scriptable* /*Sender*/, Action* parameters) +{ + core->GetGame()->StateOverrideFlag = parameters->int0Parameter; +} + +void GameScript::StateOverrideTime(Scriptable* /*Sender*/, Action* parameters) +{ + core->GetGame()->StateOverrideTime = parameters->int0Parameter; +} + +void GameScript::BanterBlockFlag(Scriptable* /*Sender*/, Action* parameters) +{ + core->GetGame()->BanterBlockFlag = parameters->int0Parameter; +} + +void GameScript::BanterBlockTime(Scriptable* /*Sender*/, Action* parameters) +{ + core->GetGame()->BanterBlockTime = parameters->int0Parameter; +} + +void GameScript::SetNamelessDeath(Scriptable* Sender, Action* parameters) +{ + ieResRef area; + + snprintf(area,8,"AR%04d", parameters->int0Parameter); + IniSpawn *sp = Sender->GetCurrentArea()->INISpawn; + if (!sp) { + return; + } + sp->SetNamelessDeath(area, parameters->pointParameter, parameters->int1Parameter); +} diff --git a/project/jni/application/gemrb/src/core/GameScript/GSUtils.cpp b/project/jni/application/gemrb/src/core/GameScript/GSUtils.cpp new file mode 100644 index 000000000..f283c2d62 --- /dev/null +++ b/project/jni/application/gemrb/src/core/GameScript/GSUtils.cpp @@ -0,0 +1,2332 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003-2005 The GemRB Project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * + */ + +#include "GameScript/GSUtils.h" +#include "GameScript/Matching.h" + +#include "strrefs.h" +#include "defsounds.h" + +#include "Audio.h" +#include "DialogHandler.h" +#include "DisplayMessage.h" +#include "Game.h" +#include "GameData.h" +#include "Interface.h" +#include "Item.h" +#include "Map.h" +#include "Spell.h" +#include "StringMgr.h" +#include "TileMap.h" +#include "Video.h" +#include "GUI/GameControl.h" + +#include + +//these tables will get freed by Core +Holder triggersTable; +Holder actionsTable; +Holder overrideActionsTable; +Holder objectsTable; +TriggerFunction triggers[MAX_TRIGGERS]; +ActionFunction actions[MAX_ACTIONS]; +short actionflags[MAX_ACTIONS]; +short triggerflags[MAX_TRIGGERS]; +ObjectFunction objects[MAX_OBJECTS]; +IDSFunction idtargets[MAX_OBJECT_FIELDS]; +Cache SrcCache; //cache for string resources (pst) +Cache BcsCache; //cache for scripts +int ObjectIDSCount = 7; +int MaxObjectNesting = 5; +bool HasAdditionalRect = false; +bool HasTriggerPoint = false; +//released by ReleaseMemory +ieResRef *ObjectIDSTableNames; +int ObjectFieldsCount = 7; +int ExtraParametersCount = 0; +int InDebug = 0; +int happiness[3][20]; +int RandomNumValue; +int *SkillStats=NULL; +int SkillCount=-1; +// reaction modifiers (by reputation and charisma) +int rmodrep[20]; +int rmodchr[25]; +Gem_Polygon **polygons; + +void InitScriptTables() +{ + //initializing the skill->stats conversion table + { + AutoTable tab("skillsta"); + if (tab) { + int rowcount = tab->GetRowCount(); + SkillCount = rowcount; + if (rowcount) { + SkillStats = (int *) malloc(rowcount * sizeof(int) ); + while(rowcount--) { + SkillStats[rowcount]=strtol(tab->QueryField(rowcount,0), NULL, 0); + } + } + } + } + //initializing the happiness table + { + AutoTable tab("happy"); + if (tab) { + for (int alignment=0;alignment<3;alignment++) { + for (int reputation=0;reputation<20;reputation++) { + happiness[alignment][reputation]=strtol(tab->QueryField(reputation,alignment), NULL, 0); + } + } + } + } + + //initializing the reaction mod. reputation table + AutoTable rmr("rmodrep"); + if (rmr) { + for (int reputation=0; reputation<20; reputation++) { + rmodrep[reputation] = strtol(rmr->QueryField(0, reputation), NULL, 0); + } + } + + //initializing the reaction mod. charisma table + AutoTable rmc("rmodchr"); + if (rmc) { + for (int charisma=0; charisma<25; charisma++) { + rmodchr[charisma] = strtol(rmc->QueryField(0, charisma), NULL, 0); + } + } +} + +int GetReaction(Actor *target, Scriptable *Sender) +{ + int chr, rep, reaction; + chr = target->GetStat(IE_CHR)-1; + if (target->GetStat(IE_EA) == EA_PC) { + rep = core->GetGame()->Reputation/10; + } else { + rep = target->GetStat(IE_REPUTATION); + } + reaction = 10 + rmodrep[rep] + rmodchr[chr]; + + // add -4 penalty when dealing with racial enemies + if (Sender && target->GetRangerLevel() && Sender->Type == ST_ACTOR && target->IsRacialEnemy((Actor *)Sender)) { + reaction -= 4; + } + + return reaction; +} + +int GetHappiness(Scriptable* Sender, int reputation) +{ + if (Sender->Type != ST_ACTOR) { + return 0; + } + Actor* ab = ( Actor* ) Sender; + int alignment = ab->GetStat(IE_ALIGNMENT)&AL_GE_MASK; //good / evil + if (reputation > 200) { + reputation = 200; + } + return happiness[alignment][reputation/10-1]; +} + +int GetHPPercent(Scriptable* Sender) +{ + if (Sender->Type != ST_ACTOR) { + return 0; + } + Actor* ab = ( Actor* ) Sender; + int hp1 = ab->GetStat(IE_MAXHITPOINTS); + if (hp1<1) { + return 0; + } + int hp2 = ab->GetBase(IE_HITPOINTS); + if (hp2<1) { + return 0; + } + return hp2*100/hp1; +} + +void HandleBitMod(ieDword &value1, ieDword value2, int opcode) +{ + switch(opcode) { + case BM_AND: + value1 = ( value1& value2 ); + break; + case BM_OR: + value1 = ( value1| value2 ); + break; + case BM_XOR: + value1 = ( value1^ value2 ); + break; + case BM_NAND: //this is a GemRB extension + value1 = ( value1& ~value2 ); + break; + case BM_SET: //this is a GemRB extension + value1 = value2; + break; + } +} + +// SPIT is not in the original engine spec, it is reserved for the +// enchantable items feature +// 0 1 2 3 4 +static const char *spell_suffices[]={"SPIT","SPPR","SPWI","SPIN","SPCL"}; + +//this function handles the polymorphism of Spell[RES] actions +//it returns spellres +bool ResolveSpellName(ieResRef spellres, Action *parameters) +{ + if (parameters->string0Parameter[0]) { + strnlwrcpy(spellres, parameters->string0Parameter, 8); + } else { + //resolve spell + int type = parameters->int0Parameter/1000; + int spellid = parameters->int0Parameter%1000; + if (type>4) { + return false; + } + sprintf(spellres, "%s%03d", spell_suffices[type], spellid); + } + return gamedata->Exists(spellres, IE_SPL_CLASS_ID); +} + +void ResolveSpellName(ieResRef spellres, ieDword number) +{ + //resolve spell + unsigned int type = number/1000; + int spellid = number%1000; + if (type>4) { + type=0; + } + sprintf(spellres, "%s%03d", spell_suffices[type], spellid); +} + +ieDword ResolveSpellNumber(const ieResRef spellres) +{ + int i; + + for(i=0;i<5;i++) { + if(!strnicmp(spellres, spell_suffices[i], 4)) { + int n = -1; + sscanf(spellres+4,"%d", &n); + if (n<0) { + return 0xffffffff; + } + return i*1000+n; + } + } + return 0xffffffff; +} + +bool ResolveItemName(ieResRef itemres, Actor *act, ieDword Slot) +{ + CREItem *itm = act->inventory.GetSlotItem(Slot); + if(itm) { + strnlwrcpy(itemres, itm->ItemResRef, 8); + return gamedata->Exists(itemres, IE_ITM_CLASS_ID); + } + return false; +} + +bool StoreHasItemCore(const ieResRef storename, const ieResRef itemname) +{ + bool had_nostore=false; + bool has_current=false; + ieResRef current; + ieDword owner = 0; + CREItem item; + + Store *store = core->GetCurrentStore(); + if (!store) { + had_nostore = true; + store = core->SetCurrentStore(storename, 0); + } else { + if (strnicmp(store->Name, storename, 8) ) { + //not the current store, we need some dirty hack + has_current = true; + strnlwrcpy(current, store->Name, 8); + owner = store->GetOwnerID(); + } + } + if (!store) { + printMessage("GameScript","Store cannot be opened!\n", LIGHT_RED); + return false; + } + + bool ret = false; + //don't use triggers (pst style), it would be possible to create infinite loops + if (store->FindItem(itemname, false) != (unsigned int)-1) { + ret=true; + } + if (has_current) { + //setting back old store (this will save our current store) + core->SetCurrentStore(current, owner); + } else if (had_nostore) { + core->CloseCurrentStore(); + } + return ret; +} + +//don't pass this point by reference, it is subject to change +void ClickCore(Scriptable *Sender, Point point, int type, int speed) +{ + Map *map = Sender->GetCurrentArea(); + if (!map) { + Sender->ReleaseCurrentAction(); + return; + } + Point p=map->TMap->GetMapSize(); + if (!p.PointInside(point)) { + Sender->ReleaseCurrentAction(); + return; + } + Video *video = core->GetVideoDriver(); + GlobalTimer *timer = core->timer; + timer->SetMoveViewPort( point.x, point.y, speed, true ); + timer->DoStep(0); + if (timer->ViewportIsMoving()) { + Sender->AddActionInFront( Sender->GetCurrentAction() ); + Sender->SetWait(1); + Sender->ReleaseCurrentAction(); + return; + } + + video->ConvertToScreen(point.x, point.y); + GameControl *win = core->GetGameControl(); + + point.x+=win->XPos; + point.y+=win->YPos; + video->MoveMouse(point.x, point.y); + video->ClickMouse(type); + Sender->ReleaseCurrentAction(); +} + +void TransformItemCore(Actor *actor, Action *parameters, bool onlyone) +{ + int i = actor->inventory.GetSlotCount(); + while(i--) { + CREItem *item = actor->inventory.GetSlotItem(i); + if (!item) { + continue; + } + if (strnicmp(item->ItemResRef, parameters->string0Parameter, 8) ) { + continue; + } + actor->inventory.SetSlotItemRes(parameters->string1Parameter,i,parameters->int0Parameter,parameters->int1Parameter,parameters->int2Parameter); + if (onlyone) { + break; + } + } +} + +//check if an inventory (container or actor) has item (could be recursive ?) +bool HasItemCore(Inventory *inventory, const ieResRef itemname, ieDword flags) +{ + if (inventory->HasItem(itemname, flags)) { + return true; + } + int i=inventory->GetSlotCount(); + while (i--) { + //maybe we could speed this up if we mark bag items with a flags bit + CREItem *itemslot = inventory->GetSlotItem(i); + if (!itemslot) + continue; + Item *item = gamedata->GetItem(itemslot->ItemResRef); + if (!item) + continue; + bool ret = false; + if (core->CanUseItemType(SLOT_BAG,item,NULL) ) { + //the store is the same as the item's name + ret = StoreHasItemCore(itemslot->ItemResRef, itemname); + } + gamedata->FreeItem(item, itemslot->ItemResRef); + if (ret) { + return true; + } + } + return false; +} + +void DisplayStringCore(Scriptable* Sender, int Strref, int flags) +{ + StringBlock sb; + char Sound[_MAX_PATH]; + + //no one hears you when you are in the Limbo! + if (!Sender->GetCurrentArea()) { + return; + } + + memset(&sb,0,sizeof(sb)); + Sound[0]=0; + printf( "Displaying string on: %s\n", Sender->GetScriptName() ); + if (flags & DS_CONST) { + if (Sender->Type!=ST_ACTOR) { + printMessage("GameScript","Verbal constant not supported for non actors!\n", LIGHT_RED); + return; + } + Actor* actor = ( Actor* ) Sender; + if ((ieDword) Strref>=VCONST_COUNT) { + printMessage("GameScript","Invalid verbal constant!\n", LIGHT_RED); + return; + } + + int tmp=(int) actor->GetVerbalConstant(Strref); + if (tmp <= 0 || (actor->GetStat(IE_MC_FLAGS) & MC_EXPORTABLE)) { + //get soundset based string constant + actor->ResolveStringConstant( sb.Sound, (unsigned int) Strref); + if (actor->PCStats && actor->PCStats->SoundFolder[0]) { + snprintf(Sound, _MAX_PATH, "%s/%s", + actor->PCStats->SoundFolder, sb.Sound); + } else { + memcpy(Sound, sb.Sound, sizeof(ieResRef) ); + } + } + Strref = tmp; + + //display the verbal constants in the console + ieDword charactersubtitles = 0; + core->GetDictionary()->Lookup("Subtitles", charactersubtitles); + if (charactersubtitles) { + flags |= DS_CONSOLE; + } + } + + if ((Strref != -1) && !sb.Sound[0]) { + sb = core->strings->GetStringBlock( Strref ); + memcpy(Sound, sb.Sound, sizeof(ieResRef) ); + if (sb.text[0] && strcmp(sb.text," ") && (flags & DS_CONSOLE)) { + //can't play the sound here, we have to delay action + //and for that, we have to know how long the text takes + if(flags&DS_NONAME) { + displaymsg->DisplayString( sb.text ); + } else { + displaymsg->DisplayStringName( Strref, 0xf0f0f0, Sender, 0); + } + } + if (sb.text[0] && strcmp(sb.text," ") && (flags & (DS_HEAD | DS_AREA))) { + Sender->DisplayHeadText( sb.text ); + //don't free sb.text, it is residing in Sender + if (flags & DS_AREA) { + Sender->FixHeadTextPos(); + } + } else { + core->FreeString( sb.text ); + } + } + if (Sound[0] && !(flags&DS_SILENT) ) { + ieDword speech = GEM_SND_RELATIVE; //disable position + if (flags&DS_SPEECH) speech|=GEM_SND_SPEECH; + unsigned int len = 0; + core->GetAudioDrv()->Play( Sound,0,0,speech,&len ); + ieDword counter = ( AI_UPDATE_TIME * len ) / 1000; + if ((counter != 0) && (flags &DS_WAIT) ) + Sender->SetWait( counter ); + } +} + +int CanSee(Scriptable* Sender, Scriptable* target, bool range, int seeflag) +{ + Map *map; + + if (target->Type==ST_ACTOR) { + Actor *tar = (Actor *) target; + + if (!tar->ValidTarget(seeflag)) { + return 0; + } + } + + map = target->GetCurrentArea(); + //if (!(seeflag&GA_GLOBAL)) { + if (!map || map!=Sender->GetCurrentArea() ) { + return 0; + } + //} + + if (range) { + unsigned int dist; + + if (Sender->Type == ST_ACTOR) { + Actor* snd = ( Actor* ) Sender; + dist = snd->Modified[IE_VISUALRANGE]; + } else { + dist = 30; + } + + if (Distance(target->Pos, Sender->Pos) > dist * 15) { + return 0; + } + } + + return map->IsVisible(target->Pos, Sender->Pos); +} + +//non actors can see too (reducing function to LOS) +//non actors can be seen too (reducing function to LOS) +int SeeCore(Scriptable* Sender, Trigger* parameters, int justlos) +{ + //see dead + int flags; + + if (parameters->int0Parameter) { + flags = GA_DETECT; + } else { + flags = GA_NO_DEAD; + } + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter, flags ); + /* don't set LastSeen if this isn't an actor */ + if (!tar) { + return 0; + } + //both are actors + if (CanSee(Sender, tar, true, flags) ) { + if (justlos) { + return 1; + } + if (Sender->Type==ST_ACTOR && tar->Type==ST_ACTOR) { + Actor* snd = ( Actor* ) Sender; + //additional checks for invisibility? + snd->LastSeen = tar->GetGlobalID(); + } + return 1; + } + return 0; +} + +//transfering item from Sender to target +//if target has no inventory, the item will be destructed +//if target can't get it, it will be dropped at its feet +int MoveItemCore(Scriptable *Sender, Scriptable *target, const char *resref, int flags, int setflag) +{ + Inventory *myinv; + Map *map; + // track whether we are dealing with our party and need to display feedback + bool lostitem = false; + bool gotitem = false; + + if (!target) { + return MIC_INVALID; + } + map=Sender->GetCurrentArea(); + switch(Sender->Type) { + case ST_ACTOR: + myinv=&((Actor *) Sender)->inventory; + if (((Actor *)Sender)->InParty) lostitem = true; + break; + case ST_CONTAINER: + myinv=&((Container *) Sender)->inventory; + break; + default: + return MIC_INVALID; + } + CREItem *item; + myinv->RemoveItem(resref, flags, &item); + if (!item) { + // nothing was removed + return MIC_NOITEM; + } + + item->Flags|=setflag; + + switch(target->Type) { + case ST_ACTOR: + myinv=&((Actor *) target)->inventory; + if (((Actor *) target)->InParty) gotitem = true; + break; + case ST_CONTAINER: + myinv=&((Container *) target)->inventory; + break; + default: + myinv = NULL; + break; + } + if (!myinv) { + delete item; + if (lostitem) displaymsg->DisplayConstantString(STR_LOSTITEM, 0xbcefbc); + return MIC_GOTITEM; // actually it was lost, not gained + } + if ( myinv->AddSlotItem(item, SLOT_ONLYINVENTORY) !=ASI_SUCCESS) { + // drop it at my feet + map->AddItemToLocation(target->Pos, item); + if (gotitem) displaymsg->DisplayConstantString(STR_INVFULL_ITEMDROP, 0xbcefbc); + return MIC_FULL; + } + if (gotitem) displaymsg->DisplayConstantString(STR_GOTITEM, 0xbcefbc); + return MIC_GOTITEM; +} + +/*FIXME: what is 'base'*/ +void PolymorphCopyCore(Actor *src, Actor *tar, bool base) +{ + tar->SetBase(IE_ANIMATION_ID, src->GetStat(IE_ANIMATION_ID) ); + if (!base) { + tar->SetBase(IE_ARMOR_TYPE, src->GetStat(IE_ARMOR_TYPE) ); + for (int i=0;i<7;i++) { + tar->SetBase(IE_COLORS+i, src->GetStat(IE_COLORS+i) ); + } + } + tar->SetName(src->GetName(0),0); + tar->SetName(src->GetName(1),1); + //add more attribute copying +} + +void CreateCreatureCore(Scriptable* Sender, Action* parameters, int flags) +{ + Scriptable *tmp = GetActorFromObject( Sender, parameters->objects[1] ); + //if there is nothing to copy, don't spawn anything + if (flags & CC_COPY) { + if (!tmp || tmp->Type != ST_ACTOR) { + return; + } + } + + Actor* ab; + if (flags & CC_STRING1) { + ab = gamedata->GetCreature(parameters->string1Parameter); + } + else { + ab = gamedata->GetCreature(parameters->string0Parameter); + } + + if (!ab) { + printMessage("GameScript","Failed to create creature! ",LIGHT_RED); + printf("(missing creature file %s?)\n", parameters->string0Parameter); + // maybe this should abort()? + return; + } + + //iwd2 allows an optional scriptname to be set + //but bg2 doesn't have this feature + //this way it works for both games + if ((flags & CC_SCRIPTNAME) && parameters->string1Parameter[0]) { + ab->SetScriptName(parameters->string1Parameter); + } + + int radius; + Point pnt; + + radius=0; + switch (flags & CC_MASK) { + //creates creature just off the screen + case CC_OFFSCREEN: + { + Region vp = core->GetVideoDriver()->GetViewport(); + radius=vp.w/2; //actually it must be further divided by the tile size, hmm 16? + } + //falling through + case CC_OBJECT://use object + offset + if (tmp) Sender=tmp; + //falling through + case CC_OFFSET://use sender + offset + pnt.x = parameters->pointParameter.x+Sender->Pos.x; + pnt.y = parameters->pointParameter.y+Sender->Pos.y; + break; + default: //absolute point, but -1,-1 means AtFeet + pnt.x = parameters->pointParameter.x; + pnt.y = parameters->pointParameter.y; + if (pnt.isempty()) { + pnt.x = Sender->Pos.x; + pnt.y = Sender->Pos.y; + } + break; + } + + Map *map = Sender->GetCurrentArea(); + map->AddActor( ab ); + ab->SetPosition( pnt, flags&CC_CHECK_IMPASSABLE, radius ); + ab->SetOrientation(parameters->int0Parameter, false ); + + //if string1 is animation, then we can't use it for a DV too + if (flags & CC_PLAY_ANIM) { + CreateVisualEffectCore( ab, ab->Pos, parameters->string1Parameter, 1); + } else { + //setting the deathvariable if it exists (iwd2) + if (parameters->string1Parameter[0]) { + ab->SetScriptName(parameters->string1Parameter); + } + } + + if (flags & CC_COPY) { + PolymorphCopyCore ( (Actor *) tmp, ab, false); + } +} + +static ScriptedAnimation *GetVVCEffect(const char *effect, int iterations) +{ + if (effect[0]) { + ScriptedAnimation* vvc = gamedata->GetScriptedAnimation(effect, false); + if (!vvc) { + printMessage("GameScript","Failed to create effect.",LIGHT_RED); + return NULL; + } + if (iterations) { + vvc->SetDefaultDuration( vvc->GetSequenceDuration(AI_UPDATE_TIME * iterations)); + } else { + vvc->PlayOnce(); + } + return vvc; + } + return NULL; +} + +void CreateVisualEffectCore(Actor *target, const char *effect, int iterations) +{ + ScriptedAnimation *vvc = GetVVCEffect(effect, iterations); + if (vvc) { + target->AddVVCell( vvc ); + } +} + +void CreateVisualEffectCore(Scriptable *Sender, const Point &position, const char *effect, int iterations) +{ + ScriptedAnimation *vvc = GetVVCEffect(effect, iterations); + if (vvc) { + vvc->XPos +=position.x; + vvc->YPos +=position.y; + Sender->GetCurrentArea( )->AddVVCell( vvc ); + } +} + +//this destroys the current actor and replaces it with another +void ChangeAnimationCore(Actor *src, const char *resref, bool effect) +{ + Actor *tar = gamedata->GetCreature(resref); + if (tar) { + Map *map = src->GetCurrentArea(); + map->AddActor( tar ); + Point pos = src->Pos; + tar->SetOrientation(src->GetOrientation(), false ); + src->DestroySelf(); + // can't SetPosition while the old actor is taking the spot + tar->SetPosition(pos, 1); + if (effect) { + CreateVisualEffectCore(tar, tar->Pos,"smokepuffeffect",1); + } + } +} + +void EscapeAreaCore(Scriptable* Sender, const Point &p, const char* area, const Point &enter, int flags, int wait) +{ + char Tmp[256]; + + if ( !p.isempty() && PersonalDistance(p, Sender)>MAX_OPERATING_DISTANCE) { + //MoveNearerTo will return 0, if the actor is in move + //it will return 1 (the fourth parameter) if the target is unreachable + if (!MoveNearerTo(Sender, p, MAX_OPERATING_DISTANCE,1) ) { + if (!Sender->InMove()) printf("At least it said so...\n"); + return; + } + } + + if (flags &EA_DESTROY) { + //this must be put into a non-const variable + sprintf( Tmp, "DestroySelf()" ); + } else { + // last parameter is 'face', which should be passed from relevant action parameter.. + sprintf( Tmp, "MoveBetweenAreas(\"%s\",[%hd.%hd],%d)", area, enter.x, enter.y, 0 ); + } + printMessage("GSUtils"," ", WHITE); + printf("Executing %s in EscapeAreaCore\n", Tmp); + //drop this action, but add another (destroyself or movebetweenareas) + //between the arrival and the final escape, there should be a wait time + //that wait time could be handled here + if (wait) { + printf("But wait a bit... (%d)\n", wait); + Sender->SetWait(wait); + } + Sender->ReleaseCurrentAction(); + Action * action = GenerateAction( Tmp); + Sender->AddActionInFront( action ); +} + +void GetTalkPositionFromScriptable(Scriptable* scr, Point &position) +{ + switch (scr->Type) { + case ST_AREA: case ST_GLOBAL: + position = scr->Pos; //fake + break; + case ST_ACTOR: + //if there are other moveables, put them here + position = ((Movable *) scr)->GetMostLikelyPosition(); + break; + case ST_TRIGGER: case ST_PROXIMITY: case ST_TRAVEL: + if (((InfoPoint *) scr)->Flags & TRAP_USEPOINT) { + position=((InfoPoint *) scr)->UsePoint; + break; + } + position=((InfoPoint *) scr)->TrapLaunch; + break; + case ST_DOOR: case ST_CONTAINER: + position=((Highlightable *) scr)->TrapLaunch; + break; + } +} + +void GetPositionFromScriptable(Scriptable* scr, Point &position, bool dest) +{ + if (!dest) { + position = scr->Pos; + return; + } + switch (scr->Type) { + case ST_AREA: case ST_GLOBAL: + position = scr->Pos; //fake + break; + case ST_ACTOR: + //if there are other moveables, put them here + position = ((Movable *) scr)->GetMostLikelyPosition(); + break; + case ST_TRIGGER: case ST_PROXIMITY: case ST_TRAVEL: + if (((InfoPoint *) scr)->Flags & TRAP_USEPOINT) { + position=((InfoPoint *) scr)->UsePoint; + break; + } + case ST_DOOR: case ST_CONTAINER: + position=((Highlightable *) scr)->TrapLaunch; + } +} + +int CheckInteract(const char *talker, const char *target) +{ + AutoTable interact("interact"); + if(!interact) + return 0; + const char *value = interact->QueryField(talker, target); + if(!value) + return 0; + switch(value[0]) { + case 's': + return I_SPECIAL; + case 'c': + return I_COMPLIMENT; + case 'i': + return I_INSULT; + } + return 0; +} + +static ieResRef PlayerDialogRes = "PLAYERx\0"; + +void BeginDialog(Scriptable* Sender, Action* parameters, int Flags) +{ + Scriptable* tar, *scr; + int seeflag = GA_NO_DEAD; + + if (InDebug&ID_VARIABLES) { + printf("BeginDialog core\n"); + } + if (Flags & BD_OWN) { + tar = GetStoredActorFromObject( Sender, parameters->objects[1], seeflag); + scr = tar; + } else { + tar = GetStoredActorFromObject( Sender, parameters->objects[1], seeflag); + scr = Sender; + } + if (!scr) { + printMessage("GameScript"," ",LIGHT_RED); + printf("Speaker for dialog couldn't be found (Sender: %s, Type: %d) Flags:%d.\n", Sender->GetScriptName(), Sender->Type, Flags); + Sender->ReleaseCurrentAction(); + return; + } + + if (!tar || tar->Type!=ST_ACTOR) { + printMessage("GameScript"," ",LIGHT_RED); + printf("Target for dialog couldn't be found (Sender: %s, Type: %d).\n", Sender->GetScriptName(), Sender->Type); + if (Sender->Type == ST_ACTOR) { + ((Actor *) Sender)->DebugDump(); + } + printf ("Target object: "); + if (parameters->objects[1]) { + parameters->objects[1]->Dump(); + } else { + printf("\n"); + } + Sender->ReleaseCurrentAction(); + return; + } + + Actor *speaker, *target; + + speaker = NULL; + target = (Actor *) tar; + if ((Flags & BD_CHECKDIST) && !CanSee(scr, target, false, seeflag) ) { + printMessage("GameScript"," ",LIGHT_RED); + printf("CanSee returned false! Speaker (%s, type %d) and target are:\n", scr->GetScriptName(), scr->Type); + if (scr->Type == ST_ACTOR) { + ((Actor *) scr)->DebugDump(); + } + ((Actor *) tar)->DebugDump(); + Sender->ReleaseCurrentAction(); + return; + } + bool swap = false; + if (scr->Type==ST_ACTOR) { + speaker = (Actor *) scr; + if (speaker->GetStat(IE_STATE_ID)&STATE_DEAD) { + printMessage("GameScript"," ",LIGHT_RED); + printf("Speaker is dead, cannot start dialogue. Speaker and target are:\n"); + speaker->DebugDump(); + target->DebugDump(); + Sender->ReleaseCurrentAction(); + return; + } + //DialogueRange is set in IWD + ieDword range = MAX_OPERATING_DISTANCE + speaker->GetBase(IE_DIALOGRANGE); + //making sure speaker is the protagonist, player, actor + if ( target->InParty == 1) swap = true; + else if ( speaker->InParty !=1 && target->InParty) swap = true; + //CHECKDIST works only for mobile scriptables + if (Flags&BD_CHECKDIST) { + if ( scr->GetCurrentArea()!=target->GetCurrentArea() || + PersonalDistance(scr, target)>range) { + MoveNearerTo(Sender, target, MAX_OPERATING_DISTANCE); + return; + } + } + } else { + //pst style dialog with trigger points + swap=true; + if (Flags&BD_CHECKDIST) { + Point TalkPos; + + if (target->InMove()) { + //waiting for target + Sender->AddActionInFront( Sender->GetCurrentAction() ); + Sender->ReleaseCurrentAction(); + Sender->SetWait(1); + return; + } + GetTalkPositionFromScriptable(scr, TalkPos); + if (PersonalDistance(TalkPos, target)>MAX_OPERATING_DISTANCE ) { + //try to force the target to come closer??? + GoNear(target, TalkPos); + Sender->AddActionInFront( Sender->GetCurrentAction() ); + Sender->ReleaseCurrentAction(); + Sender->SetWait(1); + return; + } + } + } + + GameControl* gc = core->GetGameControl(); + if (!gc) { + printMessage( "GameScript","Dialog cannot be initiated because there is no GameControl.", YELLOW ); + Sender->ReleaseCurrentAction(); + return; + } + //can't initiate dialog, because it is already there + if (gc->GetDialogueFlags()&DF_IN_DIALOG) { + if (Flags & BD_INTERRUPT) { + //break the current dialog if possible + gc->dialoghandler->EndDialog(true); + } + //check if we could manage to break it, not all dialogs are breakable! + if (gc->GetDialogueFlags()&DF_IN_DIALOG) { + printMessage( "GameScript","Dialog cannot be initiated because there is already one.", YELLOW ); + Sender->ReleaseCurrentAction(); + return; + } + } + + // starting a dialog ends cutscenes! + core->SetCutSceneMode(false); + + const char* Dialog = NULL; + AutoTable pdtable; + + switch (Flags & BD_LOCMASK) { + case BD_STRING0: + Dialog = parameters->string0Parameter; + if (Flags & BD_SETDIALOG) { + scr->SetDialog( Dialog ); + } + break; + case BD_SOURCE: + case BD_TARGET: + if (swap) Dialog = scr->GetDialog(); + else Dialog = target->GetDialog(GD_FEEDBACK); + break; + case BD_RESERVED: + //what if playerdialog was initiated from Player2? + PlayerDialogRes[5] = '1'; + Dialog = ( const char * ) PlayerDialogRes; + break; + case BD_INTERACT: //using the source for the dialog + const char* scriptingname = scr->GetScriptName(); + + /* use interact.2da for short, inlined dialogue */ + int type = CheckInteract(scriptingname, target->GetScriptName()); + if(type) { + //TODO increase interact counter in scr + speaker->Interact(type); + target->Response(type); + Sender->ReleaseCurrentAction(); + return; + } + /* banter dialogue */ + pdtable.load("interdia"); + //Dialog is a borrowed reference, we cannot free pdtable while it is being used + if (pdtable) { + Dialog = pdtable->QueryField( scriptingname, "FILE" ); + } + break; + } + + + //dialog is not meaningful + if (!Dialog || Dialog[0]=='*') { + Sender->ReleaseCurrentAction(); + return; + } + + //maybe we should remove the action queue, but i'm unsure + //no, we shouldn't even call this! + //Sender->ReleaseCurrentAction(); + + // moved this here from InitDialog, because InitDialog doesn't know which side is which + // post-swap (and non-actors always have IF_NOINT set) .. also added a check that it's + // actually busy doing something, for the same reason + if (target->GetInternalFlag()&IF_NOINT && (target->GetCurrentAction() || target->GetNextAction())) { + displaymsg->DisplayConstantString(STR_TARGETBUSY,0xff0000); + Sender->ReleaseCurrentAction(); + return; + } + + if (speaker!=target) { + if (swap) { + Scriptable *tmp = tar; + tar = scr; + scr = tmp; + } else { + if (!(Flags & BD_INTERRUPT)) { + // added CurrentAction as part of blocking action fixes + if (tar->GetCurrentAction() || tar->GetNextAction()) { + displaymsg->DisplayConstantString(STR_TARGETBUSY,0xff0000); + Sender->ReleaseCurrentAction(); + return; + } + } + } + } + + //don't clear target's actions, because a sequence like this will be broken: + //StartDialog([PC]); SetGlobal("Talked","LOCALS",1); + if (scr!=tar) { + if (scr->Type==ST_ACTOR) { + ((Actor *) scr)->SetOrientation(GetOrient( tar->Pos, scr->Pos), true); + } + if (tar->Type==ST_ACTOR) { + ((Actor *) tar)->SetOrientation(GetOrient( scr->Pos, tar->Pos), true); + } + } + + int ret; + + if (Dialog[0]) { + //increasing NumTimesTalkedTo or NumTimesInteracted + if (Flags & BD_TALKCOUNT) { + gc->SetDialogueFlags(DF_TALKCOUNT, BM_OR); + } else if ((Flags & BD_LOCMASK) == BD_INTERACT) { + gc->SetDialogueFlags(DF_INTERACT, BM_OR); + } + + core->GetDictionary()->SetAt("DialogChoose",(ieDword) -1); + ret = gc->dialoghandler->InitDialog( scr, tar, Dialog); + } + else { + ret = -1; + } + + if (ret<0) { + Sender->ReleaseCurrentAction(); + if (Flags & BD_NOEMPTY) { + return; + } + displaymsg->DisplayConstantStringName(STR_NOTHINGTOSAY,0xff0000,tar); + return; + } + + //this is a bit fishy + Sender->SetWait(1); + Sender->ReleaseCurrentAction(); + +} + +void MoveBetweenAreasCore(Actor* actor, const char *area, const Point &position, int face, bool adjust) +{ + printMessage("GameScript", " ", WHITE); + printf("MoveBetweenAreas: %s to %s [%d.%d] face: %d\n", actor->GetName(0), area,position.x,position.y, face); + Map* map2; + Game* game = core->GetGame(); + if (area[0]) { //do we need to switch area? + Map* map1 = actor->GetCurrentArea(); + //we have to change the pathfinder + //to the target area if adjust==true + map2 = game->GetMap(area, false); + if ( map1!=map2 ) { + if (map1) { + map1->RemoveActor( actor ); + } + map2->AddActor( actor ); + } + } + actor->SetPosition(position, adjust); + if (face !=-1) { + actor->SetOrientation( face, false ); + } + // should this perhaps be a 'selected' check or similar instead? + if (actor->InParty) { + GameControl *gc=core->GetGameControl(); + gc->SetScreenFlags(SF_CENTERONACTOR,BM_OR); + game->ChangeSong(false, true); + } +} + +//repeat movement, until goal isn't reached +//if int0parameter is !=0, then it will try only x times +void MoveToObjectCore(Scriptable *Sender, Action *parameters, ieDword flags, bool untilsee) +{ + if (Sender->Type != ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + Scriptable* target = GetStoredActorFromObject( Sender, parameters->objects[1] ); + if (!target) { + Sender->ReleaseCurrentAction(); + return; + } + Actor* actor = ( Actor* ) Sender; + if (untilsee && CanSee(actor, target, true, 0) ) { + Sender->ReleaseCurrentAction(); + return; + } else { + if (PersonalDistance(actor, target)ReleaseCurrentAction(); + return; + } + } + if (!actor->InMove() || actor->Destination != target->Pos) { + actor->WalkTo( target->Pos, flags, 0 ); + } + //hopefully this hack will prevent lockups + if (!actor->InMove()) { + Sender->ReleaseCurrentAction(); + return; + } + + //repeat movement... + Action *newaction = ParamCopyNoOverride(parameters); + if (newaction->int0Parameter!=1) { + if (newaction->int0Parameter) { + newaction->int0Parameter--; + } + actor->AddActionInFront(newaction); + actor->SetWait(1); + } + + Sender->ReleaseCurrentAction(); +} + +void CreateItemCore(CREItem *item, const char *resref, int a, int b, int c) +{ + strncpy(item->ItemResRef, resref, 8); + core->ResolveRandomItem(item); + if (a==-1) { + Item *origitem = gamedata->GetItem(resref); + for(int i=0;i<3;i++) { + ITMExtHeader *e = origitem->GetExtHeader(i); + item->Usages[i]=e?e->Charges:0; + } + gamedata->FreeItem(origitem, resref, false); + } else { + item->Usages[0]=(ieWord) a; + item->Usages[1]=(ieWord) b; + item->Usages[2]=(ieWord) c; + } + item->Flags=0; +} + +//It is possible to attack CONTAINERS/DOORS as well!!! +void AttackCore(Scriptable *Sender, Scriptable *target, int flags) +{ + //this is a dangerous cast, make sure actor is Actor * !!! + Actor *actor = (Actor *) Sender; + + WeaponInfo wi; + ITMExtHeader *header = NULL; + ITMExtHeader *hittingheader = NULL; + int tohit; + ieDword Flags; + int DamageBonus, CriticalBonus; + int speed, style; + + //bool leftorright = (bool) ((attacksperround-attackcount)&1); + bool leftorright = false; + + //will return false on any errors (eg, unusable weapon) + if (!actor->GetCombatDetails(tohit, leftorright, wi, header, hittingheader, Flags, DamageBonus, speed, CriticalBonus, style)) { + Sender->ReleaseCurrentAction(); + return; + } + + if (header) wi.range *= 10; + else wi.range = 0; + + if ( target->Type == ST_DOOR || target->Type == ST_CONTAINER) { + wi.range += 10; + } + Actor *tar = NULL; + ieDword targetID = 0; + if (target->Type==ST_ACTOR) { + tar = (Actor *) target; + targetID = tar->GetGlobalID(); + } + if (actor == tar) { + Sender->ReleaseCurrentAction(); + return; + } + if (!(flags&AC_NO_SOUND) ) { + if (actor->LastTarget != targetID) { + //play attack sound for party members + if (actor->InParty) { + //pick from all 5 possible verbal constants + actor->VerbalConstant(VB_ATTACK, 5); + //DisplayStringCore(Sender, VB_ATTACK, DS_CONSOLE|DS_CONST ); + } + //display attack message + displaymsg->DisplayConstantStringAction(STR_ACTION_ATTACK,0xf0f0f0, Sender, target); + } + } + //action performed + if(target->Type == ST_ACTOR) { + actor->SetTarget( target ); + } + if ( Sender->GetCurrentArea()!=target->GetCurrentArea() || + (PersonalDistance(Sender, target) > wi.range) ) { + MoveNearerTo(Sender, target, wi.range); + return; + } else if (target->Type == ST_DOOR) { + //Forcing a lock does not launch the trap... + Door* door = (Door*) target; + if(door->Flags & DOOR_LOCKED) { + door->TryBashLock(actor); + } + Sender->ReleaseCurrentAction(); + return; + } else if (target->Type == ST_CONTAINER) { + Container* cont = (Container*) target; + if(cont->Flags & CONT_LOCKED) { + cont->TryBashLock(actor); + } + Sender->ReleaseCurrentAction(); + return; + } + + actor->PerformAttack(core->GetGame()->GameTime); +} + +//we need this because some special characters like _ or * are also accepted +inline bool ismysymbol(const char letter) +{ + if (letter==']') return false; + if (letter=='[') return false; + if (letter==')') return false; + if (letter=='(') return false; + if (letter=='.') return false; + if (letter==',') return false; + return true; +} + +//this function returns a value, symbol could be a numeric string or +//a symbol from idsname +static int GetIdsValue(const char *&symbol, const char *idsname) +{ + int idsfile=core->LoadSymbol(idsname); + Holder valHook = core->GetSymbol(idsfile); + if (!valHook) { + //FIXME:missing ids file!!! + if (InDebug&ID_TRIGGERS) { + printMessage("GameScript"," ",LIGHT_RED); + printf("Missing IDS file %s for symbol %s!\n",idsname, symbol); + } + return -1; + } + char *newsymbol; + int value=strtol(symbol, &newsymbol, 0); + if (symbol!=newsymbol) { + symbol=newsymbol; + return value; + } + char symbolname[64]; + int x; + for (x=0;ismysymbol(*symbol) && x<(int) sizeof(symbolname)-1;x++) { + symbolname[x]=*symbol; + symbol++; + } + symbolname[x]=0; + return valHook->GetValue(symbolname); +} + +static void ParseIdsTarget(const char *&src, Object *&object) +{ + for (int i=0;iobjectFields[i]=GetIdsValue(src, ObjectIDSTableNames[i]); + if (*src!='.') { + break; + } + src++; + } + src++; //skipping ] +} + +//this will skip to the next element in the prototype of an action/trigger +#define SKIP_ARGUMENT() while (*str && ( *str != ',' ) && ( *str != ')' )) str++ + +static void ParseObject(const char *&str,const char *&src, Object *&object) +{ + SKIP_ARGUMENT(); + object = new Object(); + switch (*src) { + case '"': + //Scriptable Name + src++; + int i; + for (i=0;i<(int) sizeof(object->objectName)-1 && *src && *src!='"';i++) + { + object->objectName[i] = *src; + src++; + } + object->objectName[i] = 0; + src++; + break; + case '[': + src++; //skipping [ + ParseIdsTarget(src, object); + break; + default: //nested object filters + int Nesting=0; + + while (NestingobjectFilters+1, object->objectFilters, (int) sizeof(int) *(MaxObjectNesting-1) ); + object->objectFilters[0]=GetIdsValue(src,"object"); + if (*src!='(') { + break; + } + src++; //skipping ( + if (*src==')') { + break; + } + Nesting++; + } + if (*src=='[') { + ParseIdsTarget(src, object); + } + src+=Nesting; //skipping ) + } +} + +/* this function was lifted from GenerateAction, to make it clearer */ +Action* GenerateActionCore(const char *src, const char *str, unsigned short actionID) +{ + Action *newAction = new Action(true); + newAction->actionID = actionID; + //this flag tells us to merge 2 consecutive strings together to get + //a variable (context+variablename) + int mergestrings = actionflags[newAction->actionID]&AF_MERGESTRINGS; + int objectCount = ( newAction->actionID == 1 ) ? 0 : 1; + int stringsCount = 0; + int intCount = 0; + if (actionflags[newAction->actionID]&AF_DIRECT) { + Object *tmp = new Object(); + tmp->objectFields[0] = -1; + //tmp->objectFields[1] = core->GetGameControl()->targetID; + newAction->objects[objectCount++] = tmp; + } + //Here is the Action; Now we need to evaluate the parameters, if any + if (*str!=')') while (*str) { + if (*(str+1)!=':') { + printf("Warning, parser was sidetracked: %s\n",str); + } + switch (*str) { + default: + printf("Invalid type: %s\n",str); + //str++; + delete newAction; + return NULL; + break; + + case 'p': //Point + SKIP_ARGUMENT(); + src++; //Skip [ + newAction->pointParameter.x = (short) strtol( src, (char **) &src, 10 ); + src++; //Skip . + newAction->pointParameter.y = (short) strtol( src, (char **) &src, 10 ); + src++; //Skip ] + break; + + case 'i': //Integer + { + //going to the variable name + while (*str != '*' && *str !=',' && *str != ')' ) { + str++; + } + int value; + if (*str=='*') { //there may be an IDS table + str++; + ieVariable idsTabName; + char* tmp = idsTabName; + while (( *str != ',' ) && ( *str != ')' )) { + *tmp = *str; + tmp++; + str++; + } + *tmp = 0; + if (idsTabName[0]) { + value = GetIdsValue(src, idsTabName); + } + else { + value = strtol( src, (char **) &src, 0); + } + } + else { //no IDS table + value = strtol( src, (char **) &src, 0); + } + if (!intCount) { + newAction->int0Parameter = value; + } else if (intCount == 1) { + newAction->int1Parameter = value; + } else { + newAction->int2Parameter = value; + } + intCount++; + } + break; + + case 'a': + //Action + { + SKIP_ARGUMENT(); + char action[257]; + int i = 0; + int openParenthesisCount = 0; + while (true) { + if (*src == ')') { + if (!openParenthesisCount) + break; + openParenthesisCount--; + } else { + if (*src == '(') { + openParenthesisCount++; + } else { + if (( *src == ',' ) && + !openParenthesisCount) + break; + } + } + action[i] = *src; + i++; + src++; + } + action[i] = 0; + Action* act = GenerateAction( action); + if (!act) { + delete newAction; + return NULL; + } + act->objects[0] = newAction->objects[0]; + newAction->objects[0] = NULL; //avoid freeing of object + delete newAction; //freeing action + newAction = act; + } + break; + + case 'o': //Object + if (objectCount==3) { + printf("Invalid object count!\n"); + //abort(); + delete newAction; + return NULL; + } + ParseObject(str, src, newAction->objects[objectCount++]); + break; + + case 's': //String + { + SKIP_ARGUMENT(); + src++; + int i; + char* dst; + if (!stringsCount) { + dst = newAction->string0Parameter; + } else { + dst = newAction->string1Parameter; + } + //if there are 3 strings, the first 2 will be merged, + //the last one will be left alone + if (*str==')') { + mergestrings = 0; + } + //skipping the context part, which + //is to be readed later + if (mergestrings) { + for (i=0;i<6;i++) { + *dst++='*'; + } + } + else { + i=0; + } + //breaking on ',' in case of a monkey attack + //fixes bg1:melicamp.dlg, bg1:sharte.dlg + //if strings ever need a , inside, this is a FIXME + while (*src != '"' && *src !=',') { + if (*src == 0) { + delete newAction; + return NULL; + } + //sizeof(context+name) = 40 + if (i<40) { + *dst++ = (char) tolower(*src); + i++; + } + src++; + } + *dst = 0; + //reading the context part + if (mergestrings) { + str++; + if (*str!='s') { + printf("Invalid mergestrings:%s\n",str); + //abort(); + delete newAction; + return NULL; + } + SKIP_ARGUMENT(); + if (!stringsCount) { + dst = newAction->string0Parameter; + } else { + dst = newAction->string1Parameter; + } + + //this works only if there are no spaces + if (*src++!='"' || *src++!=',' || *src++!='"') { + break; + } + //reading the context string + i=0; + while (*src != '"') { + if (*src == 0) { + delete newAction; + return NULL; + } + if (i++<6) { + *dst++ = (char) tolower(*src); + } + src++; + } + } + src++; //skipping " + stringsCount++; + } + break; + } + str++; + if (*src == ',' || *src==')') + src++; + } + return newAction; +} + +void GoNear(Scriptable *Sender, const Point &p) +{ + if (Sender->GetCurrentAction()) { + printMessage("GameScript","Target busy???\n",LIGHT_RED); + return; + } + char Tmp[256]; + sprintf( Tmp, "MoveToPoint([%hd.%hd])", p.x, p.y ); + Action * action = GenerateAction( Tmp); + Sender->AddActionInFront( action ); +} + +void MoveNearerTo(Scriptable *Sender, Scriptable *target, int distance) +{ + Point p; + Map *myarea, *hisarea; + + if (Sender->Type != ST_ACTOR) { + printMessage("GameScript","MoveNearerTo only works with actors\n",LIGHT_RED); + Sender->ReleaseCurrentAction(); + return; + } + + myarea = Sender->GetCurrentArea(); + hisarea = target->GetCurrentArea(); + if (hisarea!=myarea) { + target = myarea->GetTileMap()->GetTravelTo(hisarea->GetScriptName()); + + if (!target) { + printMessage("GameScript", "MoveNearerTo failed to find an exit\n", YELLOW); + Sender->ReleaseCurrentAction(); + return; + } + ((Actor *) Sender)->UseExit(true); + } else { + ((Actor *) Sender)->UseExit(false); + } + // we deliberately don't try GetLikelyPosition here for now, + // maybe a future idea if we have a better implementation + // (the old code used it - by passing true not 0 below - when target was a movable) + GetPositionFromScriptable(target, p, 0); + + // account for PersonalDistance (which caller uses, but pathfinder doesn't) + if (distance && Sender->Type == ST_ACTOR) { + distance += ((Actor *)Sender)->size*10; + } + if (distance && target->Type == ST_ACTOR) { + distance += ((Actor *)target)->size*10; + } + + MoveNearerTo(Sender, p, distance, 0); +} + +//It is not always good to release the current action if target is unreachable +//we should also raise the trigger TargetUnreachable (if this is an Attack, at least) +//i hacked only this low level function, didn't need the higher ones so far +int MoveNearerTo(Scriptable *Sender, const Point &p, int distance, int dont_release) +{ + if (Sender->Type != ST_ACTOR) { + printMessage("GameScript","MoveNearerTo only works with actors\n",LIGHT_RED); + Sender->ReleaseCurrentAction(); + return 0; + } + + Actor *actor = (Actor *)Sender; + + if (!actor->InMove() || actor->Destination != p) { + actor->WalkTo(p, 0, distance); + } + + if (!actor->InMove()) { + //didn't release + if (dont_release) { + return dont_release; + } + // we can't walk any nearer to destination, give up + Sender->ReleaseCurrentAction(); + } + return 0; +} +/* +void GoNearAndRetry(Scriptable *Sender, Scriptable *target, bool flag, int distance) +{ + Point p; + GetPositionFromScriptable(target,p,flag); + GoNearAndRetry(Sender, p, distance); +} + +void GoNearAndRetry(Scriptable *Sender, const Point &p, int distance) +{ + if (!Sender->GetCurrentAction() ) { + printMessage("GameScript","NULL action retried???\n",LIGHT_RED); + return; + } + Sender->AddActionInFront( Sender->GetCurrentAction() ); + char Tmp[256]; + sprintf( Tmp, "MoveToPoint([%hd.%hd])", p.x, p.y ); + Action * action = GenerateAction( Tmp); + //experimental hack, this value means, + //MoveToPoint shall pop the next action too if movement fails + //and the actor is farther than distance + //this will prevent deadlocks + //(we have to add 1 because otherwise distance==0 fails, we subtract it in MoveToPoint) + action->int0Parameter = distance+1; + Sender->AddActionInFront( action ); +} +*/ +void FreeSrc(SrcVector *poi, const ieResRef key) +{ + int res = SrcCache.DecRef((void *) poi, key, true); + if (res<0) { + printMessage( "GameScript", "Corrupted Src cache encountered (reference count went below zero), ", LIGHT_RED ); + printf( "Src name is: %.8s\n", key); + abort(); + } + if (!res) { + delete poi; + } +} + +SrcVector *LoadSrc(const ieResRef resname) +{ + SrcVector *src = (SrcVector *) SrcCache.GetResource(resname); + if (src) { + return src; + } + DataStream* str = gamedata->GetResource( resname, IE_SRC_CLASS_ID ); + if ( !str) { + return NULL; + } + ieDword size=0; + str->ReadDword(&size); + src = new SrcVector(size); + SrcCache.SetAt( resname, (void *) src ); + while (size--) { + ieDword tmp; + str->ReadDword(&tmp); + src->at(size)=tmp; + str->ReadDword(&tmp); + } + delete ( str ); + return src; +} + +#define MEMCPY(a,b) memcpy((a),(b),sizeof(a) ) + +static Object *ObjectCopy(Object *object) +{ + if (!object) return NULL; + Object *newObject = new Object(); + MEMCPY( newObject->objectFields, object->objectFields ); + MEMCPY( newObject->objectFilters, object->objectFilters ); + MEMCPY( newObject->objectRect, object->objectRect ); + MEMCPY( newObject->objectName, object->objectName ); + return newObject; +} + +Action *ParamCopy(Action *parameters) +{ + Action *newAction = new Action(true); + newAction->actionID = parameters->actionID; + newAction->int0Parameter = parameters->int0Parameter; + newAction->int1Parameter = parameters->int1Parameter; + newAction->int2Parameter = parameters->int2Parameter; + newAction->pointParameter = parameters->pointParameter; + MEMCPY( newAction->string0Parameter, parameters->string0Parameter ); + MEMCPY( newAction->string1Parameter, parameters->string1Parameter ); + for (int c=0;c<3;c++) { + newAction->objects[c]= ObjectCopy( parameters->objects[c] ); + } + return newAction; +} + +Action *ParamCopyNoOverride(Action *parameters) +{ + Action *newAction = new Action(true); + newAction->actionID = parameters->actionID; + newAction->int0Parameter = parameters->int0Parameter; + newAction->int1Parameter = parameters->int1Parameter; + newAction->int2Parameter = parameters->int2Parameter; + newAction->pointParameter = parameters->pointParameter; + MEMCPY( newAction->string0Parameter, parameters->string0Parameter ); + MEMCPY( newAction->string1Parameter, parameters->string1Parameter ); + newAction->objects[0]= NULL; + newAction->objects[1]= ObjectCopy( parameters->objects[1] ); + newAction->objects[2]= ObjectCopy( parameters->objects[2] ); + return newAction; +} + +Trigger *GenerateTriggerCore(const char *src, const char *str, int trIndex, int negate) +{ + Trigger *newTrigger = new Trigger(); + newTrigger->triggerID = (unsigned short) triggersTable->GetValueIndex( trIndex )&0x3fff; + newTrigger->flags = (unsigned short) negate; + int mergestrings = triggerflags[newTrigger->triggerID]&TF_MERGESTRINGS; + int stringsCount = 0; + int intCount = 0; + //Here is the Trigger; Now we need to evaluate the parameters + if (*str!=')') while (*str) { + if (*(str+1)!=':') { + printf("Warning, parser was sidetracked: %s\n",str); + } + switch (*str) { + default: + printf("Invalid type: %s\n",str); + //str++; + delete newTrigger; + return NULL; + break; + + case 'p': //Point + SKIP_ARGUMENT(); + src++; //Skip [ + newTrigger->pointParameter.x = (short) strtol( src, (char **) &src, 10 ); + src++; //Skip . + newTrigger->pointParameter.y = (short) strtol( src, (char **) &src, 10 ); + src++; //Skip ] + break; + + case 'i': //Integer + { + //going to the variable name + while (*str != '*' && *str !=',' && *str != ')' ) { + str++; + } + int value; + if (*str=='*') { //there may be an IDS table + str++; + ieVariable idsTabName; + char* tmp = idsTabName; + while (( *str != ',' ) && ( *str != ')' )) { + *tmp = *str; + tmp++; + str++; + } + *tmp = 0; + if (idsTabName[0]) { + value = GetIdsValue(src, idsTabName); + } + else { + value = strtol( src, (char **) &src, 0); + } + } + else { //no IDS table + value = strtol( src, (char **) &src, 0); + } + if (!intCount) { + newTrigger->int0Parameter = value; + } else if (intCount == 1) { + newTrigger->int1Parameter = value; + } else { + newTrigger->int2Parameter = value; + } + intCount++; + } + break; + + case 'o': //Object + ParseObject(str, src, newTrigger->objectParameter); + break; + + case 's': //String + { + SKIP_ARGUMENT(); + src++; + int i; + char* dst; + if (!stringsCount) { + dst = newTrigger->string0Parameter; + } else { + dst = newTrigger->string1Parameter; + } + //skipping the context part, which + //is to be readed later + if (mergestrings) { + for (i=0;i<6;i++) { + *dst++='*'; + } + } + else { + i=0; + } + while (*src != '"') { + if (*src == 0) { + delete newTrigger; + return NULL; + } + + //sizeof(context+name) = 40 + if (i<40) { + *dst++ = (char) tolower(*src); + i++; + } + src++; + } + *dst = 0; + //reading the context part + if (mergestrings) { + str++; + if (*str!='s') { + printf("Invalid mergestrings:%s\n",str); + //abort(); + delete newTrigger; + return NULL; + } + SKIP_ARGUMENT(); + if (!stringsCount) { + dst = newTrigger->string0Parameter; + } else { + dst = newTrigger->string1Parameter; + } + + //this works only if there are no spaces + if (*src++!='"' || *src++!=',' || *src++!='"') { + break; + } + //reading the context string + i=0; + while (*src != '"') { + if (*src == 0) { + delete newTrigger; + return NULL; + } + + if (i++<6) { + *dst++ = (char) tolower(*src); + } + src++; + } + } + src++; //skipping " + stringsCount++; + } + break; + } + str++; + if (*src == ',' || *src==')') + src++; + } + return newTrigger; +} + +void SetVariable(Scriptable* Sender, const char* VarName, const char* Context, ieDword value) +{ + char newVarName[8+33]; + + if (InDebug&ID_VARIABLES) { + printf( "Setting variable(\"%s%s\", %d)\n", Context, + VarName, value ); + } + strncpy( newVarName, Context, 6 ); + newVarName[6]=0; + if (strnicmp( newVarName, "MYAREA", 6 ) == 0) { + Sender->GetCurrentArea()->locals->SetAt( VarName, value ); + return; + } + if (strnicmp( newVarName, "LOCALS", 6 ) == 0) { + Sender->locals->SetAt( VarName, value ); + return; + } + Game *game = core->GetGame(); + if (!strnicmp(newVarName,"KAPUTZ",6) && core->HasFeature(GF_HAS_KAPUTZ) ) { + game->kaputz->SetAt( VarName, value ); + return; + } + + if (strnicmp(newVarName,"GLOBAL",6) ) { + Map *map=game->GetMap(game->FindMap(newVarName)); + if (map) { + map->locals->SetAt( VarName, value); + } + else if (InDebug&ID_VARIABLES) { + printMessage("GameScript"," ",YELLOW); + printf("Invalid variable %s %s in setvariable\n",Context, VarName); + } + } + else { + game->locals->SetAt( VarName, ( ieDword ) value ); + } +} + +void SetVariable(Scriptable* Sender, const char* VarName, ieDword value) +{ + char newVarName[8]; + const char *poi; + + poi = &VarName[6]; + //some HoW triggers use a : to separate the scope from the variable name + if (*poi==':') { + poi++; + } + + if (InDebug&ID_VARIABLES) { + printf( "Setting variable(\"%s\", %d)\n", VarName, value ); + } + strncpy( newVarName, VarName, 6 ); + newVarName[6]=0; + if (strnicmp( newVarName, "MYAREA", 6 ) == 0) { + Sender->GetCurrentArea()->locals->SetAt( poi, value ); + return; + } + if (strnicmp( newVarName, "LOCALS", 6 ) == 0) { + Sender->locals->SetAt( poi, value ); + return; + } + Game *game = core->GetGame(); + if (!strnicmp(newVarName,"KAPUTZ",6) && core->HasFeature(GF_HAS_KAPUTZ) ) { + game->kaputz->SetAt( poi, value ); + return; + } + if (strnicmp(newVarName,"GLOBAL",6) ) { + Map *map=game->GetMap(game->FindMap(newVarName)); + if (map) { + map->locals->SetAt( poi, value); + } + else if (InDebug&ID_VARIABLES) { + printMessage("GameScript"," ",YELLOW); + printf("Invalid variable %s in setvariable\n",VarName); + } + } + else { + game->locals->SetAt( poi, ( ieDword ) value ); + } +} + +ieDword CheckVariable(Scriptable* Sender, const char* VarName, bool *valid) +{ + char newVarName[8]; + const char *poi; + ieDword value = 0; + + strncpy( newVarName, VarName, 6 ); + newVarName[6]=0; + poi = &VarName[6]; + //some HoW triggers use a : to separate the scope from the variable name + if (*poi==':') { + poi++; + } + + if (strnicmp( newVarName, "MYAREA", 6 ) == 0) { + Sender->GetCurrentArea()->locals->Lookup( poi, value ); + if (InDebug&ID_VARIABLES) { + printf("CheckVariable %s: %d\n",VarName, value); + } + return value; + } + if (strnicmp( newVarName, "LOCALS", 6 ) == 0) { + Sender->locals->Lookup( poi, value ); + if (InDebug&ID_VARIABLES) { + printf("CheckVariable %s: %d\n",VarName, value); + } + return value; + } + Game *game = core->GetGame(); + if (!strnicmp(newVarName,"KAPUTZ",6) && core->HasFeature(GF_HAS_KAPUTZ) ) { + game->kaputz->Lookup( poi, value ); + if (InDebug&ID_VARIABLES) { + printf("CheckVariable %s: %d\n",VarName, value); + } + return value; + } + if (strnicmp(newVarName,"GLOBAL",6) ) { + Map *map=game->GetMap(game->FindMap(newVarName)); + if (map) { + map->locals->Lookup( poi, value); + } else { + if (valid) { + *valid=false; + } + if (InDebug&ID_VARIABLES) { + printMessage("GameScript"," ",YELLOW); + printf("Invalid variable %s in checkvariable\n",VarName); + } + } + } else { + game->locals->Lookup( poi, value ); + } + if (InDebug&ID_VARIABLES) { + printf("CheckVariable %s: %d\n",VarName, value); + } + return value; +} + +ieDword CheckVariable(Scriptable* Sender, const char* VarName, const char* Context, bool *valid) +{ + char newVarName[8]; + ieDword value = 0; + + strncpy(newVarName, Context, 6); + newVarName[6]=0; + if (strnicmp( newVarName, "MYAREA", 6 ) == 0) { + Sender->GetCurrentArea()->locals->Lookup( VarName, value ); + if (InDebug&ID_VARIABLES) { + printf("CheckVariable %s%s: %d\n",Context, VarName, value); + } + return value; + } + if (strnicmp( newVarName, "LOCALS", 6 ) == 0) { + Sender->locals->Lookup( VarName, value ); + if (InDebug&ID_VARIABLES) { + printf("CheckVariable %s%s: %d\n",Context, VarName, value); + } + return value; + } + Game *game = core->GetGame(); + if (!strnicmp(newVarName,"KAPUTZ",6) && core->HasFeature(GF_HAS_KAPUTZ) ) { + game->kaputz->Lookup( VarName, value ); + if (InDebug&ID_VARIABLES) { + printf("CheckVariable %s%s: %d\n",Context, VarName, value); + } + return value; + } + if (strnicmp(newVarName,"GLOBAL",6) ) { + Map *map=game->GetMap(game->FindMap(newVarName)); + if (map) { + map->locals->Lookup( VarName, value); + } else { + if (valid) { + *valid=false; + } + if (InDebug&ID_VARIABLES) { + printMessage("GameScript"," ",YELLOW); + printf("Invalid variable %s %s in checkvariable\n",Context, VarName); + } + } + } else { + game->locals->Lookup( VarName, value ); + } + if (InDebug&ID_VARIABLES) { + printf("CheckVariable %s%s: %d\n",Context, VarName, value); + } + return value; +} + +int DiffCore(ieDword a, ieDword b, int diffmode) +{ + switch (diffmode) { + case LESS_THAN: + if (ab) { + return 1; + } + break; + case GREATER_OR_EQUALS: + if (a>=b) { + return 1; + } + break; + case NOT_EQUALS: + if (a!=b) { + return 1; + } + break; + case BINARY_LESS_OR_EQUALS: + if ((a&b) == a) { + return 1; + } + break; + case BINARY_MORE: + if ((a&b) != a) { + return 1; + } + break; + case BINARY_MORE_OR_EQUALS: + if ((a&b) == b) { + return 1; + } + break; + case BINARY_LESS: + if ((a&b) != b) { + return 1; + } + break; + case BINARY_INTERSECT: + if (a&b) { + return 1; + } + break; + case BINARY_NOT_INTERSECT: + if (!(a&b)) { + return 1; + } + break; + default: //less or equals + if (a<=b) { + return 1; + } + break; + } + return 0; +} + +int GetGroup(Actor *actor) +{ + int type = 2; //neutral, has no enemies + if (actor->GetStat(IE_EA) <= EA_GOODCUTOFF) { + type = 1; //PC + } + if (actor->GetStat(IE_EA) >= EA_EVILCUTOFF) { + type = 0; + } + return type; +} + +Point GetEntryPoint(const char *areaname, const char *entryname) +{ + Point p; + + AutoTable tab("entries"); + if (!tab) { + return p; + } + const char *tmpstr = tab->QueryField(areaname, entryname); + int x=-1; + int y=-1; + sscanf(tmpstr, "%d.%d", &x, &y); + p.x=(short) x; + p.y=(short) y; + return p; +} + +/* returns a spell's casting distance, it depends on the caster (level), and targeting mode too + the used header is calculated from the caster level */ +unsigned int GetSpellDistance(const ieResRef spellres, Scriptable *Sender) +{ + unsigned int dist; + + Spell* spl = gamedata->GetSpell( spellres ); + if (!spl) { + printMessage("GameScript"," ",LIGHT_RED); + printf("Spell couldn't be found:%.8s.\n", spellres); + return 0; + } + dist = spl->GetCastingDistance(Sender); + //make possible special return values (like 0xffffffff means the spell doesn't need distance) + //this is used with special targeting mode (3) + if (dist>0xff000000) { + return dist; + } + + gamedata->FreeSpell(spl, spellres, false); + return dist*15; +} + +/* returns an item's casting distance, it depends on the used header, and targeting mode too + the used header is explictly given */ +unsigned int GetItemDistance(const ieResRef itemres, int header) +{ + unsigned int dist; + + Item* itm = gamedata->GetItem( itemres ); + if (!itm) { + printMessage("GameScript"," ",LIGHT_RED); + printf("Item couldn't be found:%.8s.\n", itemres); + return 0; + } + dist=itm->GetCastingDistance(header); + //make possible special return values (like 0xffffffff means the item doesn't need distance) + //this is used with special targeting mode (3) + if (dist>0xff000000) { + return dist; + } + + gamedata->FreeItem(itm, itemres, false); + return dist*15; +} + +//read the wish 2da +void SetupWishCore(Scriptable *Sender, int column, int picks) +{ + int count; + ieVariable varname; + int *selects; + int i,j; + + //FIXME: find out what the original really used the picks parameter for + if (picks == 1) picks = 5; + + AutoTable tm("wish"); + if (!tm) { + printStatus( "ERROR", LIGHT_RED ); + printf( "Cannot find wish.2da.\n"); + return; + } + + selects = (int *) malloc(picks*sizeof(int)); + count = tm->GetRowCount(); + + for(i=0;i<99;i++) { + snprintf(varname,32, "wishpower%02d", i); + if(CheckVariable(Sender, varname, "GLOBAL") ) { + SetVariable(Sender, varname, "GLOBAL", 0); + } + } + + if (countQueryField( selects[i], column-1 ) ); + snprintf(varname,32,"wishpower%02d", spnum); + SetVariable(Sender, varname, "GLOBAL",1); + } + free(selects); +} + +#define MAX_ISLAND_POLYGONS 10 + +//read a polygon 2da +Gem_Polygon *GetPolygon2DA(ieDword index) +{ + ieResRef resref; + + if (index>=MAX_ISLAND_POLYGONS) { + return NULL; + } + + if (!polygons) { + polygons = (Gem_Polygon **) calloc(MAX_ISLAND_POLYGONS, sizeof(Gem_Polygon *) ); + } + if (polygons[index]) { + return polygons[index]; + } + snprintf(resref, sizeof(ieResRef), "ISLAND%02d", index); + AutoTable tm(resref); + if (!tm) { + return NULL; + } + int cnt = tm->GetRowCount(); + if (!cnt) { + return NULL; + } + Point *p = new Point[cnt]; + + int i = cnt; + while(i--) { + p[i].x = atoi(tm->QueryField(i, 0)); + p[i].y = atoi(tm->QueryField(i, 1)); + } + + polygons[index] = new Gem_Polygon(p, cnt, NULL); + delete [] p; + return polygons[index]; +} + diff --git a/project/jni/application/gemrb/src/core/GameScript/GSUtils.h b/project/jni/application/gemrb/src/core/GameScript/GSUtils.h new file mode 100644 index 000000000..afe9b1ae5 --- /dev/null +++ b/project/jni/application/gemrb/src/core/GameScript/GSUtils.h @@ -0,0 +1,142 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * + */ +#ifndef GSUTILS_H +#define GSUTILS_H + +#include "GameScript/GameScript.h" + +#include "defsounds.h" +#include "exports.h" +#include "strrefs.h" + +#include "Interface.h" + +//indebug flags +#define ID_REFERENCE 1 +#define ID_CUTSCENE 2 +#define ID_VARIABLES 4 +#define ID_ACTIONS 8 +#define ID_TRIGGERS 16 + +extern Holder triggersTable; +extern Holder actionsTable; +extern Holder overrideActionsTable; +extern Holder objectsTable; +extern TriggerFunction triggers[MAX_TRIGGERS]; +extern ActionFunction actions[MAX_ACTIONS]; +extern short actionflags[MAX_ACTIONS]; +extern short triggerflags[MAX_TRIGGERS]; +extern ObjectFunction objects[MAX_OBJECTS]; +extern IDSFunction idtargets[MAX_OBJECT_FIELDS]; +extern Cache SrcCache; //cache for string resources (pst) +extern Cache BcsCache; //cache for scripts +extern int ObjectIDSCount; +extern int MaxObjectNesting; +extern bool HasAdditionalRect; +extern bool HasTriggerPoint; +extern ieResRef *ObjectIDSTableNames; +extern int ObjectFieldsCount; +extern int ExtraParametersCount; +extern int InDebug; +extern int *SkillStats; +extern int SkillCount; +extern Gem_Polygon **polygons; + +#define MIC_INVALID -2 +#define MIC_FULL -1 +#define MIC_NOITEM 0 +#define MIC_GOTITEM 1 + +GEM_EXPORT int GetReaction(Actor *target, Scriptable *Sender); +int GetHappiness(Scriptable *Sender, int reputation); +int GetHPPercent(Scriptable *Sender); +bool StoreHasItemCore(const ieResRef storename, const ieResRef itemname); +bool HasItemCore(Inventory *inventory, const ieResRef itemname, ieDword flags); +void ClickCore(Scriptable *Sender, Point point, int type, int speed); +void TransformItemCore(Actor *actor, Action *parameters, bool onlyone); +void CreateVisualEffectCore(Actor *target, const char *effect, int iterations); +void CreateVisualEffectCore(Scriptable *Sender, const Point &position, const char *effect, int iterations); +void GetPositionFromScriptable(Scriptable* scr, Point &position, bool trap); +void BeginDialog(Scriptable* Sender, Action* parameters, int flags); +void ChangeAnimationCore(Actor *src, const char *resref, bool effect); +void PolymorphCopyCore(Actor *src, Actor *tar, bool base); +void CreateCreatureCore(Scriptable* Sender, Action* parameters, int flags); +int MoveItemCore(Scriptable *Sender, Scriptable *target, const char *resref, int flags, int setflag); +void MoveToObjectCore(Scriptable *Sender, Action *parameters, ieDword flags, bool untilsee); +void CreateItemCore(CREItem *item, const char *resref, int a, int b, int c); +void AttackCore(Scriptable *Sender, Scriptable *target, int flags); +void InitScriptTables(); +void HandleBitMod(ieDword &value1, ieDword value2, int opcode); +bool ResolveSpellName(ieResRef spellres, Action *parameter); +GEM_EXPORT void ResolveSpellName(ieResRef spellres, ieDword number); +GEM_EXPORT ieDword ResolveSpellNumber(const ieResRef spellres); +bool ResolveItemName(ieResRef itemres, Actor *act, ieDword Slot); +void EscapeAreaCore(Scriptable *Sender, const Point &p, const char *area, const Point &enter, int flags, int wait); +void GoNear(Scriptable *Sender, const Point &p); +void MoveNearerTo(Scriptable *Sender, Scriptable *target, int distance); +int MoveNearerTo(Scriptable *Sender, const Point &p, int distance, int no_release); +void GoNearAndRetry(Scriptable *Sender, Scriptable *target, bool destination, int distance); +void GoNearAndRetry(Scriptable *Sender, const Point &p, int distance); + +#define NO_OPERATION -1 +#define LESS_OR_EQUALS 0 +//iwd2 diffmode with gemrb enhancements +#define EQUALS 1 +#define LESS_THAN 2 +#define GREATER_THAN 3 +#define GREATER_OR_EQUALS 4 +#define NOT_EQUALS 5 +#define BINARY_LESS_OR_EQUALS 6 //(left has only bits in right) +#define BINARY_MORE_OR_EQUALS 7 //(left has equal or more bits than right) +#define BINARY_INTERSECT 8 //(left and right has at least one common bit) +#define BINARY_NOT_INTERSECT 9 //(no common bits) +#define BINARY_MORE 10 //left has more bits than right +#define BINARY_LESS 11 //left has less bits than right + +GEM_EXPORT int GetGroup(Actor *actor); + +void FreeSrc(SrcVector *poi, const ieResRef key); +SrcVector *LoadSrc(const ieResRef resname); +Action *ParamCopy(Action *parameters); +Action *ParamCopyNoOverride(Action *parameters); +void SetVariable(Scriptable* Sender, const char* VarName, ieDword value); +Point GetEntryPoint(const char *areaname, const char *entryname); +//these are used from other plugins +GEM_EXPORT int CanSee(Scriptable* Sender, Scriptable* target, bool range, int nodead); +GEM_EXPORT int SeeCore(Scriptable* Sender, Trigger* parameters, int justlos); +GEM_EXPORT int DiffCore(ieDword a, ieDword b, int diffmode); +GEM_EXPORT void DisplayStringCore(Scriptable* Sender, int Strref, int flags); +GEM_EXPORT void SetVariable(Scriptable* Sender, const char* VarName, const char* Context, ieDword value); +GEM_EXPORT void MoveBetweenAreasCore(Actor* actor, const char *area, const Point &position, int face, bool adjust); +GEM_EXPORT ieDword CheckVariable(Scriptable* Sender, const char* VarName, bool *valid = NULL); +GEM_EXPORT ieDword CheckVariable(Scriptable* Sender, const char* VarName, const char* Context, bool *valid = NULL); +Action* GenerateActionCore(const char *src, const char *str, unsigned short actionID); +Trigger *GenerateTriggerCore(const char *src, const char *str, int trIndex, int negate); +unsigned int GetSpellDistance(const ieResRef spellres, Scriptable *Sender); +unsigned int GetItemDistance(const ieResRef itemres, int header); +void SetupWishCore(Scriptable *Sender, int column, int picks); +Gem_Polygon *GetPolygon2DA(ieDword index); + +inline int Bones(ieDword value) +{ + return core->Roll((value&0xf000)>>12, (value&0xff0)>>8, value&15); +} + +#endif diff --git a/project/jni/application/gemrb/src/core/GameScript/GameScript.cpp b/project/jni/application/gemrb/src/core/GameScript/GameScript.cpp new file mode 100644 index 000000000..f7c900069 --- /dev/null +++ b/project/jni/application/gemrb/src/core/GameScript/GameScript.cpp @@ -0,0 +1,2337 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * + */ + +#include "GameScript/GameScript.h" + +#include "GameScript/GSUtils.h" +#include "GameScript/Matching.h" + +#include "win32def.h" + +#include "Game.h" +#include "GameData.h" +#include "Interface.h" +#include "PluginMgr.h" + +//debug flags +// 1 - cache +// 2 - cutscene ID +// 4 - globals +// 8 - action execution +//16 - trigger evaluation + +//Make this an ordered list, so we could use bsearch! +static const TriggerLink triggernames[] = { + {"actionlistempty", GameScript::ActionListEmpty, 0}, + {"actuallyincombat", GameScript::ActuallyInCombat, 0}, + {"acquired", GameScript::Acquired, 0}, + {"alignment", GameScript::Alignment, 0}, + {"allegiance", GameScript::Allegiance, 0}, + {"animstate", GameScript::AnimState, 0}, + {"anypconmap", GameScript::AnyPCOnMap, 0}, + {"anypcseesenemy", GameScript::AnyPCSeesEnemy, 0}, + {"areacheck", GameScript::AreaCheck, 0}, + {"areacheckobject", GameScript::AreaCheckObject, 0}, + {"areaflag", GameScript::AreaFlag, 0}, + {"arearestdisabled", GameScript::AreaRestDisabled, 0}, + {"areatype", GameScript::AreaType, 0}, + {"atlocation", GameScript::AtLocation, 0}, + {"assaltedby", GameScript::AttackedBy, 0},//pst + {"attackedby", GameScript::AttackedBy, 0}, + {"becamevisible", GameScript::BecameVisible, 0}, + {"bitcheck", GameScript::BitCheck,TF_MERGESTRINGS}, + {"bitcheckexact", GameScript::BitCheckExact,TF_MERGESTRINGS}, + {"bitglobal", GameScript::BitGlobal_Trigger,TF_MERGESTRINGS}, + {"breakingpoint", GameScript::BreakingPoint, 0}, + {"calanderday", GameScript::CalendarDay, 0}, //illiterate developers O_o + {"calendarday", GameScript::CalendarDay, 0}, + {"calanderdaygt", GameScript::CalendarDayGT, 0}, + {"calendardaygt", GameScript::CalendarDayGT, 0}, + {"calanderdaylt", GameScript::CalendarDayLT, 0}, + {"calendardaylt", GameScript::CalendarDayLT, 0}, + {"calledbyname", GameScript::CalledByName, 0}, //this is still a question + {"chargecount", GameScript::ChargeCount, 0}, + {"charname", GameScript::CharName, 0}, //not scripting name + {"checkareadifflevel", GameScript::DifficultyLT, 0},//iwd2 guess + {"checkdoorflags", GameScript::CheckDoorFlags, 0}, + {"checkpartyaveragelevel", GameScript::CheckPartyAverageLevel, 0}, + {"checkpartylevel", GameScript::CheckPartyLevel, 0}, + {"checkskill", GameScript::CheckSkill, 0}, + {"checkskillgt", GameScript::CheckSkillGT, 0}, + {"checkskilllt", GameScript::CheckSkillLT, 0}, + {"checkspellstate", GameScript::CheckSpellState, 0}, + {"checkstat", GameScript::CheckStat, 0}, + {"checkstatgt", GameScript::CheckStatGT, 0}, + {"checkstatlt", GameScript::CheckStatLT, 0}, + {"class", GameScript::Class, 0}, + {"classex", GameScript::ClassEx, 0}, //will return true for multis + {"classlevel", GameScript::ClassLevel, 0}, //pst + {"classlevelgt", GameScript::ClassLevelGT, 0}, + {"classlevellt", GameScript::ClassLevelLT, 0}, + {"clicked", GameScript::Clicked, 0}, + {"closed", GameScript::Closed, 0}, + {"combatcounter", GameScript::CombatCounter, 0}, + {"combatcountergt", GameScript::CombatCounterGT, 0}, + {"combatcounterlt", GameScript::CombatCounterLT, 0}, + {"contains", GameScript::Contains, 0}, + {"currentareais", GameScript::CurrentAreaIs, 0},//checks object + {"creaturehidden", GameScript::CreatureHidden, 0},//this is the engine level hiding feature, not the skill + {"creatureinarea", GameScript::AreaCheck, 0}, //pst, checks this object + {"damagetaken", GameScript::HPLost, 0}, + {"damagetakengt", GameScript::HPLostGT, 0}, + {"damagetakenlt", GameScript::HPLostLT, 0}, + {"dead", GameScript::Dead, 0}, + {"delay", GameScript::Delay, 0}, + {"detect", GameScript::Detect, 0}, //so far i see no difference + {"die", GameScript::Die, 0}, + {"died", GameScript::Died, 0}, + {"difficulty", GameScript::Difficulty, 0}, + {"difficultygt", GameScript::DifficultyGT, 0}, + {"difficultylt", GameScript::DifficultyLT, 0}, + {"disarmed", GameScript::Disarmed, 0}, + {"disarmfailed", GameScript::DisarmFailed, 0}, + {"entered", GameScript::Entered, 0}, + {"entirepartyonmap", GameScript::EntirePartyOnMap, 0}, + {"exists", GameScript::Exists, 0}, + {"extendedstatecheck", GameScript::ExtendedStateCheck, 0}, + {"extraproficiency", GameScript::ExtraProficiency, 0}, + {"extraproficiencygt", GameScript::ExtraProficiencyGT, 0}, + {"extraproficiencylt", GameScript::ExtraProficiencyLT, 0}, + {"faction", GameScript::Faction, 0}, + {"failedtoopen", GameScript::OpenFailed, 0}, + {"fallenpaladin", GameScript::FallenPaladin, 0}, + {"fallenranger", GameScript::FallenRanger, 0}, + {"false", GameScript::False, 0}, + {"forcemarkedspell", GameScript::ForceMarkedSpell_Trigger, 0}, + {"frame", GameScript::Frame, 0}, + {"g", GameScript::G_Trigger, 0}, + {"gender", GameScript::Gender, 0}, + {"general", GameScript::General, 0}, + {"ggt", GameScript::GGT_Trigger, 0}, + {"glt", GameScript::GLT_Trigger, 0}, + {"global", GameScript::Global,TF_MERGESTRINGS}, + {"globalandglobal", GameScript::GlobalAndGlobal_Trigger,TF_MERGESTRINGS}, + {"globalband", GameScript::BitCheck,TF_MERGESTRINGS}, + {"globalbandglobal", GameScript::GlobalBAndGlobal_Trigger,TF_MERGESTRINGS}, + {"globalbandglobalexact", GameScript::GlobalBAndGlobalExact,TF_MERGESTRINGS}, + {"globalbitglobal", GameScript::GlobalBitGlobal_Trigger,TF_MERGESTRINGS}, + {"globalequalsglobal", GameScript::GlobalsEqual,TF_MERGESTRINGS}, //this is the same + {"globalgt", GameScript::GlobalGT,TF_MERGESTRINGS}, + {"globalgtglobal", GameScript::GlobalGTGlobal,TF_MERGESTRINGS}, + {"globallt", GameScript::GlobalLT,TF_MERGESTRINGS}, + {"globalltglobal", GameScript::GlobalLTGlobal,TF_MERGESTRINGS}, + {"globalorglobal", GameScript::GlobalOrGlobal_Trigger,TF_MERGESTRINGS}, + {"globalsequal", GameScript::GlobalsEqual, 0}, + {"globalsgt", GameScript::GlobalsGT, 0}, + {"globalslt", GameScript::GlobalsLT, 0}, + {"globaltimerexact", GameScript::GlobalTimerExact, 0}, + {"globaltimerexpired", GameScript::GlobalTimerExpired, 0}, + {"globaltimernotexpired", GameScript::GlobalTimerNotExpired, 0}, + {"globaltimerstarted", GameScript::GlobalTimerStarted, 0}, + {"happiness", GameScript::Happiness, 0}, + {"happinessgt", GameScript::HappinessGT, 0}, + {"happinesslt", GameScript::HappinessLT, 0}, + {"harmlessclosed", GameScript::Closed, 0}, //pst, not sure + {"harmlessentered", GameScript::HarmlessEntered, 0}, //??? + {"harmlessopened", GameScript::Opened, 0}, //pst, not sure + {"hasbounceeffects", GameScript::HasBounceEffects, 0}, + {"hasimmunityeffects", GameScript::HasImmunityEffects, 0}, + {"hasinnateability", GameScript::HaveSpell, 0}, //these must be the same + {"hasitem", GameScript::HasItem, 0}, + {"hasitemequiped", GameScript::HasItemEquipped, 0}, //typo in bg2 + {"hasitemequipedreal", GameScript::HasItemEquipped, 0}, //not sure + {"hasitemequipped", GameScript::HasItemEquipped, 0}, + {"hasitemequippedreal", GameScript::HasItemEquipped, 0}, //not sure + {"hasiteminslot", GameScript::HasItemSlot, 0}, + {"hasitemslot", GameScript::HasItemSlot, 0}, + {"hasitemtypeslot", GameScript::HasItemTypeSlot, 0},//gemrb extension + {"hasweaponequiped", GameScript::HasWeaponEquipped, 0},//a typo again + {"hasweaponequipped", GameScript::HasWeaponEquipped, 0}, + {"haveanyspells", GameScript::HaveAnySpells, 0}, + {"havespell", GameScript::HaveSpell, 0}, //these must be the same + {"havespellparty", GameScript::HaveSpellParty, 0}, + {"havespellres", GameScript::HaveSpell, 0}, //they share the same ID + {"haveusableweaponequipped", GameScript::HaveUsableWeaponEquipped, 0}, + {"heard", GameScript::Heard, 0}, + {"help", GameScript::Help_Trigger, 0}, + {"helpex", GameScript::HelpEX, 0}, + {"hitby", GameScript::HitBy, 0}, + {"hotkey", GameScript::HotKey, 0}, + {"hp", GameScript::HP, 0}, + {"hpgt", GameScript::HPGT, 0}, + {"hplost", GameScript::HPLost, 0}, + {"hplostgt", GameScript::HPLostGT, 0}, + {"hplostlt", GameScript::HPLostLT, 0}, + {"hplt", GameScript::HPLT, 0}, + {"hppercent", GameScript::HPPercent, 0}, + {"hppercentgt", GameScript::HPPercentGT, 0}, + {"hppercentlt", GameScript::HPPercentLT, 0}, + {"inactivearea", GameScript::InActiveArea, 0}, + {"incutscenemode", GameScript::InCutSceneMode, 0}, + {"inline", GameScript::InLine, 0}, + {"inmyarea", GameScript::InMyArea, 0}, + {"inmygroup", GameScript::InMyGroup, 0}, + {"inparty", GameScript::InParty, 0}, + {"inpartyallowdead", GameScript::InPartyAllowDead, 0}, + {"inpartyslot", GameScript::InPartySlot, 0}, + {"internal", GameScript::Internal, 0}, + {"internalgt", GameScript::InternalGT, 0}, + {"internallt", GameScript::InternalLT, 0}, + {"interactingwith", GameScript::InteractingWith, 0}, + {"intrap", GameScript::InTrap, 0}, + {"inventoryfull", GameScript::InventoryFull, 0}, + {"inview", GameScript::LOS, 0}, //it seems the same, needs research + {"inwatcherskeep", GameScript::AreaStartsWith, 0}, + {"inweaponrange", GameScript::InWeaponRange, 0}, + {"isaclown", GameScript::IsAClown, 0}, + {"isactive", GameScript::IsActive, 0}, + {"isanimationid", GameScript::AnimationID, 0}, + {"iscreatureareaflag", GameScript::IsCreatureAreaFlag, 0}, + {"isfacingobject", GameScript::IsFacingObject, 0}, + {"isfacingsavedrotation", GameScript::IsFacingSavedRotation, 0}, + {"isgabber", GameScript::IsGabber, 0}, + {"isheartoffurymodeon", GameScript::NightmareModeOn, 0}, + {"islocked", GameScript::IsLocked, 0}, + {"isextendednight", GameScript::IsExtendedNight, 0}, + {"ismarkedspell", GameScript::IsMarkedSpell, 0}, + {"isoverme", GameScript::IsOverMe, 0}, + {"ispathcriticalobject", GameScript::IsPathCriticalObject, 0}, + {"isplayernumber", GameScript::IsPlayerNumber, 0}, + {"isrotation", GameScript::IsRotation, 0}, + {"isscriptname", GameScript::CalledByName, 0}, //seems the same + {"isspelltargetvalid", GameScript::IsSpellTargetValid, 0}, + {"isteambiton", GameScript::IsTeamBitOn, 0}, + {"isvalidforpartydialog", GameScript::IsValidForPartyDialog, 0}, + {"isvalidforpartydialogue", GameScript::IsValidForPartyDialog, 0}, + {"isweaponranged", GameScript::IsWeaponRanged, 0}, + {"isweather", GameScript::IsWeather, 0}, //gemrb extension + {"itemisidentified", GameScript::ItemIsIdentified, 0}, + {"joins", GameScript::Joins, 0}, + {"kit", GameScript::Kit, 0}, + {"knowspell", GameScript::KnowSpell, 0}, //gemrb specific + {"lastmarkedobject", GameScript::LastMarkedObject_Trigger, 0}, + {"lastpersontalkedto", GameScript::LastPersonTalkedTo, 0}, //pst + {"leaves", GameScript::Leaves, 0}, + {"level", GameScript::Level, 0}, + {"levelgt", GameScript::LevelGT, 0}, + {"levelinclass", GameScript::LevelInClass, 0}, //iwd2 + {"levelinclassgt", GameScript::LevelInClassGT, 0}, + {"levelinclasslt", GameScript::LevelInClassLT, 0}, + {"levellt", GameScript::LevelLT, 0}, + {"levelparty", GameScript::LevelParty, 0}, + {"levelpartygt", GameScript::LevelPartyGT, 0}, + {"levelpartylt", GameScript::LevelPartyLT, 0}, + {"localsequal", GameScript::LocalsEqual, 0}, + {"localsgt", GameScript::LocalsGT, 0}, + {"localslt", GameScript::LocalsLT, 0}, + {"los", GameScript::LOS, 0}, + {"modalstate", GameScript::ModalState, 0}, + {"morale", GameScript::Morale, 0}, + {"moralegt", GameScript::MoraleGT, 0}, + {"moralelt", GameScript::MoraleLT, 0}, + {"name", GameScript::CalledByName, 0}, //this is the same too? + {"namelessbitthedust", GameScript::NamelessBitTheDust, 0}, + {"nearbydialog", GameScript::NearbyDialog, 0}, + {"nearbydialogue", GameScript::NearbyDialog, 0}, + {"nearlocation", GameScript::NearLocation, 0}, + {"nearsavedlocation", GameScript::NearSavedLocation, 0}, + {"nightmaremodeon", GameScript::NightmareModeOn, 0}, + {"notstatecheck", GameScript::NotStateCheck, 0}, + {"nulldialog", GameScript::NullDialog, 0}, + {"nulldialogue", GameScript::NullDialog, 0}, + {"numcreature", GameScript::NumCreatures, 0}, + {"numcreaturegt", GameScript::NumCreaturesGT, 0}, + {"numcreaturelt", GameScript::NumCreaturesLT, 0}, + {"numcreaturesatmylevel", GameScript::NumCreaturesAtMyLevel, 0}, + {"numcreaturesgtmylevel", GameScript::NumCreaturesGTMyLevel, 0}, + {"numcreaturesltmylevel", GameScript::NumCreaturesLTMyLevel, 0}, + {"numcreaturevsparty", GameScript::NumCreatureVsParty, 0}, + {"numcreaturevspartygt", GameScript::NumCreatureVsPartyGT, 0}, + {"numcreaturevspartylt", GameScript::NumCreatureVsPartyLT, 0}, + {"numdead", GameScript::NumDead, 0}, + {"numdeadgt", GameScript::NumDeadGT, 0}, + {"numdeadlt", GameScript::NumDeadLT, 0}, + {"numinparty", GameScript::PartyCountEQ, 0}, + {"numinpartyalive", GameScript::PartyCountAliveEQ, 0}, + {"numinpartyalivegt", GameScript::PartyCountAliveGT, 0}, + {"numinpartyalivelt", GameScript::PartyCountAliveLT, 0}, + {"numinpartygt", GameScript::PartyCountGT, 0}, + {"numinpartylt", GameScript::PartyCountLT, 0}, + {"numitems", GameScript::NumItems, 0}, + {"numitemsgt", GameScript::NumItemsGT, 0}, + {"numitemslt", GameScript::NumItemsLT, 0}, + {"numitemsparty", GameScript::NumItemsParty, 0}, + {"numitemspartygt", GameScript::NumItemsPartyGT, 0}, + {"numitemspartylt", GameScript::NumItemsPartyLT, 0}, + {"numtimesinteracted", GameScript::NumTimesInteracted, 0}, + {"numtimesinteractedgt", GameScript::NumTimesInteractedGT, 0}, + {"numtimesinteractedlt", GameScript::NumTimesInteractedLT, 0}, + {"numtimesinteractedobject", GameScript::NumTimesInteractedObject, 0},//gemrb + {"numtimesinteractedobjectgt", GameScript::NumTimesInteractedObjectGT, 0},//gemrb + {"numtimesinteractedobjectlt", GameScript::NumTimesInteractedObjectLT, 0},//gemrb + {"numtimestalkedto", GameScript::NumTimesTalkedTo, 0}, + {"numtimestalkedtogt", GameScript::NumTimesTalkedToGT, 0}, + {"numtimestalkedtolt", GameScript::NumTimesTalkedToLT, 0}, + {"objectactionlistempty", GameScript::ObjectActionListEmpty, 0}, //same function + {"objitemcounteq", GameScript::NumItems, 0}, + {"objitemcountgt", GameScript::NumItemsGT, 0}, + {"objitemcountlt", GameScript::NumItemsLT, 0}, + {"oncreation", GameScript::OnCreation, 0}, + {"onisland", GameScript::OnIsland, 0}, + {"onscreen", GameScript::OnScreen, 0}, + {"opened", GameScript::Opened, 0}, + {"openfailed", GameScript::OpenFailed, 0}, + {"openstate", GameScript::OpenState, 0}, + {"or", GameScript::Or, 0}, + {"outofammo", GameScript::OutOfAmmo, 0}, + {"ownsfloatermessage", GameScript::OwnsFloaterMessage, 0}, + {"partycounteq", GameScript::PartyCountEQ, 0}, + {"partycountgt", GameScript::PartyCountGT, 0}, + {"partycountlt", GameScript::PartyCountLT, 0}, + {"partygold", GameScript::PartyGold, 0}, + {"partygoldgt", GameScript::PartyGoldGT, 0}, + {"partygoldlt", GameScript::PartyGoldLT, 0}, + {"partyhasitem", GameScript::PartyHasItem, 0}, + {"partyhasitemidentified", GameScript::PartyHasItemIdentified, 0}, + {"partyitemcounteq", GameScript::NumItemsParty, 0}, + {"partyitemcountgt", GameScript::NumItemsPartyGT, 0}, + {"partyitemcountlt", GameScript::NumItemsPartyLT, 0}, + {"partymemberdied", GameScript::PartyMemberDied, 0}, + {"partyrested", GameScript::PartyRested, 0}, + {"pccanseepoint", GameScript::PCCanSeePoint, 0}, + {"pcinstore", GameScript::PCInStore, 0}, + {"personalspacedistance", GameScript::PersonalSpaceDistance, 0}, + {"picklockfailed", GameScript::PickLockFailed, 0}, + {"pickpocketfailed", GameScript::PickpocketFailed, 0}, + {"proficiency", GameScript::Proficiency, 0}, + {"proficiencygt", GameScript::ProficiencyGT, 0}, + {"proficiencylt", GameScript::ProficiencyLT, 0}, + {"race", GameScript::Race, 0}, + {"randomnum", GameScript::RandomNum, 0}, + {"randomnumgt", GameScript::RandomNumGT, 0}, + {"randomnumlt", GameScript::RandomNumLT, 0}, + {"randomstatcheck", GameScript::RandomStatCheck, 0}, + {"range", GameScript::Range, 0}, + {"reaction", GameScript::Reaction, 0}, + {"reactiongt", GameScript::ReactionGT, 0}, + {"reactionlt", GameScript::ReactionLT, 0}, + {"realglobaltimerexact", GameScript::RealGlobalTimerExact, 0}, + {"realglobaltimerexpired", GameScript::RealGlobalTimerExpired, 0}, + {"realglobaltimernotexpired", GameScript::RealGlobalTimerNotExpired, 0}, + {"receivedorder", GameScript::ReceivedOrder, 0}, + {"reputation", GameScript::Reputation, 0}, + {"reputationgt", GameScript::ReputationGT, 0}, + {"reputationlt", GameScript::ReputationLT, 0}, + {"school", GameScript::School, 0}, //similar to kit + {"see", GameScript::See, 0}, + {"sequence", GameScript::Sequence, 0}, + {"setlastmarkedobject", GameScript::SetLastMarkedObject, 0}, + {"setmarkedspell", GameScript::SetMarkedSpell_Trigger, 0}, + {"specifics", GameScript::Specifics, 0}, + {"spellcast", GameScript::SpellCast, 0}, + {"spellcastinnate", GameScript::SpellCastInnate, 0}, + {"spellcastonme", GameScript::SpellCastOnMe, 0}, + {"spellcastpriest", GameScript::SpellCastPriest, 0}, + {"statecheck", GameScript::StateCheck, 0}, + {"stealfailed", GameScript::StealFailed, 0}, + {"storehasitem", GameScript::StoreHasItem, 0}, + {"stuffglobalrandom", GameScript::StuffGlobalRandom, 0},//hm, this is a trigger + {"subrace", GameScript::SubRace, 0}, + {"systemvariable", GameScript::SystemVariable_Trigger, 0}, //gemrb + {"targetunreachable", GameScript::TargetUnreachable, 0}, + {"team", GameScript::Team, 0}, + {"time", GameScript::Time, 0}, + {"timegt", GameScript::TimeGT, 0}, + {"timelt", GameScript::TimeLT, 0}, + {"timeofday", GameScript::TimeOfDay, 0}, + {"timeractive", GameScript::TimerActive, 0}, + {"timerexpired", GameScript::TimerExpired, 0}, + {"tookdamage", GameScript::TookDamage, 0}, + {"totalitemcnt", GameScript::TotalItemCnt, 0}, //iwd2 + {"totalitemcntexclude", GameScript::TotalItemCntExclude, 0}, //iwd2 + {"totalitemcntexcludegt", GameScript::TotalItemCntExcludeGT, 0}, //iwd2 + {"totalitemcntexcludelt", GameScript::TotalItemCntExcludeLT, 0}, //iwd2 + {"totalitemcntgt", GameScript::TotalItemCntGT, 0}, //iwd2 + {"totalitemcntlt", GameScript::TotalItemCntLT, 0}, //iwd2 + {"traptriggered", GameScript::TrapTriggered, 0}, + {"trigger", GameScript::TriggerTrigger, 0}, + {"triggerclick", GameScript::Clicked, 0}, //not sure + {"triggersetglobal", GameScript::TriggerSetGlobal,0}, //iwd2, but never used + {"true", GameScript::True, 0}, + {"turnedby", GameScript::TurnedBy, 0}, + {"unlocked", GameScript::Unlocked, 0}, + {"unselectablevariable", GameScript::UnselectableVariable, 0}, + {"unselectablevariablegt", GameScript::UnselectableVariableGT, 0}, + {"unselectablevariablelt", GameScript::UnselectableVariableLT, 0}, + {"unusable",GameScript::Unusable, 0}, + {"vacant",GameScript::Vacant, 0}, + {"walkedtotrigger", GameScript::WalkedToTrigger, 0}, + {"wasindialog", GameScript::WasInDialog, 0}, + {"xor", GameScript::Xor,TF_MERGESTRINGS}, + {"xp", GameScript::XP, 0}, + {"xpgt", GameScript::XPGT, 0}, + {"xplt", GameScript::XPLT, 0}, + { NULL,NULL,0} +}; + +//Make this an ordered list, so we could use bsearch! +static const ActionLink actionnames[] = { + {"actionoverride",NULL, AF_INVALID}, //will this function ever be reached + {"activate", GameScript::Activate, 0}, + {"activateportalcursor", GameScript::ActivatePortalCursor, 0}, + {"addareaflag", GameScript::AddAreaFlag, 0}, + {"addareatype", GameScript::AddAreaType, 0}, + {"addexperienceparty", GameScript::AddExperienceParty, 0}, + {"addexperiencepartycr", GameScript::AddExperiencePartyCR, 0}, + {"addexperiencepartyglobal", GameScript::AddExperiencePartyGlobal, 0}, + {"addfeat", GameScript::AddFeat, 0}, + {"addglobals", GameScript::AddGlobals, 0}, + {"addhp", GameScript::AddHP, 0}, + {"addjournalentry", GameScript::AddJournalEntry, 0}, + {"addkit", GameScript::AddKit, 0}, + {"addmapnote", GameScript::AddMapnote, 0}, + {"addpartyexperience", GameScript::AddExperienceParty, 0}, + {"addspecialability", GameScript::AddSpecialAbility, 0}, + {"addsuperkit", GameScript::AddSuperKit, 0}, + {"addwaypoint", GameScript::AddWayPoint,AF_BLOCKING}, + {"addxp2da", GameScript::AddXP2DA, 0}, + {"addxpobject", GameScript::AddXPObject, 0}, + {"addxpvar", GameScript::AddXP2DA, 0}, + {"advancetime", GameScript::AdvanceTime, 0}, + {"allowarearesting", GameScript::SetAreaRestFlag, 0},//iwd2 + {"ally", GameScript::Ally, 0}, + {"ambientactivate", GameScript::AmbientActivate, 0}, + {"ankhegemerge", GameScript::AnkhegEmerge, AF_ALIVE}, + {"ankheghide", GameScript::AnkhegHide, AF_ALIVE}, + {"applydamage", GameScript::ApplyDamage, 0}, + {"applydamagepercent", GameScript::ApplyDamagePercent, 0}, + {"applyspell", GameScript::ApplySpell, 0}, + {"applyspellpoint", GameScript::ApplySpellPoint, 0}, //gemrb extension + {"attachtransitiontodoor", GameScript::AttachTransitionToDoor, 0}, + {"attack", GameScript::Attack,AF_BLOCKING|AF_ALIVE}, + {"attacknosound", GameScript::AttackNoSound,AF_BLOCKING|AF_ALIVE}, //no sound yet anyway + {"attackoneround", GameScript::AttackOneRound,AF_BLOCKING|AF_ALIVE}, + {"attackreevaluate", GameScript::AttackReevaluate,AF_BLOCKING|AF_ALIVE}, + {"backstab", GameScript::Attack,AF_BLOCKING|AF_ALIVE},//actually hide+attack + {"banterblockflag", GameScript::BanterBlockFlag,0}, + {"banterblocktime", GameScript::BanterBlockTime,0}, + {"bashdoor", GameScript::BashDoor,AF_BLOCKING|AF_ALIVE}, //the same until we know better + {"battlesong", GameScript::BattleSong, AF_ALIVE}, + {"berserk", GameScript::Berserk, AF_ALIVE}, + {"bitclear", GameScript::BitClear,AF_MERGESTRINGS}, + {"bitglobal", GameScript::BitGlobal,AF_MERGESTRINGS}, + {"bitset", GameScript::GlobalBOr,AF_MERGESTRINGS}, //probably the same + {"breakinstants", GameScript::BreakInstants, AF_BLOCKING},//delay execution of instants to the next AI cycle??? + {"calllightning", GameScript::Kill, 0}, //TODO: call lightning projectile + {"calm", GameScript::Calm, 0}, + {"changeaiscript", GameScript::ChangeAIScript, 0}, + {"changeaitype", GameScript::ChangeAIType, 0}, + {"changealignment", GameScript::ChangeAlignment, 0}, + {"changeallegiance", GameScript::ChangeAllegiance, 0}, + {"changeanimation", GameScript::ChangeAnimation, 0}, + {"changeanimationnoeffect", GameScript::ChangeAnimationNoEffect, 0}, + {"changeclass", GameScript::ChangeClass, 0}, + {"changecolor", GameScript::ChangeColor, 0}, + {"changecurrentscript", GameScript::ChangeAIScript,AF_SCRIPTLEVEL}, + {"changedestination", GameScript::ChangeDestination,0}, //gemrb extension (iwd hack) + {"changedialog", GameScript::ChangeDialogue, 0}, + {"changedialogue", GameScript::ChangeDialogue, 0}, + {"changegender", GameScript::ChangeGender, 0}, + {"changegeneral", GameScript::ChangeGeneral, 0}, + {"changeenemyally", GameScript::ChangeAllegiance, 0}, //this is the same + {"changefaction", GameScript::SetFaction, 0}, //pst + {"changerace", GameScript::ChangeRace, 0}, + {"changespecifics", GameScript::ChangeSpecifics, 0}, + {"changestat", GameScript::ChangeStat, 0}, + {"changestatglobal", GameScript::ChangeStatGlobal, 0}, + {"changestoremarkup", GameScript::ChangeStoreMarkup, 0},//iwd2 + {"changeteam", GameScript::SetTeam, 0}, //pst + {"changetilestate", GameScript::ChangeTileState, 0}, //bg2 + {"chunkcreature", GameScript::Kill, 0}, //should be more graphical + {"clearactions", GameScript::ClearActions, 0}, + {"clearallactions", GameScript::ClearAllActions, 0}, + {"clearpartyeffects", GameScript::ClearPartyEffects, 0}, + {"clearspriteeffects", GameScript::ClearSpriteEffects, 0}, + {"clicklbuttonobject", GameScript::ClickLButtonObject, AF_BLOCKING}, + {"clicklbuttonpoint", GameScript::ClickLButtonPoint, AF_BLOCKING}, + {"clickrbuttonobject", GameScript::ClickLButtonObject, AF_BLOCKING}, + {"clickrbuttonpoint", GameScript::ClickLButtonPoint, AF_BLOCKING}, + {"closedoor", GameScript::CloseDoor,0}, + {"containerenable", GameScript::ContainerEnable, 0}, + {"continue", GameScript::Continue,AF_IMMEDIATE | AF_CONTINUE}, + {"copygroundpilesto", GameScript::CopyGroundPilesTo, 0}, + {"createcreature", GameScript::CreateCreature, 0}, //point is relative to Sender + {"createcreaturecopypoint", GameScript::CreateCreatureCopyPoint, 0}, //point is relative to Sender + {"createcreaturedoor", GameScript::CreateCreatureDoor, 0}, + {"createcreatureatfeet", GameScript::CreateCreatureAtFeet, 0}, + {"createcreatureatlocation", GameScript::CreateCreatureAtLocation, 0}, + {"createcreatureimpassable", GameScript::CreateCreatureImpassable, 0}, + {"createcreatureimpassableallowoverlap", GameScript::CreateCreatureImpassableAllowOverlap, 0}, + {"createcreatureobject", GameScript::CreateCreatureObjectOffset, 0}, //the same + {"createcreatureobjectcopy", GameScript::CreateCreatureObjectCopy, 0}, + {"createcreatureobjectcopyeffect", GameScript::CreateCreatureObjectCopy, 0}, //the same + {"createcreatureobjectdoor", GameScript::CreateCreatureObjectDoor, 0},//same as createcreatureobject, but with dimension door animation + {"createcreatureobjectoffscreen", GameScript::CreateCreatureObjectOffScreen, 0}, //same as createcreature object, but starts looking for a place far away from the player + {"createcreatureobjectoffset", GameScript::CreateCreatureObjectOffset, 0}, //the same + {"createcreatureoffscreen", GameScript::CreateCreatureOffScreen, 0}, + {"createitem", GameScript::CreateItem, 0}, + {"createitemglobal", GameScript::CreateItemNumGlobal, 0}, + {"createitemnumglobal", GameScript::CreateItemNumGlobal, 0}, + {"createpartygold", GameScript::CreatePartyGold, 0}, + {"createvisualeffect", GameScript::CreateVisualEffect, 0}, + {"createvisualeffectobject", GameScript::CreateVisualEffectObject, 0}, + {"createvisualeffectobjectSticky", GameScript::CreateVisualEffectObjectSticky, 0}, + {"cutsceneid", GameScript::CutSceneID,0}, + {"damage", GameScript::Damage, 0}, + {"daynight", GameScript::DayNight, 0}, + {"deactivate", GameScript::Deactivate, 0}, + {"debug", GameScript::Debug, 0}, + {"debugoutput", GameScript::Debug, 0}, + {"deletejournalentry", GameScript::RemoveJournalEntry, 0}, + {"demoend", GameScript::QuitGame, 0}, //same for now + {"destroyalldestructableequipment", GameScript::DestroyAllDestructableEquipment, 0}, + {"destroyallequipment", GameScript::DestroyAllEquipment, 0}, + {"destroygold", GameScript::DestroyGold, 0}, + {"destroyitem", GameScript::DestroyItem, 0}, + {"destroypartygold", GameScript::DestroyPartyGold, 0}, + {"destroypartyitem", GameScript::DestroyPartyItem, 0}, + {"destroyself", GameScript::DestroySelf, 0}, + {"detectsecretdoor", GameScript::DetectSecretDoor, 0}, + {"dialog", GameScript::Dialogue,AF_BLOCKING}, + {"dialogforceinterrupt", GameScript::DialogueForceInterrupt,AF_BLOCKING}, + {"dialoginterrupt", GameScript::DialogueInterrupt,0}, + {"dialogue", GameScript::Dialogue,AF_BLOCKING}, + {"dialogueforceinterrupt", GameScript::DialogueForceInterrupt,AF_BLOCKING}, + {"dialogueinterrupt", GameScript::DialogueInterrupt,0}, + {"disablefogdither", GameScript::DisableFogDither, 0}, + {"disablespritedither", GameScript::DisableSpriteDither, 0}, + {"displaymessage", GameScript::DisplayMessage, 0}, + {"displaystring", GameScript::DisplayString, 0}, + {"displaystringhead", GameScript::DisplayStringHead, 0}, + {"displaystringheadowner", GameScript::DisplayStringHeadOwner, 0}, + {"displaystringheaddead", GameScript::DisplayStringHead, 0}, //same? + {"displaystringnoname", GameScript::DisplayStringNoName, 0}, + {"displaystringnonamehead", GameScript::DisplayStringNoNameHead, 0}, + {"displaystringwait", GameScript::DisplayStringWait,AF_BLOCKING}, + {"doubleclicklbuttonobject", GameScript::DoubleClickLButtonObject, AF_BLOCKING}, + {"doubleclicklbuttonpoint", GameScript::DoubleClickLButtonPoint, AF_BLOCKING}, + {"doubleclickrbuttonobject", GameScript::DoubleClickLButtonObject, AF_BLOCKING}, + {"doubleclickrbuttonpoint", GameScript::DoubleClickLButtonPoint, AF_BLOCKING}, + {"dropinventory", GameScript::DropInventory, 0}, + {"dropinventoryex", GameScript::DropInventoryEX, 0}, + {"dropinventoryexexclude", GameScript::DropInventoryEX, 0}, //same + {"dropitem", GameScript::DropItem, AF_BLOCKING}, + {"enablefogdither", GameScript::EnableFogDither, 0}, + {"enableportaltravel", GameScript::EnablePortalTravel, 0}, + {"enablespritedither", GameScript::EnableSpriteDither, 0}, + {"endcredits", GameScript::EndCredits, 0},//movie + {"endcutscenemode", GameScript::EndCutSceneMode, 0}, + {"endgame", GameScript::QuitGame, 0}, //ending in iwd2 + {"enemy", GameScript::Enemy, 0}, + {"equipitem", GameScript::EquipItem, 0}, + {"equipmostdamagingmelee",GameScript::EquipMostDamagingMelee,0}, + {"equipranged", GameScript::EquipRanged,0}, + {"equipweapon", GameScript::EquipWeapon,0}, + {"erasejournalentry", GameScript::RemoveJournalEntry, 0}, + {"escapearea", GameScript::EscapeArea, AF_BLOCKING}, + {"escapeareadestroy", GameScript::EscapeAreaDestroy, AF_BLOCKING}, + {"escapeareanosee", GameScript::EscapeAreaNoSee, AF_BLOCKING}, + {"escapeareaobject", GameScript::EscapeAreaObject, AF_BLOCKING}, + {"escapeareaobjectnosee", GameScript::EscapeAreaObjectNoSee, AF_BLOCKING}, + {"exitpocketplane", GameScript::ExitPocketPlane, 0}, + {"expansionendcredits", GameScript::QuitGame, 0},//ends game too + {"explore", GameScript::Explore, 0}, + {"exploremapchunk", GameScript::ExploreMapChunk, 0}, + {"exportparty", GameScript::ExportParty, 0}, + {"face", GameScript::Face,AF_BLOCKING}, + {"faceobject", GameScript::FaceObject, AF_BLOCKING}, + {"facesavedlocation", GameScript::FaceSavedLocation, AF_BLOCKING}, + {"fadefromblack", GameScript::FadeFromColor, AF_BLOCKING}, //probably the same + {"fadefromcolor", GameScript::FadeFromColor, AF_BLOCKING}, + {"fadetoandfromcolor", GameScript::FadeToAndFromColor, AF_BLOCKING}, + {"fadetoblack", GameScript::FadeToColor, AF_BLOCKING}, //probably the same + {"fadetocolor", GameScript::FadeToColor, AF_BLOCKING}, + {"fakeeffectexpirycheck", GameScript::FakeEffectExpiryCheck, 0}, + {"fillslot", GameScript::FillSlot, 0}, + {"finalsave", GameScript::SaveGame, 0}, //synonym + {"findtraps", GameScript::FindTraps, 0}, + {"floatmessage", GameScript::DisplayStringHead, 0}, + {"floatmessagefixed", GameScript::FloatMessageFixed, 0}, + {"floatmessagefixedrnd", GameScript::FloatMessageFixedRnd, 0}, + {"floatmessagernd", GameScript::FloatMessageRnd, 0}, + {"floatrebus", GameScript::FloatRebus, 0}, + {"follow", GameScript::Follow, AF_ALIVE}, + {"followcreature", GameScript::FollowCreature, AF_BLOCKING|AF_ALIVE}, //pst + {"followobjectformation", GameScript::FollowObjectFormation, AF_BLOCKING|AF_ALIVE}, + {"forceaiscript", GameScript::ForceAIScript, 0}, + {"forceattack", GameScript::ForceAttack, 0}, + {"forcefacing", GameScript::ForceFacing, 0}, + {"forcehide", GameScript::ForceHide, 0}, + {"forceleavearealua", GameScript::ForceLeaveAreaLUA, 0}, + {"forcemarkedspell", GameScript::ForceMarkedSpell, 0}, + {"forcespell", GameScript::ForceSpell, AF_BLOCKING}, + {"forcespellpoint", GameScript::ForceSpellPoint, AF_BLOCKING}, + {"forceusecontainer", GameScript::ForceUseContainer,AF_BLOCKING}, + {"formation", GameScript::Formation, AF_BLOCKING}, + {"fullheal", GameScript::FullHeal, 0}, + {"fullhealex", GameScript::FullHeal, 0}, //pst, not sure what's different + {"generatepartymember", GameScript::GeneratePartyMember, 0}, + {"getitem", GameScript::GetItem, 0}, + {"getstat", GameScript::GetStat, 0}, //gemrb specific + {"giveexperience", GameScript::AddXPObject, 0}, + {"givegoldforce", GameScript::CreatePartyGold, 0}, //this is the same + {"giveitem", GameScript::GiveItem, 0}, + {"giveitemcreate", GameScript::CreateItem, 0}, //actually this is a targeted createitem + {"giveorder", GameScript::GiveOrder, 0}, + {"givepartyallequipment", GameScript::GivePartyAllEquipment, 0}, + {"givepartygold", GameScript::GivePartyGold, 0}, + {"givepartygoldglobal", GameScript::GivePartyGoldGlobal,0},//no mergestrings! + {"globaladdglobal", GameScript::GlobalAddGlobal,AF_MERGESTRINGS}, + {"globalandglobal", GameScript::GlobalAndGlobal,AF_MERGESTRINGS}, + {"globalband", GameScript::GlobalBAnd,AF_MERGESTRINGS}, + {"globalbandglobal", GameScript::GlobalBAndGlobal,AF_MERGESTRINGS}, + {"globalbitglobal", GameScript::GlobalBitGlobal, AF_MERGESTRINGS}, + {"globalbor", GameScript::GlobalBOr,AF_MERGESTRINGS}, + {"globalborglobal", GameScript::GlobalBOrGlobal,AF_MERGESTRINGS}, + {"globalmax", GameScript::GlobalMax,AF_MERGESTRINGS}, + {"globalmaxglobal", GameScript::GlobalMaxGlobal,AF_MERGESTRINGS}, + {"globalmin", GameScript::GlobalMin,AF_MERGESTRINGS}, + {"globalminglobal", GameScript::GlobalMinGlobal,AF_MERGESTRINGS}, + {"globalorglobal", GameScript::GlobalOrGlobal,AF_MERGESTRINGS}, + {"globalset", GameScript::SetGlobal,AF_MERGESTRINGS}, + {"globalsetglobal", GameScript::GlobalSetGlobal,AF_MERGESTRINGS}, + {"globalshl", GameScript::GlobalShL,AF_MERGESTRINGS}, + {"globalshlglobal", GameScript::GlobalShLGlobal,AF_MERGESTRINGS}, + {"globalshout", GameScript::GlobalShout, 0}, + {"globalshr", GameScript::GlobalShR,AF_MERGESTRINGS}, + {"globalshrglobal", GameScript::GlobalShRGlobal,AF_MERGESTRINGS}, + {"globalsubglobal", GameScript::GlobalSubGlobal,AF_MERGESTRINGS}, + {"globalxor", GameScript::GlobalXor,AF_MERGESTRINGS}, + {"globalxorglobal", GameScript::GlobalXorGlobal,AF_MERGESTRINGS}, + {"gotostartscreen", GameScript::QuitGame, 0},//ending + {"help", GameScript::Help, 0}, + {"hide", GameScript::Hide, 0}, + {"hideareaonmap", GameScript::HideAreaOnMap, 0}, + {"hidecreature", GameScript::HideCreature, 0}, + {"hidegui", GameScript::HideGUI, 0}, + {"incinternal", GameScript::IncInternal, 0}, //pst + {"incrementinternal", GameScript::IncInternal, 0},//iwd + {"incmoraleai", GameScript::IncMoraleAI, 0}, + {"incrementchapter", GameScript::IncrementChapter, AF_BLOCKING}, + {"incrementextraproficiency", GameScript::IncrementExtraProficiency, 0}, + {"incrementglobal", GameScript::IncrementGlobal,AF_MERGESTRINGS}, + {"incrementglobalonce", GameScript::IncrementGlobalOnce,AF_MERGESTRINGS}, + {"incrementkillstat", GameScript::IncrementKillStat, 0}, + {"incrementproficiency", GameScript::IncrementProficiency, 0}, + {"interact", GameScript::Interact, 0}, + {"joinparty", GameScript::JoinParty, 0}, //this action appears to be blocking in bg2 + {"journalentrydone", GameScript::SetQuestDone, 0}, + {"jumptoobject", GameScript::JumpToObject, 0}, + {"jumptopoint", GameScript::JumpToPoint, 0}, + {"jumptopointinstant", GameScript::JumpToPointInstant, 0}, + {"jumptosavedlocation", GameScript::JumpToSavedLocation, 0}, + {"kill", GameScript::Kill, 0}, + {"killfloatmessage", GameScript::KillFloatMessage, 0}, + {"leader", GameScript::Leader, AF_ALIVE}, + {"leavearea", GameScript::LeaveAreaLUA, 0}, //so far the same + {"leavearealua", GameScript::LeaveAreaLUA, 0}, + {"leavearealuaentry", GameScript::LeaveAreaLUAEntry,AF_BLOCKING}, + {"leavearealuapanic", GameScript::LeaveAreaLUAPanic, 0}, + {"leavearealuapanicentry", GameScript::LeaveAreaLUAPanicEntry,AF_BLOCKING}, + {"leaveparty", GameScript::LeaveParty, 0}, + {"lock", GameScript::Lock, 0},//key not checked at this time! + {"lockscroll", GameScript::LockScroll, 0}, + {"log", GameScript::Debug, 0}, //the same until we know better + {"makeglobal", GameScript::MakeGlobal, 0}, + {"makeunselectable", GameScript::MakeUnselectable, 0}, + {"markobject", GameScript::MarkObject, 0}, + {"markspellandobject", GameScript::MarkSpellAndObject, 0}, + {"moraledec", GameScript::MoraleDec, 0}, + {"moraleinc", GameScript::MoraleInc, 0}, + {"moraleset", GameScript::MoraleSet, 0}, + {"matchhp", GameScript::MatchHP, 0}, + {"movebetweenareas", GameScript::MoveBetweenAreas, 0}, + {"movebetweenareaseffect", GameScript::MoveBetweenAreas, 0}, + {"movecursorpoint", GameScript::MoveCursorPoint, 0},//immediate move + {"moveglobal", GameScript::MoveGlobal, 0}, + {"moveglobalobject", GameScript::MoveGlobalObject, 0}, + {"moveglobalobjectoffscreen", GameScript::MoveGlobalObjectOffScreen, 0}, + {"moveglobalsto", GameScript::MoveGlobalsTo, 0}, + {"transferinventory", GameScript::MoveInventory, 0}, + {"movetocenterofscreen", GameScript::MoveToCenterOfScreen,AF_BLOCKING}, + {"movetoexpansion", GameScript::MoveToExpansion,AF_BLOCKING}, + {"movetoobject", GameScript::MoveToObject,AF_BLOCKING|AF_ALIVE}, + {"movetoobjectfollow", GameScript::MoveToObjectFollow,AF_BLOCKING|AF_ALIVE}, + {"movetoobjectnointerrupt", GameScript::MoveToObjectNoInterrupt,AF_BLOCKING|AF_ALIVE}, + {"movetoobjectuntilsee", GameScript::MoveToObjectUntilSee,AF_BLOCKING|AF_ALIVE}, + {"movetooffset", GameScript::MoveToOffset,AF_BLOCKING|AF_ALIVE}, + {"movetopoint", GameScript::MoveToPoint,AF_BLOCKING|AF_ALIVE}, + {"movetopointnointerrupt", GameScript::MoveToPointNoInterrupt,AF_BLOCKING|AF_ALIVE}, + {"movetopointnorecticle", GameScript::MoveToPointNoRecticle,AF_BLOCKING|AF_ALIVE},//the same until we know better + {"movetosavedlocation", GameScript::MoveToSavedLocation,AF_MERGESTRINGS|AF_BLOCKING}, + //take care of the typo in the original bg2 action.ids + //FIXME: why doesn't this have MERGESTRINGS like the above entry? + {"movetosavedlocationn", GameScript::MoveToSavedLocation,AF_BLOCKING}, + {"moveviewobject", GameScript::MoveViewObject, AF_BLOCKING}, + {"moveviewpoint", GameScript::MoveViewPoint, AF_BLOCKING}, + {"moveviewpointuntildone", GameScript::MoveViewPoint, 0}, + {"nidspecial1", GameScript::NIDSpecial1,AF_BLOCKING|AF_DIRECT|AF_ALIVE},//we use this for dialogs, hack + {"nidspecial2", GameScript::NIDSpecial2,AF_BLOCKING},//we use this for worldmap, another hack + {"nidspecial3", GameScript::Attack,AF_BLOCKING|AF_DIRECT|AF_ALIVE},//this hack is for attacking preset target + {"nidspecial4", GameScript::ProtectObject,AF_BLOCKING|AF_DIRECT|AF_ALIVE}, + {"nidspecial5", GameScript::UseItem, AF_BLOCKING|AF_DIRECT|AF_ALIVE}, + {"nidspecial6", GameScript::Spell, AF_BLOCKING|AF_DIRECT|AF_ALIVE}, + {"nidspecial7", GameScript::UseItemPoint, AF_BLOCKING|AF_ALIVE}, + {"nidspecial8", GameScript::SpellPoint, AF_BLOCKING|AF_ALIVE}, + {"nidspecial9", GameScript::ToggleDoor, AF_BLOCKING},//another internal hack, maybe we should use UseDoor instead + {"noaction", GameScript::NoAction, 0}, + {"opendoor", GameScript::OpenDoor,0}, + {"panic", GameScript::Panic, AF_ALIVE}, + {"permanentstatchange", GameScript::PermanentStatChange, 0}, //pst + {"pausegame", GameScript::PauseGame, AF_BLOCKING}, //this is almost surely blocking + {"picklock", GameScript::PickLock,AF_BLOCKING}, + {"pickpockets", GameScript::PickPockets, AF_BLOCKING}, + {"pickupitem", GameScript::PickUpItem, 0}, + {"playbardsong", GameScript::PlayBardSong, AF_ALIVE}, + {"playdead", GameScript::PlayDead,AF_BLOCKING|AF_ALIVE}, + {"playdeadinterruptable", GameScript::PlayDeadInterruptable,AF_BLOCKING|AF_ALIVE}, + {"playerdialog", GameScript::PlayerDialogue,AF_BLOCKING}, + {"playerdialogue", GameScript::PlayerDialogue,AF_BLOCKING}, + {"playsequence", GameScript::PlaySequence, 0}, + {"playsequencetimed", GameScript::PlaySequenceTimed, 0},//pst + {"playsong", GameScript::StartSong, 0}, + {"playsound", GameScript::PlaySound, 0}, + {"playsoundnotranged", GameScript::PlaySoundNotRanged, 0}, + {"playsoundpoint", GameScript::PlaySoundPoint, 0}, + {"plunder", GameScript::Plunder,AF_BLOCKING|AF_ALIVE}, + {"polymorph", GameScript::Polymorph, 0}, + {"polymorphcopy", GameScript::PolymorphCopy, 0}, + {"polymorphcopybase", GameScript::PolymorphCopyBase, 0}, + {"protectobject", GameScript::ProtectObject, 0}, + {"protectpoint", GameScript::ProtectPoint, AF_BLOCKING}, + {"quitgame", GameScript::QuitGame, 0}, + {"randomfly", GameScript::RandomFly, AF_BLOCKING|AF_ALIVE}, + {"randomrun", GameScript::RandomRun, AF_BLOCKING|AF_ALIVE}, + {"randomturn", GameScript::RandomTurn, AF_BLOCKING}, + {"randomwalk", GameScript::RandomWalk, AF_BLOCKING|AF_ALIVE}, + {"randomwalkcontinuous", GameScript::RandomWalkContinuous, AF_BLOCKING|AF_ALIVE}, + {"realsetglobaltimer", GameScript::RealSetGlobalTimer,AF_MERGESTRINGS}, + {"reallyforcespell", GameScript::ReallyForceSpell, AF_BLOCKING}, + {"reallyforcespelldead", GameScript::ReallyForceSpellDead, AF_BLOCKING}, + {"reallyforcespelllevel", GameScript::ReallyForceSpell, AF_BLOCKING},//this is the same action + {"reallyforcespellpoint", GameScript::ReallyForceSpellPoint, AF_BLOCKING}, + {"recoil", GameScript::Recoil, AF_ALIVE}, + {"regainpaladinhood", GameScript::RegainPaladinHood, 0}, + {"regainrangerhood", GameScript::RegainRangerHood, 0}, + {"removeareaflag", GameScript::RemoveAreaFlag, 0}, + {"removeareatype", GameScript::RemoveAreaType, 0}, + {"removejournalentry", GameScript::RemoveJournalEntry, 0}, + {"removemapnote", GameScript::RemoveMapnote, 0}, + {"removepaladinhood", GameScript::RemovePaladinHood, 0}, + {"removerangerhood", GameScript::RemoveRangerHood, 0}, + {"removespell", GameScript::RemoveSpell, 0}, + {"removetraps", GameScript::RemoveTraps, AF_BLOCKING}, + {"reputationinc", GameScript::ReputationInc, 0}, + {"reputationset", GameScript::ReputationSet, 0}, + {"resetfogofwar", GameScript::UndoExplore, 0}, //pst + {"rest", GameScript::Rest, AF_ALIVE}, + {"restnospells", GameScript::RestNoSpells, 0}, + {"restorepartylocations", GameScript:: RestorePartyLocation, 0}, + {"restparty", GameScript::RestParty, 0}, + {"restuntilhealed", GameScript::RestUntilHealed, 0}, + //this is in iwd2, same as movetosavedlocation, but with stats + {"returntosavedlocation", GameScript::ReturnToSavedLocation, AF_BLOCKING|AF_ALIVE}, + {"returntosavedlocationdelete", GameScript::ReturnToSavedLocationDelete, AF_BLOCKING|AF_ALIVE}, + {"returntosavedplace", GameScript::ReturnToSavedLocation, AF_BLOCKING|AF_ALIVE}, + {"revealareaonmap", GameScript::RevealAreaOnMap, 0}, + {"runawayfrom", GameScript::RunAwayFrom,AF_BLOCKING|AF_ALIVE}, + {"runawayfromnointerrupt", GameScript::RunAwayFromNoInterrupt,AF_BLOCKING|AF_ALIVE}, + {"runawayfromnoleavearea", GameScript::RunAwayFromNoLeaveArea,AF_BLOCKING|AF_ALIVE}, + {"runawayfrompoint", GameScript::RunAwayFromPoint,AF_BLOCKING|AF_ALIVE}, + {"runfollow", GameScript::RunAwayFrom,AF_BLOCKING|AF_ALIVE}, + {"runningattack", GameScript::RunningAttack,AF_BLOCKING|AF_ALIVE}, + {"runningattacknosound", GameScript::RunningAttackNoSound,AF_BLOCKING|AF_ALIVE}, + {"runtoobject", GameScript::RunToObject,AF_BLOCKING|AF_ALIVE}, + {"runtopoint", GameScript::RunToPoint,AF_BLOCKING}, + {"runtopointnorecticle", GameScript::RunToPointNoRecticle,AF_BLOCKING|AF_ALIVE}, + {"runtosavedlocation", GameScript::RunToSavedLocation,AF_BLOCKING|AF_ALIVE}, + {"savegame", GameScript::SaveGame, 0}, + {"savelocation", GameScript::SaveLocation, 0}, + {"saveplace", GameScript::SaveLocation, 0}, + {"saveobjectlocation", GameScript::SaveObjectLocation, 0}, + {"screenshake", GameScript::ScreenShake,AF_BLOCKING}, + {"selectweaponability", GameScript::SelectWeaponAbility, 0}, + {"sendtrigger", GameScript::SendTrigger, 0}, + {"setanimstate", GameScript::PlaySequence, AF_ALIVE},//pst + {"setapparentnamestrref", GameScript::SetApparentName, 0}, + {"setareaflags", GameScript::SetAreaFlags, 0}, + {"setarearestflag", GameScript::SetAreaRestFlag, 0}, + {"setbeeninpartyflags", GameScript::SetBeenInPartyFlags, 0}, + {"setbestweapon", GameScript::SetBestWeapon, 0}, + {"setcorpseenabled", GameScript::AmbientActivate, 0},//another weird name + {"setcutsceneline", GameScript::SetCursorState, 0}, //same as next + {"setcursorstate", GameScript::SetCursorState, 0}, + {"setcreatureareaflag", GameScript::SetCreatureAreaFlag, 0}, + {"setcriticalpathobject", GameScript::SetCriticalPathObject, 0}, + {"setdialog", GameScript::SetDialogue,0}, + {"setdialogrange", GameScript::SetDialogueRange, 0}, + {"setdialogue", GameScript::SetDialogue,0}, + {"setdialoguerange", GameScript::SetDialogueRange, 0}, + {"setdoorflag", GameScript::SetDoorFlag,0}, + {"setdoorlocked", GameScript::SetDoorLocked,0}, + {"setencounterprobability", GameScript::SetEncounterProbability,0}, + {"setextendednight", GameScript::SetExtendedNight, 0}, + {"setfaction", GameScript::SetFaction, 0}, + {"setgabber", GameScript::SetGabber, 0}, + {"setglobal", GameScript::SetGlobal,AF_MERGESTRINGS}, + {"setglobalrandom", GameScript::SetGlobalRandom, AF_MERGESTRINGS}, + {"setglobaltimer", GameScript::SetGlobalTimer,AF_MERGESTRINGS}, + {"setglobaltimeronce", GameScript::SetGlobalTimerOnce,AF_MERGESTRINGS}, + {"setglobaltimerrandom", GameScript::SetGlobalTimerRandom,AF_MERGESTRINGS}, + {"setglobaltint", GameScript::SetGlobalTint, 0}, + {"sethomelocation", GameScript::SetSavedLocation, 0}, //bg2 + {"sethp", GameScript::SetHP, 0}, + {"sethppercent", GameScript::SetHPPercent, 0}, + {"setinternal", GameScript::SetInternal, 0}, + {"setinterrupt", GameScript::SetInterrupt, 0}, + {"setleavepartydialogfile", GameScript::SetLeavePartyDialogFile, 0}, + {"setleavepartydialoguefile", GameScript::SetLeavePartyDialogFile, 0}, + {"setmarkedspell", GameScript::SetMarkedSpell, 0}, + {"setmasterarea", GameScript::SetMasterArea, 0}, + {"setmazeeasier", GameScript::SetMazeEasier, 0}, //pst specific crap + {"setmazeharder", GameScript::SetMazeHarder, 0}, //pst specific crap + {"setmoraleai", GameScript::SetMoraleAI, 0}, + {"setmusic", GameScript::SetMusic, 0}, + {"setname", GameScript::SetApparentName, 0}, + {"setnamelessclass", GameScript::SetNamelessClass, 0}, + {"setnamelessdeath", GameScript::SetNamelessDeath, 0}, + {"setnamelessdisguise", GameScript::SetNamelessDisguise, 0}, + {"setnooneontrigger", GameScript::SetNoOneOnTrigger, 0}, + {"setnumtimestalkedto", GameScript::SetNumTimesTalkedTo, 0}, + {"setplayersound", GameScript::SetPlayerSound, 0}, + {"setquestdone", GameScript::SetQuestDone, 0}, + {"setregularnamestrref", GameScript::SetRegularName, 0}, + {"setrestencounterchance", GameScript::SetRestEncounterChance, 0}, + {"setrestencounterprobabilityday", GameScript::SetRestEncounterProbabilityDay, 0}, + {"setrestencounterprobabilitynight", GameScript::SetRestEncounterProbabilityNight, 0}, + {"setsavedlocation", GameScript::SetSavedLocation, 0}, + {"setsavedlocationpoint", GameScript::SetSavedLocationPoint, 0}, + {"setscriptname", GameScript::SetScriptName, 0}, + {"setselection", GameScript::SetSelection, 0}, + {"setsequence", GameScript::PlaySequence, 0}, //bg2 (only own) + {"setstartpos", GameScript::SetStartPos, 0}, + {"setteam", GameScript::SetTeam, 0}, + {"setteambit", GameScript::SetTeamBit, 0}, + {"settextcolor", GameScript::SetTextColor, 0}, + {"settrackstring", GameScript::SetTrackString, 0}, + {"settoken", GameScript::SetToken, 0}, + {"settoken2da", GameScript::SetToken2DA, 0}, //GemRB specific + {"settokenglobal", GameScript::SetTokenGlobal,AF_MERGESTRINGS}, + {"settokenobject", GameScript::SetTokenObject,0}, + {"setupwish", GameScript::SetupWish, 0}, + {"setupwishobject", GameScript::SetupWishObject, 0}, + {"setvisualrange", GameScript::SetVisualRange, 0}, + {"sg", GameScript::SG, 0}, + {"shout", GameScript::Shout, 0}, + {"sinisterpoof", GameScript::CreateVisualEffect, 0}, + {"smallwait", GameScript::SmallWait,AF_BLOCKING}, + {"smallwaitrandom", GameScript::SmallWaitRandom,AF_BLOCKING}, + {"soundactivate", GameScript::SoundActivate, 0}, + {"spawnptactivate", GameScript::SpawnPtActivate, 0}, + {"spawnptdeactivate", GameScript::SpawnPtDeactivate, 0}, + {"spawnptspawn", GameScript::SpawnPtSpawn, 0}, + {"spell", GameScript::Spell, AF_BLOCKING|AF_ALIVE}, + {"spellcasteffect", GameScript::SpellCastEffect, 0}, + {"spellhiteffectpoint", GameScript::SpellHitEffectPoint, 0}, + {"spellhiteffectsprite", GameScript::SpellHitEffectSprite, 0}, + {"spellnodec", GameScript::SpellNoDec, AF_BLOCKING|AF_ALIVE}, + {"spellpoint", GameScript::SpellPoint, AF_BLOCKING|AF_ALIVE}, + {"spellpointnodec", GameScript::SpellPointNoDec, AF_BLOCKING|AF_ALIVE}, + {"startcombatcounter", GameScript::StartCombatCounter, 0}, + {"startcutscene", GameScript::StartCutScene, 0}, + {"startcutsceneex", GameScript::StartCutScene, 0}, //pst (unknown) + {"startcutscenemode", GameScript::StartCutSceneMode, 0}, + {"startdialog", GameScript::StartDialogue,AF_BLOCKING}, + {"startdialoginterrupt", GameScript::StartDialogueInterrupt,AF_BLOCKING}, + {"startdialogue", GameScript::StartDialogue,AF_BLOCKING}, + {"startdialogueinterrupt", GameScript::StartDialogueInterrupt,AF_BLOCKING}, + {"startdialognoname", GameScript::StartDialogue,AF_BLOCKING}, + {"startdialognoset", GameScript::StartDialogueNoSet,AF_BLOCKING}, + {"startdialognosetinterrupt", GameScript::StartDialogueNoSetInterrupt,AF_BLOCKING}, + {"startdialogoverride", GameScript::StartDialogueOverride,AF_BLOCKING}, + {"startdialogoverrideinterrupt", GameScript::StartDialogueOverrideInterrupt,AF_BLOCKING}, + {"startdialoguenoname", GameScript::StartDialogue,AF_BLOCKING}, + {"startdialoguenoset", GameScript::StartDialogueNoSet,AF_BLOCKING}, + {"startdialoguenosetinterrupt", GameScript::StartDialogueNoSetInterrupt,AF_BLOCKING}, + {"startdialogueoverride", GameScript::StartDialogueOverride,AF_BLOCKING}, + {"startdialogueoverrideinterrupt", GameScript::StartDialogueOverrideInterrupt,AF_BLOCKING}, + {"startmovie", GameScript::StartMovie,AF_BLOCKING}, + {"startmusic", GameScript::StartMusic, 0}, + {"startrainnow", GameScript::StartRainNow, 0}, + {"startrandomtimer", GameScript::StartRandomTimer, 0}, + {"startsong", GameScript::StartSong, 0}, + {"startstore", GameScript::StartStore, 0}, + {"starttimer", GameScript::StartTimer, 0}, + {"stateoverrideflag", GameScript::StateOverrideFlag, 0}, + {"stateoverridetime", GameScript::StateOverrideTime, 0}, + {"staticpalette", GameScript::StaticPalette, 0}, + {"staticsequence", GameScript::PlaySequence, 0},//bg2 animation sequence + {"staticstart", GameScript::StaticStart, 0}, + {"staticstop", GameScript::StaticStop, 0}, + {"stickysinisterpoof", GameScript::CreateVisualEffectObjectSticky, 0}, + {"stopmoving", GameScript::StopMoving, 0}, + {"storepartylocations", GameScript::StorePartyLocation, 0}, + {"swing", GameScript::Swing, AF_ALIVE}, + {"swingonce", GameScript::SwingOnce, AF_ALIVE}, + {"takeitemlist", GameScript::TakeItemList, 0}, + {"takeitemlistparty", GameScript::TakeItemListParty, 0}, + {"takeitemlistpartynum", GameScript::TakeItemListPartyNum, 0}, + {"takeitemreplace", GameScript::TakeItemReplace, 0}, + {"takepartygold", GameScript::TakePartyGold, 0}, + {"takepartyitem", GameScript::TakePartyItem, 0}, + {"takepartyitemall", GameScript::TakePartyItemAll, 0}, + {"takepartyitemnum", GameScript::TakePartyItemNum, 0}, + {"takepartyitemrange", GameScript::TakePartyItemRange, 0}, + {"teleportparty", GameScript::TeleportParty, 0}, + {"textscreen", GameScript::TextScreen, AF_BLOCKING}, + {"timedmovetopoint", GameScript::TimedMoveToPoint,AF_BLOCKING|AF_ALIVE}, + {"tomsstringdisplayer", GameScript::DisplayMessage, 0}, + {"transformitem", GameScript::TransformItem, 0}, + {"transformitemall", GameScript::TransformItemAll, 0}, + {"transformpartyitem", GameScript::TransformPartyItem, 0}, + {"transformpartyitemall", GameScript::TransformPartyItemAll, 0}, + {"triggeractivation", GameScript::TriggerActivation, 0}, + {"triggerwalkto", GameScript::MoveToObject,AF_BLOCKING|AF_ALIVE}, //something like this + {"turn", GameScript::Turn, 0}, + {"turnamt", GameScript::TurnAMT, AF_BLOCKING}, //relative Face() + {"undoexplore", GameScript::UndoExplore, 0}, + {"unhidegui", GameScript::UnhideGUI, 0}, + {"unloadarea", GameScript::UnloadArea, 0}, + {"unlock", GameScript::Unlock, 0}, + {"unlockscroll", GameScript::UnlockScroll, 0}, + {"unmakeglobal", GameScript::UnMakeGlobal, 0}, //this is a GemRB extension + {"usecontainer", GameScript::UseContainer,AF_BLOCKING}, + {"usedoor", GameScript::UseDoor,AF_BLOCKING}, + {"useitem", GameScript::UseItem,AF_BLOCKING}, + {"useitempoint", GameScript::UseItemPoint,AF_BLOCKING}, + {"useitempointslot", GameScript::UseItemPoint,AF_BLOCKING}, + {"useitemslot", GameScript::UseItem,AF_BLOCKING}, + {"vequip",GameScript::SetArmourLevel, 0}, + {"verbalconstant", GameScript::VerbalConstant, 0}, + {"verbalconstanthead", GameScript::VerbalConstantHead, 0}, + {"wait", GameScript::Wait, AF_BLOCKING}, + {"waitanimation", GameScript::WaitAnimation,AF_BLOCKING},//iwd2 + {"waitrandom", GameScript::WaitRandom, AF_BLOCKING}, + {"weather", GameScript::Weather, 0}, + {"xequipitem", GameScript::XEquipItem, 0}, + { NULL,NULL, 0} +}; + +//Make this an ordered list, so we could use bsearch! +static const ObjectLink objectnames[] = { + {"bestac", GameScript::BestAC}, + {"eighthnearest", GameScript::EighthNearest}, + {"eighthnearestdoor", GameScript::EighthNearestDoor}, + {"eighthnearestenemyof", GameScript::EighthNearestEnemyOf}, + {"eighthnearestenemyoftype", GameScript::EighthNearestEnemyOfType}, + {"eighthnearestmygroupoftype", GameScript::EighthNearestEnemyOfType}, + {"eigthnearestenemyof", GameScript::EighthNearestEnemyOf}, //typo in iwd + {"eigthnearestenemyoftype", GameScript::EighthNearestEnemyOfType}, //bg2 + {"eigthnearestmygroupoftype", GameScript::EighthNearestEnemyOfType},//bg2 + {"farthest", GameScript::Farthest}, + {"farthestenemyof", GameScript::FarthestEnemyOf}, + {"fifthnearest", GameScript::FifthNearest}, + {"fifthnearestdoor", GameScript::FifthNearestDoor}, + {"fifthnearestenemyof", GameScript::FifthNearestEnemyOf}, + {"fifthnearestenemyoftype", GameScript::FifthNearestEnemyOfType}, + {"fifthnearestmygroupoftype", GameScript::FifthNearestEnemyOfType}, + {"fourthnearest", GameScript::FourthNearest}, + {"fourthnearestdoor", GameScript::FourthNearestDoor}, + {"fourthnearestenemyof", GameScript::FourthNearestEnemyOf}, + {"fourthnearestenemyoftype", GameScript::FourthNearestEnemyOfType}, + {"fourthnearestmygroupoftype", GameScript::FourthNearestEnemyOfType}, + {"gabber", GameScript::Gabber}, + {"groupof", GameScript::GroupOf}, + {"lastattackerof", GameScript::LastAttackerOf}, + {"lastcommandedby", GameScript::LastCommandedBy}, + {"lastheardby", GameScript::LastHeardBy}, + {"lasthelp", GameScript::LastHelp}, + {"lasthitter", GameScript::LastHitter}, + {"lastmarkedobject", GameScript::LastMarkedObject}, + {"lastseenby", GameScript::LastSeenBy}, + {"lastsummonerof", GameScript::LastSummonerOf}, + {"lasttalkedtoby", GameScript::LastTalkedToBy}, + {"lasttargetedby", GameScript::LastTargetedBy}, + {"lasttrigger", GameScript::LastTrigger}, + {"leaderof", GameScript::LeaderOf}, + {"leastdamagedof", GameScript::LeastDamagedOf}, + {"marked", GameScript::LastMarkedObject}, //pst + {"mostdamagedof", GameScript::MostDamagedOf}, + {"myself", GameScript::Myself}, + {"mytarget", GameScript::MyTarget},//see lasttargetedby(myself) + {"nearest", GameScript::Nearest}, //actually this seems broken in IE and resolve as Myself + {"nearestdoor", GameScript::NearestDoor}, + {"nearestenemyof", GameScript::NearestEnemyOf}, + {"nearestenemyoftype", GameScript::NearestEnemyOfType}, + {"nearestenemysummoned", GameScript::NearestEnemySummoned}, + {"nearestmygroupoftype", GameScript::NearestMyGroupOfType}, + {"nearestpc", GameScript::NearestPC}, + {"ninthnearest", GameScript::NinthNearest}, + {"ninthnearestdoor", GameScript::NinthNearestDoor}, + {"ninthnearestenemyof", GameScript::NinthNearestEnemyOf}, + {"ninthnearestenemyoftype", GameScript::NinthNearestEnemyOfType}, + {"ninthnearestmygroupoftype", GameScript::NinthNearestMyGroupOfType}, + {"nothing", GameScript::Nothing}, + {"player1", GameScript::Player1}, + {"player1fill", GameScript::Player1Fill}, + {"player2", GameScript::Player2}, + {"player2fill", GameScript::Player2Fill}, + {"player3", GameScript::Player3}, + {"player3fill", GameScript::Player3Fill}, + {"player4", GameScript::Player4}, + {"player4fill", GameScript::Player4Fill}, + {"player5", GameScript::Player5}, + {"player5fill", GameScript::Player5Fill}, + {"player6", GameScript::Player6}, + {"player6fill", GameScript::Player6Fill}, + {"player7", GameScript::Player7}, + {"player7fill", GameScript::Player7Fill}, + {"player8", GameScript::Player8}, + {"player8fill", GameScript::Player8Fill}, + {"protectedby", GameScript::ProtectedBy}, + {"protectorof", GameScript::ProtectorOf}, + {"protagonist", GameScript::Protagonist}, + {"secondnearest", GameScript::SecondNearest}, + {"secondnearestdoor", GameScript::SecondNearestDoor}, + {"secondnearestenemyof", GameScript::SecondNearestEnemyOf}, + {"secondnearestenemyoftype", GameScript::SecondNearestEnemyOfType}, + {"secondnearestmygroupoftype", GameScript::SecondNearestMyGroupOfType}, + {"selectedcharacter", GameScript::SelectedCharacter}, + {"seventhnearest", GameScript::SeventhNearest}, + {"seventhnearestdoor", GameScript::SeventhNearestDoor}, + {"seventhnearestenemyof", GameScript::SeventhNearestEnemyOf}, + {"seventhnearestenemyoftype", GameScript::SeventhNearestEnemyOfType}, + {"seventhnearestmygroupoftype", GameScript::SeventhNearestMyGroupOfType}, + {"sixthnearest", GameScript::SixthNearest}, + {"sixthnearestdoor", GameScript::SixthNearestDoor}, + {"sixthnearestenemyof", GameScript::SixthNearestEnemyOf}, + {"sixthnearestenemyoftype", GameScript::SixthNearestEnemyOfType}, + {"sixthnearestmygroupoftype", GameScript::SixthNearestMyGroupOfType}, + {"strongestof", GameScript::StrongestOf}, + {"strongestofmale", GameScript::StrongestOfMale}, + {"tenthnearest", GameScript::TenthNearest}, + {"tenthnearestdoor", GameScript::TenthNearestDoor}, + {"tenthnearestenemyof", GameScript::TenthNearestEnemyOf}, + {"tenthnearestenemyoftype", GameScript::TenthNearestEnemyOfType}, + {"tenthnearestmygroupoftype", GameScript::TenthNearestMyGroupOfType}, + {"thirdnearest", GameScript::ThirdNearest}, + {"thirdnearestdoor", GameScript::ThirdNearestDoor}, + {"thirdnearestenemyof", GameScript::ThirdNearestEnemyOf}, + {"thirdnearestenemyoftype", GameScript::ThirdNearestEnemyOfType}, + {"thirdnearestmygroupoftype", GameScript::ThirdNearestMyGroupOfType}, + {"weakestof", GameScript::WeakestOf}, + {"worstac", GameScript::WorstAC}, + { NULL,NULL} +}; + +static const IDSLink idsnames[] = { + {"align", GameScript::ID_Alignment}, + {"alignmen", GameScript::ID_Alignment}, + {"alignmnt", GameScript::ID_Alignment}, + {"class20", GameScript::ID_AVClass}, + {"class", GameScript::ID_Class}, + {"classmsk", GameScript::ID_ClassMask}, + {"ea", GameScript::ID_Allegiance}, + {"faction", GameScript::ID_Faction}, + {"gender", GameScript::ID_Gender}, + {"general", GameScript::ID_General}, + {"race", GameScript::ID_Race}, + {"specific", GameScript::ID_Specific}, + {"subrace", GameScript::ID_Subrace}, + {"team", GameScript::ID_Team}, + { NULL,NULL} +}; + +static const TriggerLink* FindTrigger(const char* triggername) +{ + if (!triggername) { + return NULL; + } + int len = strlench( triggername, '(' ); + for (int i = 0; triggernames[i].Name; i++) { + if (!strnicmp( triggernames[i].Name, triggername, len )) { + if (!triggernames[i].Name[len]) { + return triggernames + i; + } + } + } + return NULL; +} + +static const ActionLink* FindAction(const char* actionname) +{ + if (!actionname) { + return NULL; + } + int len = strlench( actionname, '(' ); + for (int i = 0; actionnames[i].Name; i++) { + if (!strnicmp( actionnames[i].Name, actionname, len )) { + if (!actionnames[i].Name[len]) { + return actionnames + i; + } + } + } + return NULL; +} + +static const ObjectLink* FindObject(const char* objectname) +{ + if (!objectname) { + return NULL; + } + int len = strlench( objectname, '(' ); + for (int i = 0; objectnames[i].Name; i++) { + if (!strnicmp( objectnames[i].Name, objectname, len )) { + if (!objectnames[i].Name[len]) { + return objectnames + i; + } + } + } + return NULL; +} + +static const IDSLink* FindIdentifier(const char* idsname) +{ + if (!idsname) { + return NULL; + } + int len = (int)strlen( idsname ); + for (int i = 0; idsnames[i].Name; i++) { + if (!strnicmp( idsnames[i].Name, idsname, len )) { + return idsnames + i; + } + } + + printMessage( "GameScript"," ", YELLOW ); + printf( "Couldn't assign ids target: %.*s\n", len, idsname ); + return NULL; +} + +void SetScriptDebugMode(int arg) +{ + InDebug=arg; +} + + + +/********************** Targets **********************************/ + +int Targets::Count() const +{ + return (int)objects.size(); +} + +targettype *Targets::RemoveTargetAt(targetlist::iterator &m) +{ + m=objects.erase(m); + if (m!=objects.end() ) { + return &(*m); + } + return NULL; +} + +const targettype *Targets::GetLastTarget(int Type) +{ + targetlist::const_iterator m = objects.end(); + while (m--!=objects.begin() ) { + if ( (Type==-1) || ((*m).actor->Type==Type) ) { + return &(*(m)); + } + } + return NULL; +} + +const targettype *Targets::GetFirstTarget(targetlist::iterator &m, int Type) +{ + m=objects.begin(); + while (m!=objects.end() ) { + if ( (Type!=-1) && ( (*m).actor->Type!=Type)) { + m++; + continue; + } + return &(*m); + } + return NULL; +} + +const targettype *Targets::GetNextTarget(targetlist::iterator &m, int Type) +{ + m++; + while (m!=objects.end() ) { + if ( (Type!=-1) && ( (*m).actor->Type!=Type)) { + m++; + continue; + } + return &(*m); + } + return NULL; +} + +Scriptable *Targets::GetTarget(unsigned int index, int Type) +{ + targetlist::iterator m = objects.begin(); + while(m!=objects.end() ) { + if ( (Type==-1) || ((*m).actor->Type==Type)) { + if (!index) { + return (*m).actor; + } + index--; + } + m++; + } + return NULL; +} + +//this stuff should be refined, dead actors are sometimes targetable by script? +void Targets::AddTarget(Scriptable* target, unsigned int distance, int ga_flags) +{ + if (!target) { + return; + } + + switch (target->Type) { + case ST_ACTOR: + //i don't know if unselectable actors are targetable by script + //if yes, then remove GA_SELECT + if (ga_flags) { + if (!((Actor *) target)->ValidTarget(ga_flags) ) { + return; + } + } + break; + case ST_GLOBAL: + // this doesn't seem a good idea to allow + return; + default: + break; + } + targettype Target = {target, distance}; + targetlist::iterator m; + for (m = objects.begin(); m != objects.end(); ++m) { + if ( (*m).distance>distance) { + objects.insert( m, Target); + return; + } + } + objects.push_back( Target ); +} + +void Targets::Clear() +{ + objects.clear(); +} + +/** releasing global memory */ +static void CleanupIEScript() +{ + if (SkillStats) + free(SkillStats); + SkillStats = NULL; + SkillCount = -1; + if (ObjectIDSTableNames) + free(ObjectIDSTableNames); + ObjectIDSTableNames = NULL; +} + +void printFunction(Holder table, int index) +{ + const char *str = table->GetStringIndex(index); + int value = table->GetValueIndex(index); + + int len = strchr(str,'(')-str; + if (len<0) { + printf("%d %s\n", value, str); + } else { + printf("%d %.*s\n", value, len, str); + } +} + +void InitializeIEScript() +{ + std::list missing_triggers; + std::list missing_actions; + std::list missing_objects; + std::list::iterator l; + + PluginMgr::Get()->RegisterCleanup(CleanupIEScript); + + InitScriptTables(); + int tT = core->LoadSymbol( "trigger" ); + int aT = core->LoadSymbol( "action" ); + int oT = core->LoadSymbol( "object" ); + int gaT = core->LoadSymbol( "gemact" ); + AutoTable objNameTable("script"); + if (tT < 0 || aT < 0 || oT < 0 || !objNameTable) { + printMessage( "GameScript","A critical scripting file is missing!\n",LIGHT_RED ); + abort(); + } + triggersTable = core->GetSymbol( tT ); + actionsTable = core->GetSymbol( aT ); + objectsTable = core->GetSymbol( oT ); + overrideActionsTable = core->GetSymbol( gaT ); + if (!triggersTable || !actionsTable || !objectsTable || !objNameTable) { + printMessage( "GameScript","A critical scripting file is damaged!\n",LIGHT_RED ); + abort(); + } + + int i; + + /* Loading Script Configuration Parameters */ + + ObjectIDSCount = atoi( objNameTable->QueryField() ); + if (ObjectIDSCount<0 || ObjectIDSCount>MAX_OBJECT_FIELDS) { + printMessage("GameScript","The IDS Count shouldn't be more than 10!\n",LIGHT_RED); + abort(); + } + + ObjectIDSTableNames = (ieResRef *) malloc( sizeof(ieResRef) * ObjectIDSCount ); + for (i = 0; i < ObjectIDSCount; i++) { + const char *idsname; + idsname=objNameTable->QueryField( 0, i + 1 ); + const IDSLink *poi=FindIdentifier( idsname ); + if (poi==NULL) { + idtargets[i]=NULL; + } + else { + idtargets[i]=poi->Function; + } + strnlwrcpy(ObjectIDSTableNames[i], idsname, 8 ); + } + MaxObjectNesting = atoi( objNameTable->QueryField( 1 ) ); + if (MaxObjectNesting<0 || MaxObjectNesting>MAX_NESTING) { + printMessage("GameScript","The Object Nesting Count shouldn't be more than 5!\n", LIGHT_RED); + abort(); + } + HasAdditionalRect = ( atoi( objNameTable->QueryField( 2 ) ) != 0 ); + ExtraParametersCount = atoi( objNameTable->QueryField( 3 ) ); + HasTriggerPoint = ( atoi( objNameTable->QueryField( 4 ) ) != 0 ); + ObjectFieldsCount = ObjectIDSCount - ExtraParametersCount; + + /* Initializing the Script Engine */ + + memset( triggers, 0, sizeof( triggers ) ); + memset( triggerflags, 0, sizeof( triggerflags ) ); + memset( actions, 0, sizeof( actions ) ); + memset( actionflags, 0, sizeof( actionflags ) ); + memset( objects, 0, sizeof( objects ) ); + + int j; + + j = triggersTable->GetSize(); + while (j--) { + i = triggersTable->GetValueIndex( j ); + const TriggerLink* poi = FindTrigger(triggersTable->GetStringIndex( j )); + //maybe we should watch for this bit? + //bool triggerflag = i & 0x4000; + i &= 0x3fff; + if (i >= MAX_TRIGGERS) { + printMessage("GameScript"," ", RED); + printf("trigger %d (%s) is too high, ignoring\n", i, triggersTable->GetStringIndex( j ) ); + continue; + } + if (triggers[i]) { + if (poi && triggers[i]!=poi->Function) { + printMessage("GameScript"," ", YELLOW); + printf("%s is in collision with ", triggersTable->GetStringIndex( j ) ); + printFunction(triggersTable,triggersTable->FindValue(triggersTable->GetValueIndex( j ))); + //printFunction(triggersTable->GetStringIndex(triggersTable->FindValue(triggersTable->GetValueIndex( j )) )); + } else { + if (InDebug&ID_TRIGGERS) { + printMessage("GameScript"," ", WHITE); + printf("%s is a synonym of ", triggersTable->GetStringIndex( j ) ); + printFunction(triggersTable,triggersTable->FindValue(triggersTable->GetValueIndex( j ))); + //printFunction(triggersTable->GetStringIndex(triggersTable->FindValue(triggersTable->GetValueIndex( j ) ) ) ); + } + } + continue; //we already found an alternative + } + if (poi == NULL) { + triggers[i] = NULL; + triggerflags[i] = 0; + missing_triggers.push_back(j); + continue; + } + triggers[i] = poi->Function; + triggerflags[i] = poi->Flags; + } + + for (l = missing_triggers.begin(); l!=missing_triggers.end();l++) { + j = *l; + // found later as a different name + int ii = triggersTable->GetValueIndex( j ) & 0x3fff; + if (ii >= MAX_TRIGGERS) { + continue; + } + + TriggerFunction f = triggers[ii]; + if (f) { + for (i = 0; triggernames[i].Name; i++) { + if (f == triggernames[i].Function) { + if (InDebug&ID_TRIGGERS) { + printMessage("GameScript"," ", WHITE); + printf("%s is a synonym of %s\n", triggersTable->GetStringIndex( j ), triggernames[i].Name ); + break; + } + } + } + continue; + } + printMessage("GameScript","Couldn't assign function to trigger: ", YELLOW); + printFunction(triggersTable,j); +//->GetStringIndex(j) ); + } + + j = actionsTable->GetSize(); + while (j--) { + i = actionsTable->GetValueIndex( j ); + if (i >= MAX_ACTIONS) { + printMessage("GameScript"," ", RED); + printf("action %d (%s) is too high, ignoring\n", i, actionsTable->GetStringIndex( j ) ); + continue; + } + const ActionLink* poi = FindAction( actionsTable->GetStringIndex( j )); + if (actions[i]) { + if (poi && actions[i]!=poi->Function) { + printMessage("GameScript"," ", YELLOW); + printf("%s is in collision with ", actionsTable->GetStringIndex( j ) ); + printFunction(actionsTable, actionsTable->FindValue(actionsTable->GetValueIndex(j))); +//->GetStringIndex(actionsTable->FindValue(actionsTable->GetValueIndex( j )) ) ); + } else { + if (InDebug&ID_ACTIONS) { + printMessage("GameScript"," ", WHITE); + printf("%s is a synonym of ", actionsTable->GetStringIndex( j ) ); + printFunction(actionsTable, actionsTable->FindValue(actionsTable->GetValueIndex( j ))); +//actionsTable->GetStringIndex(actionsTable->FindValue(actionsTable->GetValueIndex( j )) ) ); + } + } + continue; //we already found an alternative + } + if (poi == NULL) { + actions[i] = NULL; + actionflags[i] = 0; + missing_actions.push_back(j); + continue; + } + actions[i] = poi->Function; + actionflags[i] = poi->Flags; + } + + if (overrideActionsTable) { + /* + * we add/replace some actions from gemact.ids + * right now you can't print or generate these actions! + */ + j = overrideActionsTable->GetSize(); + while (j--) { + i = overrideActionsTable->GetValueIndex( j ); + if (i >= MAX_ACTIONS) { + printMessage("GameScript"," ", RED); + printf("action %d (%s) is too high, ignoring\n", i, overrideActionsTable->GetStringIndex( j ) ); + continue; + } + const ActionLink *poi = FindAction( overrideActionsTable->GetStringIndex( j )); + if (!poi) { + continue; + } + if (actions[i]) { + printMessage("GameScript"," ", WHITE); + printf("%s overrides existing action ", overrideActionsTable->GetStringIndex( j ) ); + printFunction( actionsTable, actionsTable->FindValue(overrideActionsTable->GetValueIndex( j ))); + //printFunction( actionsTable->GetStringIndex(actionsTable->FindValue(overrideActionsTable->GetValueIndex( j )) ) ); + } + actions[i] = poi->Function; + actionflags[i] = poi->Flags; + } + } + + for (l = missing_actions.begin(); l!=missing_actions.end();l++) { + j = *l; + // found later as a different name + int ii = actionsTable->GetValueIndex( j ); + if (ii>=MAX_ACTIONS) { + continue; + } + + ActionFunction f = actions[ii]; + if (f) { + for (i = 0; actionnames[i].Name; i++) { + if (f == actionnames[i].Function) { + if (InDebug&ID_ACTIONS) { + printMessage("GameScript"," ", WHITE); + printf("%s is a synonym of %s\n", actionsTable->GetStringIndex( j ), actionnames[i].Name ); + break; + } + } + } + continue; + } + printMessage("GameScript","Couldn't assign function to action: ", YELLOW); + printFunction(actionsTable,j); + //printFunction(actionsTable->GetStringIndex(j) ); + } + + j = objectsTable->GetSize(); + while (j--) { + i = objectsTable->GetValueIndex( j ); + if (i >= MAX_OBJECTS) { + printMessage("GameScript"," ", RED); + printf("object %d (%s) is too high, ignoring\n", i, objectsTable->GetStringIndex( j ) ); + continue; + } + const ObjectLink* poi = FindObject( objectsTable->GetStringIndex( j )); + if (objects[i]) { + if (poi && objects[i]!=poi->Function) { + printMessage("GameScript"," ", YELLOW); + printf("%s is in collision with ", objectsTable->GetStringIndex( j ) ); + printFunction(objectsTable,objectsTable->FindValue(objectsTable->GetValueIndex( j ))); + //printFunction(objectsTable->GetStringIndex(objectsTable->FindValue(objectsTable->GetValueIndex( j )) ) ); + } else { + printMessage("GameScript"," ", WHITE); + printf("%s is a synonym of ", objectsTable->GetStringIndex( j ) ); + printFunction(objectsTable, objectsTable->FindValue(objectsTable->GetValueIndex( j ))); + //printFunction(objectsTable->GetStringIndex(objectsTable->FindValue(objectsTable->GetValueIndex( j )) ) ); + } + continue; + } + if (poi == NULL) { + objects[i] = NULL; + missing_objects.push_back(j); + } else { + objects[i] = poi->Function; + } + } + + for (l = missing_objects.begin(); l!=missing_objects.end();l++) { + j = *l; + // found later as a different name + int ii = objectsTable->GetValueIndex( j ); + if (ii>=MAX_ACTIONS) { + continue; + } + + ObjectFunction f = objects[ii]; + if (f) { + for (i = 0; objectnames[i].Name; i++) { + if (f == objectnames[i].Function) { + printMessage("GameScript"," ", WHITE); + printf("%s is a synonym of %s\n", objectsTable->GetStringIndex( j ), objectnames[i].Name ); + break; + } + } + continue; + } + printMessage("GameScript","Couldn't assign function to object: ", YELLOW); + printFunction(objectsTable,j); + //printFunction(objectsTable->GetStringIndex(j) ); + } + + int instantTableIndex = core->LoadSymbol("instant"); + if (instantTableIndex < 0) { + printMessage("GameScript", "Couldn't find instant symbols!\n", LIGHT_RED); + abort(); + } + Holder instantTable = core->GetSymbol(instantTableIndex); + if (!instantTable) { + printMessage("GameScript", "Couldn't load instant symbols!\n", LIGHT_RED); + abort(); + } + j = instantTable->GetSize(); + while (j--) { + i = instantTable->GetValueIndex( j ); + if (i >= MAX_ACTIONS) { + printMessage("GameScript"," ", RED); + printf("instant action %d (%s) is too high, ignoring\n", i, instantTable->GetStringIndex( j ) ); + continue; + } + if (!actions[i]) { + printMessage("GameScript"," ", YELLOW); + printf("instant action %d (%s) doesn't exist, ignoring\n", i, instantTable->GetStringIndex( j ) ); + continue; + } + actionflags[i] |= AF_INSTANT; + } +} + +/********************** GameScript *******************************/ +GameScript::GameScript(const ieResRef ResRef, Scriptable* MySelf, + int ScriptLevel, bool AIScript) + : MySelf(MySelf) +{ + scriptlevel = ScriptLevel; + lastAction = (unsigned int) ~0; + + strnlwrcpy( Name, ResRef, 8 ); + + script = CacheScript( Name, AIScript); +} + +GameScript::~GameScript(void) +{ + if (script) { + //set 3. parameter to true if you want instant free + //and possible death + if (InDebug&ID_REFERENCE) { + printf("One instance of %s is dropped from %d.\n", Name, BcsCache.RefCount(Name) ); + } + int res = BcsCache.DecRef(script, Name, true); + + if (res<0) { + printMessage( "GameScript", "Corrupted Script cache encountered (reference count went below zero), ", LIGHT_RED ); + printf( "Script name is: %.8s\n", Name); + abort(); + } + if (!res) { + //printf("Freeing script %s because its refcount has reached 0.\n", Name); + script->Release(); + } + script = NULL; + } +} + +Script* GameScript::CacheScript(ieResRef ResRef, bool AIScript) +{ + char line[10]; + + SClass_ID type = AIScript ? IE_BS_CLASS_ID : IE_BCS_CLASS_ID; + + Script *newScript = (Script *) BcsCache.GetResource(ResRef); + if ( newScript ) { + if (InDebug&ID_REFERENCE) { + printf("Caching %s for the %d. time\n", ResRef, BcsCache.RefCount(ResRef) ); + } + return newScript; + } + + DataStream* stream = gamedata->GetResource( ResRef, type ); + if (!stream) { + return NULL; + } + stream->ReadLine( line, 10 ); + if (strncmp( line, "SC", 2 ) != 0) { + printMessage( "GameScript","Not a Compiled Script file\n", YELLOW ); + delete( stream ); + return NULL; + } + newScript = new Script( ); + BcsCache.SetAt( ResRef, (void *) newScript ); + if (InDebug&ID_REFERENCE) { + printf("Caching %s for the %d. time\n", ResRef, BcsCache.RefCount(ResRef) ); + } + + while (true) { + ResponseBlock* rB = ReadResponseBlock( stream ); + if (!rB) + break; + newScript->responseBlocks.push_back( rB ); + stream->ReadLine( line, 10 ); + } + delete( stream ); + return newScript; +} + +static int ParseInt(const char*& src) +{ + char number[33]; + + char* tmp = number; + while (isdigit(*src) || *src=='-') { + *tmp = *src; + tmp++; + src++; + } + *tmp = 0; + if (*src) + src++; + return atoi( number ); +} + +static void ParseString(const char*& src, char* tmp) +{ + while (*src != '"' && *src) { + *tmp = *src; + tmp++; + src++; + } + *tmp = 0; + if (*src) + src++; +} + +static Object* DecodeObject(const char* line) +{ + int i; + const char *origline = line; // for debug below + + Object* oB = new Object(); + for (i = 0; i < ObjectFieldsCount; i++) { + oB->objectFields[i] = ParseInt( line ); + } + for (i = 0; i < MaxObjectNesting; i++) { + oB->objectFilters[i] = ParseInt( line ); + } + //iwd tolerates the missing rectangle, so we do so too + if (HasAdditionalRect && (*line=='[') ) { + line++; //Skip [ + for (i = 0; i < 4; i++) { + oB->objectRect[i] = ParseInt( line ); + } + if (*line == ' ') + line++; //Skip ] (not really... it skips a ' ' since the ] was skipped by the ParseInt function + } + if (*line == '"') + line++; //Skip " + ParseString( line, oB->objectName ); + if (*line == '"') + line++; //Skip " (the same as above) + //this seems to be needed too + if (ExtraParametersCount && *line) { + line++; + } + for (i = 0; i < ExtraParametersCount; i++) { + oB->objectFields[i + ObjectFieldsCount] = ParseInt( line ); + } + if (*line != 'O' || *(line + 1) != 'B') { + printMessage( "GameScript","Got confused parsing object line: ", YELLOW ); + printf("%s\n", origline); + } + //let the object realize it has no future (in case of null objects) + if (oB->ReadyToDie()) { + oB = NULL; + } + return oB; +} + +static Trigger* ReadTrigger(DataStream* stream) +{ + char* line = ( char* ) malloc( 1024 ); + stream->ReadLine( line, 1024 ); + if (strncmp( line, "TR", 2 ) != 0) { + free( line ); + return NULL; + } + stream->ReadLine( line, 1024 ); + Trigger* tR = new Trigger(); + //this exists only in PST? + if (HasTriggerPoint) { + sscanf( line, "%hu %d %d %d %d [%hd,%hd] \"%[^\"]\" \"%[^\"]\" OB", + &tR->triggerID, &tR->int0Parameter, &tR->flags, + &tR->int1Parameter, &tR->int2Parameter, &tR->pointParameter.x, + &tR->pointParameter.y, tR->string0Parameter, tR->string1Parameter ); + } else { + sscanf( line, "%hu %d %d %d %d \"%[^\"]\" \"%[^\"]\" OB", + &tR->triggerID, &tR->int0Parameter, &tR->flags, + &tR->int1Parameter, &tR->int2Parameter, tR->string0Parameter, + tR->string1Parameter ); + } + strlwr(tR->string0Parameter); + strlwr(tR->string1Parameter); + tR->triggerID &= 0x3fff; + stream->ReadLine( line, 1024 ); + tR->objectParameter = DecodeObject( line ); + stream->ReadLine( line, 1024 ); + free( line ); + return tR; +} + +static Condition* ReadCondition(DataStream* stream) +{ + char line[10]; + + stream->ReadLine( line, 10 ); + if (strncmp( line, "CO", 2 ) != 0) { + return NULL; + } + Condition* cO = new Condition(); + while (true) { + Trigger* tR = ReadTrigger( stream ); + if (!tR) + break; + cO->triggers.push_back( tR ); + } + return cO; +} + +/* + * if you pass non-NULL parameters, continuing is set to whether we Continue()ed + * (should start false and be passed to next script's Update), + * and done is set to whether we processed a block without Continue() + */ +bool GameScript::Update(bool *continuing, bool *done) +{ + if (!MySelf) + return false; + + if (!script) + return false; + + //ieDword thisTime = core->GetGame()->Ticks; + //if (( thisTime - lastRunTime ) < scriptRunDelay) { + // return false; + //} + + //lastRunTime = thisTime; + + if(!(MySelf->GetInternalFlag()&IF_ACTIVE) ) { + return true; + } + + bool continueExecution = false; + if (continuing) continueExecution = *continuing; + + RandomNumValue=rand(); + for (size_t a = 0; a < script->responseBlocks.size(); a++) { + ResponseBlock* rB = script->responseBlocks[a]; + if (rB->condition->Evaluate(MySelf)) { + //if this isn't a continue-d block, we have to clear the queue + //we cannot clear the queue and cannot execute the new block + //if we already have stuff on the queue! + if (!continueExecution) { + if (MySelf->GetCurrentAction() || MySelf->GetNextAction()) { + if (MySelf->GetInternalFlag()&IF_NOINT) { + // we presumably don't want any further execution? + if (done) *done = true; + return true; + } + + if (lastAction==a) { + // we presumably don't want any further execution? + // this one is a bit more complicated, due to possible + // interactions with Continue() (lastAction here is always + // the first block encountered), needs more testing + //if (done) *done = true; + return true; + } + + //movetoobjectfollow would break if this isn't called + //(what is broken if it is here?) + MySelf->ClearActions(); + //IE even clears the path, shall we? + //yes we must :) + if (MySelf->Type == ST_ACTOR) { + ((Movable *)MySelf)->ClearPath(); + } + } + lastAction=a; + } + continueExecution = ( rB->responseSet->Execute(MySelf) != 0); + if (continuing) *continuing = continueExecution; + //clear triggers after response executed + //MySelf->ClearTriggers(); + if (!continueExecution) { + if (done) *done = true; + break; + } + //continueExecution = false; + } + } + return true; +} + +//IE simply takes the first action's object for cutscene object +//then adds these actions to its queue: +// SetInterrupt(false), , SetInterrupt(true) + +void GameScript::EvaluateAllBlocks() +{ + if (!MySelf || !(MySelf->GetInternalFlag()&IF_ACTIVE) ) { + return; + } + + if (!script) { + return; + } + +#ifdef GEMRB_CUTSCENES + // this is the (unused) more logical way of executing a cutscene, which + // evaluates conditions and doesn't just use the first response + for (size_t a = 0; a < script->responseBlocks.size(); a++) { + ResponseBlock* rB = script->responseBlocks[a]; + if (rB->Condition->Evaluate(MySelf)) { + // TODO: this no longer works since the cutscene changes + rB->Execute(MySelf); + } + } +#else + // this is the original IE behaviour: + // cutscenes don't evaluate conditions - they just choose the + // first response, take the object from the first action, + // and then add the actions to that object's queue. + for (size_t a = 0; a < script->responseBlocks.size(); a++) { + ResponseBlock* rB = script->responseBlocks[a]; + ResponseSet * rS = rB->responseSet; + if (rS->responses.size()) { + Response *response = rS->responses[0]; + if (response->actions.size()) { + Action *action = response->actions[0]; + Scriptable *target = GetActorFromObject(MySelf, action->objects[1]); + if (target) { + // TODO: sometimes SetInterrupt(false) and SetInterrupt(true) are added before/after? + rS->responses[0]->Execute(target); + // TODO: this will break blocking instants, if there are any + target->ReleaseCurrentAction(); + } else if (InDebug&ID_CUTSCENE) { + printMessage("GameScript","Failed to find CutSceneID target!\n",YELLOW); + if (action->objects[1]) { + action->objects[1]->Dump(); + } + } + } + } + } +#endif +} + +ResponseBlock* GameScript::ReadResponseBlock(DataStream* stream) +{ + char line[10]; + + stream->ReadLine( line, 10 ); + if (strncmp( line, "CR", 2 ) != 0) { + return NULL; + } + ResponseBlock* rB = new ResponseBlock(); + rB->condition = ReadCondition( stream ); + rB->responseSet = ReadResponseSet( stream ); + return rB; +} + +ResponseSet* GameScript::ReadResponseSet(DataStream* stream) +{ + char line[10]; + + stream->ReadLine( line, 10 ); + if (strncmp( line, "RS", 2 ) != 0) { + return NULL; + } + ResponseSet* rS = new ResponseSet(); + while (true) { + Response* rE = ReadResponse( stream ); + if (!rE) + break; + rS->responses.push_back( rE ); + } + return rS; +} + +//this is the border of the GameScript object (all subsequent functions are library functions) +//we can't make this a library function, because scriptlevel is set here +Response* GameScript::ReadResponse(DataStream* stream) +{ + char* line = ( char* ) malloc( 1024 ); + stream->ReadLine( line, 1024 ); + if (strncmp( line, "RE", 2 ) != 0) { + free( line ); + return NULL; + } + Response* rE = new Response(); + rE->weight = 0; + int count = stream->ReadLine( line, 1024 ); + char *poi; + rE->weight = (unsigned char)strtoul(line,&poi,10); + if (strncmp(poi,"AC",2)==0) + while (true) { + //not autofreed, because it is referenced by the Script + Action* aC = new Action(false); + count = stream->ReadLine( line, 1024 ); + aC->actionID = (unsigned short)strtoul(line, NULL,10); + for (int i = 0; i < 3; i++) { + stream->ReadLine( line, 1024 ); + Object* oB = DecodeObject( line ); + aC->objects[i] = oB; + if (i != 2) + stream->ReadLine( line, 1024 ); + } + stream->ReadLine( line, 1024 ); + sscanf( line, "%d %hd %hd %d %d\"%[^\"]\" \"%[^\"]\" AC", + &aC->int0Parameter, &aC->pointParameter.x, &aC->pointParameter.y, + &aC->int1Parameter, &aC->int2Parameter, aC->string0Parameter, + aC->string1Parameter ); + strlwr(aC->string0Parameter); + strlwr(aC->string1Parameter); + if (aC->actionID>=MAX_ACTIONS) { + aC->actionID=0; + printMessage("GameScript","Invalid script action ID!",LIGHT_RED); + } else { + if (actionflags[aC->actionID] & AF_SCRIPTLEVEL) { + aC->int0Parameter = scriptlevel; + } + } + rE->actions.push_back( aC ); + stream->ReadLine( line, 1024 ); + if (strncmp( line, "RE", 2 ) == 0) + break; + } + free( line ); + return rE; +} + +void GameScript::ExecuteString(Scriptable* Sender, char* String) +{ + if (String[0] == 0) { + return; + } + Action* act = GenerateAction( String ); + if (!act) { + return; + } + Sender->AddActionInFront(act); +} + +//This must return integer because Or(3) returns 3 +int GameScript::EvaluateString(Scriptable* Sender, char* String) +{ + if (String[0] == 0) { + return 0; + } + Trigger* tri = GenerateTrigger( String ); + if (tri) { + int ret = tri->Evaluate(Sender); + tri->Release(); + return ret; + } + return 0; +} + +bool Condition::Evaluate(Scriptable* Sender) +{ + int ORcount = 0; + unsigned int result = 0; + bool subresult = true; + + for (size_t i = 0; i < triggers.size(); i++) { + Trigger* tR = triggers[i]; + //do not evaluate triggers in an Or() block if one of them + //was already True() + if (!ORcount || !subresult) { + result = tR->Evaluate(Sender); + } + if (result > 1) { + //we started an Or() block + if (ORcount) { + printMessage( "GameScript","Unfinished OR block encountered!\n",YELLOW ); + } + ORcount = result; + subresult = false; + continue; + } + if (ORcount) { + subresult |= ( result != 0 ); + if (--ORcount) { + continue; + } + result = subresult; + } + if (!result) { + return 0; + } + } + if (ORcount) { + printMessage( "GameScript","Unfinished OR block encountered!\n",YELLOW ); + } + return 1; +} + +/* this may return more than a boolean, in case of Or(x) */ +int Trigger::Evaluate(Scriptable* Sender) +{ + if (!this) { + printMessage( "GameScript","Trigger evaluation fails due to NULL trigger.\n",LIGHT_RED ); + return 0; + } + TriggerFunction func = triggers[triggerID]; + const char *tmpstr=triggersTable->GetValue(triggerID); + if (!tmpstr) { + tmpstr=triggersTable->GetValue(triggerID|0x4000); + } + if (!func) { + triggers[triggerID] = GameScript::False; + printMessage("GameScript"," ",YELLOW); + printf("Unhandled trigger code: 0x%04x %s\n", + triggerID, tmpstr ); + return 0; + } + if (InDebug&ID_TRIGGERS) { + printMessage("GameScript"," ",YELLOW); + printf( "Executing trigger code: 0x%04x %s\n", + triggerID, tmpstr ); + } + int ret = func( Sender, this ); + if (flags & NEGATE_TRIGGER) { + return !ret; + } + return ret; +} + +int ResponseSet::Execute(Scriptable* Sender) +{ + size_t i; + + switch(responses.size()) { + case 0: + return 0; + case 1: + return responses[0]->Execute(Sender); + } + /*default*/ + int randWeight; + int maxWeight = 0; + + for (i = 0; i < responses.size(); i++) { + maxWeight += responses[i]->weight; + } + if (maxWeight) { + randWeight = rand() % maxWeight; + } + else { + randWeight = 0; + } + + for (i = 0; i < responses.size(); i++) { + Response* rE = responses[i]; + if (rE->weight > randWeight) { + return rE->Execute(Sender); + /* this break is only symbolic */ + break; + } + randWeight-=rE->weight; + } + return 0; +} + +//continue is effective only as the last action in the block +int Response::Execute(Scriptable* Sender) +{ + int ret = 0; // continue or not + for (size_t i = 0; i < actions.size(); i++) { + Action* aC = actions[i]; + switch (actionflags[aC->actionID] & AF_MASK) { + case AF_IMMEDIATE: + GameScript::ExecuteAction( Sender, aC ); + ret = 0; + break; + case AF_NONE: + Sender->AddAction( aC ); + ret = 0; + break; + case AF_CONTINUE: + case AF_MASK: + ret = 1; + break; + } + } + return ret; +} + +void PrintAction(int actionID) +{ + printf("Action: %d %s\n", actionID , actionsTable->GetValue(actionID) ); +} + +void GameScript::ExecuteAction(Scriptable* Sender, Action* aC) +{ + int actionID = aC->actionID; + + if (aC->objects[0]) { + Scriptable *scr = GetActorFromObject(Sender, aC->objects[0]); + + aC->IncRef(); // if aC is us, we don't want it deleted! + Sender->ReleaseCurrentAction(); + + if (scr) { + if (InDebug&ID_ACTIONS) { + printMessage("GameScript"," ",YELLOW); + printf("Sender: %s-->override: %s\n",Sender->GetScriptName(), scr->GetScriptName() ); + } + scr->ReleaseCurrentAction(); + scr->AddAction(ParamCopyNoOverride(aC)); + if (!(actionflags[actionID] & AF_INSTANT)) { + assert(scr->GetNextAction()); + // TODO: below was written before i added instants, this might be unnecessary now + + // there are plenty of places where it's vital that ActionOverride is not interrupted and if + // there are actions left on the queue after the release above, we can't instant-execute, + // so this is my best guess for now.. + scr->CurrentActionInterruptable = false; + } + } else { + printMessage("GameScript","Actionoverride failed for object: \n",LIGHT_RED); + aC->objects[0]->Dump(); + } + + aC->Release(); + return; + } + if (InDebug&ID_ACTIONS) { + printMessage("GameScript"," ",YELLOW); + PrintAction(actionID); + printf("Sender: %s\n",Sender->GetScriptName() ); + } + ActionFunction func = actions[actionID]; + if (func) { + //turning off interruptable flag + //uninterruptable actions will set it back + if (Sender->Type==ST_ACTOR) { + Sender->Activate(); + if (actionflags[actionID]&AF_ALIVE) { + if (Sender->GetInternalFlag()&IF_STOPATTACK) { + printMessage("GameScript", "Aborted action due to death\n", YELLOW); + Sender->ReleaseCurrentAction(); + return; + } + } + } + func( Sender, aC ); + } else { + actions[actionID] = NoActionAtAll; + printMessage("GameScript", "Unknown ", YELLOW); + textcolor(YELLOW); + PrintAction(actionID); + Sender->ReleaseCurrentAction(); + textcolor(WHITE); + return; + } + + //don't bother with special flow control actions + if (actionflags[actionID] & AF_IMMEDIATE) { + //this action never entered the action queue, therefore shouldn't be freed + if (aC->GetRef()!=1) { + printf("Immediate action got queued!\n"); + PrintAction(actionID); + abort(); + } + return; + } + + //Releasing nonblocking actions, blocking actions will release themselves + if (!( actionflags[actionID] & AF_BLOCKING )) { + Sender->ReleaseCurrentAction(); + //aC is invalid beyond this point, so we return! + return; + } +} + +Trigger* GenerateTrigger(char* String) +{ + strlwr( String ); + if (InDebug&ID_TRIGGERS) { + printMessage("GameScript"," ",YELLOW); + printf("Compiling:%s\n",String); + } + int negate = 0; + if (*String == '!') { + String++; + negate = 1; + } + int len = strlench(String,'(')+1; //including ( + int i = triggersTable->FindString(String, len); + if (i<0) { + printMessage("GameScript"," ",LIGHT_RED); + printf("Invalid scripting trigger: %s\n", String); + return NULL; + } + char *src = String+len; + char *str = triggersTable->GetStringIndex( i )+len; + Trigger *trigger = GenerateTriggerCore(src, str, i, negate); + if (!trigger) { + printMessage("GameScript"," ",LIGHT_RED); + printf("Malformed scripting trigger: %s\n", String); + return NULL; + } + return trigger; +} + +Action* GenerateAction(char* String) +{ + strlwr( String ); + if (InDebug&ID_ACTIONS) { + printMessage("GameScript"," ",YELLOW); + printf("Compiling:%s\n",String); + } + int len = strlench(String,'(')+1; //including ( + char *src = String+len; + int i = -1; + char *str; + unsigned short actionID; + if (overrideActionsTable) { + i = overrideActionsTable->FindString(String, len); + if (i >= 0) { + str = overrideActionsTable->GetStringIndex( i )+len; + actionID = overrideActionsTable->GetValueIndex(i); + } + } + if (i<0) { + i = actionsTable->FindString(String, len); + if (i < 0) { + printMessage("GameScript"," ",LIGHT_RED); + printf("Invalid scripting action: %s\n", String); + return NULL; + } + str = actionsTable->GetStringIndex( i )+len; + actionID = actionsTable->GetValueIndex(i); + } + Action *action = GenerateActionCore( src, str, actionID); + if (!action) { + printMessage("GameScript"," ",LIGHT_RED); + printf("Malformed scripting action: %s\n", String); + return NULL; + } + return action; +} + +Action* GenerateActionDirect(char *String, Scriptable *object) +{ + Action* action = GenerateAction(String); + Object *tmp = action->objects[1]; + if (tmp && tmp->objectFields[0]==-1) { + tmp->objectFields[1] = object->GetGlobalID(); + } + action->pointParameter.empty(); + return action; +} + +/** Self-destructing object if it is empty */ +bool Object::ReadyToDie() +{ + if (objectName[0]!=0) { + return false; + } + if (objectFilters[0]) { + return false; + } + for (int i=0;i +#include + +class Action; +class GameScript; + +//escapearea flags +#define EA_DESTROY 1 //destroy actor at the exit (otherwise move to new place) +#define EA_NOSEE 2 //no need to see the exit + +//displaystring flags +#define DS_WAIT 1 +#define DS_HEAD 2 +#define DS_CONSOLE 4 +#define DS_CONST 8 +#define DS_NONAME 16 +#define DS_SILENT 32 +#define DS_SPEECH 64 +#define DS_AREA 128 + +//verbal constant (bg2), we need a lookup table for other games +#define VB_PANIC 1 +#define VB_HAPPY 2 +#define VB_UNHAPPY 3 +#define VB_LEADER 6 +#define VB_TIRED 7 +#define VB_BORED 8 +#define VB_ATTACK 9 +#define VB_DAMAGE 18 +#define VB_DIE 19 +#define VB_SELECT 26 +#define VB_INSULT 44 +#define VB_COMPLIMENT 47 +#define VB_SPECIAL 50 +#define VB_REACT 53 +#define VB_REACT_S 54 +#define VB_RESP_COMP 55 +#define VB_RESP_INS 58 +#define VB_HOSTILE 59 +#define VB_DIALOG 60 +#define VB_CRITHIT 65 +#define VB_CRITMISS 66 +#define VB_TIMMUNE 67 +#define VB_INVENTORY 68 +#define VB_PP_SUCC 69 +#define VB_BIO 74 + +//diffmode (iwd2) +#define DM_EQUAL 1 +#define DM_LESS 2 +#define DM_GREATER 3 + +//markspellandobject (iwd2) +#define MSO_IGNORE_SEE 1 +#define MSO_IGNORE_INVALID 2 +#define MSO_RANDOM_SPELL 4 +#define MSO_IGNORE_HAVE 8 +#define MSO_IGNORE_RANGE 16 +#define MSO_IGNORE_NULL 32 + +//delta (pst) +#define DM_LOWER 1 +#define DM_RAISE 2 +#define DM_SET 3 + +//attack core flags +#define AC_NO_SOUND 1 +#define AC_RUNNING 2 + +//trigger flags stored in triggers in .bcs files +#define NEGATE_TRIGGER 1 + +#define MAX_OBJECT_FIELDS 10 +#define MAX_NESTING 5 + +#define GSASSERT(f,c) \ + if(!(f)) \ + { \ + printf("Assertion failed: %s [0x%08lX] Line %d",#f, c, __LINE__); \ + abort(); \ + } + +typedef std::vector SrcVector; + +struct targettype { + Scriptable *actor; //hmm, could be door + unsigned int distance; +}; + +typedef std::list targetlist; + +class GEM_EXPORT Targets { +public: + Targets() + { + } + + ~Targets() + { + Clear(); + } +private: + targetlist objects; +public: + int Count() const; + targettype *RemoveTargetAt(targetlist::iterator &m); + const targettype *GetNextTarget(targetlist::iterator &m, int Type); + const targettype *GetLastTarget(int Type); + const targettype *GetFirstTarget(targetlist::iterator &m, int Type); + Scriptable *GetTarget(unsigned int index, int Type); + void AddTarget(Scriptable* target, unsigned int distance, int flags); + void Clear(); +}; + +class GEM_EXPORT Object { +public: + Object() + { + objectName[0] = 0; + + memset( objectFields, 0, MAX_OBJECT_FIELDS * sizeof( int ) ); + memset( objectFilters, 0, MAX_NESTING * sizeof( int ) ); + memset( objectRect, 0, 4 * sizeof( int ) ); + + canary = (unsigned long) 0xdeadbeef; + } + ~Object() + { + } +public: + int objectFields[MAX_OBJECT_FIELDS]; + int objectFilters[MAX_NESTING]; + int objectRect[4]; + char objectName[65]; +private: + volatile unsigned long canary; +public: + void Dump() + { + int i; + + GSASSERT( canary == (unsigned long) 0xdeadbeef, canary ); + if(objectName[0]) { + printf("Object: %s\n",objectName); + return; + } + printf("IDS Targeting: "); + for(i=0;iRelease(); + objectParameter = NULL; + } + } + int Evaluate(Scriptable* Sender); +public: + unsigned short triggerID; + int int0Parameter; + int flags; + int int1Parameter; + int int2Parameter; + Point pointParameter; + char string0Parameter[65]; + char string1Parameter[65]; + Object* objectParameter; +private: + volatile unsigned long canary; +public: + void Dump() + { + GSASSERT( canary == (unsigned long) 0xdeadbeef, canary ); + printf ("Trigger: %d\n", triggerID); + printf ("Int parameters: %d %d %d\n", int0Parameter, int1Parameter, int2Parameter); + printf ("Point: [%d.%d]\n", pointParameter.x, pointParameter.y); + printf ("String0: %s\n", string0Parameter); + printf ("String1: %s\n", string1Parameter); + if (objectParameter) { + objectParameter->Dump(); + } else { + printf("No object\n"); + } + printf("\n"); + } + + void Release() + { + GSASSERT( canary == (unsigned long) 0xdeadbeef, canary ); + canary = 0xdddddddd; + delete this; + } +}; + +class GEM_EXPORT Condition { +public: + Condition() + { + canary = (unsigned long) 0xdeadbeef; + } + ~Condition() + { + for (size_t c = 0; c < triggers.size(); ++c) { + if (triggers[c]) { + triggers[c]->Release(); + triggers[c] = NULL; + } + } + } + bool Evaluate(Scriptable* Sender); +public: + std::vector triggers; +private: + volatile unsigned long canary; +public: + void Release() + { + GSASSERT( canary == (unsigned long) 0xdeadbeef, canary ); + canary = 0xdddddddd; + delete this; + } +}; + +class GEM_EXPORT Action { +public: + Action(bool autoFree) + { + actionID = 0; + objects[0] = NULL; + objects[1] = NULL; + objects[2] = NULL; + string0Parameter[0] = 0; + string1Parameter[0] = 0; + int0Parameter = 0; + pointParameter.null(); + int1Parameter = 0; + int2Parameter = 0; + //changed now + if (autoFree) { + RefCount = 0; //refcount will be increased by each AddAction + } else { + RefCount = 1; //one reference hold by the script + } + canary = (unsigned long) 0xdeadbeef; + } + ~Action() + { + for (int c = 0; c < 3; c++) { + if (objects[c]) { + objects[c]->Release(); + objects[c] = NULL; + } + } + } +public: + unsigned short actionID; + Object* objects[3]; + int int0Parameter; + Point pointParameter; + int int1Parameter; + int int2Parameter; + char string0Parameter[65]; + char string1Parameter[65]; +private: + int RefCount; + volatile unsigned long canary; +public: + int GetRef() { + return RefCount; + } + void Dump() + { + int i; + + GSASSERT( canary == (unsigned long) 0xdeadbeef, canary ); + printf("Int0: %d, Int1: %d, Int2: %d\n",int0Parameter, int1Parameter, int2Parameter); + printf("String0: %s, String1: %s\n", string0Parameter?string0Parameter:"", string1Parameter?string1Parameter:""); + for (i=0;i<3;i++) { + if (objects[i]) { + printf( "%d. ",i+1); + objects[i]->Dump(); + } else { + printf( "%d. Object - NULL\n",i+1); + } + } + + printf("RefCount: %d\n", RefCount); + } + + void Release() + { + GSASSERT( canary == (unsigned long) 0xdeadbeef, canary ); + if (!RefCount) { + printf( "WARNING!!! Double Freeing in %s: Line %d\n", __FILE__, + __LINE__ ); + abort(); + } + RefCount--; + if (!RefCount) { + canary = 0xdddddddd; + delete this; + } + } + void IncRef() + { + GSASSERT( canary == (unsigned long) 0xdeadbeef, canary ); + RefCount++; + if (RefCount >= 65536) { + printf( "Refcount increased to: %d in action %d\n", RefCount, + actionID ); + abort(); + } + } +}; + +class GEM_EXPORT Response { +public: + Response() + { + weight = 0; + canary = (unsigned long) 0xdeadbeef; + } + ~Response() + { + for (size_t c = 0; c < actions.size(); c++) { + if (actions[c]) { + if (actions[c]->GetRef()>2) { + printf("Residue action %d with refcount %d\n", actions[c]->actionID, actions[c]->GetRef()); + } + actions[c]->Release(); + actions[c] = NULL; + } + } + } + int Execute(Scriptable* Sender); +public: + unsigned char weight; + std::vector actions; +private: + volatile unsigned long canary; +public: + void Release() + { + GSASSERT( canary == (unsigned long) 0xdeadbeef, canary ); + canary = 0xdddddddd; + delete this; + } +}; + +class GEM_EXPORT ResponseSet { +public: + ResponseSet() + { + canary = (unsigned long) 0xdeadbeef; + } + ~ResponseSet() + { + for (size_t b = 0; b < responses.size(); b++) { + responses[b]->Release(); + responses[b] = NULL; + } + } + int Execute(Scriptable* Sender); +public: + std::vector responses; +private: + volatile unsigned long canary; +public: + void Release() + { + GSASSERT( canary == (unsigned long) 0xdeadbeef, canary ); + canary = 0xdddddddd; + delete this; + } +}; + +class GEM_EXPORT ResponseBlock { +public: + ResponseBlock() + { + condition = NULL; + responseSet = NULL; + canary = (unsigned long) 0xdeadbeef; + } + ~ResponseBlock() + { + if (condition) { + condition->Release(); + condition = NULL; + } + if (responseSet) { + responseSet->Release(); + responseSet = NULL; + } + } +public: + Condition* condition; + ResponseSet* responseSet; +private: + volatile unsigned long canary; +public: + void Release() + { + GSASSERT( canary == (unsigned long) 0xdeadbeef, canary ); + canary = 0xdddddddd; + delete this; + } +}; + +class GEM_EXPORT Script { +public: + Script() + { + canary = (unsigned long) 0xdeadbeef; + } + ~Script() + { + for (unsigned int i = 0; i < responseBlocks.size(); i++) { + if (responseBlocks[i]) { + responseBlocks[i]->Release(); + responseBlocks[i] = NULL; + } + } + } +public: + std::vector responseBlocks; +private: + volatile unsigned long canary; +public: + void Release() + { + GSASSERT( canary == (unsigned long) 0xdeadbeef, canary ); + canary = 0xdddddddd; + delete this; + } +}; + +typedef int (* TriggerFunction)(Scriptable*, Trigger*); +typedef void (* ActionFunction)(Scriptable*, Action*); +typedef Targets* (* ObjectFunction)(Scriptable *, Targets*, int ga_flags); +typedef int (* IDSFunction)(Actor *, int parameter); + +#define TF_NONE 0 +#define TF_CONDITION 1 //this isn't a trigger, just a condition (0x4000) +#define TF_MERGESTRINGS 8 //same value as actions' mergestring + +struct TriggerLink { + const char* Name; + TriggerFunction Function; + short Flags; +}; + +//createcreature flags +#define CC_OFFSET 1 +#define CC_OBJECT 2 +#define CC_OFFSCREEN 3 +#define CC_MASK 3 +#define CC_CHECK_IMPASSABLE 4 //adjust position (searchmap) +#define CC_PLAY_ANIM 8 //play animation +#define CC_STRING1 16 //resref is in second string +#define CC_CHECK_OVERLAP 32 //other actors +#define CC_COPY 64 //copy appearance +#define CC_SCRIPTNAME 128 //scriptname in 2nd string + +//begindialog flags +#define BD_STRING0 0 +#define BD_TARGET 1 +#define BD_SOURCE 2 +#define BD_RESERVED 3 //playerX resref +#define BD_INTERACT 4 //banter dialogs +#define BD_LOCMASK 7 //where is the dialog resref +#define BD_TALKCOUNT 8 //increases talkcount +#define BD_SETDIALOG 16 //also sets dialog (for string0) +#define BD_CHECKDIST 32 //checks distance, if needs, walks up +#define BD_OWN 64 //source == target, works for player only +#define BD_INTERRUPT 128 //interrupts action +#define BD_NUMERIC 256 //target is numeric +#define BD_ITEM 512 //talk to an item +#define BD_NOEMPTY 1024 //don't display '... has nothing to say to you' + +#define AF_NONE 0 +#define AF_IMMEDIATE 1 +#define AF_CONTINUE 2 +#define AF_MASK 3 //none, immediate or continue +#define AF_BLOCKING 4 +#define AF_MERGESTRINGS 8 +//we could use this flag to restrict player scripts from using dangerous +//opcodes, it would be a very useful and easy to implement feature! +#define AF_RESTRICTED 16 +//#define AF_RESTRICTED_LEVEL2 32 //maybe we could use 2 bits for this??? +#define AF_SCRIPTLEVEL 64 //this hack will transfer scriptlevel to int0parameter at runtime (changecurrentscript relies on it) +#define AF_INVALID 128 +#define AF_DIRECT 256 //this hack will transfer target from gamecontrol to object1 at compile time +#define AF_ALIVE 512 //only alive actors can do this +#define AF_INSTANT 1024 + +struct ActionLink { + const char* Name; + ActionFunction Function; + short Flags; +}; + +struct ObjectLink { + const char* Name; + ObjectFunction Function; +}; + +struct IDSLink { + const char* Name; + IDSFunction Function; +}; + +#define MAX_TRIGGERS 0xFF +#define MAX_ACTIONS 400 +#define MAX_OBJECTS 128 +#define AI_SCRIPT_LEVEL 4 //the script level of special ai scripts + +extern void SetScriptDebugMode(int arg); +extern int RandomNumValue; + +class GEM_EXPORT GameScript { +public: + GameScript(const ieResRef ResRef, Scriptable* Myself, + int ScriptLevel = 0, bool AIScript = false); + ~GameScript(); + const char *GetName() { return this?Name:"NONE\0\0\0\0"; } + static void ExecuteString(Scriptable* Sender, char* String); + static int EvaluateString(Scriptable* Sender, char* String); + static void ExecuteAction(Scriptable* Sender, Action* aC); +public: + bool Update(bool *continuing = NULL, bool *done = NULL); + void EvaluateAllBlocks(); +private: //Internal Functions + Script* CacheScript(ieResRef ResRef, bool AIScript); + ResponseBlock* ReadResponseBlock(DataStream* stream); + ResponseSet* ReadResponseSet(DataStream* stream); + Response* ReadResponse(DataStream* stream); + Trigger* ReadTrigger(DataStream* stream); + static int ParseInt(const char*& src); + static void ParseString(const char*& src, char* tmp); +private: //Internal variables + Scriptable* const MySelf; + ieResRef Name; + Script* script; + unsigned int lastAction; + int scriptlevel; +public: //Script Functions + static int ID_Alignment(Actor *actor, int parameter); + static int ID_Allegiance(Actor *actor, int parameter); + static int ID_AVClass(Actor *actor, int parameter); + static int ID_Class(Actor *actor, int parameter); + static int ID_ClassMask(Actor *actor, int parameter); + static int ID_Faction(Actor *actor, int parameter); + static int ID_Gender(Actor *actor, int parameter); + static int ID_General(Actor *actor, int parameter); + static int ID_Race(Actor *actor, int parameter); + static int ID_Specific(Actor *actor, int parameter); + static int ID_Subrace(Actor *actor, int parameter); + static int ID_Team(Actor *actor, int parameter); + + //Triggers + static int ActionListEmpty(Scriptable* Sender, Trigger* parameters); + static int ActuallyInCombat(Scriptable* Sender, Trigger* parameters); + static int Acquired(Scriptable* Sender, Trigger* parameters); + static int Alignment(Scriptable* Sender, Trigger* parameters); + static int Allegiance(Scriptable* Sender, Trigger* parameters); + static int AnimationID(Scriptable* Sender, Trigger* parameters); + static int AnimState(Scriptable* Sender, Trigger* parameters); + static int AnyPCOnMap(Scriptable* Sender, Trigger* parameters); + static int AnyPCSeesEnemy(Scriptable* Sender, Trigger* parameters); + static int AreaCheck(Scriptable* Sender, Trigger* parameter); + static int AreaCheckObject(Scriptable* Sender, Trigger* parameter); + static int AreaFlag(Scriptable* Sender, Trigger* parameter); + static int AreaRestDisabled(Scriptable* Sender, Trigger* parameter); + static int AreaStartsWith(Scriptable* Sender, Trigger* parameter); //InWatchersKeep + static int AreaType(Scriptable* Sender, Trigger* parameter); + static int AtLocation(Scriptable* Sender, Trigger* parameter); + static int AttackedBy(Scriptable* Sender, Trigger* parameters); + static int BecameVisible(Scriptable* Sender, Trigger* parameters); + static int BitCheck(Scriptable* Sender, Trigger* parameters); + static int BitCheckExact(Scriptable* Sender, Trigger* parameters); + static int BitGlobal_Trigger(Scriptable* Sender, Trigger* parameters); + static int BreakingPoint(Scriptable* Sender, Trigger* parameters); + static int CalendarDay(Scriptable* Sender, Trigger* parameters); + static int CalendarDayGT(Scriptable* Sender, Trigger* parameters); + static int CalendarDayLT(Scriptable* Sender, Trigger* parameters); + static int CalledByName(Scriptable* Sender, Trigger* parameters); + static int ChargeCount(Scriptable* Sender, Trigger* parameters); + static int CharName(Scriptable* Sender, Trigger* parameters); + static int CheckDoorFlags(Scriptable* Sender, Trigger* parameters); + static int CheckPartyAverageLevel(Scriptable* Sender, Trigger* parameters); + static int CheckPartyLevel(Scriptable* Sender, Trigger* parameters); + static int CheckSkill(Scriptable* Sender, Trigger* parameters); + static int CheckSkillGT(Scriptable* Sender, Trigger* parameters); + static int CheckSkillLT(Scriptable* Sender, Trigger* parameters); + static int CheckSpellState(Scriptable* Sender, Trigger* parameters); + static int CheckStat(Scriptable* Sender, Trigger* parameters); + static int CheckStatGT(Scriptable* Sender, Trigger* parameters); + static int CheckStatLT(Scriptable* Sender, Trigger* parameters); + static int Class(Scriptable* Sender, Trigger* parameters); + static int ClassEx(Scriptable* Sender, Trigger* parameters); + static int ClassLevel(Scriptable* Sender, Trigger* parameters); + static int ClassLevelGT(Scriptable* Sender, Trigger* parameters); + static int ClassLevelLT(Scriptable* Sender, Trigger* parameters); + static int Clicked(Scriptable* Sender, Trigger* parameters); + static int Closed(Scriptable* Sender, Trigger* parameters); + static int CombatCounter(Scriptable* Sender, Trigger* parameters); + static int CombatCounterGT(Scriptable* Sender, Trigger* parameters); + static int CombatCounterLT(Scriptable* Sender, Trigger* parameters); + static int Contains(Scriptable* Sender, Trigger* parameters); + static int CreatureHidden( Scriptable* Sender, Trigger* parameters); + static int CurrentAreaIs(Scriptable* Sender, Trigger* parameters); + //static int DamageTaken(Scriptable* Sender, Trigger* parameters); + //static int DamageTakenGT(Scriptable* Sender, Trigger* parameters); + //static int DamageTakenLT(Scriptable* Sender, Trigger* parameters); + static int Dead(Scriptable* Sender, Trigger* parameters); + static int Delay(Scriptable* Sender, Trigger* parameters); + static int Detect(Scriptable* Sender, Trigger* parameters); + static int Die(Scriptable* Sender, Trigger* parameters); + static int Died(Scriptable* Sender, Trigger* parameters); + static int Difficulty(Scriptable* Sender, Trigger* parameters); + static int DifficultyGT(Scriptable* Sender, Trigger* parameters); + static int DifficultyLT(Scriptable* Sender, Trigger* parameters); + static int Disarmed(Scriptable* Sender, Trigger* parameters); + static int DisarmFailed(Scriptable* Sender, Trigger* parameters); + static int Entered(Scriptable* Sender, Trigger* parameters); + static int EntirePartyOnMap(Scriptable* Sender, Trigger* parameters); + static int Exists(Scriptable* Sender, Trigger* parameters); + static int ExtendedStateCheck(Scriptable* Sender, Trigger* parameters); + static int ExtraProficiency(Scriptable* Sender, Trigger* parameters); + static int ExtraProficiencyGT(Scriptable* Sender, Trigger* parameters); + static int ExtraProficiencyLT(Scriptable* Sender, Trigger* parameters); + static int Faction(Scriptable* Sender, Trigger* parameters); + static int FallenPaladin(Scriptable* Sender, Trigger* parameters); + static int FallenRanger(Scriptable* Sender, Trigger* parameters); + static int False(Scriptable* Sender, Trigger* parameters); + static int ForceMarkedSpell_Trigger(Scriptable* Sender, Trigger* parameters); + static int Frame(Scriptable* Sender, Trigger* parameters); + static int Gender(Scriptable* Sender, Trigger* parameters); + static int General(Scriptable* Sender, Trigger* parameters); + static int G_Trigger(Scriptable* Sender, Trigger* parameters); + static int Global(Scriptable* Sender, Trigger* parameters); + static int GlobalAndGlobal_Trigger(Scriptable* Sender, Trigger* parameters); + static int GlobalBAndGlobal_Trigger(Scriptable* Sender, Trigger* parameters); + static int GlobalBAndGlobalExact(Scriptable* Sender, Trigger* parameters); + static int GlobalBitGlobal_Trigger(Scriptable* Sender, Trigger* parameters); + static int GlobalGT(Scriptable* Sender, Trigger* parameters); + static int GlobalGTGlobal(Scriptable* Sender, Trigger* parameters); + static int GlobalLT(Scriptable* Sender, Trigger* parameters); + static int GlobalLTGlobal(Scriptable* Sender, Trigger* parameters); + static int GlobalOrGlobal_Trigger(Scriptable* Sender, Trigger* parameters); + static int GlobalsEqual(Scriptable* Sender, Trigger* parameters); + static int GlobalsGT(Scriptable* Sender, Trigger* parameters); + static int GlobalsLT(Scriptable* Sender, Trigger* parameters); + static int GlobalTimerExact(Scriptable* Sender, Trigger* parameters); + static int GlobalTimerExpired(Scriptable* Sender, Trigger* parameters); + static int GlobalTimerNotExpired(Scriptable* Sender, Trigger* parameters); + static int GlobalTimerStarted(Scriptable* Sender, Trigger* parameters); + static int GGT_Trigger(Scriptable* Sender, Trigger* parameters); + static int GLT_Trigger(Scriptable* Sender, Trigger* parameters); + static int Happiness(Scriptable* Sender, Trigger* parameters); + static int HappinessGT(Scriptable* Sender, Trigger* parameters); + static int HappinessLT(Scriptable* Sender, Trigger* parameters); + static int HarmlessEntered(Scriptable* Sender, Trigger* parameters); + static int HasBounceEffects(Scriptable* Sender, Trigger* parameters); + static int HasImmunityEffects(Scriptable* Sender, Trigger* parameters); + static int HasInnateAbility(Scriptable* Sender, Trigger* parameters); + static int HasItem(Scriptable* Sender, Trigger* parameters); + static int HasItemEquipped(Scriptable* Sender, Trigger* parameters); + static int HasItemSlot(Scriptable* Sender, Trigger* parameters); + static int HasItemTypeSlot(Scriptable* Sender, Trigger* parameters); + static int HasWeaponEquipped(Scriptable* Sender, Trigger* parameters); + static int HaveAnySpells(Scriptable* Sender, Trigger* parameters); + static int HaveSpellParty(Scriptable* Sender, Trigger* parameters); + static int HaveSpell(Scriptable* Sender, Trigger* parameters); + static int HaveUsableWeaponEquipped(Scriptable* Sender, Trigger* parameters); + static int Heard(Scriptable* Sender, Trigger* parameters); + static int Help_Trigger(Scriptable* Sender, Trigger* parameters); + static int HelpEX(Scriptable* Sender, Trigger* parameters); + static int HitBy(Scriptable* Sender, Trigger* parameters); + static int HotKey(Scriptable* Sender, Trigger* parameters); + static int HP(Scriptable* Sender, Trigger* parameters); + static int HPGT(Scriptable* Sender, Trigger* parameters); + static int HPLost(Scriptable* Sender, Trigger* parameters); + static int HPLostGT(Scriptable* Sender, Trigger* parameters); + static int HPLostLT(Scriptable* Sender, Trigger* parameters); + static int HPLT(Scriptable* Sender, Trigger* parameters); + static int HPPercent(Scriptable* Sender, Trigger* parameters); + static int HPPercentGT(Scriptable* Sender, Trigger* parameters); + static int HPPercentLT(Scriptable* Sender, Trigger* parameters); + static int InActiveArea(Scriptable* Sender, Trigger* parameter); + static int InCutSceneMode(Scriptable *Sender, Trigger* parameter); + static int InLine(Scriptable* Sender, Trigger* parameter); + static int InMyArea(Scriptable* Sender, Trigger* parameter); + static int InMyGroup(Scriptable* Sender, Trigger* parameter); + static int InParty(Scriptable* Sender, Trigger* parameters); + static int InPartyAllowDead(Scriptable* Sender, Trigger* parameters); + static int InPartySlot(Scriptable* Sender, Trigger* parameters); + static int InteractingWith(Scriptable* Sender, Trigger* parameters); + static int Internal(Scriptable* Sender, Trigger* parameters); + static int InternalGT(Scriptable* Sender, Trigger* parameters); + static int InternalLT(Scriptable* Sender, Trigger* parameters); + static int InTrap(Scriptable* Sender, Trigger* parameters); + static int InventoryFull(Scriptable* Sender, Trigger* parameter); + static int InWeaponRange(Scriptable* Sender, Trigger* parameter); + static int IsAClown(Scriptable* Sender, Trigger* parameters); + static int IsActive(Scriptable* Sender, Trigger* parameters); + static int IsCreatureAreaFlag( Scriptable* Sender, Trigger* parameters); + static int IsCreatureHiddenInShadows( Scriptable* Sender, Trigger* parameters); + static int IsGabber(Scriptable* Sender, Trigger* parameters); + static int IsExtendedNight(Scriptable* Sender, Trigger* parameters); + static int IsFacingObject(Scriptable* Sender, Trigger* parameters); + static int IsFacingSavedRotation(Scriptable* Sender, Trigger* parameters); + static int IsLocked(Scriptable* Sender, Trigger* parameters); + static int IsMarkedSpell(Scriptable* Sender, Trigger* parameters); + static int IsOverMe(Scriptable* Sender, Trigger* parameters); + static int IsPathCriticalObject( Scriptable* Sender, Trigger* parameters); + static int IsPlayerNumber( Scriptable* Sender, Trigger* parameters); + static int IsRotation(Scriptable* Sender, Trigger* parameters); + static int IsSpellTargetValid( Scriptable* Sender, Trigger* parameters); + static int IsTeamBitOn(Scriptable* Sender, Trigger* parameters); + static int IsValidForPartyDialog(Scriptable* Sender, Trigger* parameters); + static int IsWeaponRanged(Scriptable* Sender, Trigger* parameters); + static int IsWeather(Scriptable* Sender, Trigger* parameters); + static int ItemIsIdentified(Scriptable* Sender, Trigger* parameters); + static int Joins(Scriptable* Sender, Trigger* parameters); + static int Kit(Scriptable* Sender, Trigger* parameters); + static int KnowSpell(Scriptable* Sender, Trigger* parameters); + static int LastMarkedObject_Trigger(Scriptable* Sender, Trigger* parameters); + static int LastPersonTalkedTo(Scriptable* Sender, Trigger* parameters); + static int Leaves(Scriptable* Sender, Trigger* parameters); + static int Level(Scriptable* Sender, Trigger* parameters); + static int LevelGT(Scriptable* Sender, Trigger* parameters); + static int LevelLT(Scriptable* Sender, Trigger* parameters); + static int LevelInClass(Scriptable* Sender, Trigger* parameters); + static int LevelInClassGT(Scriptable* Sender, Trigger* parameters); + static int LevelInClassLT(Scriptable* Sender, Trigger* parameters); + static int LevelParty(Scriptable* Sender, Trigger* parameters); + static int LevelPartyGT(Scriptable* Sender, Trigger* parameters); + static int LevelPartyLT(Scriptable* Sender, Trigger* parameters); + static int LocalsEqual(Scriptable* Sender, Trigger* parameters); + static int LocalsGT(Scriptable* Sender, Trigger* parameters); + static int LocalsLT(Scriptable* Sender, Trigger* parameters); + static int LOS(Scriptable* Sender, Trigger* parameters); + static int ModalState(Scriptable* Sender, Trigger* parameters); + static int Morale(Scriptable* Sender, Trigger* parameters); + static int MoraleGT(Scriptable* Sender, Trigger* parameters); + static int MoraleLT(Scriptable* Sender, Trigger* parameters); + static int NamelessBitTheDust(Scriptable* Sender, Trigger* parameters); + static int NearbyDialog(Scriptable* Sender, Trigger* parameters); + static int NearLocation(Scriptable* Sender, Trigger* parameters); + static int NearSavedLocation(Scriptable* Sender, Trigger* parameters); + static int NightmareModeOn(Scriptable* Sender, Trigger* parameters); + static int NotStateCheck(Scriptable* Sender, Trigger* parameters); + static int NullDialog(Scriptable* Sender, Trigger* parameters); + static int NumCreatures(Scriptable* Sender, Trigger* parameters); + static int NumCreaturesAtMyLevel(Scriptable* Sender, Trigger* parameters); + static int NumCreaturesGT(Scriptable* Sender, Trigger* parameters); + static int NumCreaturesGTMyLevel(Scriptable* Sender, Trigger* parameters); + static int NumCreaturesLT(Scriptable* Sender, Trigger* parameters); + static int NumCreaturesLTMyLevel(Scriptable* Sender, Trigger* parameters); + static int NumCreatureVsParty(Scriptable* Sender, Trigger* parameters); + static int NumCreatureVsPartyGT(Scriptable* Sender, Trigger* parameters); + static int NumCreatureVsPartyLT(Scriptable* Sender, Trigger* parameters); + static int NumDead(Scriptable* Sender, Trigger* parameters); + static int NumDeadGT(Scriptable* Sender, Trigger* parameters); + static int NumDeadLT(Scriptable* Sender, Trigger* parameters); + static int NumItems(Scriptable* Sender, Trigger* parameters); + static int NumItemsGT(Scriptable* Sender, Trigger* parameters); + static int NumItemsLT(Scriptable* Sender, Trigger* parameters); + static int NumItemsParty(Scriptable* Sender, Trigger* parameters); + static int NumItemsPartyGT(Scriptable* Sender, Trigger* parameters); + static int NumItemsPartyLT(Scriptable* Sender, Trigger* parameters); + static int NumTimesInteracted(Scriptable* Sender, Trigger* parameters); + static int NumTimesInteractedGT(Scriptable* Sender, Trigger* parameters); + static int NumTimesInteractedLT(Scriptable* Sender, Trigger* parameters); + static int NumTimesInteractedObject(Scriptable* Sender, Trigger* parameters); + static int NumTimesInteractedObjectGT(Scriptable* Sender, Trigger* parameters); + static int NumTimesInteractedObjectLT(Scriptable* Sender, Trigger* parameters); + static int NumTimesTalkedTo(Scriptable* Sender, Trigger* parameters); + static int NumTimesTalkedToGT(Scriptable* Sender, Trigger* parameters); + static int NumTimesTalkedToLT(Scriptable* Sender, Trigger* parameters); + static int ObjectActionListEmpty(Scriptable* Sender, Trigger* parameters); + static int OnCreation(Scriptable* Sender, Trigger* parameters); + static int OnIsland(Scriptable* Sender, Trigger* parameters); + static int OnScreen(Scriptable* Sender, Trigger* parameters); + static int Opened(Scriptable* Sender, Trigger* parameters); + static int OpenFailed(Scriptable* Sender, Trigger* parameters); + static int OpenState(Scriptable* Sender, Trigger* parameters); + static int Or(Scriptable* Sender, Trigger* parameters); + static int OutOfAmmo(Scriptable* Sender, Trigger* parameters); + static int OwnsFloaterMessage(Scriptable* Sender, Trigger* parameters); + static int PartyCountEQ(Scriptable* Sender, Trigger* parameters); + static int PartyCountGT(Scriptable* Sender, Trigger* parameters); + static int PartyCountLT(Scriptable* Sender, Trigger* parameters); + static int PartyCountAliveEQ(Scriptable* Sender, Trigger* parameters); + static int PartyCountAliveGT(Scriptable* Sender, Trigger* parameters); + static int PartyCountAliveLT(Scriptable* Sender, Trigger* parameters); + static int PartyGold(Scriptable* Sender, Trigger* parameters); + static int PartyGoldGT(Scriptable* Sender, Trigger* parameters); + static int PartyGoldLT(Scriptable* Sender, Trigger* parameters); + static int PartyHasItem(Scriptable* Sender, Trigger* parameters); + static int PartyHasItemIdentified(Scriptable* Sender, Trigger* parameters); + static int PartyMemberDied(Scriptable* Sender, Trigger* parameters); + static int PartyRested(Scriptable* Sender, Trigger* parameters); + static int PCCanSeePoint(Scriptable* Sender, Trigger* parameters); + static int PCInStore(Scriptable* Sender, Trigger* parameters); + static int PersonalSpaceDistance(Scriptable* Sender, Trigger* parameters); + static int PickLockFailed(Scriptable* Sender, Trigger* parameters); + static int PickpocketFailed(Scriptable* Sender, Trigger* parameters); + static int Proficiency(Scriptable* Sender, Trigger* parameters); + static int ProficiencyGT(Scriptable* Sender, Trigger* parameters); + static int ProficiencyLT(Scriptable* Sender, Trigger* parameters); + static int Race(Scriptable* Sender, Trigger* parameters); + static int RandomNum(Scriptable* Sender, Trigger* parameters); + static int RandomNumGT(Scriptable* Sender, Trigger* parameters); + static int RandomNumLT(Scriptable* Sender, Trigger* parameters); + static int RandomStatCheck(Scriptable* Sender, Trigger* parameters); + static int Range(Scriptable* Sender, Trigger* parameters); + static int Reaction(Scriptable* Sender, Trigger* parameters); + static int ReactionLT(Scriptable* Sender, Trigger* parameters); + static int ReactionGT(Scriptable* Sender, Trigger* parameters); + static int RealGlobalTimerExact(Scriptable* Sender, Trigger* parameters); + static int RealGlobalTimerExpired(Scriptable* Sender, Trigger* parameters); + static int RealGlobalTimerNotExpired(Scriptable* Sender, Trigger* parameters); + static int ReceivedOrder(Scriptable* Sender, Trigger* parameters); + static int Reputation(Scriptable* Sender, Trigger* parameters); + static int ReputationGT(Scriptable* Sender, Trigger* parameters); + static int ReputationLT(Scriptable* Sender, Trigger* parameters); + static int School(Scriptable* Sender, Trigger* parameters); + static int See(Scriptable* Sender, Trigger* parameters); + static int Sequence(Scriptable* Sender, Trigger* parameters); + static int SetLastMarkedObject(Scriptable* Sender, Trigger* parameters); + static int SetMarkedSpell_Trigger(Scriptable* Sender, Trigger* parameters); + static int Specifics(Scriptable* Sender, Trigger* parameters); + static int SpellCast(Scriptable* Sender, Trigger* parameters); + static int SpellCastInnate(Scriptable* Sender, Trigger* parameters); + static int SpellCastOnMe(Scriptable* Sender, Trigger* parameters); + static int SpellCastPriest(Scriptable* Sender, Trigger* parameters); + static int StateCheck(Scriptable* Sender, Trigger* parameters); + static int StealFailed(Scriptable* Sender, Trigger* parameters); + static int StoreHasItem(Scriptable* Sender, Trigger* parameters); + static int StuffGlobalRandom(Scriptable* Sender, Trigger* parameters); + static int SubRace(Scriptable* Sender, Trigger* parameters); + static int SystemVariable_Trigger(Scriptable* Sender, Trigger* parameters); + static int TargetUnreachable(Scriptable* Sender, Trigger* parameters); + static int Team(Scriptable* Sender, Trigger* parameters); + static int Time(Scriptable* Sender, Trigger* parameters); + static int TimeGT(Scriptable* Sender, Trigger* parameters); + static int TimeLT(Scriptable* Sender, Trigger* parameters); + static int TimeOfDay(Scriptable* Sender, Trigger* parameters); + static int TimerActive(Scriptable* Sender, Trigger* parameters); + static int TimerExpired(Scriptable* Sender, Trigger* parameters); + static int TookDamage(Scriptable* Sender, Trigger* parameters); + static int TotalItemCnt(Scriptable* Sender, Trigger* parameters); + static int TotalItemCntExclude(Scriptable* Sender, Trigger* parameters); + static int TotalItemCntExcludeGT(Scriptable* Sender, Trigger* parameters); + static int TotalItemCntExcludeLT(Scriptable* Sender, Trigger* parameters); + static int TotalItemCntGT(Scriptable* Sender, Trigger* parameters); + static int TotalItemCntLT(Scriptable* Sender, Trigger* parameters); + static int TrapTriggered(Scriptable* Sender, Trigger* parameters); + static int TriggerTrigger(Scriptable* Sender, Trigger* parameters); + static int TriggerSetGlobal(Scriptable* Sender, Trigger* parameters); + static int True(Scriptable* Sender, Trigger* parameters); + static int TurnedBy(Scriptable* Sender, Trigger* parameters); + static int Unlocked(Scriptable* Sender, Trigger* parameters); + static int UnselectableVariable(Scriptable* Sender, Trigger* parameters); + static int UnselectableVariableGT(Scriptable* Sender, Trigger* parameters); + static int UnselectableVariableLT(Scriptable* Sender, Trigger* parameters); + static int Unusable(Scriptable* Sender, Trigger* parameters); + static int Vacant(Scriptable* Sender, Trigger* parameters); + static int WalkedToTrigger(Scriptable* Sender, Trigger* parameters); + static int WasInDialog(Scriptable* Sender, Trigger* parameters); + static int Xor(Scriptable* Sender, Trigger* parameters); + static int XP(Scriptable* Sender, Trigger* parameters); + static int XPGT(Scriptable* Sender, Trigger* parameters); + static int XPLT(Scriptable* Sender, Trigger* parameters); +public: + //Actions + static void Activate(Scriptable* Sender, Action* parameters); + static void ActivatePortalCursor(Scriptable* Sender, Action* parameters); + static void AddAreaFlag(Scriptable* Sender, Action* parameters); + static void AddAreaType(Scriptable* Sender, Action* parameters); + static void AddExperienceParty(Scriptable *Sender, Action* parameters); + static void AddExperiencePartyCR(Scriptable *Sender, Action* parameters); + static void AddExperiencePartyGlobal(Scriptable *Sender, Action* parameters); + static void AddFeat(Scriptable *Sender, Action* parameters); + static void AddGlobals(Scriptable* Sender, Action* parameters); + static void AddHP(Scriptable* Sender, Action* parameters); + static void AddJournalEntry(Scriptable* Sender, Action* parameters); + static void AddKit(Scriptable* Sender, Action* parameters); + static void AddMapnote(Scriptable* Sender, Action* parameters); + static void AddSpecialAbility(Scriptable* Sender, Action* parameters); + static void AddSuperKit(Scriptable* Sender, Action* parameters); + static void AddWayPoint(Scriptable* Sender, Action* parameters); + static void AddXP2DA(Scriptable *Sender, Action* parameters); + static void AddXPObject(Scriptable *Sender, Action* parameters); + static void AdvanceTime(Scriptable *Sender, Action* parameters); + static void Ally(Scriptable* Sender, Action* parameters); + static void AmbientActivate(Scriptable* Sender, Action* parameters); + static void AnkhegEmerge(Scriptable* Sender, Action* parameters); + static void AnkhegHide(Scriptable* Sender, Action* parameters); + static void ApplyDamage(Scriptable* Sender, Action* parameters); + static void ApplyDamagePercent(Scriptable* Sender, Action* parameters); + static void ApplySpell(Scriptable* Sender, Action* parameters); + static void ApplySpellPoint(Scriptable* Sender, Action* parameters); + static void AttachTransitionToDoor(Scriptable* Sender, Action* parameters); + static void Attack(Scriptable* Sender, Action* parameters); + static void AttackNoSound(Scriptable* Sender, Action* parameters); + static void AttackOneRound(Scriptable* Sender, Action* parameters); + static void AttackReevaluate(Scriptable* Sender, Action* parameters); + static void BanterBlockFlag(Scriptable* Sender, Action* parameters); + static void BanterBlockTime(Scriptable* Sender, Action* parameters); + static void BashDoor(Scriptable* Sender, Action* parameters); + static void BattleSong(Scriptable* Sender, Action* parameters); + static void Berserk(Scriptable* Sender, Action* parameters); + static void BitClear(Scriptable* Sender, Action* parameters); + static void BitGlobal(Scriptable* Sender, Action* parameters); + static void BreakInstants(Scriptable* Sender, Action* parameters); + static void Calm(Scriptable* Sender, Action* parameters); + static void ChangeAIScript(Scriptable* Sender, Action* parameters); + static void ChangeAIType(Scriptable* Sender, Action* parameters); + static void ChangeAlignment(Scriptable* Sender, Action* parameters); + static void ChangeAllegiance(Scriptable* Sender, Action* parameters); + static void ChangeAnimation(Scriptable* Sender, Action* parameters); + static void ChangeAnimationNoEffect(Scriptable* Sender, Action* parameters); + static void ChangeClass(Scriptable* Sender, Action* parameters); + static void ChangeColor(Scriptable* Sender, Action* parameters); + static void ChangeCurrentScript(Scriptable* Sender, Action* parameters); + static void ChangeDestination(Scriptable* Sender, Action* parameters); + static void ChangeDialogue(Scriptable* Sender, Action* parameters); + static void ChangeGender(Scriptable* Sender, Action* parameters); + static void ChangeGeneral(Scriptable* Sender, Action* parameters); + static void ChangeRace(Scriptable* Sender, Action* parameters); + static void ChangeSpecifics(Scriptable* Sender, Action* parameters); + static void ChangeStat(Scriptable* Sender, Action* parameters); + static void ChangeStatGlobal(Scriptable* Sender, Action* parameters); + static void ChangeStoreMarkup(Scriptable* Sender, Action* parameters); + static void ChangeTileState(Scriptable* Sender, Action* parameters); + static void ClearActions(Scriptable* Sender, Action* parameters); + static void ClearAllActions(Scriptable* Sender, Action* parameters); + static void ClearPartyEffects(Scriptable* Sender, Action* parameters); + static void ClearSpriteEffects(Scriptable* Sender, Action* parameters); + static void ClickLButtonObject(Scriptable* Sender, Action* parameters); + static void ClickLButtonPoint(Scriptable* Sender, Action* parameters); + static void ClickRButtonObject(Scriptable* Sender, Action* parameters); + static void ClickRButtonPoint(Scriptable* Sender, Action* parameters); + static void CloseDoor(Scriptable* Sender, Action* parameters); + static void ContainerEnable(Scriptable* Sender, Action* parameters); + static void Continue(Scriptable* Sender, Action* parameters); + static void CopyGroundPilesTo(Scriptable* Sender, Action* parameters); + static void CreateCreature(Scriptable* Sender, Action* parameters); + static void CreateCreatureAtLocation(Scriptable* Sender, Action* parameters); + static void CreateCreatureAtFeet(Scriptable* Sender, Action* parameters); + static void CreateCreatureCopyPoint(Scriptable* Sender, Action* parameters); + static void CreateCreatureDoor(Scriptable* Sender, Action* parameters); + static void CreateCreatureImpassable(Scriptable* Sender, Action* parameters); + static void CreateCreatureImpassableAllowOverlap(Scriptable* Sender, + Action* parameters); + static void CreateCreatureObject(Scriptable* Sender, Action* parameters); + static void CreateCreatureObjectCopy(Scriptable* Sender, Action* parameters); + static void CreateCreatureObjectDoor(Scriptable* Sender, Action* parameters); + static void CreateCreatureObjectOffset(Scriptable* Sender, Action* parameters); + static void CreateCreatureObjectOffScreen(Scriptable* Sender, Action* parameters); + static void CreateCreatureOffScreen(Scriptable* Sender, Action* parameters); + static void CreateItem(Scriptable* Sender, Action* parameters); + static void CreateItemNumGlobal(Scriptable* Sender, Action* parameters); + static void CreatePartyGold(Scriptable *Sender, Action *parameters); + static void CreateVisualEffect(Scriptable* Sender, Action* parameters); + static void CreateVisualEffectObject(Scriptable* Sender, + Action* parameters); + static void CreateVisualEffectObjectSticky(Scriptable* Sender, + Action* parameters); + static void CutSceneID(Scriptable* Sender, Action* parameters); + static void Damage(Scriptable* Sender, Action* parameters); + static void DayNight(Scriptable *Sender, Action* parameters); + static void Deactivate(Scriptable* Sender, Action* parameters); + static void Debug(Scriptable* Sender, Action* parameters); + static void DestroyAllDestructableEquipment(Scriptable* Sender, + Action* parameters); + static void DestroyAllEquipment(Scriptable* Sender, Action* parameters); + static void DestroyGold(Scriptable* Sender, Action* parameters); + static void DestroyItem(Scriptable* Sender, Action* parameters); + static void DestroyPartyGold(Scriptable* Sender, Action* parameters); + static void DestroyPartyItem(Scriptable* Sender, Action* parameters); + static void DestroyPartyItemNum(Scriptable* Sender, Action* parameters); + static void DestroySelf(Scriptable* Sender, Action* parameters); + static void DetectSecretDoor(Scriptable* Sender, Action* parameters); + static void Dialogue(Scriptable* Sender, Action* parameters); + static void DialogueForceInterrupt(Scriptable* Sender, Action* parameters); + static void DialogueInterrupt(Scriptable* Sender, Action* parameters); + static void DisableFogDither(Scriptable* Sender, Action* parameters); + static void DisableSpriteDither(Scriptable* Sender, Action* parameters); + static void DisplayMessage(Scriptable* Sender, Action* parameters); + static void DisplayString(Scriptable* Sender, Action* parameters); + static void DisplayStringHead(Scriptable* Sender, Action* parameters); + static void DisplayStringHeadOwner(Scriptable* Sender, Action* parameters); + static void DisplayStringNoName(Scriptable* Sender, Action* parameters); + static void DisplayStringNoNameHead(Scriptable* Sender, Action* parameters); + static void DisplayStringWait(Scriptable* Sender, Action* parameters); + static void DoubleClickLButtonObject(Scriptable* Sender, Action* parameters); + static void DoubleClickLButtonPoint(Scriptable* Sender, Action* parameters); + static void DoubleClickRButtonObject(Scriptable* Sender, Action* parameters); + static void DoubleClickRButtonPoint(Scriptable* Sender, Action* parameters); + static void DropInventory(Scriptable* Sender, Action* parameters); + static void DropInventoryEX(Scriptable* Sender, Action* parameters); + static void DropItem(Scriptable* Sender, Action* parameters); + static void EnableFogDither(Scriptable* Sender, Action* parameters); + static void EnablePortalTravel(Scriptable* Sender, Action* parameters); + static void EnableSpriteDither(Scriptable* Sender, Action* parameters); + static void EndCredits(Scriptable* Sender, Action* parameters); + static void EndCutSceneMode(Scriptable* Sender, Action* parameters); + static void Enemy(Scriptable* Sender, Action* parameters); + static void EscapeArea(Scriptable* Sender, Action* parameters); + static void EscapeAreaDestroy(Scriptable* Sender, Action* parameters); + static void EscapeAreaNoSee(Scriptable* Sender, Action* parameters); + static void EscapeAreaObject(Scriptable* Sender, Action* parameters); + static void EscapeAreaObjectNoSee(Scriptable* Sender, Action* parameters); + static void EquipItem(Scriptable *Sender, Action *parameters); + static void EquipMostDamagingMelee(Scriptable *Sender, Action *parameters); + static void EquipRanged(Scriptable *Sender, Action *parameters); + static void EquipWeapon(Scriptable *Sender, Action *parameters); + static void ExitPocketPlane(Scriptable* Sender, Action* parameters); + static void ExpansionEndCredits(Scriptable* Sender, Action* parameters); + static void Explore(Scriptable *Sender, Action *parameters); + static void ExploreMapChunk(Scriptable *Sender, Action *parameters); + static void ExportParty(Scriptable *Sender, Action *parameters); + static void Face(Scriptable* Sender, Action* parameters); + static void FaceObject(Scriptable* Sender, Action* parameters); + static void FaceSavedLocation(Scriptable* Sender, Action* parameters); + static void FadeFromColor(Scriptable* Sender, Action* parameters); + static void FadeToAndFromColor(Scriptable* Sender, Action* parameters); + static void FadeToColor(Scriptable* Sender, Action* parameters); + static void FakeEffectExpiryCheck(Scriptable* Sender, Action* parameters); + static void FillSlot(Scriptable *Sender, Action* parameters); + static void FindTraps(Scriptable* Sender, Action* parameters); + static void FloatMessageFixed(Scriptable* Sender, Action* parameters); + static void FloatMessageFixedRnd(Scriptable* Sender, Action* parameters); + static void FloatMessageRnd(Scriptable* Sender, Action* parameters); + static void FloatRebus(Scriptable* Sender, Action* parameters); + static void Follow(Scriptable* Sender, Action* parameters); + static void FollowCreature(Scriptable* Sender, Action* parameters); + static void FollowObjectFormation(Scriptable* Sender, Action* parameters); + static void ForceAIScript(Scriptable* Sender, Action* parameters); + static void ForceAttack(Scriptable* Sender, Action* parameters); + static void ForceFacing(Scriptable* Sender, Action* parameters); + static void ForceHide(Scriptable* Sender, Action* parameters); + static void ForceLeaveAreaLUA(Scriptable* Sender, Action* parameters); + static void ForceMarkedSpell(Scriptable* Sender, Action* parameters); + static void ForceSpell(Scriptable* Sender, Action* parameters); + static void ForceSpellPoint(Scriptable* Sender, Action* parameters); + static void ForceUseContainer(Scriptable* Sender, Action* parameters); + static void Formation(Scriptable* Sender, Action* parameters); + static void FullHeal(Scriptable* Sender, Action* parameters); + static void GeneratePartyMember(Scriptable* Sender, Action* parameters); + static void GetItem(Scriptable* Sender, Action* parameters); + static void GetStat(Scriptable* Sender, Action* parameters); + static void GiveItem(Scriptable* Sender, Action* parameters); + static void GiveOrder(Scriptable* Sender, Action* parameters); + static void GivePartyAllEquipment(Scriptable* Sender, Action* parameters); + static void GivePartyGold(Scriptable* Sender, Action* parameters); + static void GivePartyGoldGlobal(Scriptable* Sender, Action* parameters); + static void GlobalAddGlobal(Scriptable* Sender, Action* parameters); + static void GlobalAndGlobal(Scriptable* Sender, Action* parameters); + static void GlobalBAnd(Scriptable* Sender, Action* parameters); + static void GlobalBAndGlobal(Scriptable* Sender, Action* parameters); + static void GlobalBitGlobal(Scriptable* Sender, Action* parameters); + static void GlobalBOr(Scriptable* Sender, Action* parameters); + static void GlobalBOrGlobal(Scriptable* Sender, Action* parameters); + static void GlobalMax(Scriptable* Sender, Action* parameters); + static void GlobalMaxGlobal(Scriptable* Sender, Action* parameters); + static void GlobalMin(Scriptable* Sender, Action* parameters); + static void GlobalMinGlobal(Scriptable* Sender, Action* parameters); + static void GlobalOrGlobal(Scriptable* Sender, Action* parameters); + static void GlobalSetGlobal(Scriptable* Sender, Action* parameters); + static void GlobalShL(Scriptable* Sender, Action* parameters); + static void GlobalShLGlobal(Scriptable* Sender, Action* parameters); + static void GlobalShout(Scriptable* Sender, Action* parameters); + static void GlobalShR(Scriptable* Sender, Action* parameters); + static void GlobalShRGlobal(Scriptable* Sender, Action* parameters); + static void GlobalSubGlobal(Scriptable* Sender, Action* parameters); + static void GlobalXor(Scriptable* Sender, Action* parameters); + static void GlobalXorGlobal(Scriptable* Sender, Action* parameters); + static void Help(Scriptable* Sender, Action* parameters); + static void Hide(Scriptable* Sender, Action* parameters); + static void HideAreaOnMap(Scriptable* Sender, Action* parameters); + static void HideCreature(Scriptable* Sender, Action* parameters); + static void HideGUI(Scriptable* Sender, Action* parameters); + static void IncInternal(Scriptable* Sender, Action* parameters); + static void IncMoraleAI(Scriptable* Sender, Action* parameters); + static void IncrementChapter(Scriptable* Sender, Action* parameters); + static void IncrementExtraProficiency(Scriptable* Sender, Action* parameters); + static void IncrementGlobal(Scriptable* Sender, Action* parameters); + static void IncrementGlobalOnce(Scriptable* Sender, Action* parameters); + static void IncrementKillStat(Scriptable* Sender, Action* parameters); + static void IncrementProficiency(Scriptable* Sender, Action* parameters); + static void Interact(Scriptable* Sender, Action* parameters); + static void JoinParty(Scriptable* Sender, Action* parameters); + static void JumpToObject(Scriptable* Sender, Action* parameters); + static void JumpToPoint(Scriptable* Sender, Action* parameters); + static void JumpToPointInstant(Scriptable* Sender, Action* parameters); + static void JumpToSavedLocation(Scriptable* Sender, Action* parameters); + static void Kill(Scriptable* Sender, Action* parameters); + static void KillFloatMessage(Scriptable* Sender, Action* parameters); + static void Leader(Scriptable* Sender, Action* parameters); + static void LeaveArea(Scriptable* Sender, Action* parameters); + static void LeaveAreaLUA(Scriptable* Sender, Action* parameters); + static void LeaveAreaLUAEntry(Scriptable* Sender, Action* parameters); + static void LeaveAreaLUAPanic(Scriptable* Sender, Action* parameters); + static void LeaveAreaLUAPanicEntry(Scriptable* Sender, Action* parameters); + static void LeaveParty(Scriptable* Sender, Action* parameters); + static void Lock(Scriptable* Sender, Action* parameters); + static void LockScroll(Scriptable* Sender, Action* parameters); + static void MakeGlobal(Scriptable* Sender, Action* parameters); + static void MakeUnselectable(Scriptable* Sender, Action* parameters); + static void MarkObject(Scriptable* Sender, Action* parameters); + static void MarkSpellAndObject(Scriptable* Sender, Action* parameters); + static void MatchHP(Scriptable* Sender, Action* parameters); + static void MoraleDec(Scriptable* Sender, Action* parameters); + static void MoraleInc(Scriptable* Sender, Action* parameters); + static void MoraleSet(Scriptable* Sender, Action* parameters); + static void MoveBetweenAreas(Scriptable* Sender, Action* parameters); + static void MoveBetweenAreasEffect(Scriptable* Sender, Action* parameters); + static void MoveCursorPoint(Scriptable* Sender, Action* parameters); + static void MoveGlobal(Scriptable* Sender, Action* parameters); + static void MoveGlobalObject(Scriptable* Sender, Action* parameters); + static void MoveGlobalObjectOffScreen(Scriptable* Sender, Action* parameters); + static void MoveGlobalsTo(Scriptable* Sender, Action* parameters); + static void MoveInventory(Scriptable *Sender, Action* parameters); + static void MoveToCenterOfScreen(Scriptable* Sender, Action* parameters); + static void MoveToExpansion(Scriptable* Sender, Action* parameters); + static void MoveToObject(Scriptable* Sender, Action* parameters); + static void MoveToObjectFollow(Scriptable* Sender, Action* parameters); + static void MoveToObjectNoInterrupt(Scriptable* Sender, Action* parameters); + static void MoveToObjectUntilSee(Scriptable* Sender, Action* parameters); + static void MoveToOffset(Scriptable* Sender, Action* parameters); + static void MoveToPoint(Scriptable* Sender, Action* parameters); + static void MoveToPointNoInterrupt(Scriptable* Sender, Action* parameters); + static void MoveToPointNoRecticle(Scriptable* Sender, Action* parameters); + static void MoveToSavedLocation(Scriptable* Sender, Action* parameters); + static void MoveViewPoint(Scriptable* Sender, Action* parameters); + static void MoveViewObject(Scriptable* Sender, Action* parameters); + static void NIDSpecial1(Scriptable* Sender, Action* parameters); + static void NIDSpecial2(Scriptable* Sender, Action* parameters); + static void NoAction(Scriptable* Sender, Action* parameters); + static void NoActionAtAll(Scriptable* Sender, Action* parameters); + static void OpenDoor(Scriptable* Sender, Action* parameters); + static void Panic(Scriptable* Sender, Action* parameters); + static void PauseGame(Scriptable *Sender, Action* parameters); + static void PermanentStatChange(Scriptable* Sender, Action* parameters); + static void PickLock(Scriptable* Sender, Action* parameters); + static void PickPockets(Scriptable* Sender, Action* parameters); + static void PickUpItem(Scriptable* Sender, Action* parameters); + static void PlayBardSong(Scriptable* Sender, Action* parameters); + static void PlayDead(Scriptable* Sender, Action* parameters); + static void PlayDeadInterruptable(Scriptable* Sender, Action* parameters); + static void PlayerDialogue(Scriptable* Sender, Action* parameters); + static void PlaySequence(Scriptable* Sender, Action* parameters); + static void PlaySequenceTimed(Scriptable* Sender, Action* parameters); + static void PlaySound(Scriptable* Sender, Action* parameters); + static void PlaySoundNotRanged(Scriptable* Sender, Action* parameters); + static void PlaySoundPoint(Scriptable* Sender, Action* parameters); + static void Plunder(Scriptable* Sender, Action* parameters); + static void Polymorph(Scriptable* Sender, Action* parameters); + static void PolymorphCopy(Scriptable* Sender, Action* parameters); + static void PolymorphCopyBase(Scriptable* Sender, Action* parameters); + static void ProtectObject(Scriptable* Sender, Action* parameters); + static void ProtectPoint(Scriptable* Sender, Action* parameters); + static void QuitGame(Scriptable* Sender, Action* parameters); + static void RandomFly(Scriptable* Sender, Action* parameters); + static void RandomRun(Scriptable* Sender, Action* parameters); + static void RandomTurn(Scriptable* Sender, Action* parameters); + static void RandomWalk(Scriptable* Sender, Action* parameters); + static void RandomWalkContinuous(Scriptable* Sender, Action* parameters); + static void RealSetGlobalTimer(Scriptable* Sender, Action* parameters); + static void ReallyForceSpell(Scriptable* Sender, Action* parameters); + static void ReallyForceSpellDead(Scriptable* Sender, Action* parameters); + static void ReallyForceSpellPoint(Scriptable* Sender, Action* parameters); + static void Recoil(Scriptable* Sender, Action* parameters); + static void RegainPaladinHood(Scriptable* Sender, Action* parameters); + static void RegainRangerHood(Scriptable* Sender, Action* parameters); + static void RemoveAreaFlag(Scriptable* Sender, Action* parameters); + static void RemoveAreaType(Scriptable* Sender, Action* parameters); + static void RemoveJournalEntry(Scriptable* Sender, Action* parameters); + static void RemoveMapnote(Scriptable* Sender, Action* parameters); + static void RemovePaladinHood(Scriptable* Sender, Action* parameters); + static void RemoveRangerHood(Scriptable* Sender, Action* parameters); + static void RemoveSpell(Scriptable* Sender, Action* parameters); + static void RemoveTraps(Scriptable* Sender, Action* parameters); + static void ReputationInc(Scriptable* Sender, Action* parameters); + static void ReputationSet(Scriptable* Sender, Action* parameters); + static void RestorePartyLocation(Scriptable *Sender, Action* parameters); + static void Rest(Scriptable *Sender, Action* parameters); + static void RestNoSpells(Scriptable *Sender, Action* parameters); + static void RestParty(Scriptable *Sender, Action* parameters); + static void RestUntilHealed(Scriptable *Sender, Action* parameters); + static void ReturnToSavedLocation(Scriptable* Sender, Action* parameters); + static void ReturnToSavedLocationDelete(Scriptable* Sender, Action* parameters); + static void RevealAreaOnMap(Scriptable* Sender, Action* parameters); + static void RunAwayFrom(Scriptable* Sender, Action* parameters); + static void RunAwayFromNoInterrupt(Scriptable* Sender, Action* parameters); + static void RunAwayFromNoLeaveArea(Scriptable* Sender, Action* parameters); + static void RunFollow(Scriptable* Sender, Action* parameters); + static void RunningAttack(Scriptable* Sender, Action* parameters); + static void RunningAttackNoSound(Scriptable* Sender, Action* parameters); + static void RunToObject(Scriptable* Sender, Action* parameters); + static void RunToPoint(Scriptable* Sender, Action* parameters); + static void RunToPointNoRecticle(Scriptable* Sender, Action* parameters); + static void RunToSavedLocation(Scriptable* Sender, Action* parameters); + static void SaveGame(Scriptable* Sender, Action* parameters); + static void SaveLocation(Scriptable* Sender, Action* parameters); + static void SaveObjectLocation(Scriptable* Sender, Action* parameters); + static void ScreenShake(Scriptable* Sender, Action* parameters); + static void SelectWeaponAbility(Scriptable* Sender, Action* parameters); + static void SendTrigger(Scriptable* Sender, Action* parameters); + static void SetAnimState(Scriptable* Sender, Action* parameters); + static void SetApparentName(Scriptable* Sender, Action* parameters); + static void SetAreaFlags(Scriptable* Sender, Action* parameters); + static void SetAreaRestFlag(Scriptable* Sender, Action* parameters); + static void SetArmourLevel(Scriptable* Sender, Action* parameters); + static void SetBeenInPartyFlags(Scriptable* Sender, Action* parameters); + static void SetBestWeapon(Scriptable *Sender, Action *parameters); + static void SetCursorState(Scriptable* Sender, Action* parameters); + static void SetCreatureAreaFlag(Scriptable* Sender, Action* parameters); + static void SetCriticalPathObject(Scriptable* Sender, Action* parameters); + static void SetDialogue(Scriptable* Sender, Action* parameters); + static void SetDialogueRange(Scriptable* Sender, Action* parameters); + static void SetDoorFlag(Scriptable* Sender, Action* parameters); + static void SetDoorLocked(Scriptable* Sender, Action* parameters); + static void SetEncounterProbability(Scriptable* Sender, Action* parameters); + static void SetExtendedNight(Scriptable* Sender, Action* parameters); + static void SetFaction(Scriptable* Sender, Action* parameters); + static void SetGabber(Scriptable* Sender, Action* parameters); + static void SetGlobal(Scriptable* Sender, Action* parameters); + static void SetGlobalRandom(Scriptable* Sender, Action* parameters); + static void SetGlobalTimer(Scriptable* Sender, Action* parameters); + static void SetGlobalTimerOnce(Scriptable* Sender, Action* parameters); + static void SetGlobalTimerRandom(Scriptable* Sender, Action* parameters); + static void SetGlobalTint(Scriptable* Sender, Action* parameters); + static void SetHP(Scriptable* Sender, Action* parameters); + static void SetHPPercent(Scriptable* Sender, Action* parameters); + static void SetInternal(Scriptable* Sender, Action* parameters); + static void SetInterrupt(Scriptable* Sender, Action* parameters); + static void SetLeavePartyDialogFile(Scriptable* Sender, Action* parameters); + static void SetMarkedSpell(Scriptable* Sender, Action* parameters); + static void SetMasterArea(Scriptable* Sender, Action* parameters); + static void SetMazeEasier(Scriptable* Sender, Action* parameters); + static void SetMazeHarder(Scriptable* Sender, Action* parameters); + static void SetMoraleAI(Scriptable* Sender, Action* parameters); + static void SetMusic(Scriptable* Sender, Action* parameters); + static void SetNamelessClass(Scriptable* Sender, Action* parameters); + static void SetNamelessDeath(Scriptable* Sender, Action* parameters); + static void SetNamelessDisguise(Scriptable* Sender, Action* parameters); + static void SetNoOneOnTrigger(Scriptable* Sender, Action* parameters); + static void SetNumTimesTalkedTo(Scriptable* Sender, Action* parameters); + static void SetPlayerSound(Scriptable* Sender, Action* parameters); + static void SetQuestDone(Scriptable* Sender, Action* parameters); + static void SetRegularName(Scriptable* Sender, Action* parameters); + static void SetRestEncounterChance(Scriptable* Sender, Action* parameters); + static void SetRestEncounterProbabilityDay(Scriptable* Sender, Action* parameters); + static void SetRestEncounterProbabilityNight(Scriptable* Sender, Action* parameters); + static void SetSavedLocation(Scriptable* Sender, Action* parameters); + static void SetSavedLocationPoint(Scriptable* Sender, Action* parameters); + static void SetScriptName(Scriptable* Sender, Action* parameters); + static void SetSelection(Scriptable* Sender, Action* parameters); + static void SetStartPos(Scriptable* Sender, Action* parameters); + static void SetTeam(Scriptable* Sender, Action* parameters); + static void SetTeamBit(Scriptable* Sender, Action* parameters); + static void SetTextColor(Scriptable* Sender, Action* parameters); + static void SetToken(Scriptable* Sender, Action* parameters); + static void SetToken2DA(Scriptable* Sender, Action* parameters); + static void SetTokenGlobal(Scriptable* Sender, Action* parameters); + static void SetTokenObject(Scriptable* Sender, Action* parameters); + static void SetTrackString(Scriptable* Sender, Action* parameters); + static void SetupWish(Scriptable* Sender, Action* parameters); + static void SetupWishObject(Scriptable* Sender, Action* parameters); + static void SetVisualRange(Scriptable* Sender, Action* parameters); + static void SG(Scriptable* Sender, Action* parameters); + static void Shout(Scriptable* Sender, Action* parameters); + static void SmallWait(Scriptable* Sender, Action* parameters); + static void SmallWaitRandom(Scriptable* Sender, Action* parameters); + static void SoundActivate(Scriptable* Sender, Action* parameters); + static void SpawnPtActivate(Scriptable* Sender, Action* parameters); + static void SpawnPtDeactivate(Scriptable* Sender, Action* parameters); + static void SpawnPtSpawn(Scriptable* Sender, Action* parameters); + static void Spell(Scriptable* Sender, Action* parameters); + static void SpellCastEffect(Scriptable* Sender, Action* parameters); + static void SpellHitEffectPoint(Scriptable* Sender, Action* parameters); + static void SpellHitEffectSprite(Scriptable* Sender, Action* parameters); + static void SpellNoDec(Scriptable* Sender, Action* parameters); + static void SpellPoint(Scriptable* Sender, Action* parameters); + static void SpellPointNoDec(Scriptable* Sender, Action* parameters); + static void StartCombatCounter(Scriptable* Sender, Action* parameters); + static void StartCutScene(Scriptable* Sender, Action* parameters); + static void StartCutSceneMode(Scriptable* Sender, Action* parameters); + static void StartDialogue(Scriptable* Sender, Action* parameters); + static void StartDialogueInterrupt(Scriptable* Sender, Action* parameters); + static void StartDialogueNoSet(Scriptable* Sender, Action* parameters); + static void StartDialogueNoSetInterrupt(Scriptable* Sender, + Action* parameters); + static void StartDialogueOverride(Scriptable* Sender, Action* parameters); + static void StartDialogueOverrideInterrupt(Scriptable* Sender, + Action* parameters); + static void StartMovie(Scriptable* Sender, Action* parameters); + static void StartMusic(Scriptable* Sender, Action* parameters); + static void StartRainNow(Scriptable* Sender, Action* parameters); + static void StartRandomTimer(Scriptable* Sender, Action* parameters); + static void StartSong(Scriptable* Sender, Action* parameters); + static void StartStore(Scriptable* Sender, Action* parameters); + static void StartTimer(Scriptable* Sender, Action* parameters); + static void StateOverrideFlag(Scriptable* Sender, Action* parameters); + static void StateOverrideTime(Scriptable* Sender, Action* parameters); + static void StaticPalette(Scriptable* Sender, Action* parameters); + static void StaticStart(Scriptable* Sender, Action* parameters); + static void StaticStop(Scriptable* Sender, Action* parameters); + static void StopMoving(Scriptable* Sender, Action* parameters); + static void StorePartyLocation(Scriptable *Sender, Action* parameters); + static void Swing(Scriptable* Sender, Action* parameters); + static void SwingOnce(Scriptable* Sender, Action* parameters); + static void TakeItemList(Scriptable* Sender, Action* parameters); + static void TakeItemListParty(Scriptable* Sender, Action* parameters); + static void TakeItemListPartyNum(Scriptable* Sender, Action* parameters); + static void TakeItemReplace(Scriptable* Sender, Action* parameters); + static void TakePartyGold(Scriptable* Sender, Action* parameters); + static void TakePartyItem(Scriptable* Sender, Action* parameters); + static void TakePartyItemAll(Scriptable* Sender, Action* parameters); + static void TakePartyItemNum(Scriptable* Sender, Action* parameters); + static void TakePartyItemRange(Scriptable* Sender, Action* parameters); + static void TeleportParty(Scriptable* Sender, Action* parameters); + static void TextScreen(Scriptable* Sender, Action* parameters); + static void ToggleDoor(Scriptable* Sender, Action* parameters); + static void TimedMoveToPoint(Scriptable* Sender, Action* parameters); + static void TransformItem(Scriptable* Sender, Action* parameters); + static void TransformItemAll(Scriptable* Sender, Action* parameters); + static void TransformPartyItem(Scriptable* Sender, Action* parameters); + static void TransformPartyItemAll(Scriptable* Sender, Action* parameters); + static void TriggerActivation(Scriptable* Sender, Action* parameters); + static void Turn(Scriptable* Sender, Action* parameters); + static void TurnAMT(Scriptable* Sender, Action* parameters); + static void UndoExplore(Scriptable *Sender, Action *parameters); + static void UnhideGUI(Scriptable* Sender, Action* parameters); + static void Unlock(Scriptable* Sender, Action* parameters); + static void UnlockScroll(Scriptable* Sender, Action* parameters); + static void UseContainer(Scriptable* Sender, Action* parameters); + static void UseDoor(Scriptable* Sender, Action* parameters); + static void UseItem(Scriptable* Sender, Action* parameters); + static void UseItemPoint(Scriptable* Sender, Action* parameters); + static void VerbalConstant(Scriptable* Sender, Action* parameters); + static void VerbalConstantHead(Scriptable* Sender, Action* parameters); + static void Wait(Scriptable* Sender, Action* parameters); + static void WaitAnimation(Scriptable* Sender, Action* parameters); + static void WaitRandom(Scriptable* Sender, Action* parameters); + static void Weather(Scriptable* Sender, Action* parameters); + static void XEquipItem(Scriptable *Sender, Action *parameters); +public: + //Objects + static Targets *BestAC(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *EighthNearest(Scriptable *Sender, Targets *parameter, int ga_flagss); + static Targets *EighthNearestDoor(Scriptable *Sender, Targets *parameter, int ga_flagss); + static Targets *EighthNearestEnemyOf(Scriptable *Sender, Targets *parameter, int ga_flagss); + static Targets *EighthNearestEnemyOfType(Scriptable *Sender, Targets *parameter, int ga_flagss); + static Targets *EighthNearestMyGroupOfType(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *Farthest(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *FarthestEnemyOf(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *FifthNearest(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *FifthNearestDoor(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *FifthNearestEnemyOf(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *FifthNearestEnemyOfType(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *FifthNearestMyGroupOfType(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *FourthNearest(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *FourthNearestDoor(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *FourthNearestEnemyOf(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *FourthNearestEnemyOfType(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *FourthNearestMyGroupOfType(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *Gabber(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *GroupOf(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *LastAttackerOf(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *LastCommandedBy(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *LastHeardBy(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *LastHelp(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *LastHitter(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *LastMarkedObject(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *LastSeenBy(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *LastSummonerOf(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *LastTalkedToBy(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *LastTargetedBy(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *LastTrigger(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *LeaderOf(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *LeastDamagedOf(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *MostDamagedOf(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *Myself(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *MyTarget(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *Nearest(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *NearestDoor(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *NearestEnemyOf(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *NearestEnemyOfType(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *NearestEnemySummoned(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *NearestMyGroupOfType(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *NearestPC(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *NinthNearest(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *NinthNearestDoor(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *NinthNearestEnemyOf(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *NinthNearestEnemyOfType(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *NinthNearestMyGroupOfType(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *Nothing(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *Player1(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *Player1Fill(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *Player2(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *Player2Fill(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *Player3(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *Player3Fill(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *Player4(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *Player4Fill(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *Player5(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *Player5Fill(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *Player6(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *Player6Fill(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *Protagonist(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *ProtectedBy(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *ProtectorOf(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *SecondNearest(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *SecondNearestDoor(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *SecondNearestEnemyOf(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *SecondNearestEnemyOfType(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *SecondNearestMyGroupOfType(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *SelectedCharacter(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *SeventhNearest(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *SeventhNearestDoor(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *SeventhNearestEnemyOf(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *SeventhNearestEnemyOfType(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *SeventhNearestMyGroupOfType(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *SixthNearest(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *SixthNearestDoor(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *SixthNearestEnemyOf(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *SixthNearestEnemyOfType(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *SixthNearestMyGroupOfType(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *StrongestOf(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *StrongestOfMale(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *TenthNearest(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *TenthNearestDoor(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *TenthNearestEnemyOf(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *TenthNearestEnemyOfType(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *TenthNearestMyGroupOfType(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *ThirdNearest(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *ThirdNearestDoor(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *ThirdNearestEnemyOf(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *ThirdNearestEnemyOfType(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *ThirdNearestMyGroupOfType(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *WeakestOf(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *WorstAC(Scriptable *Sender, Targets *parameters, int ga_flags); + +public: + /*GemRB extensions/actions*/ + static void RunAwayFromPoint(Scriptable* Sender, Action* parameters); + static void UnMakeGlobal(Scriptable* Sender, Action* parameters); + static void UnloadArea(Scriptable* Sender, Action* parameters); + + /*GemRB extensions/objects*/ + static Targets *Player7(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *Player7Fill(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *Player8(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *Player8Fill(Scriptable *Sender, Targets *parameters, int ga_flags); +}; + +GEM_EXPORT Action* GenerateAction(char* String); +Action* GenerateActionDirect(char* String, Scriptable *object); +GEM_EXPORT Trigger* GenerateTrigger(char* String); + +void InitializeIEScript(); + +#endif diff --git a/project/jni/application/gemrb/src/core/GameScript/Matching.cpp b/project/jni/application/gemrb/src/core/GameScript/Matching.cpp new file mode 100644 index 000000000..4665a8a2f --- /dev/null +++ b/project/jni/application/gemrb/src/core/GameScript/Matching.cpp @@ -0,0 +1,685 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003-2005 The GemRB Project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * + */ + +#include "GameScript/Matching.h" + +#include "GameScript/GSUtils.h" + +#include "Interface.h" +#include "Game.h" +#include "TileMap.h" + +/* return a Targets object with a single actor inside */ +inline static Targets* ReturnActorAsTarget(Actor *aC) +{ + if (!aC) return NULL; + Targets *tgts = new Targets(); + tgts->AddTarget(aC, 0, 0); + return tgts; +} + +/*Actor *FindActorNearby(const char *name, Map *except, int ga_flags) +{ + Game *game = core->GetGame(); + size_t mc = game->GetLoadedMapCount(); + while(mc--) { + Map *map = game->GetMap(mc); + if (map==except) continue; + Actor * aC = map->GetActor(name, ga_flags); + if (aC) { + return aC; + } + } + return NULL; +}*/ + +/* do IDS filtering: [PC], [ENEMY], etc */ +inline static bool DoObjectIDSCheck(Object *oC, Actor *ac, bool *filtered) { + for (int j = 0; j < ObjectIDSCount; j++) { + if (!oC->objectFields[j]) { + continue; + } + *filtered = true; + IDSFunction func = idtargets[j]; + if (!func) { + printMessage("GameScript"," ", YELLOW); + printf("Unimplemented IDS targeting opcode: %d\n", j); + continue; + } + if (!func( ac, oC->objectFields[j] ) ) { + return false; + } + } + return true; +} + +/* do object filtering: Myself, LastAttackerOf(Player1), etc */ +inline static Targets *DoObjectFiltering(Scriptable *Sender, Targets *tgts, Object *oC, int ga_flags) { + for (int i = 0; i < MaxObjectNesting; i++) { + int filterid = oC->objectFilters[i]; + if (!filterid) break; + + ObjectFunction func = objects[filterid]; + if (!func) { + printMessage("GameScript"," ", YELLOW); + printf("Unknown object filter: %d %s\n", filterid, objectsTable->GetValue(filterid)); + continue; + } + + tgts = func(Sender, tgts, ga_flags); + if (!tgts->Count()) { + delete tgts; + return NULL; + } + } + return tgts; +} + +static EffectRef fx_protection_creature_ref = { "Protection:Creature", NULL, -1 }; + +inline static bool DoObjectChecks(Map *map, Scriptable *Sender, Actor *target, int &dist, bool ignoreinvis=false) +{ + dist = SquaredMapDistance(Sender, target); + + // TODO: what do we check for non-actors? + // non-actors have a visual range (15), we should do visual range and LOS + + if (Sender->Type == ST_ACTOR) { + Actor *source = (Actor *)Sender; + + // Detect() ignores invisibility completely + if (!ignoreinvis) { + // TODO: move this stuff into a shared function so it can be used elsewhere? + + // SEEINVISIBLE skips these checks :-) + if (source->Modified[IE_SEEINVISIBLE] == 0) { + ieDword state = target->Modified[IE_STATE_ID]; + // check for invisibility + if ((state & STATE_INVISIBLE) != 0) return false; + // check for improved invisibility? probably not + //if ((state & STATE_INVIS2) != 0) return false; + } + + // maybe this should be setting an invis flag? + // TODO: should SEEINVISIBLE ignore this? Detect()? + if (target->Modified[IE_AVATARREMOVAL]) return false; + } + + // visual range check + int visualrange = source->Modified[IE_VISUALRANGE]; + if (dist > visualrange*visualrange) return false; + + // LOS check + if (!map->IsVisible(Sender->Pos, target->Pos)) return false; + + // protection against creature + if (target->fxqueue.HasEffect(fx_protection_creature_ref)) { + // TODO: de-hardcode these (may not all be correct anyway) + if (target->fxqueue.HasEffectWithParamPair(fx_protection_creature_ref, 2, source->Modified[IE_EA])) return false; + if (target->fxqueue.HasEffectWithParamPair(fx_protection_creature_ref, 3, source->Modified[IE_GENERAL])) return false; + if (target->fxqueue.HasEffectWithParamPair(fx_protection_creature_ref, 4, source->Modified[IE_RACE])) return false; + if (target->fxqueue.HasEffectWithParamPair(fx_protection_creature_ref, 5, source->Modified[IE_CLASS])) return false; + if (target->fxqueue.HasEffectWithParamPair(fx_protection_creature_ref, 6, source->Modified[IE_SPECIFIC])) return false; + if (target->fxqueue.HasEffectWithParamPair(fx_protection_creature_ref, 7, source->Modified[IE_SEX])) return false; + if (target->fxqueue.HasEffectWithParamPair(fx_protection_creature_ref, 8, source->Modified[IE_ALIGNMENT])) return false; + } + } + return true; +} + +/* returns actors that match the [x.y.z] expression */ +static Targets* EvaluateObject(Map *map, Scriptable* Sender, Object* oC, int ga_flags) +{ + // if you ActionOverride a global actor, they might not have a map :( + // TODO: don't allow this to happen? + if (!map) { + return NULL; + } + + if (oC->objectName[0]) { + //We want the object by its name... (doors/triggers don't play here!) + Actor* aC = map->GetActor( oC->objectName, ga_flags ); + + /*if (!aC && (ga_flags&GA_GLOBAL) ) { + aC = FindActorNearby(oC->objectName, map, ga_flags ); + }*/ + + //return here because object name/IDS targeting are mutually exclusive + return ReturnActorAsTarget(aC); + } + + if (oC->objectFields[0]==-1) { + // this is an internal hack, allowing us to pass actor ids around as objects + Actor* aC = map->GetActorByGlobalID( (ieDword) oC->objectFields[1] ); + /* TODO: this hack will throw away an invalid target */ + /* Consider putting this in GetActorByGlobalID */ + if (aC && !aC->ValidTarget(ga_flags)) { + aC = NULL; + } + return ReturnActorAsTarget(aC); + } + + Targets *tgts = NULL; + + //we need to get a subset of actors from the large array + //if this gets slow, we will need some index tables + int i = map->GetActorCount(true); + while (i--) { + Actor *ac = map->GetActor(i, true); + if (!ac) continue; // is this check really needed? + // don't return Sender in IDS targeting! + if (ac == Sender) continue; + bool filtered = false; + if (DoObjectIDSCheck(oC, ac, &filtered)) { + if (!filtered) { + // if no filters were applied.. + assert(!tgts); + return NULL; + } + int dist; + if (DoObjectChecks(map, Sender, ac, dist, (ga_flags & GA_DETECT) != 0)) { + if (!tgts) tgts = new Targets(); + tgts->AddTarget((Scriptable *) ac, dist, ga_flags); + } + } + } + + return tgts; +} + +Targets* GetAllObjects(Map *map, Scriptable* Sender, Object* oC, int ga_flags) +{ + if (!oC) { + return NULL; + } + Targets* tgts = EvaluateObject(map, Sender, oC, ga_flags); + //if we couldn't find an endpoint by name or object qualifiers + //it is not an Actor, but could still be a Door or Container (scriptable) + if (!tgts && oC->objectName[0]) { + return NULL; + } + //now lets do the object filter stuff, we create Targets because + //it is possible to start from blank sheets using endpoint filters + //like (Myself, Protagonist etc) + if (!tgts) { + tgts = new Targets(); + } + tgts = DoObjectFiltering(Sender, tgts, oC, ga_flags); + return tgts; +} + +/*Targets *GetAllObjectsNearby(Scriptable* Sender, Object* oC, int ga_flags) +{ + Game *game = core->GetGame(); + size_t mc = game->GetLoadedMapCount(); + while(mc--) { + Map *map = game->GetMap(mc); + if (map==Sender->GetCurrentArea()) continue; + Targets *tgts = GetAllObjects(map, Sender, oC, ga_flags); + if (tgts) { + return tgts; + } + } + return NULL; +}*/ + +Targets *GetAllActors(Scriptable *Sender, int ga_flags) +{ + Map *map = Sender->GetCurrentArea(); + + int i = map->GetActorCount(true); + Targets *tgts = new Targets(); + while (i--) { + Actor *ac = map->GetActor(i,true); + int dist = Distance(Sender->Pos, ac->Pos); + tgts->AddTarget((Scriptable *) ac, dist, ga_flags); + } + return tgts; +} + +/* get a non-actor object from a map, by name */ +Scriptable *GetActorObject(TileMap *TMap, const char *name) +{ + Scriptable * aC = TMap->GetDoor( name ); + if (aC) { + return aC; + } + + //containers should have a precedence over infopoints because otherwise + //AR1512 sanity test quest would fail + //If this order couldn't be maintained, then 'Contains' should have a + //unique call to get containers only + + //No... it was not an door... maybe a Container? + aC = TMap->GetContainer( name ); + if (aC) { + return aC; + } + + //No... it was not a container ... maybe an InfoPoint? + aC = TMap->GetInfoPoint( name ); + if (aC) { + return aC; + } + return aC; +} + +// blocking actions need to store some kinds of objects between ticks +Scriptable* GetStoredActorFromObject(Scriptable* Sender, Object* oC, int ga_flags) +{ + Scriptable *tar = NULL; + // retrieve an existing target if it still exists and is valid + if (Sender->CurrentActionTarget) { + tar = core->GetGame()->GetActorByGlobalID(Sender->CurrentActionTarget); + if (tar) { + // always an actor, check if it satisfies flags + if (((Actor *)tar)->ValidTarget(ga_flags)) { + return tar; + } + } + return NULL; // target invalid/gone + } + tar = GetActorFromObject(Sender, oC, ga_flags); + // maybe store the target if it's an actor.. + if (tar && tar->Type == ST_ACTOR) { + // .. but we only want objects created via objectFilters + if (oC->objectFilters[0]) { + Sender->CurrentActionTarget = tar->GetGlobalID(); + } + } + return tar; +} + +Scriptable* GetActorFromObject(Scriptable* Sender, Object* oC, int ga_flags) +{ + Scriptable *aC = NULL; + + if (!oC) { + return NULL; + } + Game *game = core->GetGame(); + Targets *tgts = GetAllObjects(Sender->GetCurrentArea(), Sender, oC, ga_flags); + if (tgts) { + //now this could return other than actor objects + aC = tgts->GetTarget(0,-1); + delete tgts; + if (aC || oC->objectFields[0]!=-1) { + return aC; + } + + //global actors are always found by object ID! + return game->GetGlobalActorByGlobalID(oC->objectFields[1]); + } + + if (oC->objectName[0]) { + // if you ActionOverride a global actor, they might not have a map :( + // TODO: don't allow this to happen? + if (Sender->GetCurrentArea()) { + aC = GetActorObject(Sender->GetCurrentArea()->GetTileMap(), oC->objectName ); + if (aC) { + return aC; + } + } + + //global actors are always found by scripting name! + aC = game->FindPC(oC->objectName); + if (aC) { + return aC; + } + aC = game->FindNPC(oC->objectName); + if (aC) { + return aC; + } + } + return NULL; +} + +bool MatchActor(Scriptable *Sender, ieDword actorID, Object* oC) +{ + if (!Sender) { + return false; + } + Actor *ac = Sender->GetCurrentArea()->GetActorByGlobalID(actorID); + if (!ac) { + return false; + } + + // [0]/[ANYONE] can match all actors + if (!oC) { + return true; + } + + bool filtered = false; + + // name matching + if (oC->objectName[0]) { + if (strnicmp(ac->GetScriptName(), oC->objectName, 32) != 0) { + return false; + } + filtered = true; + } + + // IDS targeting + // (if we already matched by name, we don't do this) + // TODO: check distance? area? visibility? + if (!filtered && !DoObjectIDSCheck(oC, ac, &filtered)) return false; + + // globalID hack should never get here + assert(oC->objectFilters[0] != -1); + + // object filters + if (oC->objectFilters[0]) { + // object filters insist on having a stupid targets list, + // so we waste a lot of time here + Targets *tgts = new Targets(); + int ga_flags = 0; // TODO: correct? + + // handle already-filtered vs not-yet-filtered cases + // e.g. LastTalkedToBy(Myself) vs LastTalkedToBy + if (filtered) tgts->AddTarget(ac, 0, ga_flags); + + tgts = DoObjectFiltering(Sender, tgts, oC, ga_flags); + if (!tgts) return false; + + // and sometimes object filters are lazy and not only don't filter + // what we give them, they clear it and return a list :( + // so we have to search the whole list.. + bool ret = false; + targetlist::iterator m; + const targettype *tt = tgts->GetFirstTarget(m, ST_ACTOR); + while (tt) { + Actor *actor = (Actor *) tt->actor; + if (actor->GetGlobalID() == actorID) { + ret = true; + break; + } + tt = tgts->GetNextTarget(m, ST_ACTOR); + } + delete tgts; + if (!ret) return false; + } + return true; +} + +int GetObjectCount(Scriptable* Sender, Object* oC) +{ + if (!oC) { + return 0; + } + // EvaluateObject will return [PC] + // GetAllObjects will also return Myself (evaluates object filters) + // i believe we need the latter here + Targets* tgts = GetAllObjects(Sender->GetCurrentArea(), Sender, oC, 0); + int count = tgts->Count(); + delete tgts; + return count; +} + +//TODO: +//check numcreaturesatmylevel(myself, 1) +//when the actor is alone +//it should (obviously) return true if the trigger +//evaluates object filters +//also check numcreaturesgtmylevel(myself,0) with +//actor having at high level +int GetObjectLevelCount(Scriptable* Sender, Object* oC) +{ + if (!oC) { + return 0; + } + // EvaluateObject will return [PC] + // GetAllObjects will also return Myself (evaluates object filters) + // i believe we need the latter here + Targets* tgts = GetAllObjects(Sender->GetCurrentArea(), Sender, oC, 0); + int count = 0; + if (tgts) { + targetlist::iterator m; + const targettype *tt = tgts->GetFirstTarget(m, ST_ACTOR); + while (tt) { + count += ((Actor *) tt->actor)->GetXPLevel(true); + tt = tgts->GetNextTarget(m, ST_ACTOR); + } + } + delete tgts; + return count; +} + +Targets *GetMyTarget(Scriptable *Sender, Actor *actor, Targets *parameters, int ga_flags) +{ + if (!actor) { + if (Sender->Type==ST_ACTOR) { + actor = (Actor *) Sender; + } + } + parameters->Clear(); + if (actor) { + Actor *target = actor->GetCurrentArea()->GetActorByGlobalID(actor->LastTarget); + if (target) { + parameters->AddTarget(target, 0, ga_flags); + } + } + return parameters; +} + +Targets *XthNearestDoor(Targets *parameters, unsigned int count) +{ + //get the origin + Scriptable *origin = parameters->GetTarget(0, -1); + parameters->Clear(); + if (!origin) { + return parameters; + } + //get the doors based on it + Map *map = origin->GetCurrentArea(); + unsigned int i =(unsigned int) map->TMap->GetDoorCount(); + if (count>i) { + return parameters; + } + while (i--) { + Door *door = map->TMap->GetDoor(i); + unsigned int dist = Distance(origin->Pos, door->Pos); + parameters->AddTarget(door, dist, 0); + } + + //now get the xth door + origin = parameters->GetTarget(count, ST_DOOR); + parameters->Clear(); + if (!origin) { + return parameters; + } + parameters->AddTarget(origin, 0, 0); + return parameters; +} + +Targets *XthNearestOf(Targets *parameters, int count, int ga_flags) +{ + Scriptable *origin; + + if (count<0) { + const targettype *t = parameters->GetLastTarget(ST_ACTOR); + origin = t->actor; + } else { + origin = parameters->GetTarget(count, ST_ACTOR); + } + parameters->Clear(); + if (!origin) { + return parameters; + } + parameters->AddTarget(origin, 0, ga_flags); + return parameters; +} + +//mygroup means the same specifics as origin +Targets *XthNearestMyGroupOfType(Scriptable *origin, Targets *parameters, unsigned int count, int ga_flags) +{ + if (origin->Type != ST_ACTOR) { + parameters->Clear(); + return parameters; + } + + targetlist::iterator m; + const targettype *t = parameters->GetFirstTarget(m, ST_ACTOR); + if (!t) { + return parameters; + } + Actor *actor = (Actor *) origin; + //determining the specifics of origin + ieDword type = actor->GetStat(IE_SPECIFIC); //my group + + while ( t ) { + if (t->actor->Type!=ST_ACTOR) { + t=parameters->RemoveTargetAt(m); + continue; + } + Actor *actor = (Actor *) (t->actor); + if (actor->GetStat(IE_SPECIFIC) != type) { + t=parameters->RemoveTargetAt(m); + continue; + } + t = parameters->GetNextTarget(m, ST_ACTOR); + } + return XthNearestOf(parameters,count, ga_flags); +} + +Targets *ClosestEnemySummoned(Scriptable *origin, Targets *parameters, int ga_flags) +{ + if (origin->Type != ST_ACTOR) { + parameters->Clear(); + return parameters; + } + + targetlist::iterator m; + const targettype *t = parameters->GetFirstTarget(m, ST_ACTOR); + if (!t) { + return parameters; + } + Actor *actor = (Actor *) origin; + //determining the allegiance of the origin + int type = GetGroup(actor); + + if (type==2) { + parameters->Clear(); + return parameters; + } + + actor = NULL; + while ( t ) { + Actor *tmp = (Actor *) (t->actor); + if (tmp->GetStat(IE_SEX) != SEX_SUMMON) { + continue; + } + if (type) { //origin is PC + if (tmp->GetStat(IE_EA) <= EA_GOODCUTOFF) { + continue; + } + } else { + if (tmp->GetStat(IE_EA) >= EA_EVILCUTOFF) { + continue; + } + } + actor = tmp; + t = parameters->GetNextTarget(m, ST_ACTOR); + } + parameters->Clear(); + parameters->AddTarget(actor, 0, ga_flags); + return parameters; +} + +Targets *XthNearestEnemyOfType(Scriptable *origin, Targets *parameters, unsigned int count, int ga_flags) +{ + if (origin->Type != ST_ACTOR) { + parameters->Clear(); + return parameters; + } + + targetlist::iterator m; + const targettype *t = parameters->GetFirstTarget(m, ST_ACTOR); + if (!t) { + return parameters; + } + Actor *actor = (Actor *) origin; + //determining the allegiance of the origin + int type = GetGroup(actor); + + if (type==2) { + parameters->Clear(); + return parameters; + } + + while ( t ) { + if (t->actor->Type!=ST_ACTOR) { + t=parameters->RemoveTargetAt(m); + continue; + } + Actor *actor = (Actor *) (t->actor); + // IDS targeting already did object checks (unless we need to override Detect?) + if (type) { //origin is PC + if (actor->GetStat(IE_EA) <= EA_GOODCUTOFF) { + t=parameters->RemoveTargetAt(m); + continue; + } + } else { + if (actor->GetStat(IE_EA) >= EA_EVILCUTOFF) { + t=parameters->RemoveTargetAt(m); + continue; + } + } + t = parameters->GetNextTarget(m, ST_ACTOR); + } + return XthNearestOf(parameters,count, ga_flags); +} + +Targets *XthNearestEnemyOf(Targets *parameters, int count, int ga_flags) +{ + Actor *origin = (Actor *) parameters->GetTarget(0, ST_ACTOR); + parameters->Clear(); + if (!origin) { + return parameters; + } + //determining the allegiance of the origin + int type = GetGroup(origin); + + if (type==2) { + return parameters; + } + Map *map = origin->GetCurrentArea(); + int i = map->GetActorCount(true); + Actor *ac; + while (i--) { + ac=map->GetActor(i,true); + int distance; + //int distance = Distance(ac, origin); + // TODO: if it turns out you need to check Sender here, beware you take the right distance! + // (n the original games, this is only used for NearestEnemyOf(Player1) in obsgolem.bcs) + if (!DoObjectChecks(map, origin, ac, distance)) continue; + if (type) { //origin is PC + if (ac->GetStat(IE_EA) >= EA_EVILCUTOFF) { + parameters->AddTarget(ac, distance, ga_flags); + } + } + else { + if (ac->GetStat(IE_EA) <= EA_GOODCUTOFF) { + parameters->AddTarget(ac, distance, ga_flags); + } + } + } + return XthNearestOf(parameters,count, ga_flags); +} + diff --git a/project/jni/application/gemrb/src/core/GameScript/Matching.h b/project/jni/application/gemrb/src/core/GameScript/Matching.h new file mode 100644 index 000000000..6a6562191 --- /dev/null +++ b/project/jni/application/gemrb/src/core/GameScript/Matching.h @@ -0,0 +1,47 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * + */ +#ifndef MATCHING_H +#define MATCHING_H + +#include "GameScript/GameScript.h" + +#include "exports.h" + +Targets* GetAllObjects(Map *map, Scriptable* Sender, Object* oC, int ga_flags); +Targets* GetAllActors(Scriptable* Sender, int ga_flags); +Scriptable* GetActorFromObject(Scriptable* Sender, Object* oC, int ga_flags = 0); +Scriptable* GetStoredActorFromObject(Scriptable* Sender, Object* oC, int ga_flags = 0); +Scriptable *GetActorObject(TileMap *TMap, const char *name); + +Targets *GetMyTarget(Scriptable *Sender, Actor *actor, Targets *parameters, int ga_flags); +Targets *XthNearestOf(Targets *parameters, int count, int ga_flags); +Targets *XthNearestDoor(Targets *parameters, unsigned int count); +Targets *XthNearestEnemyOf(Targets *parameters, int count, int ga_flags); +Targets *ClosestEnemySummoned(Scriptable *origin, Targets *parameters, int ga_flags); +Targets *XthNearestEnemyOfType(Scriptable *origin, Targets *parameters, unsigned int count, int ga_flags); +Targets *XthNearestMyGroupOfType(Scriptable *origin, Targets *parameters, unsigned int count, int ga_flags); + +/* returns true if actor matches the object specs. */ +bool MatchActor(Scriptable *Sender, ieDword ID, Object* oC); +/* returns the number of actors matching the IDS targeting */ +int GetObjectCount(Scriptable* Sender, Object* oC); +int GetObjectLevelCount(Scriptable* Sender, Object* oC); + +#endif diff --git a/project/jni/application/gemrb/src/core/GameScript/Objects.cpp b/project/jni/application/gemrb/src/core/GameScript/Objects.cpp new file mode 100644 index 000000000..2cc9b3b31 --- /dev/null +++ b/project/jni/application/gemrb/src/core/GameScript/Objects.cpp @@ -0,0 +1,1158 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * + */ + +#include "GameScript/GameScript.h" + +#include "GameScript/GSUtils.h" +#include "GameScript/Matching.h" + +#include "win32def.h" + +#include "DialogHandler.h" +#include "Game.h" +#include "GUI/GameControl.h" + +//------------------------------------------------------------- +// Object Functions +//------------------------------------------------------------- + +//in this implementation, Myself will drop the parameter array +//i think all object filters could be expected to do so +//they should remove unnecessary elements from the parameters +Targets *GameScript::Myself(Scriptable* Sender, Targets* parameters, int ga_flags) +{ + parameters->Clear(); + parameters->AddTarget(Sender, 0, ga_flags); + return parameters; +} + +Targets *GameScript::NearestDoor(Scriptable* /*Sender*/, Targets *parameters, int /*ga_flags*/) +{ + return XthNearestDoor(parameters, 0); +} + +Targets *GameScript::SecondNearestDoor(Scriptable* /*Sender*/, Targets *parameters, int /*ga_flags*/) +{ + return XthNearestDoor(parameters, 1); +} + +Targets *GameScript::ThirdNearestDoor(Scriptable* /*Sender*/, Targets *parameters, int /*ga_flags*/) +{ + return XthNearestDoor(parameters, 2); +} + +Targets *GameScript::FourthNearestDoor(Scriptable* /*Sender*/, Targets *parameters, int /*ga_flags*/) +{ + return XthNearestDoor(parameters, 3); +} + +Targets *GameScript::FifthNearestDoor(Scriptable* /*Sender*/, Targets *parameters, int /*ga_flags*/) +{ + return XthNearestDoor(parameters, 4); +} + +Targets *GameScript::SixthNearestDoor(Scriptable* /*Sender*/, Targets *parameters, int /*ga_flags*/) +{ + return XthNearestDoor(parameters, 5); +} + +Targets *GameScript::SeventhNearestDoor(Scriptable* /*Sender*/, Targets *parameters, int /*ga_flags*/) +{ + return XthNearestDoor(parameters, 6); +} + +Targets *GameScript::EighthNearestDoor(Scriptable* /*Sender*/, Targets *parameters, int /*ga_flags*/) +{ + return XthNearestDoor(parameters, 7); +} + +Targets *GameScript::NinthNearestDoor(Scriptable* /*Sender*/, Targets *parameters, int /*ga_flags*/) +{ + return XthNearestDoor(parameters, 8); +} + +Targets *GameScript::TenthNearestDoor(Scriptable* /*Sender*/, Targets *parameters, int /*ga_flags*/) +{ + return XthNearestDoor(parameters, 9); +} + +//in bg2 it is same as player1 so far +//in iwd2 this is the Gabber!!! +//but also, if there is no gabber, it is the first PC +//probably it is simply the nearest exportable character... +Targets *GameScript::Protagonist(Scriptable* Sender, Targets *parameters, int ga_flags) +{ + parameters->Clear(); + //this sucks but IWD2 is like that... + static bool charnameisgabber = core->HasFeature(GF_CHARNAMEISGABBER); + if (charnameisgabber) { + GameControl* gc = core->GetGameControl(); + if (gc) { + parameters->AddTarget(gc->dialoghandler->GetSpeaker(), 0, ga_flags); + } + if (parameters->Count()) { + return parameters; + } + //ok, this will return the nearest PC in the first slot + Game *game = core->GetGame(); + int i = game->GetPartySize(false); + while(i--) { + Actor *target = game->GetPC(i,false); + parameters->AddTarget(target, Distance(Sender, target), ga_flags); + } + return parameters; + } + parameters->AddTarget(core->GetGame()->GetPC(0, false), 0, ga_flags); + return parameters; +} + +//last talker +Targets *GameScript::Gabber(Scriptable* /*Sender*/, Targets *parameters, int ga_flags) +{ + parameters->Clear(); + GameControl* gc = core->GetGameControl(); + if (gc) { + parameters->AddTarget(gc->dialoghandler->GetSpeaker(), 0, ga_flags); + } + return parameters; +} + +Targets *GameScript::LastTrigger(Scriptable *Sender, Targets *parameters, int ga_flags) +{ + parameters->Clear(); + if (Sender->LastTriggerObject) { + Actor *target = Sender->GetCurrentArea()->GetActorByGlobalID(Sender->LastTriggerObject); + parameters->AddTarget(target, 0, ga_flags); + } + return parameters; +} + +Targets *GameScript::LastMarkedObject(Scriptable *Sender, Targets *parameters, int ga_flags) +{ + Actor *actor = (Actor *) parameters->GetTarget(0, ST_ACTOR); + if (!actor) { + if (Sender->Type==ST_ACTOR) { + actor = (Actor *) Sender; + } + } + parameters->Clear(); + if (actor) { + Actor *target = actor->GetCurrentArea()->GetActorByGlobalID(actor->LastMarked); + if (target) { + parameters->AddTarget(target, 0, ga_flags); + } + } + return parameters; +} + +//actions should always use LastMarkedObject, because LastSeen could be deleted +Targets *GameScript::LastSeenBy(Scriptable *Sender, Targets *parameters, int ga_flags) +{ + Actor *actor = (Actor *) parameters->GetTarget(0, ST_ACTOR); + if (!actor) { + if (Sender->Type==ST_ACTOR) { + actor = (Actor *) Sender; + } + } + parameters->Clear(); + if (actor) { + Actor *target = actor->GetCurrentArea()->GetActorByGlobalID(actor->LastSeen); + if (target) { + parameters->AddTarget(target, 0, ga_flags); + } + } + return parameters; +} + +Targets *GameScript::LastHelp(Scriptable *Sender, Targets *parameters, int ga_flags) +{ + Actor *actor = (Actor *) parameters->GetTarget(0, ST_ACTOR); + if (!actor) { + if (Sender->Type==ST_ACTOR) { + actor = (Actor *) Sender; + } + } + parameters->Clear(); + if (actor) { + Actor *target = actor->GetCurrentArea()->GetActorByGlobalID(actor->LastHelp); + if (target) { + parameters->AddTarget(target, 0, ga_flags); + } + } + return parameters; +} + +Targets *GameScript::LastHeardBy(Scriptable *Sender, Targets *parameters, int ga_flags) +{ + Actor *actor = (Actor *) parameters->GetTarget(0, ST_ACTOR); + if (!actor) { + if (Sender->Type==ST_ACTOR) { + actor = (Actor *) Sender; + } + } + parameters->Clear(); + if (actor) { + Actor *target = actor->GetCurrentArea()->GetActorByGlobalID(actor->LastHeard); + if (target) { + parameters->AddTarget(target, 0, ga_flags); + } + } + return parameters; +} + +//i was told that Group means the same specifics, so this is just an +//object selector for everyone with the same specifics as the current object +Targets *GameScript::GroupOf(Scriptable *Sender, Targets *parameters, int ga_flags) +{ + Actor *actor = (Actor *) parameters->GetTarget(0, ST_ACTOR); + if (!actor) { + if (Sender->Type==ST_ACTOR) { + actor = (Actor *) Sender; + } + } + parameters->Clear(); + if (actor) { + ieDword tmp = actor->GetStat(IE_SPECIFIC); + Map *cm = Sender->GetCurrentArea(); + int i = cm->GetActorCount(true); + while (i--) { + Actor *target=cm->GetActor(i,true); + if (target && (target->GetStat(IE_SPECIFIC)==tmp) ) { + parameters->AddTarget(target, 0, ga_flags); + } + } + } + return parameters; +} + +/*this one is tough, but done */ +Targets *GameScript::ProtectorOf(Scriptable *Sender, Targets *parameters, int ga_flags) +{ + Actor *actor = (Actor *) parameters->GetTarget(0, ST_ACTOR); + if (!actor) { + if (Sender->Type==ST_ACTOR) { + actor = (Actor *) Sender; + } + } + parameters->Clear(); + if (actor) { + ieWord tmp = actor->LastProtected; + Map *cm = Sender->GetCurrentArea(); + int i = cm->GetActorCount(true); + while (i--) { + Actor *target=cm->GetActor(i,true); + if (target && (target->LastProtected ==tmp) ) { + parameters->AddTarget(target, 0, ga_flags); + } + } + } + return parameters; +} + +Targets *GameScript::ProtectedBy(Scriptable *Sender, Targets *parameters, int ga_flags) +{ + Actor *actor = (Actor *) parameters->GetTarget(0, ST_ACTOR); + if (!actor) { + if (Sender->Type==ST_ACTOR) { + actor = (Actor *) Sender; + } + } + parameters->Clear(); + if (actor) { + Actor *target = actor->GetCurrentArea()->GetActorByGlobalID(actor->LastProtected); + if (target) { + parameters->AddTarget(target, 0, ga_flags); + } + } + return parameters; +} + +Targets *GameScript::LastCommandedBy(Scriptable *Sender, Targets *parameters, int ga_flags) +{ + Actor *actor = (Actor *) parameters->GetTarget(0, ST_ACTOR); + if (!actor) { + if (Sender->Type==ST_ACTOR) { + actor = (Actor *) Sender; + } + } + parameters->Clear(); + if (actor) { + Actor *target = actor->GetCurrentArea()->GetActorByGlobalID(actor->LastCommander); + if (target) { + parameters->AddTarget(target, 0, ga_flags); + } + } + return parameters; +} + +// this is essentially a LastTargetedBy(0) - or MySelf +// but IWD2 defines it +Targets *GameScript::MyTarget(Scriptable *Sender, Targets *parameters, int ga_flags) +{ + return GetMyTarget(Sender, NULL, parameters, ga_flags); +} + +Targets *GameScript::LastTargetedBy(Scriptable *Sender, Targets *parameters, int ga_flags) +{ + Actor *actor = (Actor *) parameters->GetTarget(0, ST_ACTOR); + return GetMyTarget(Sender, actor, parameters, ga_flags); +} + +Targets *GameScript::LastAttackerOf(Scriptable *Sender, Targets *parameters, int ga_flags) +{ + Actor *actor = (Actor *) parameters->GetTarget(0, ST_ACTOR); + if (!actor) { + if (Sender->Type==ST_ACTOR) { + actor = (Actor *) Sender; + } + } + parameters->Clear(); + if (actor) { + Actor *target = actor->GetCurrentArea()->GetActorByGlobalID(actor->LastHitter); + if (target) { + parameters->AddTarget(target, 0, ga_flags); + } + } + return parameters; +} + +Targets *GameScript::LastHitter(Scriptable *Sender, Targets *parameters, int ga_flags) +{ + Actor *actor = (Actor *) parameters->GetTarget(0, ST_ACTOR); + if (!actor) { + if (Sender->Type==ST_ACTOR) { + actor = (Actor *) Sender; + } + } + parameters->Clear(); + if (actor) { + Actor *target = actor->GetCurrentArea()->GetActorByGlobalID(actor->LastHitter); + if (target) { + parameters->AddTarget(target, 0, ga_flags); + } + } + return parameters; +} + +Targets *GameScript::LeaderOf(Scriptable *Sender, Targets *parameters, int ga_flags) +{ + Actor *actor = (Actor *) parameters->GetTarget(0, ST_ACTOR); + if (!actor) { + if (Sender->Type==ST_ACTOR) { + actor = (Actor *) Sender; + } + } + parameters->Clear(); + if (actor) { + Actor *target = actor->GetCurrentArea()->GetActorByGlobalID(actor->LastFollowed); + if (target) { + parameters->AddTarget(target, 0, ga_flags); + } + } + return parameters; +} + +Targets *GameScript::LastTalkedToBy(Scriptable *Sender, Targets *parameters, int ga_flags) +{ + Actor *actor = (Actor *) parameters->GetTarget(0, ST_ACTOR); + if (!actor) { + if (Sender->Type==ST_ACTOR) { + actor = (Actor *) Sender; + } + } + parameters->Clear(); + if (actor) { + Actor *target = actor->GetCurrentArea()->GetActorByGlobalID(actor->LastTalkedTo); + if (target) { + parameters->AddTarget(target, 0, ga_flags); + } + } + return parameters; +} + +Targets *GameScript::LastSummonerOf(Scriptable* Sender, Targets *parameters, int ga_flags) +{ + Actor *actor = (Actor *) parameters->GetTarget(0, ST_ACTOR); + if (!actor) { + if (Sender->Type==ST_ACTOR) { + actor = (Actor *) Sender; + } + } + parameters->Clear(); + if (actor) { + Actor *target = actor->GetCurrentArea()->GetActorByGlobalID(actor->LastSummoner); + if (target) { + parameters->AddTarget(target, 0, ga_flags); + } + } + return parameters; +} + +Targets *GameScript::Player1(Scriptable* /*Sender*/, Targets *parameters, int ga_flags) +{ + parameters->Clear(); + parameters->AddTarget(core->GetGame()->GetPC(0,false), 0, ga_flags); + return parameters; +} + +Targets *GameScript::Player1Fill(Scriptable* /*Sender*/, Targets *parameters, int ga_flags) +{ + parameters->Clear(); + parameters->AddTarget(core->GetGame()->FindPC(1), 0, ga_flags); + return parameters; +} + +Targets *GameScript::Player2(Scriptable* /*Sender*/, Targets *parameters, int ga_flags) +{ + parameters->Clear(); + parameters->AddTarget(core->GetGame()->GetPC(1,false), 0, ga_flags); + return parameters; +} + +Targets *GameScript::Player2Fill(Scriptable* /*Sender*/, Targets *parameters, int ga_flags) +{ + parameters->Clear(); + parameters->AddTarget(core->GetGame()->FindPC(2), 0, ga_flags); + return parameters; +} + +Targets *GameScript::Player3(Scriptable* /*Sender*/, Targets *parameters, int ga_flags) +{ + parameters->Clear(); + parameters->AddTarget(core->GetGame()->GetPC(2,false), 0, ga_flags); + return parameters; +} + +Targets *GameScript::Player3Fill(Scriptable* /*Sender*/, Targets *parameters, int ga_flags) +{ + parameters->Clear(); + parameters->AddTarget(core->GetGame()->FindPC(3), 0, ga_flags); + return parameters; +} + +Targets *GameScript::Player4(Scriptable* /*Sender*/, Targets *parameters, int ga_flags) +{ + parameters->Clear(); + parameters->AddTarget(core->GetGame()->GetPC(3,false), 0, ga_flags); + return parameters; +} + +Targets *GameScript::Player4Fill(Scriptable* /*Sender*/, Targets *parameters, int ga_flags) +{ + parameters->Clear(); + parameters->AddTarget(core->GetGame()->FindPC(4), 0, ga_flags); + return parameters; +} + +Targets *GameScript::Player5(Scriptable* /*Sender*/, Targets *parameters, int ga_flags) +{ + parameters->Clear(); + parameters->AddTarget(core->GetGame()->GetPC(4,false), 0, ga_flags); + return parameters; +} + +Targets *GameScript::Player5Fill(Scriptable* /*Sender*/, Targets *parameters, int ga_flags) +{ + parameters->Clear(); + parameters->AddTarget(core->GetGame()->FindPC(5), 0, ga_flags); + return parameters; +} + +Targets *GameScript::Player6(Scriptable* /*Sender*/, Targets *parameters, int ga_flags) +{ + parameters->Clear(); + parameters->AddTarget(core->GetGame()->GetPC(5,false), 0, ga_flags); + return parameters; +} + +Targets *GameScript::Player6Fill(Scriptable* /*Sender*/, Targets *parameters, int ga_flags) +{ + parameters->Clear(); + parameters->AddTarget(core->GetGame()->FindPC(6), 0, ga_flags); + return parameters; +} + +Targets *GameScript::Player7(Scriptable* /*Sender*/, Targets *parameters, int ga_flags) +{ + parameters->Clear(); + parameters->AddTarget(core->GetGame()->GetPC(6,false), 0, ga_flags); + return parameters; +} + +Targets *GameScript::Player7Fill(Scriptable* /*Sender*/, Targets *parameters, int ga_flags) +{ + parameters->Clear(); + parameters->AddTarget(core->GetGame()->FindPC(7), 0, ga_flags); + return parameters; +} + +Targets *GameScript::Player8(Scriptable* /*Sender*/, Targets *parameters, int ga_flags) +{ + parameters->Clear(); + parameters->AddTarget(core->GetGame()->GetPC(7,false), 0, ga_flags); + return parameters; +} + +Targets *GameScript::Player8Fill(Scriptable* /*Sender*/, Targets *parameters, int ga_flags) +{ + parameters->Clear(); + parameters->AddTarget(core->GetGame()->FindPC(8), 0, ga_flags); + return parameters; +} + +Targets *GameScript::BestAC(Scriptable* /*Sender*/, Targets *parameters, int ga_flags) +{ + targetlist::iterator m; + const targettype *t = parameters->GetFirstTarget(m, ST_ACTOR); + if (!t) { + return parameters; + } + Scriptable *scr=t->actor; + Actor *actor=(Actor *) scr; + int bestac=actor->GetStat(IE_ARMORCLASS); + // assignment in while + while ( (t = parameters->GetNextTarget(m, ST_ACTOR) ) ) { + actor = (Actor *) t->actor; + int ac=actor->GetStat(IE_ARMORCLASS); + if (bestacactor; + } + } + + parameters->Clear(); + parameters->AddTarget(scr, 0, ga_flags); + return parameters; +} + +/*no idea why this object exists since the gender could be filtered easier*/ +Targets *GameScript::StrongestOfMale(Scriptable* /*Sender*/, Targets *parameters, int ga_flags) +{ + targetlist::iterator m; + const targettype *t = parameters->GetFirstTarget(m, ST_ACTOR); + if (!t) { + return parameters; + } + int pos=-1; + int worsthp=-1; + Scriptable *scr = NULL; + //assignment intentional + while ( (t = parameters->GetNextTarget(m, ST_ACTOR) ) ) { + Actor *actor = (Actor *) t->actor; + if (actor->GetStat(IE_SEX)!=SEX_MALE) continue; + int hp=actor->GetStat(IE_HITPOINTS); + if ((pos==-1) || (worsthpactor; + } + } + parameters->Clear(); + if (scr) { + parameters->AddTarget(scr, 0, ga_flags); + } + return parameters; +} + +Targets *GameScript::StrongestOf(Scriptable* /*Sender*/, Targets *parameters, int ga_flags) +{ + targetlist::iterator m; + const targettype *t = parameters->GetFirstTarget(m, ST_ACTOR); + if (!t) { + return parameters; + } + Scriptable *scr=t->actor; + Actor *actor=(Actor *) scr; + int besthp=actor->GetStat(IE_HITPOINTS); + // assignment in while + while ( (t = parameters->GetNextTarget(m, ST_ACTOR) ) ) { + actor = (Actor *) t->actor; + int hp=actor->GetStat(IE_HITPOINTS); + if (besthpactor; + } + } + parameters->Clear(); + parameters->AddTarget(scr, 0, ga_flags); + return parameters; +} + +Targets *GameScript::WeakestOf(Scriptable* /*Sender*/, Targets *parameters, int ga_flags) +{ + targetlist::iterator m; + const targettype *t = parameters->GetFirstTarget(m, ST_ACTOR); + if (!t) { + return parameters; + } + Scriptable *scr=t->actor; + Actor *actor=(Actor *) scr; + int worsthp=actor->GetStat(IE_HITPOINTS); + // assignment in while + while ( (t = parameters->GetNextTarget(m, ST_ACTOR) ) ) { + actor = (Actor *) t->actor; + int hp=actor->GetStat(IE_HITPOINTS); + if (worsthp>hp) { + worsthp=hp; + scr=t->actor; + } + } + parameters->Clear(); + parameters->AddTarget(scr, 0, ga_flags); + return parameters; +} + +Targets *GameScript::WorstAC(Scriptable* /*Sender*/, Targets *parameters, int ga_flags) +{ + targetlist::iterator m; + const targettype *t = parameters->GetFirstTarget(m, ST_ACTOR); + if (!t) { + return parameters; + } + Scriptable *scr=t->actor; + Actor *actor=(Actor *) scr; + int worstac=actor->GetStat(IE_ARMORCLASS); + // assignment in while + while ( (t = parameters->GetNextTarget(m, ST_ACTOR) ) ) { + actor = (Actor *) t->actor; + int ac=actor->GetStat(IE_ARMORCLASS); + if (worstac>ac) { + worstac=ac; + scr=t->actor; + } + } + parameters->Clear(); + parameters->AddTarget(scr, 0, ga_flags); + return parameters; +} + +Targets *GameScript::MostDamagedOf(Scriptable* Sender, Targets *parameters, int ga_flags) +{ + //Original engines restrict this to the PCs... + /*targetlist::iterator m; + const targettype *t = parameters->GetFirstTarget(m, ST_ACTOR); + if (!t) { + return parameters; + } + Scriptable *scr = t->actor; + Actor *actor=(Actor *) scr; + int worsthp=actor->GetStat(IE_MAXHITPOINTS)-actor->GetBase(IE_HITPOINTS); + // assignment in while + while ( (t = parameters->GetNextTarget(m, ST_ACTOR) ) ) { + actor = (Actor *) t->actor; + int hp=actor->GetStat(IE_MAXHITPOINTS)-actor->GetBase(IE_HITPOINTS); + if (worsthp>hp) { + worsthp=hp; + scr=t->actor; + } + } + parameters->Clear(); + parameters->AddTarget(scr, 0, ga_flags); + return parameters;*/ + Map* area = Sender->GetCurrentArea() ; + Game *game = core->GetGame(); + Scriptable* scr = NULL ; + int worsthp = 0xffff ; + int i = game->GetPartySize(false); + while (i--) { + Actor *actor = game->GetPC(i, false); + if(actor->GetCurrentArea() == area) { + int hp=actor->GetStat(IE_MAXHITPOINTS)-actor->GetBase(IE_HITPOINTS); + if (worsthp>hp) { + worsthp=hp; + scr=actor; + } + } + } + parameters->Clear(); + parameters->AddTarget(scr, 0, ga_flags); + return parameters; +} +Targets *GameScript::LeastDamagedOf(Scriptable* /*Sender*/, Targets *parameters, int ga_flags) +{ + targetlist::iterator m; + const targettype *t = parameters->GetFirstTarget(m, ST_ACTOR); + if (!t) { + return parameters; + } + Scriptable *scr = t->actor; + Actor *actor = (Actor *) scr; + int besthp=actor->GetStat(IE_MAXHITPOINTS)-actor->GetBase(IE_HITPOINTS); + // assignment in while + while ( (t = parameters->GetNextTarget(m, ST_ACTOR) ) ) { + actor = (Actor *) t->actor; + int hp=actor->GetStat(IE_MAXHITPOINTS)-actor->GetBase(IE_HITPOINTS); + if (besthpactor; + } + } + parameters->Clear(); + parameters->AddTarget(scr, 0, ga_flags); + return parameters; +} + +Targets *GameScript::Farthest(Scriptable* /*Sender*/, Targets *parameters, int ga_flags) +{ + const targettype *t = parameters->GetLastTarget(ST_ACTOR); + parameters->Clear(); + if (t) { + parameters->AddTarget(t->actor, 0, ga_flags); + } + return parameters; +} + +Targets *GameScript::FarthestEnemyOf(Scriptable* /*Sender*/, Targets *parameters, int ga_flags) +{ + return XthNearestEnemyOf(parameters, -1, ga_flags); +} + +Targets *GameScript::NearestEnemyOf(Scriptable* /*Sender*/, Targets *parameters, int ga_flags) +{ + return XthNearestEnemyOf(parameters, 0, ga_flags); +} + +Targets *GameScript::SecondNearestEnemyOf(Scriptable* /*Sender*/, Targets *parameters, int ga_flags) +{ + return XthNearestEnemyOf(parameters, 1, ga_flags); +} + +Targets *GameScript::ThirdNearestEnemyOf(Scriptable* /*Sender*/, Targets *parameters, int ga_flags) +{ + return XthNearestEnemyOf(parameters, 2, ga_flags); +} + +Targets *GameScript::FourthNearestEnemyOf(Scriptable* /*Sender*/, Targets *parameters, int ga_flags) +{ + return XthNearestEnemyOf(parameters, 3, ga_flags); +} + +Targets *GameScript::FifthNearestEnemyOf(Scriptable* /*Sender*/, Targets *parameters, int ga_flags) +{ + return XthNearestEnemyOf(parameters, 4, ga_flags); +} + +Targets *GameScript::SixthNearestEnemyOf(Scriptable* /*Sender*/, Targets *parameters, int ga_flags) +{ + return XthNearestEnemyOf(parameters, 5, ga_flags); +} + +Targets *GameScript::SeventhNearestEnemyOf(Scriptable* /*Sender*/, Targets *parameters, int ga_flags) +{ + return XthNearestEnemyOf(parameters, 6, ga_flags); +} + +Targets *GameScript::EighthNearestEnemyOf(Scriptable* /*Sender*/, Targets *parameters, int ga_flags) +{ + return XthNearestEnemyOf(parameters, 7, ga_flags); +} + +Targets *GameScript::NinthNearestEnemyOf(Scriptable* /*Sender*/, Targets *parameters, int ga_flags) +{ + return XthNearestEnemyOf(parameters, 8, ga_flags); +} + +Targets *GameScript::TenthNearestEnemyOf(Scriptable* /*Sender*/, Targets *parameters, int ga_flags) +{ + return XthNearestEnemyOf(parameters, 9, ga_flags); +} + +Targets *GameScript::NearestEnemySummoned(Scriptable* Sender, Targets *parameters, int ga_flags) +{ + return ClosestEnemySummoned(Sender, parameters, ga_flags); +} + +Targets *GameScript::NearestEnemyOfType(Scriptable* Sender, Targets *parameters, int ga_flags) +{ + return XthNearestEnemyOfType(Sender, parameters, 0, ga_flags); +} + +Targets *GameScript::SecondNearestEnemyOfType(Scriptable* Sender, Targets *parameters, int ga_flags) +{ + return XthNearestEnemyOfType(Sender, parameters, 1, ga_flags); +} + +Targets *GameScript::ThirdNearestEnemyOfType(Scriptable* Sender, Targets *parameters, int ga_flags) +{ + return XthNearestEnemyOfType(Sender, parameters, 2, ga_flags); +} + +Targets *GameScript::FourthNearestEnemyOfType(Scriptable* Sender, Targets *parameters, int ga_flags) +{ + return XthNearestEnemyOfType(Sender, parameters, 3, ga_flags); +} + +Targets *GameScript::FifthNearestEnemyOfType(Scriptable* Sender, Targets *parameters, int ga_flags) +{ + return XthNearestEnemyOfType(Sender, parameters, 4, ga_flags); +} + +Targets *GameScript::SixthNearestEnemyOfType(Scriptable* Sender, Targets *parameters, int ga_flags) +{ + return XthNearestEnemyOfType(Sender, parameters, 5, ga_flags); +} + +Targets *GameScript::SeventhNearestEnemyOfType(Scriptable* Sender, Targets *parameters, int ga_flags) +{ + return XthNearestEnemyOfType(Sender, parameters, 6, ga_flags); +} + +Targets *GameScript::EighthNearestEnemyOfType(Scriptable* Sender, Targets *parameters, int ga_flags) +{ + return XthNearestEnemyOfType(Sender, parameters, 7, ga_flags); +} + +Targets *GameScript::NinthNearestEnemyOfType(Scriptable* Sender, Targets *parameters, int ga_flags) +{ + return XthNearestEnemyOfType(Sender, parameters, 8, ga_flags); +} + +Targets *GameScript::TenthNearestEnemyOfType(Scriptable* Sender, Targets *parameters, int ga_flags) +{ + return XthNearestEnemyOfType(Sender, parameters, 9, ga_flags); +} + +Targets *GameScript::NearestMyGroupOfType(Scriptable* Sender, Targets *parameters, int ga_flags) +{ + return XthNearestMyGroupOfType(Sender, parameters, 0, ga_flags); +} + +Targets *GameScript::SecondNearestMyGroupOfType(Scriptable* Sender, Targets *parameters, int ga_flags) +{ + return XthNearestMyGroupOfType(Sender, parameters, 1, ga_flags); +} + +Targets *GameScript::ThirdNearestMyGroupOfType(Scriptable* Sender, Targets *parameters, int ga_flags) +{ + return XthNearestMyGroupOfType(Sender, parameters, 2, ga_flags); +} + +Targets *GameScript::FourthNearestMyGroupOfType(Scriptable* Sender, Targets *parameters, int ga_flags) +{ + return XthNearestMyGroupOfType(Sender, parameters, 3, ga_flags); +} + +Targets *GameScript::FifthNearestMyGroupOfType(Scriptable* Sender, Targets *parameters, int ga_flags) +{ + return XthNearestMyGroupOfType(Sender, parameters, 4, ga_flags); +} + +Targets *GameScript::SixthNearestMyGroupOfType(Scriptable* Sender, Targets *parameters, int ga_flags) +{ + return XthNearestMyGroupOfType(Sender, parameters, 5, ga_flags); +} + +Targets *GameScript::SeventhNearestMyGroupOfType(Scriptable* Sender, Targets *parameters, int ga_flags) +{ + return XthNearestMyGroupOfType(Sender, parameters, 6, ga_flags); +} + +Targets *GameScript::EighthNearestMyGroupOfType(Scriptable* Sender, Targets *parameters, int ga_flags) +{ + return XthNearestMyGroupOfType(Sender, parameters, 7, ga_flags); +} + +Targets *GameScript::NinthNearestMyGroupOfType(Scriptable* Sender, Targets *parameters, int ga_flags) +{ + return XthNearestMyGroupOfType(Sender, parameters, 8, ga_flags); +} + +Targets *GameScript::TenthNearestMyGroupOfType(Scriptable* Sender, Targets *parameters, int ga_flags) +{ + return XthNearestMyGroupOfType(Sender, parameters, 9, ga_flags); +} + +/* returns only living PC's? if not, alter getpartysize/getpc flag*/ +Targets *GameScript::NearestPC(Scriptable* Sender, Targets *parameters, int ga_flags) +{ + parameters->Clear(); + Map *map = Sender->GetCurrentArea(); + Game *game = core->GetGame(); + int i = game->GetPartySize(true); + int mindist = -1; + Actor *ac = NULL; + while (i--) { + Actor *newactor=game->GetPC(i,true); + //NearestPC for PC's will not give themselves as a result + //this might be different from the original engine + if ((Sender->Type==ST_ACTOR) && (newactor == (Actor *) Sender)) { + continue; + } + if (newactor->GetCurrentArea()!=map) { + continue; + } + int dist = Distance(Sender, newactor); + if ( (mindist == -1) || (distAddTarget(ac, 0, ga_flags); + } + return parameters; +} + +Targets *GameScript::Nearest(Scriptable* /*Sender*/, Targets *parameters, int ga_flags) +{ + return XthNearestOf(parameters, 0, ga_flags); +} + +Targets *GameScript::SecondNearest(Scriptable* /*Sender*/, Targets *parameters, int ga_flags) +{ + return XthNearestOf(parameters, 1, ga_flags); +} + +Targets *GameScript::ThirdNearest(Scriptable* /*Sender*/, Targets *parameters, int ga_flags) +{ + return XthNearestOf(parameters, 2, ga_flags); +} + +Targets *GameScript::FourthNearest(Scriptable* /*Sender*/, Targets *parameters, int ga_flags) +{ + return XthNearestOf(parameters, 3, ga_flags); +} + +Targets *GameScript::FifthNearest(Scriptable* /*Sender*/, Targets *parameters, int ga_flags) +{ + return XthNearestOf(parameters, 4, ga_flags); +} + +Targets *GameScript::SixthNearest(Scriptable* /*Sender*/, Targets *parameters, int ga_flags) +{ + return XthNearestOf(parameters, 5, ga_flags); +} + +Targets *GameScript::SeventhNearest(Scriptable* /*Sender*/, Targets *parameters, int ga_flags) +{ + return XthNearestOf(parameters, 6, ga_flags); +} + +Targets *GameScript::EighthNearest(Scriptable* /*Sender*/, Targets *parameters, int ga_flags) +{ + return XthNearestOf(parameters, 7, ga_flags); +} + +Targets *GameScript::NinthNearest(Scriptable* /*Sender*/, Targets *parameters, int ga_flags) +{ + return XthNearestOf(parameters, 8, ga_flags); +} + +Targets *GameScript::TenthNearest(Scriptable* /*Sender*/, Targets *parameters, int ga_flags) +{ + return XthNearestOf(parameters, 9, ga_flags); +} + +Targets *GameScript::SelectedCharacter(Scriptable* Sender, Targets* parameters, int ga_flags) +{ + Map *cm = Sender->GetCurrentArea(); + parameters->Clear(); + int i = cm->GetActorCount(true); + while (i--) { + Actor *ac=cm->GetActor(i,true); + if (ac->GetCurrentArea()!=cm) { + continue; + } + if (ac->IsSelected()) { + parameters->AddTarget(ac, Distance(Sender, ac), ga_flags ); + } + } + return parameters; +} + +Targets *GameScript::Nothing(Scriptable* /*Sender*/, Targets* parameters, int /*ga_flags*/) +{ + parameters->Clear(); + return parameters; +} + +//------------------------------------------------------------- +// IDS Functions +//------------------------------------------------------------- + +int GameScript::ID_Alignment(Actor *actor, int parameter) +{ + int value = actor->GetStat( IE_ALIGNMENT ); + int a = parameter&15; + if (a) { + if (a != ( value & 15 )) { + return 0; + } + } + a = parameter & 240; + if (a) { + if (a != ( value & 240 )) { + return 0; + } + } + return 1; +} + +int GameScript::ID_Allegiance(Actor *actor, int parameter) +{ + int value = actor->GetStat( IE_EA ); + switch (parameter) { + case EA_GOODCUTOFF: + return value <= EA_GOODCUTOFF; + + case EA_NOTGOOD: + return value >= EA_NOTGOOD; + + case EA_NOTNEUTRAL: + return value >=EA_EVILCUTOFF || value <= EA_GOODCUTOFF; + + case EA_NOTEVIL: + return value <= EA_NOTEVIL; + + case EA_EVILCUTOFF: + return value >= EA_EVILCUTOFF; + + case 0: + case EA_ANYTHING: + return true; + + } + //default + return parameter == value; +} + +// *_ALL constants are different in iwd2 due to different classes (see note below) +// bard, cleric, druid, fighter, mage, paladin, ranger, thief +static const int all_bg_classes[] = { 206, 204, 208, 203, 202, 207, 209, 205 }; +static const int all_iwd2_classes[] = { 202, 203, 204, 205, 209, 206, 207, 208 }; + +// Dual-classed characters will detect only as their new class until their +// original class is re-activated, when they will detect as a multi-class +// GetClassLevel takes care of this automatically! +inline bool idclass(Actor *actor, int parameter, bool iwd2) { + int value = 0; + if (parameter < 202 || parameter > 209) { + value = actor->GetStat(IE_CLASS); + return parameter==value; + } + + const int *classes; + if (iwd2) { + classes = all_iwd2_classes; + } else { + classes = all_bg_classes; + } + + // we got one of the *_ALL values + if (parameter == classes[4]) { + // MAGE_ALL (also sorcerers) + value = actor->GetMageLevel() + actor->GetSorcererLevel(); + } else if (parameter == classes[3]) { + // FIGHTER_ALL (also monks) + value = actor->GetFighterLevel() + actor->GetMonkLevel(); + } else if (parameter == classes[1]) { + // CLERIC_ALL + value = actor->GetClericLevel(); + } else if (parameter == classes[7]) { + // THIEF_ALL + value = actor->GetThiefLevel(); + } else if (parameter == classes[0]) { + // BARD_ALL + value = actor->GetBardLevel(); + } else if (parameter == classes[5]) { + // PALADIN_ALL + value = actor->GetPaladinLevel(); + } else if (parameter == classes[2]) { + // DRUID_ALL + value = actor->GetDruidLevel(); + } else if (parameter == classes[6]) { + // RANGER_ALL + value = actor->GetRangerLevel(); + } + return value > 0; +} + +int GameScript::ID_Class(Actor *actor, int parameter) +{ + if (core->HasFeature(GF_3ED_RULES)) { + //iwd2 has different values, see also the note for AVClass + return idclass(actor, parameter, 1); + } + return idclass(actor, parameter, 0); +} + +// IE_CLASS holds only one class, not a bitmask like with iwd2 kits. The ids values +// are friendly to binary comparison, so we just need to build such a class value +int GameScript::ID_ClassMask(Actor *actor, int parameter) +{ + // maybe we're lucky... + int value = actor->GetStat(IE_CLASS); + if (parameter&(1<<(value-1))) return 1; + + // otherwise iterate over all the classes + value = actor->GetClassMask(); + + if (parameter&value) return 1; + return 0; +} + +// this is only present in iwd2 +// the function is identical to ID_Class, but uses the class20 IDS, +// iwd2's class.ids is different than the rest, while class20 is identical (remnant) +int GameScript::ID_AVClass(Actor *actor, int parameter) +{ + return idclass(actor, parameter, 0); +} + +int GameScript::ID_Race(Actor *actor, int parameter) +{ + int value = actor->GetStat(IE_RACE); + return parameter==value; +} + +int GameScript::ID_Subrace(Actor *actor, int parameter) +{ + int value = actor->GetStat(IE_SUBRACE); + return parameter==value; +} + +int GameScript::ID_Faction(Actor *actor, int parameter) +{ + int value = actor->GetStat(IE_FACTION); + return parameter==value; +} + +int GameScript::ID_Team(Actor *actor, int parameter) +{ + int value = actor->GetStat(IE_TEAM); + return parameter==value; +} + +int GameScript::ID_Gender(Actor *actor, int parameter) +{ + int value = actor->GetStat(IE_SEX); + return parameter==value; +} + +int GameScript::ID_General(Actor *actor, int parameter) +{ + int value = actor->GetStat(IE_GENERAL); + return parameter==value; +} + +int GameScript::ID_Specific(Actor *actor, int parameter) +{ + int value = actor->GetStat(IE_SPECIFIC); + return parameter==value; +} diff --git a/project/jni/application/gemrb/src/core/GameScript/Triggers.cpp b/project/jni/application/gemrb/src/core/GameScript/Triggers.cpp new file mode 100644 index 000000000..a780d818f --- /dev/null +++ b/project/jni/application/gemrb/src/core/GameScript/Triggers.cpp @@ -0,0 +1,4443 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003-2005 The GemRB Project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * + */ + +#include "GameScript/GameScript.h" + +#include "GameScript/GSUtils.h" +#include "GameScript/Matching.h" + +#include "win32def.h" + +#include "Calendar.h" +#include "DialogHandler.h" +#include "Game.h" +#include "GameData.h" +#include "Video.h" +#include "GUI/GameControl.h" +#include "math.h" //needs for acos + +//------------------------------------------------------------- +// Trigger Functions +//------------------------------------------------------------- +int GameScript::BreakingPoint(Scriptable* Sender, Trigger* /*parameters*/) +{ + int value=GetHappiness(Sender, core->GetGame()->Reputation ); + return value < -300; +} + +int GameScript::Reaction(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* scr = GetActorFromObject( Sender, parameters->objectParameter ); + if (!scr || scr->Type != ST_ACTOR) { + parameters->Dump(); + return 0; + } + int value = GetReaction(((Actor*) scr), Sender); + return value == parameters->int0Parameter; +} + +int GameScript::ReactionGT(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* scr = GetActorFromObject( Sender, parameters->objectParameter ); + if (!scr || scr->Type != ST_ACTOR) { + parameters->Dump(); + return 0; + } + int value = GetReaction(((Actor*) scr), Sender); + return value > parameters->int0Parameter; +} + +int GameScript::ReactionLT(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* scr = GetActorFromObject( Sender, parameters->objectParameter ); + if (!scr || scr->Type != ST_ACTOR) { + parameters->Dump(); + return 0; + } + int value = GetReaction(((Actor*) scr), Sender); + return value < parameters->int0Parameter; +} + +int GameScript::Happiness(Scriptable* Sender, Trigger* parameters) +{ + int value=GetHappiness(Sender, core->GetGame()->Reputation ); + return value == parameters->int0Parameter; +} + +int GameScript::HappinessGT(Scriptable* Sender, Trigger* parameters) +{ + int value=GetHappiness(Sender, core->GetGame()->Reputation ); + return value > parameters->int0Parameter; +} + +int GameScript::HappinessLT(Scriptable* Sender, Trigger* parameters) +{ + int value=GetHappiness(Sender, core->GetGame()->Reputation ); + return value < parameters->int0Parameter; +} + +int GameScript::Reputation(Scriptable* /*Sender*/, Trigger* parameters) +{ + return core->GetGame()->Reputation/10 == (ieDword) parameters->int0Parameter; +} + +int GameScript::ReputationGT(Scriptable* /*Sender*/, Trigger* parameters) +{ + return core->GetGame()->Reputation/10 > (ieDword) parameters->int0Parameter; +} + +int GameScript::ReputationLT(Scriptable* /*Sender*/, Trigger* parameters) +{ + return core->GetGame()->Reputation/10 < (ieDword) parameters->int0Parameter; +} + +int GameScript::Alignment(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* scr = GetActorFromObject( Sender, parameters->objectParameter ); + if (!scr || scr->Type != ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) scr; + return ID_Alignment( actor, parameters->int0Parameter); +} + +int GameScript::Allegiance(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* scr = GetActorFromObject( Sender, parameters->objectParameter ); + if (!scr || scr->Type != ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) scr; + return ID_Allegiance( actor, parameters->int0Parameter); +} + +//should return *_ALL stuff +int GameScript::Class(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* scr = GetActorFromObject( Sender, parameters->objectParameter ); + if (!scr || scr->Type != ST_ACTOR) { + return 0; + } + Actor* actor = (Actor*)scr; + return ID_Class( actor, parameters->int0Parameter); +} + +int GameScript::ClassEx(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* scr = GetActorFromObject( Sender, parameters->objectParameter ); + if (!scr || scr->Type != ST_ACTOR) { + return 0; + } + Actor* actor = (Actor*)scr; + return ID_AVClass( actor, parameters->int0Parameter); +} + +int GameScript::Faction(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* scr = GetActorFromObject( Sender, parameters->objectParameter ); + if (!scr || scr->Type != ST_ACTOR) { + return 0; + } + Actor* actor = (Actor*)scr; + return ID_Faction( actor, parameters->int0Parameter); +} + +int GameScript::Team(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* scr = GetActorFromObject( Sender, parameters->objectParameter ); + if (!scr || scr->Type != ST_ACTOR) { + return 0; + } + Actor* actor = (Actor*)scr; + return ID_Team( actor, parameters->int0Parameter); +} + +int GameScript::SubRace(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* scr = GetActorFromObject( Sender, parameters->objectParameter ); + if (!scr || scr->Type != ST_ACTOR) { + return 0; + } + Actor* actor = (Actor*)scr; + //subrace trigger uses a weird system, cannot use ID_* + //return ID_Subrace( actor, parameters->int0Parameter); + int value = actor->GetStat(IE_SUBRACE); + if (value) { + value |= actor->GetStat(IE_RACE)<<16; + } + if (value == parameters->int0Parameter) { + return 1; + } + return 0; +} + +//if object parameter is given (gemrb) it is used +//otherwise it works on the current object (iwd2) +int GameScript::IsTeamBitOn(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* scr = Sender; + if (parameters->objectParameter) { + scr = GetActorFromObject( Sender, parameters->objectParameter ); + } + if (!scr || scr->Type != ST_ACTOR) { + return 0; + } + Actor* actor = (Actor*)scr; + if (actor->GetStat(IE_TEAM) & parameters->int0Parameter) { + return 1; + } + return 0; +} + +int GameScript::NearbyDialog(Scriptable* Sender, Trigger* parameters) +{ + Actor *target = Sender->GetCurrentArea()->GetActorByDialog(parameters->string0Parameter); + if ( !target ) { + return 0; + } + return CanSee( Sender, target, true, GA_NO_DEAD | GA_NO_HIDDEN ); +} + +//atm this checks for InParty and See, it is unsure what is required +int GameScript::IsValidForPartyDialog(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* scr = GetActorFromObject( Sender, parameters->objectParameter ); + if (!scr) { + scr = Sender; + } + if (scr->Type != ST_ACTOR) { + return 0; + } + Actor *target = (Actor *) scr; + //inparty returns -1 if not in party + if (core->GetGame()->InParty( target )<0) { + return 0; + } + //don't accept parties currently in dialog! + //this might disturb some modders, but this is the correct behaviour + //for example the aaquatah dialog in irenicus dungeon depends on it + GameControl *gc = core->GetGameControl(); + Actor *pc = (Actor *) scr; + if (pc->GetGlobalID() == gc->dialoghandler->targetID || pc->GetGlobalID()==gc->dialoghandler->speakerID) { + return 0; + } + + //don't accept parties with the no interrupt flag + //this fixes bug #2573808 on gamescript level + //(still someone has to turn the no interrupt flag off) + if(!pc->GetDialog(GD_CHECK)) { + return 0; + } + return CanSee( Sender, target, false, GA_NO_DEAD ); +} + +int GameScript::InParty(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* scr; + + if (parameters->objectParameter) { + scr = GetActorFromObject( Sender, parameters->objectParameter ); + } else { + scr = Sender; + } + if (!scr || scr->Type != ST_ACTOR) { + return 0; + } + Actor *tar = (Actor *) scr; + if (core->GetGame()->InParty( tar ) <0) { + return 0; + } + //don't allow dead, don't allow maze and similar effects + if (tar->ValidTarget(GA_NO_DEAD|GA_NO_HIDDEN)) { + return 1; + } + return 0; +} + +int GameScript::InPartyAllowDead(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* scr; + + if (parameters->objectParameter) { + scr = GetActorFromObject( Sender, parameters->objectParameter ); + } else { + scr = Sender; + } + if (!scr || scr->Type != ST_ACTOR) { + return 0; + } + return core->GetGame()->InParty( ( Actor * ) scr ) >= 0 ? 1 : 0; +} + +int GameScript::InPartySlot(Scriptable* Sender, Trigger* parameters) +{ + Actor *actor = core->GetGame()->GetPC(parameters->int0Parameter, false); + return MatchActor(Sender, actor->GetGlobalID(), parameters->objectParameter); +} + +int GameScript::Exists(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* scr = GetActorFromObject( Sender, parameters->objectParameter ); + if (!scr) { + return 0; + } + return 1; +} + +int GameScript::IsAClown(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* scr = GetActorFromObject( Sender, parameters->objectParameter ); + if (!scr || scr->Type!=ST_ACTOR) { + return 0; + } + return 1; +} + +int GameScript::IsGabber(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* scr = GetActorFromObject( Sender, parameters->objectParameter ); + if (!scr || scr->Type!=ST_ACTOR) { + return 0; + } + if (scr->GetGlobalID() == core->GetGameControl()->dialoghandler->speakerID) + return 1; + return 0; +} + +int GameScript::IsActive(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* scr = GetActorFromObject( Sender, parameters->objectParameter ); + if (!scr) { + return 0; + } + if (scr->GetInternalFlag()&IF_ACTIVE) { + return 1; + } + return 0; +} + +int GameScript::InTrap(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* scr = GetActorFromObject( Sender, parameters->objectParameter ); + if (!scr) { + return 0; + } + if (scr->GetInternalFlag()&IF_INTRAP) { + return 1; + } + return 0; +} + +/* checks if targeted actor is in the specified region + GemRB allows different regions, referenced by int0Parameter + The polygons are stored in island.2da files */ +int GameScript::OnIsland(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* scr = GetActorFromObject( Sender, parameters->objectParameter ); + if (!scr) { + return 0; + } + Gem_Polygon *p = GetPolygon2DA(parameters->int0Parameter); + if (!p) { + return 0; + } + return p->PointIn(scr->Pos); +} + +int GameScript::School(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* scr = GetActorFromObject( Sender, parameters->objectParameter ); + if (!scr || scr->Type!=ST_ACTOR) { + return 0; + } + Actor* actor = (Actor *) scr; + //only the low 2 bytes count + //the School values start from 1 to 9 and the first school value is 0x40 + //so this mild hack will do + if ( actor->GetStat(IE_KIT) == (ieDword) (0x20<int0Parameter)) { + return 1; + } + return 0; +} + +int GameScript::Kit(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* scr = GetActorFromObject( Sender, parameters->objectParameter ); + if (!scr || scr->Type!=ST_ACTOR) { + return 0; + } + Actor* actor = (Actor *) scr; + + ieDword kit = actor->GetStat(IE_KIT); + //TODO: fix baseclass / barbarian confusion + + //IWD2 style kit matching (also used for mage schools) + if (kit == (ieDword) (parameters->int0Parameter)) { + return 1; + } + //BG2 style kit matching (not needed anymore?), we do it on load + //kit = (kit>>16)|(kit<<16); + if ( kit == (ieDword) (parameters->int0Parameter)) { + return 1; + } + return 0; +} + +int GameScript::General(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* scr = GetActorFromObject( Sender, parameters->objectParameter ); + if (!scr) { + scr = Sender; + } + if (scr->Type != ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) scr; + if (actor == NULL) { + return 0; + } + return ID_General(actor, parameters->int0Parameter); +} + +int GameScript::Specifics(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* scr = GetActorFromObject( Sender, parameters->objectParameter ); + if (!scr) { + scr = Sender; + } + if (scr->Type != ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) scr; + if (actor == NULL) { + return 0; + } + return ID_Specific(actor, parameters->int0Parameter); +} + +int GameScript::BitCheck(Scriptable* Sender, Trigger* parameters) +{ + bool valid=true; + + ieDword value = CheckVariable(Sender, parameters->string0Parameter, &valid ); + if (valid) { + if ( value & parameters->int0Parameter ) return 1; + } + return 0; +} + +int GameScript::BitCheckExact(Scriptable* Sender, Trigger* parameters) +{ + bool valid=true; + + ieDword value = CheckVariable(Sender, parameters->string0Parameter, &valid ); + if (valid) { + ieDword tmp = (ieDword) parameters->int0Parameter ; + if ((value & tmp) == tmp) return 1; + } + return 0; +} + +//BM_OR would make sense only if this trigger changes the value of the variable +//should I do that??? +int GameScript::BitGlobal_Trigger(Scriptable* Sender, Trigger* parameters) +{ + bool valid=true; + + ieDword value = CheckVariable(Sender, parameters->string0Parameter, &valid ); + if (valid) { + HandleBitMod(value, parameters->int0Parameter, parameters->int1Parameter); + if (value!=0) return 1; + } + return 0; +} + +int GameScript::GlobalOrGlobal_Trigger(Scriptable* Sender, Trigger* parameters) +{ + bool valid=true; + + ieDword value1 = CheckVariable(Sender, parameters->string0Parameter, &valid ); + if (valid) { + if ( value1 ) return 1; + ieDword value2 = CheckVariable(Sender, parameters->string1Parameter, &valid ); + if (valid) { + if ( value2 ) return 1; + } + } + return 0; +} + +int GameScript::GlobalAndGlobal_Trigger(Scriptable* Sender, Trigger* parameters) +{ + bool valid=true; + + ieDword value1 = CheckVariable( Sender, parameters->string0Parameter, &valid ); + if (valid && value1) { + ieDword value2 = CheckVariable( Sender, parameters->string1Parameter, &valid ); + if (valid && value2) return 1; + } + return 0; +} + +int GameScript::GlobalBAndGlobal_Trigger(Scriptable* Sender, Trigger* parameters) +{ + bool valid=true; + + ieDword value1 = CheckVariable(Sender, parameters->string0Parameter, &valid ); + if (valid) { + ieDword value2 = CheckVariable(Sender, parameters->string1Parameter, &valid ); + if (valid) { + if ((value1& value2 ) != 0) return 1; + } + } + return 0; +} + +int GameScript::GlobalBAndGlobalExact(Scriptable* Sender, Trigger* parameters) +{ + bool valid=true; + + ieDword value1 = CheckVariable(Sender, parameters->string0Parameter, &valid ); + if (valid) { + ieDword value2 = CheckVariable(Sender, parameters->string1Parameter, &valid ); + if (valid) { + if (( value1& value2 ) == value2) return 1; + } + } + return 0; +} + +int GameScript::GlobalBitGlobal_Trigger(Scriptable* Sender, Trigger* parameters) +{ + bool valid=true; + + ieDword value1 = CheckVariable(Sender, parameters->string0Parameter, &valid ); + if (valid) { + ieDword value2 = CheckVariable(Sender, parameters->string1Parameter, &valid ); + if (valid) { + HandleBitMod( value1, value2, parameters->int1Parameter); + if (value1!=0) return 1; + } + } + return 0; +} + +//no what exactly this trigger would do, defined in iwd2, but never used +//i just assume it sets a global in the trigger block +int GameScript::TriggerSetGlobal(Scriptable* Sender, Trigger* parameters) +{ + SetVariable( Sender, parameters->string0Parameter, parameters->int0Parameter ); + return 1; +} + +//would this function also alter the variable? +int GameScript::Xor(Scriptable* Sender, Trigger* parameters) +{ + bool valid=true; + + ieDword value = CheckVariable(Sender, parameters->string0Parameter, &valid ); + if (valid) { + if (( value ^ parameters->int0Parameter ) != 0) return 1; + } + return 0; +} + +//TODO: +//no sprite is dead for iwd, they use KILL__CNT +int GameScript::NumDead(Scriptable* Sender, Trigger* parameters) +{ + ieDword value; + + if (core->HasFeature(GF_HAS_KAPUTZ) ) { + value = CheckVariable(Sender, parameters->string0Parameter, "KAPUTZ"); + } else { + ieVariable VariableName; + snprintf(VariableName, 32, core->GetDeathVarFormat(), parameters->string0Parameter); + value = CheckVariable(Sender, VariableName, "GLOBAL" ); + } + return ( value == (ieDword) parameters->int0Parameter ); +} + +int GameScript::NumDeadGT(Scriptable* Sender, Trigger* parameters) +{ + ieDword value; + + if (core->HasFeature(GF_HAS_KAPUTZ) ) { + value = CheckVariable(Sender, parameters->string0Parameter, "KAPUTZ"); + } else { + ieVariable VariableName; + snprintf(VariableName, 32, core->GetDeathVarFormat(), parameters->string0Parameter); + value = CheckVariable(Sender, VariableName, "GLOBAL" ); + } + return ( value > (ieDword) parameters->int0Parameter ); +} + +int GameScript::NumDeadLT(Scriptable* Sender, Trigger* parameters) +{ + ieDword value; + + if (core->HasFeature(GF_HAS_KAPUTZ) ) { + value = CheckVariable(Sender, parameters->string0Parameter, "KAPUTZ"); + } else { + ieVariable VariableName; + + snprintf(VariableName, 32, core->GetDeathVarFormat(), parameters->string0Parameter); + value = CheckVariable(Sender, VariableName, "GLOBAL" ); + } + return ( value < (ieDword) parameters->int0Parameter ); +} + +int GameScript::G_Trigger(Scriptable* Sender, Trigger* parameters) +{ + ieDwordSigned value = CheckVariable(Sender, parameters->string0Parameter, "GLOBAL" ); + return ( value == parameters->int0Parameter ); +} + +int GameScript::Global(Scriptable* Sender, Trigger* parameters) +{ + bool valid=true; + + ieDwordSigned value = CheckVariable(Sender, parameters->string0Parameter, &valid ); + if (valid) { + if ( value == parameters->int0Parameter ) return 1; + } + return 0; +} + +int GameScript::GLT_Trigger(Scriptable* Sender, Trigger* parameters) +{ + ieDwordSigned value = CheckVariable(Sender, parameters->string0Parameter,"GLOBAL" ); + return ( value < parameters->int0Parameter ); +} + +int GameScript::GlobalLT(Scriptable* Sender, Trigger* parameters) +{ + bool valid=true; + + ieDwordSigned value = CheckVariable(Sender, parameters->string0Parameter, &valid ); + if (valid) { + if ( value < parameters->int0Parameter ) return 1; + } + return 0; +} + +int GameScript::GGT_Trigger(Scriptable* Sender, Trigger* parameters) +{ + ieDwordSigned value = CheckVariable(Sender, parameters->string0Parameter, "GLOBAL" ); + return ( value > parameters->int0Parameter ); +} + +int GameScript::GlobalGT(Scriptable* Sender, Trigger* parameters) +{ + bool valid=true; + + ieDwordSigned value = CheckVariable(Sender, parameters->string0Parameter, &valid ); + if (valid) { + if ( value > parameters->int0Parameter ) return 1; + } + return 0; +} + +int GameScript::GlobalLTGlobal(Scriptable* Sender, Trigger* parameters) +{ + bool valid=true; + + ieDwordSigned value1 = CheckVariable(Sender, parameters->string0Parameter, &valid ); + if (valid) { + ieDwordSigned value2 = CheckVariable(Sender, parameters->string1Parameter, &valid ); + if (valid) { + if ( value1 < value2 ) return 1; + } + } + return 0; +} + +int GameScript::GlobalGTGlobal(Scriptable* Sender, Trigger* parameters) +{ + bool valid=true; + + ieDwordSigned value1 = CheckVariable(Sender, parameters->string0Parameter, &valid ); + if (valid) { + ieDwordSigned value2 = CheckVariable(Sender, parameters->string1Parameter, &valid ); + if (valid) { + if ( value1 > value2 ) return 1; + } + } + return 0; +} + +int GameScript::GlobalsEqual(Scriptable* Sender, Trigger* parameters) +{ + ieDword value1 = CheckVariable(Sender, parameters->string0Parameter, "GLOBAL" ); + ieDword value2 = CheckVariable(Sender, parameters->string1Parameter, "GLOBAL" ); + return ( value1 == value2 ); +} + +int GameScript::GlobalsGT(Scriptable* Sender, Trigger* parameters) +{ + ieDword value1 = CheckVariable(Sender, parameters->string0Parameter, "GLOBAL" ); + ieDword value2 = CheckVariable(Sender, parameters->string1Parameter, "GLOBAL" ); + return ( value1 > value2 ); +} + +int GameScript::GlobalsLT(Scriptable* Sender, Trigger* parameters) +{ + ieDword value1 = CheckVariable(Sender, parameters->string0Parameter, "GLOBAL" ); + ieDword value2 = CheckVariable(Sender, parameters->string1Parameter, "GLOBAL" ); + return ( value1 < value2 ); +} + +int GameScript::LocalsEqual(Scriptable* Sender, Trigger* parameters) +{ + ieDword value1 = CheckVariable(Sender, parameters->string0Parameter, "LOCALS" ); + ieDword value2 = CheckVariable(Sender, parameters->string1Parameter, "LOCALS" ); + return ( value1 == value2 ); +} + +int GameScript::LocalsGT(Scriptable* Sender, Trigger* parameters) +{ + ieDword value1 = CheckVariable(Sender, parameters->string0Parameter, "LOCALS" ); + ieDword value2 = CheckVariable(Sender, parameters->string1Parameter, "LOCALS" ); + return ( value1 > value2 ); +} + +int GameScript::LocalsLT(Scriptable* Sender, Trigger* parameters) +{ + ieDword value1 = CheckVariable(Sender, parameters->string0Parameter, "LOCALS" ); + ieDword value2 = CheckVariable(Sender, parameters->string1Parameter, "LOCALS" ); + return ( value1 < value2 ); +} + +int GameScript::RealGlobalTimerExact(Scriptable* Sender, Trigger* parameters) +{ + bool valid=true; + + ieDword value1 = CheckVariable(Sender, parameters->string0Parameter, parameters->string1Parameter, &valid ); + if (valid && value1) { + ieDword value2 = core->GetGame()->RealTime; + if ( value1 == value2 ) return 1; + } + return 0; +} + +int GameScript::RealGlobalTimerExpired(Scriptable* Sender, Trigger* parameters) +{ + bool valid=true; + + ieDword value1 = CheckVariable(Sender, parameters->string0Parameter, parameters->string1Parameter, &valid ); + if (valid && value1) { + if ( value1 < core->GetGame()->RealTime ) return 1; + } + return 0; +} + +int GameScript::RealGlobalTimerNotExpired(Scriptable* Sender, Trigger* parameters) +{ + bool valid=true; + + ieDword value1 = CheckVariable(Sender, parameters->string0Parameter, parameters->string1Parameter, &valid ); + if (valid && value1) { + if ( value1 > core->GetGame()->RealTime ) return 1; + } + return 0; +} + +int GameScript::GlobalTimerExact(Scriptable* Sender, Trigger* parameters) +{ + bool valid=true; + + ieDword value1 = CheckVariable(Sender, parameters->string0Parameter, parameters->string1Parameter, &valid ); + if (valid) { + if ( value1 == core->GetGame()->GameTime ) return 1; + } + return 0; +} + +int GameScript::GlobalTimerExpired(Scriptable* Sender, Trigger* parameters) +{ + bool valid=true; + + ieDword value1 = CheckVariable(Sender, parameters->string0Parameter, parameters->string1Parameter, &valid ); + if (valid && value1) { + if ( value1 < core->GetGame()->GameTime ) return 1; + } + return 0; +} + +//globaltimernotexpired returns false if the timer doesn't exist +int GameScript::GlobalTimerNotExpired(Scriptable* Sender, Trigger* parameters) +{ + bool valid=true; + + ieDword value1 = CheckVariable(Sender, parameters->string0Parameter, parameters->string1Parameter, &valid ); + if (valid && value1) { + if ( value1 > core->GetGame()->GameTime ) return 1; + } + return 0; +} + +//globaltimerstarted returns false if the timer doesn't exist +//is it the same as globaltimernotexpired? +int GameScript::GlobalTimerStarted(Scriptable* Sender, Trigger* parameters) +{ + bool valid=true; + + ieDword value1 = CheckVariable(Sender, parameters->string0Parameter, parameters->string1Parameter, &valid ); + if (valid && value1) { + if ( value1 > core->GetGame()->GameTime ) return 1; + } + return 0; +} + +int GameScript::WasInDialog(Scriptable* Sender, Trigger* /*parameters*/) +{ + if (Sender->GetInternalFlag()&IF_WASINDIALOG) { + Sender->SetBitTrigger(BT_WASINDIALOG); + return 1; + } + return 0; +} + +int GameScript::OnCreation(Scriptable* Sender, Trigger* /*parameters*/) +{ + if (Sender->GetInternalFlag()&IF_ONCREATION) { + Sender->SetBitTrigger(BT_ONCREATION); + return 1; + } + return 0; +} + +int GameScript::NumItemsParty(Scriptable* /*Sender*/, Trigger* parameters) +{ + int cnt = 0; + Game *game=core->GetGame(); + + int i = game->GetPartySize(true); + while(i--) { + Actor *actor = game->GetPC(i, true); + cnt+=actor->inventory.CountItems(parameters->string0Parameter,1); + } + return cnt==parameters->int0Parameter; +} + +int GameScript::NumItemsPartyGT(Scriptable* /*Sender*/, Trigger* parameters) +{ + int cnt = 0; + Game *game=core->GetGame(); + + int i = game->GetPartySize(true); + while(i--) { + Actor *actor = game->GetPC(i, true); + cnt+=actor->inventory.CountItems(parameters->string0Parameter,1); + } + return cnt>parameters->int0Parameter; +} + +int GameScript::NumItemsPartyLT(Scriptable* /*Sender*/, Trigger* parameters) +{ + int cnt = 0; + Game *game=core->GetGame(); + + int i = game->GetPartySize(true); + while(i--) { + Actor *actor = game->GetPC(i, true); + cnt+=actor->inventory.CountItems(parameters->string0Parameter,1); + } + return cntint0Parameter; +} + +int GameScript::NumItems(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar) { + return 0; + } + + Inventory *inv = NULL; + switch (tar->Type) { + case ST_ACTOR: + inv = &(((Actor *) tar)->inventory); + break; + case ST_CONTAINER: + inv = &(((Container *) tar)->inventory); + break; + default:; + } + if (!inv) { + return 0; + } + + int cnt = inv->CountItems(parameters->string0Parameter,1); + return cnt==parameters->int0Parameter; +} + +int GameScript::TotalItemCnt(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if ( !tar || tar->Type!=ST_ACTOR) { + return 0; + } + Actor *actor = (Actor *) tar; + int cnt = actor->inventory.CountItems("",1); //shall we count heaps or not? + return cnt==parameters->int0Parameter; +} + +int GameScript::TotalItemCntExclude(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if ( !tar || tar->Type!=ST_ACTOR) { + return 0; + } + Actor *actor = (Actor *) tar; + int cnt = actor->inventory.CountItems("",1)-actor->inventory.CountItems(parameters->string0Parameter,1); //shall we count heaps or not? + return cnt==parameters->int0Parameter; +} + +int GameScript::NumItemsGT(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar) { + return 0; + } + + Inventory *inv = NULL; + switch (tar->Type) { + case ST_ACTOR: + inv = &(((Actor *) tar)->inventory); + break; + case ST_CONTAINER: + inv = &(((Container *) tar)->inventory); + break; + default:; + } + if (!inv) { + return 0; + } + + int cnt = inv->CountItems(parameters->string0Parameter,1); + return cnt>parameters->int0Parameter; +} + +int GameScript::TotalItemCntGT(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if ( !tar || tar->Type!=ST_ACTOR) { + return 0; + } + Actor *actor = (Actor *) tar; + int cnt = actor->inventory.CountItems("",1); //shall we count heaps or not? + return cnt>parameters->int0Parameter; +} + +int GameScript::TotalItemCntExcludeGT(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if ( !tar || tar->Type!=ST_ACTOR) { + return 0; + } + Actor *actor = (Actor *) tar; + int cnt = actor->inventory.CountItems("",1)-actor->inventory.CountItems(parameters->string0Parameter,1); //shall we count heaps or not? + return cnt>parameters->int0Parameter; +} + +int GameScript::NumItemsLT(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar) { + return 0; + } + + Inventory *inv = NULL; + switch (tar->Type) { + case ST_ACTOR: + inv = &(((Actor *) tar)->inventory); + break; + case ST_CONTAINER: + inv = &(((Container *) tar)->inventory); + break; + default:; + } + if (!inv) { + return 0; + } + + int cnt = inv->CountItems(parameters->string0Parameter,1); + return cntint0Parameter; +} + +int GameScript::TotalItemCntLT(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if ( !tar || tar->Type!=ST_ACTOR) { + return 0; + } + Actor *actor = (Actor *) tar; + int cnt = actor->inventory.CountItems("",1); //shall we count heaps or not? + return cntint0Parameter; +} + +int GameScript::TotalItemCntExcludeLT(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if ( !tar || tar->Type!=ST_ACTOR) { + return 0; + } + Actor *actor = (Actor *) tar; + int cnt = actor->inventory.CountItems("",1)-actor->inventory.CountItems(parameters->string0Parameter,1); //shall we count heaps or not? + return cntint0Parameter; +} + +//the int0 parameter is an addition, normally it is 0 +int GameScript::Contains(Scriptable* Sender, Trigger* parameters) +{ +//actually this should be a container + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if ( !tar || tar->Type!=ST_CONTAINER) { + return 0; + } + Container *cnt = (Container *) tar; + if (HasItemCore(&cnt->inventory, parameters->string0Parameter, parameters->int0Parameter) ) { + return 1; + } + return 0; +} + +int GameScript::StoreHasItem(Scriptable* /*Sender*/, Trigger* parameters) +{ + return StoreHasItemCore(parameters->string0Parameter, parameters->string1Parameter); +} + +//the int0 parameter is an addition, normally it is 0 +int GameScript::HasItem(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* scr = GetActorFromObject( Sender, parameters->objectParameter ); + if ( !scr ) { + return 0; + } + Inventory *inventory; + switch (scr->Type) { + case ST_ACTOR: + inventory = &( (Actor *) scr)->inventory; + break; + case ST_CONTAINER: + inventory = &( (Container *) scr)->inventory; + break; + default: + inventory = NULL; + break; + } + if (inventory && HasItemCore(inventory, parameters->string0Parameter, parameters->int0Parameter) ) { + return 1; + } + return 0; +} + +int GameScript::ItemIsIdentified(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* scr = GetActorFromObject( Sender, parameters->objectParameter ); + if ( !scr || scr->Type!=ST_ACTOR) { + return 0; + } + Actor *actor = (Actor *) scr; + if (HasItemCore(&actor->inventory, parameters->string0Parameter, IE_INV_ITEM_IDENTIFIED) ) { + return 1; + } + return 0; +} + +/** if the string is zero, then it will return true if there is any item in the slot (BG2)*/ +/** if the string is non-zero, it will return true, if the given item was in the slot (IWD2)*/ +int GameScript::HasItemSlot(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* scr = GetActorFromObject( Sender, parameters->objectParameter ); + if ( !scr || scr->Type!=ST_ACTOR) { + return 0; + } + Actor *actor = (Actor *) scr; + //this might require a conversion of the slots + if (actor->inventory.HasItemInSlot(parameters->string0Parameter, parameters->int0Parameter) ) { + return 1; + } + return 0; +} + +//this is a GemRB extension +//HasItemTypeSlot(Object, SLOT, ItemType) +//returns true if the item in SLOT is of ItemType +int GameScript::HasItemTypeSlot(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* scr = GetActorFromObject( Sender, parameters->objectParameter ); + if ( !scr || scr->Type!=ST_ACTOR) { + return 0; + } + Inventory *inv = &((Actor *) scr)->inventory; + if (parameters->int0Parameter>=inv->GetSlotCount()) { + return 0; + } + CREItem *slot = inv->GetSlotItem(parameters->int0Parameter); + if (!slot) { + return 0; + } + Item *itm = gamedata->GetItem(slot->ItemResRef); + int itemtype = itm->ItemType; + gamedata->FreeItem(itm, slot->ItemResRef, 0); + if (itemtype==parameters->int1Parameter) { + return 1; + } + return 0; +} + +int GameScript::HasItemEquipped(Scriptable * Sender, Trigger* parameters) +{ + Scriptable* scr = GetActorFromObject( Sender, parameters->objectParameter ); + if ( !scr || scr->Type!=ST_ACTOR) { + return 0; + } + Actor *actor = (Actor *) scr; + if (actor->inventory.HasItem(parameters->string0Parameter, IE_INV_ITEM_EQUIPPED) ) { + return 1; + } + return 0; +} + +int GameScript::Acquired(Scriptable * Sender, Trigger* parameters) +{ + if ( Sender->Type!=ST_ACTOR) { + return 0; + } + Actor *actor = (Actor *) Sender; + if (actor->inventory.HasItem(parameters->string0Parameter, IE_INV_ITEM_ACQUIRED) ) { + return 1; + } + return 0; +} + +/** this trigger accepts a numeric parameter, this number could be: */ +/** 0 - normal, 1 - equipped, 2 - identified, 3 - equipped&identified */ +/** this is a GemRB extension */ +int GameScript::PartyHasItem(Scriptable * /*Sender*/, Trigger* parameters) +{ + Game *game=core->GetGame(); + + int i = game->GetPartySize(true); + while(i--) { + Actor *actor = game->GetPC(i, true); + if (HasItemCore(&actor->inventory, parameters->string0Parameter, parameters->int0Parameter) ) { + return 1; + } + } + return 0; +} + +int GameScript::PartyHasItemIdentified(Scriptable * /*Sender*/, Trigger* parameters) +{ + Game *game=core->GetGame(); + + int i = game->GetPartySize(true); + while(i--) { + Actor *actor = game->GetPC(i, true); + if (HasItemCore(&actor->inventory, parameters->string0Parameter, IE_INV_ITEM_IDENTIFIED) ) { + return 1; + } + } + return 0; +} + +int GameScript::InventoryFull( Scriptable* Sender, Trigger* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar || tar->Type!=ST_ACTOR) { + return 0; + } + Actor *actor = (Actor *) tar; + if (actor->inventory.FindCandidateSlot( SLOT_INVENTORY, 0 )==-1) { + return 1; + } + return 0; +} + +int GameScript::HasInnateAbility(Scriptable *Sender, Trigger *parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar || tar->Type!=ST_ACTOR) { + return 0; + } + Actor *actor = (Actor *) tar; + if (parameters->string0Parameter[0]) { + return actor->spellbook.HaveSpell(parameters->string0Parameter, 0); + } + return actor->spellbook.HaveSpell(parameters->int0Parameter, 0); +} + +int GameScript::HaveSpell(Scriptable *Sender, Trigger *parameters) +{ + if (Sender->Type!=ST_ACTOR) { + return 0; + } + Actor *actor = (Actor *) Sender; + if (parameters->string0Parameter[0]) { + return actor->spellbook.HaveSpell(parameters->string0Parameter, 0); + } + return actor->spellbook.HaveSpell(parameters->int0Parameter, 0); +} + +int GameScript::HaveAnySpells(Scriptable* Sender, Trigger* /*parameters*/) +{ + if (Sender->Type!=ST_ACTOR) { + return 0; + } + Actor *actor = (Actor *) Sender; + return actor->spellbook.HaveSpell("", 0); +} + +int GameScript::HaveSpellParty(Scriptable* /*Sender*/, Trigger *parameters) +{ + Game *game=core->GetGame(); + + int i = game->GetPartySize(true); + + if (parameters->string0Parameter[0]) { + while(i--) { + Actor *actor = game->GetPC(i, true); + if (actor->spellbook.HaveSpell(parameters->string0Parameter, 0) ) { + return 1; + } + } + } else { + while(i--) { + Actor *actor = game->GetPC(i, true); + if (actor->spellbook.HaveSpell(parameters->int0Parameter, 0) ) { + return 1; + } + } + } + return 0; +} + +int GameScript::KnowSpell(Scriptable *Sender, Trigger *parameters) +{ + if (Sender->Type!=ST_ACTOR) { + return 0; + } + Actor *actor = (Actor *) Sender; + if (parameters->string0Parameter[0]) { + return actor->spellbook.KnowSpell(parameters->string0Parameter); + } + return actor->spellbook.KnowSpell(parameters->int0Parameter); +} + +int GameScript::True(Scriptable * /* Sender*/, Trigger * /*parameters*/) +{ + return 1; +} + +//in fact this could be used only on Sender, but we want to enhance these +//triggers and actions to accept an object argument whenever possible. +//0 defaults to Myself (Sender) +int GameScript::NumTimesTalkedTo(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* scr = GetActorFromObject( Sender, parameters->objectParameter ); + if (!scr) { + scr = Sender; + } + if (scr->Type != ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) scr; + return actor->TalkCount == (ieDword) parameters->int0Parameter ? 1 : 0; +} + +int GameScript::NumTimesTalkedToGT(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* scr = GetActorFromObject( Sender, parameters->objectParameter ); + if (!scr) { + scr = Sender; + } + if (scr->Type != ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) scr; + return actor->TalkCount > (ieDword) parameters->int0Parameter ? 1 : 0; +} + +int GameScript::NumTimesTalkedToLT(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* scr = GetActorFromObject( Sender, parameters->objectParameter ); + if (!scr) { + scr = Sender; + } + if (scr->Type != ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) scr; + return actor->TalkCount < (ieDword) parameters->int0Parameter ? 1 : 0; +} + +int GameScript::NumTimesInteracted(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* scr = GetActorFromObject( Sender, parameters->objectParameter ); + if (!scr) { + scr = Sender; + } + if (scr->Type != ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) scr; + ieDword npcid = parameters->int0Parameter; + if (npcid>=MAX_INTERACT) return 0; + if (!actor->PCStats) return 0; + return actor->PCStats->Interact[npcid] == (ieDword) parameters->int1Parameter ? 1 : 0; +} + +int GameScript::NumTimesInteractedGT(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* scr = GetActorFromObject( Sender, parameters->objectParameter ); + if (!scr) { + scr = Sender; + } + if (scr->Type != ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) scr; + ieDword npcid = parameters->int0Parameter; + if (npcid>=MAX_INTERACT) return 0; + if (!actor->PCStats) return 0; + return actor->PCStats->Interact[npcid] > (ieDword) parameters->int1Parameter ? 1 : 0; +} + +int GameScript::NumTimesInteractedLT(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* scr = GetActorFromObject( Sender, parameters->objectParameter ); + if (!scr) { + scr = Sender; + } + if (scr->Type != ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) scr; + ieDword npcid = parameters->int0Parameter; + if (npcid>=MAX_INTERACT) return 0; + if (!actor->PCStats) return 0; + return actor->PCStats->Interact[npcid] < (ieDword) parameters->int1Parameter ? 1 : 0; +} + +//GemRB specific +//interacting npc counts were restricted to 24 +//gemrb will increase a local variable in the interacting npc, with the scriptname of the +//target npc +int GameScript::NumTimesInteractedObject(Scriptable* Sender, Trigger* parameters) +{ + if (Sender->Type != ST_ACTOR) { + return 0; + } + + Scriptable* scr = GetActorFromObject( Sender, parameters->objectParameter ); + if (!scr) { + return 0; + } + if (scr->Type != ST_ACTOR) { + return 0; + } + Actor* tar = ( Actor* ) scr; + return CheckVariable(Sender, tar->GetScriptName(), "LOCALS") == (ieDword) parameters->int0Parameter ? 1 : 0; +} + +int GameScript::NumTimesInteractedObjectGT(Scriptable* Sender, Trigger* parameters) +{ + if (Sender->Type != ST_ACTOR) { + return 0; + } + + Scriptable* scr = GetActorFromObject( Sender, parameters->objectParameter ); + if (!scr) { + return 0; + } + if (scr->Type != ST_ACTOR) { + return 0; + } + Actor* tar = ( Actor* ) scr; + return CheckVariable(Sender, tar->GetScriptName(), "LOCALS") > (ieDword) parameters->int0Parameter ? 1 : 0; +} + +int GameScript::NumTimesInteractedObjectLT(Scriptable* Sender, Trigger* parameters) +{ + if (Sender->Type != ST_ACTOR) { + return 0; + } + + Scriptable* scr = GetActorFromObject( Sender, parameters->objectParameter ); + if (!scr) { + return 0; + } + if (scr->Type != ST_ACTOR) { + return 0; + } + Actor* tar = ( Actor* ) scr; + return CheckVariable(Sender, tar->GetScriptName(), "LOCALS") < (ieDword) parameters->int0Parameter ? 1 : 0; +} + +int GameScript::ObjectActionListEmpty(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* scr = GetActorFromObject( Sender, parameters->objectParameter ); + if (!scr) { + return 0; + } + + // added CurrentAction as part of blocking action fixes + if (scr->GetCurrentAction() || scr->GetNextAction()) { + return 0; + } + return 1; +} + +int GameScript::ActionListEmpty(Scriptable* Sender, Trigger* /*parameters*/) +{ + // added CurrentAction as part of blocking action fixes + if (Sender->GetCurrentAction() || Sender->GetNextAction()) { + return 0; + } + return 1; +} + +int GameScript::False(Scriptable* /*Sender*/, Trigger* /*parameters*/) +{ + return 0; +} + +/* i guess this is a range of circle edges (instead of centers) */ +int GameScript::PersonalSpaceDistance(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* scr = GetActorFromObject( Sender, parameters->objectParameter ); + if (!scr) { + return 0; + } + int range = parameters->int0Parameter; + + int distance = PersonalDistance(Sender, scr); + if (distance <= ( range * 10 )) { + return 1; + } + return 0; +} + +int GameScript::Range(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* scr = GetActorFromObject( Sender, parameters->objectParameter ); + if (!scr) { + return 0; + } + int distance = SquaredMapDistance(Sender, scr); + return DiffCore(distance, (parameters->int0Parameter+1)*(parameters->int0Parameter+1), parameters->int1Parameter); +} + +int GameScript::InLine(Scriptable* Sender, Trigger* parameters) +{ + Map *map = Sender->GetCurrentArea(); + if (!map) { + return 0; + } + + Scriptable* scr1 = GetActorFromObject( Sender, parameters->objectParameter ); + if (!scr1) { + return 0; + } + + //looking for a scriptable by scriptname only + Scriptable* scr2 = map->GetActor( parameters->string0Parameter, 0 ); + if (!scr2) { + scr2 = GetActorObject(map->GetTileMap(), parameters->string0Parameter); + } + if (!scr2) { + return 0; + } + + double fdm1 = SquaredDistance(Sender, scr1); + double fdm2 = SquaredDistance(Sender, scr2); + double fd12 = SquaredDistance(scr1, scr2); + double dm1 = sqrt(fdm1); + double dm2 = sqrt(fdm2); + + if (fdm1>fdm2 || fd12>fdm2) { + return 0; + } + double angle = acos(( fdm2 + fdm1 - fd12 ) / (2*dm1*dm2)); + if (angle*180.0*M_PI<30.0) return 1; + return 0; +} + +//PST +int GameScript::AtLocation( Scriptable* Sender, Trigger* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar) { + return 0; + } + if ( (tar->Pos.x==parameters->pointParameter.x) && + (tar->Pos.y==parameters->pointParameter.y) ) { + return 1; + } + return 0; +} + +//in pst this is a point +//in iwd2 this is not a point +int GameScript::NearLocation(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* scr = GetActorFromObject( Sender, parameters->objectParameter ); + if (!scr) { + return 0; + } + if (parameters->pointParameter.isnull()) { + Point p((short) parameters->int0Parameter, (short) parameters->int1Parameter); + int distance = PersonalDistance(p, scr); + if (distance <= ( parameters->int2Parameter * 10 )) { + return 1; + } + return 0; + } + // should this be PersonalDistance? + int distance = Distance(parameters->pointParameter, scr); + if (distance <= ( parameters->int0Parameter * 10 )) { + return 1; + } + return 0; +} + +int GameScript::NearSavedLocation(Scriptable* Sender, Trigger* parameters) +{ + if (Sender->Type!=ST_ACTOR) { + return 0; + } + if (core->HasFeature(GF_HAS_KAPUTZ)) { + // we don't understand how this works in pst yet + return 1; + } + Actor *actor = (Actor *) Sender; + Point p( (short) actor->GetStat(IE_SAVEDXPOS), (short) actor->GetStat(IE_SAVEDYPOS) ); + // should this be PersonalDistance? + int distance = Distance(p, Sender); + if (distance <= ( parameters->int0Parameter * 10 )) { + return 1; + } + return 0; +} + +int GameScript::Or(Scriptable* /*Sender*/, Trigger* parameters) +{ + return parameters->int0Parameter; +} + +int GameScript::TriggerTrigger(Scriptable* Sender, Trigger* parameters) +{ + if(Sender->TriggerID==(ieDword) parameters->int0Parameter) { + Sender->AddTrigger (&Sender->TriggerID); + return 1; + } + return 0; +} + +int GameScript::WalkedToTrigger(Scriptable* Sender, Trigger* parameters) +{ + Actor *target = Sender->GetCurrentArea()->GetActorByGlobalID(Sender->LastTrigger); + if (!target) { + return 0; + } + if (PersonalDistance(target, Sender) > 3*MAX_OPERATING_DISTANCE ) { + return 0; + } + //now objects suicide themselves if they are empty objects + //so checking an empty object is easier + if (parameters->objectParameter == NULL) { + Sender->AddTrigger (&Sender->LastTrigger); + return 1; + } + if (MatchActor(Sender, Sender->LastTrigger, parameters->objectParameter)) { + Sender->AddTrigger (&Sender->LastTrigger); + return 1; + } + return 0; +} + +int GameScript::Clicked(Scriptable* Sender, Trigger* parameters) +{ + //now objects suicide themselves if they are empty objects + //so checking an empty object is easier + if (parameters->objectParameter == NULL) { + if (Sender->LastTrigger) { + Sender->AddTrigger (&Sender->LastTrigger); + return 1; + } + return 0; + } + if (MatchActor(Sender, Sender->LastTrigger, parameters->objectParameter)) { + Sender->AddTrigger (&Sender->LastTrigger); + return 1; + } + return 0; +} + +int GameScript::Disarmed(Scriptable* Sender, Trigger* parameters) +{ + switch(Sender->Type) { + case ST_DOOR: case ST_CONTAINER: case ST_PROXIMITY: + break; + default: + return 0; + } + if (parameters->objectParameter == NULL) { + if (Sender->LastDisarmed) { + Sender->AddTrigger (&Sender->LastDisarmed); + return 1; + } + return 0; + } + if (MatchActor(Sender, Sender->LastDisarmed, parameters->objectParameter)) { + Sender->AddTrigger (&Sender->LastDisarmed); + return 1; + } + return 0; +} + +//stealing from a store failed, owner triggered +int GameScript::StealFailed(Scriptable* Sender, Trigger* parameters) +{ + switch(Sender->Type) { + case ST_ACTOR: + break; + default: + return 0; + } + // maybe check if Sender is a shopkeeper??? + + if (parameters->objectParameter == NULL) { + if (Sender->LastDisarmFailed) { + Sender->AddTrigger (&Sender->LastDisarmFailed); + return 1; + } + return 0; + } + if (MatchActor(Sender, Sender->LastDisarmFailed, parameters->objectParameter)) { + Sender->AddTrigger (&Sender->LastDisarmFailed); + return 1; + } + return 0; +} + +int GameScript::PickpocketFailed(Scriptable* Sender, Trigger* parameters) +{ + switch(Sender->Type) { + case ST_ACTOR: + break; + default: + return 0; + } + if (parameters->objectParameter == NULL) { + if (Sender->LastOpenFailed) { + Sender->AddTrigger (&Sender->LastOpenFailed); + return 1; + } + return 0; + } + if (MatchActor(Sender, Sender->LastOpenFailed, parameters->objectParameter)) { + Sender->AddTrigger (&Sender->LastOpenFailed); + return 1; + } + return 0; +} + +int GameScript::PickLockFailed(Scriptable* Sender, Trigger* parameters) +{ + switch(Sender->Type) { + case ST_DOOR: case ST_CONTAINER: + break; + default: + return 0; + } + if (parameters->objectParameter == NULL) { + if (Sender->LastPickLockFailed) { + Sender->AddTrigger (&Sender->LastPickLockFailed); + return 1; + } + return 0; + } + if (MatchActor(Sender, Sender->LastPickLockFailed, parameters->objectParameter)) { + Sender->AddTrigger (&Sender->LastPickLockFailed); + return 1; + } + return 0; +} + +int GameScript::OpenFailed(Scriptable* Sender, Trigger* parameters) +{ + switch(Sender->Type) { + case ST_DOOR: case ST_CONTAINER: + break; + default: + return 0; + } + if (parameters->objectParameter == NULL) { + if (Sender->LastOpenFailed) { + Sender->AddTrigger (&Sender->LastOpenFailed); + return 1; + } + return 0; + } + if (MatchActor(Sender, Sender->LastOpenFailed, parameters->objectParameter +)) { + Sender->AddTrigger (&Sender->LastOpenFailed); + return 1; + } + return 0; +} + +int GameScript::DisarmFailed(Scriptable* Sender, Trigger* parameters) +{ + switch(Sender->Type) { + case ST_DOOR: case ST_CONTAINER: case ST_PROXIMITY: + break; + default: + return 0; + } + if (parameters->objectParameter == NULL) { + if (Sender->LastDisarmFailed) { + Sender->AddTrigger (&Sender->LastDisarmFailed); + return 1; + } + return 0; + } + if (MatchActor(Sender, Sender->LastDisarmFailed, parameters->objectParameter)) { + Sender->AddTrigger (&Sender->LastDisarmFailed); + return 1; + } + return 0; +} + +//opened for doors/containers (using lastEntered) +int GameScript::Opened(Scriptable* Sender, Trigger* parameters) +{ + Door *door; + + switch (Sender->Type) { + case ST_DOOR: + door = (Door *) Sender; + if (!door->IsOpen()) { + return 0; + } + break; + case ST_CONTAINER: + break; + default: + return 0; + } + + if (parameters->objectParameter == NULL) { + if (Sender->LastEntered) { + Sender->AddTrigger (&Sender->LastEntered); + return 1; + } + return 0; + } + if (MatchActor(Sender, Sender->LastEntered, parameters->objectParameter)) { + Sender->AddTrigger (&Sender->LastEntered); + return 1; + } + return 0; +} + +//closed for doors (using lastTrigger) +int GameScript::Closed(Scriptable* Sender, Trigger* parameters) +{ + if (Sender->Type != ST_DOOR) { + return 0; + } + Door *door = (Door *) Sender; + if (door->IsOpen()) { + return 0; + } + + if (parameters->objectParameter == NULL) { + if (Sender->LastTrigger) { + Sender->AddTrigger (&Sender->LastTrigger); + return 1; + } + return 0; + } + if (MatchActor(Sender, Sender->LastTrigger, parameters->objectParameter)) { + Sender->AddTrigger (&Sender->LastTrigger); + return 1; + } + return 0; +} + +//unlocked for doors/containers (using lastUnlocked) +int GameScript::Unlocked(Scriptable* Sender, Trigger* parameters) +{ + Door *door; + + switch (Sender->Type) { + case ST_DOOR: + door = (Door *) Sender; + if ((door->Flags&DOOR_LOCKED) ) { + return 0; + } + break; + case ST_CONTAINER: + break; + default: + return 0; + } + + if (parameters->objectParameter == NULL) { + if (Sender->LastUnlocked) { + Sender->AddTrigger (&Sender->LastUnlocked); + return 1; + } + return 0; + } + if (MatchActor(Sender, Sender->LastUnlocked, parameters->objectParameter)) { + Sender->AddTrigger (&Sender->LastUnlocked); + return 1; + } + return 0; +} + +int GameScript::Entered(Scriptable* Sender, Trigger* parameters) +{ + if (Sender->Type != ST_PROXIMITY) { + return 0; + } + InfoPoint *ip = (InfoPoint *) Sender; + if (!ip->Trapped) { + return 0; + } + + if (parameters->objectParameter == NULL) { + if (Sender->LastEntered) { + Sender->AddTrigger (&Sender->LastEntered); + return 1; + } + return 0; + } + if (MatchActor(Sender, Sender->LastEntered, parameters->objectParameter)) { + Sender->AddTrigger (&Sender->LastEntered); + return 1; + } + return 0; +} + +int GameScript::HarmlessEntered(Scriptable* Sender, Trigger* parameters) +{ + if (Sender->Type != ST_PROXIMITY) { + return 0; + } + if (parameters->objectParameter == NULL) { + if (Sender->LastEntered) { + Sender->AddTrigger (&Sender->LastEntered); + return 1; + } + return 0; + } + if (MatchActor(Sender, Sender->LastEntered, parameters->objectParameter)) { + Sender->AddTrigger (&Sender->LastEntered); + return 1; + } + return 0; +} + +int GameScript::IsOverMe(Scriptable* Sender, Trigger* parameters) +{ + if (Sender->Type != ST_PROXIMITY) { + return 0; + } + Highlightable *trap = (Highlightable *)Sender; + + Targets *tgts = GetAllObjects(Sender->GetCurrentArea(), Sender, parameters->objectParameter, GA_NO_DEAD); + int ret = 0; + if (tgts) { + targetlist::iterator m; + const targettype *tt = tgts->GetFirstTarget(m, ST_ACTOR); + while (tt) { + Actor *actor = (Actor *) tt->actor; + if (trap->IsOver(actor->Pos)) { + ret = 1; + break; + } + tt = tgts->GetNextTarget(m, ST_ACTOR); + } + } + delete tgts; + return ret; +} + +//this function is different in every engines, if you use a string0parameter +//then it will be considered as a variable check +//you can also use an object parameter (like in iwd) +int GameScript::Dead(Scriptable* Sender, Trigger* parameters) +{ + if (parameters->string0Parameter[0]) { + ieDword value; + ieVariable Variable; + + if (core->HasFeature( GF_HAS_KAPUTZ )) { + value = CheckVariable( Sender, parameters->string0Parameter, "KAPUTZ"); + } else { + snprintf( Variable, 32, core->GetDeathVarFormat(), parameters->string0Parameter ); + } + value = CheckVariable( Sender, Variable, "GLOBAL" ); + if (value>0) { + return 1; + } + return 0; + } + Scriptable* target = GetActorFromObject( Sender, parameters->objectParameter ); + if (!target) { + return 1; + } + if (target->Type != ST_ACTOR) { + return 1; + } + Actor* actor = ( Actor* ) target; + if (actor->GetStat( IE_STATE_ID ) & STATE_DEAD) { + return 1; + } + return 0; +} + +int GameScript::CreatureHidden(Scriptable* Sender, Trigger* /*parameters*/) +{ + if (Sender->Type!=ST_ACTOR) { + return 0; + } + Actor *act=(Actor *) Sender; + + //this stuff is not completely clear, but HoW has a flag for this + //and GemRB uses the avatarremoval stat for it. + //HideCreature also sets this stat, so... + if (act->GetStat(IE_AVATARREMOVAL)) { + return 1; + } + + if (act->GetInternalFlag()&IF_VISIBLE) { + return 0; + } + return 1; +} +int GameScript::BecameVisible(Scriptable* Sender, Trigger* /*parameters*/) +{ + if (Sender->Type!=ST_ACTOR) { + return 0; + } + Actor *act=(Actor *) Sender; + if (act->GetInternalFlag()&IF_BECAMEVISIBLE) { + //set trigger to erase + act->SetBitTrigger(BT_BECAMEVISIBLE); + return 1; + } + return 0; +} + +int GameScript::Die(Scriptable* Sender, Trigger* /*parameters*/) +{ + if (Sender->Type!=ST_ACTOR) { + return 0; + } + Actor *act=(Actor *) Sender; + if (act->GetInternalFlag()&IF_JUSTDIED) { + //set trigger to erase + act->SetBitTrigger(BT_DIE); + return 1; + } + return 0; +} + +int GameScript::Died(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar || tar->Type!=ST_ACTOR) { + return 0; + } + Actor *act=(Actor *) tar; + if (act->GetInternalFlag()&IF_JUSTDIED) { + //set trigger to erase + act->SetBitTrigger(BT_DIE); + return 1; + } + return 0; +} + +int GameScript::PartyMemberDied(Scriptable* /*Sender*/, Trigger* /*parameters*/) +{ + Game *game = core->GetGame(); + int i = game->PartyMemberDied(); + if (i==-1) { + return 0; + } + //set trigger to erase + game->GetPC(i,false)->SetBitTrigger(BT_DIE); + return 1; +} + +int GameScript::NamelessBitTheDust(Scriptable* /*Sender*/, Trigger* /*parameters*/) +{ + Actor* actor = core->GetGame()->GetPC(0, false); + if (actor->GetInternalFlag()&IF_JUSTDIED) { + //set trigger to clear + actor->SetBitTrigger(BT_DIE); + return 1; + } + return 0; +} + +int GameScript::Race(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* scr = GetActorFromObject( Sender, parameters->objectParameter ); + if (!scr) { + return 0; + } + if (scr->Type != ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) scr; + return ID_Race(actor, parameters->int0Parameter); +} + +int GameScript::Gender(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* scr = GetActorFromObject( Sender, parameters->objectParameter ); + if (!scr) { + return 0; + } + if (scr->Type != ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) scr; + return ID_Gender(actor, parameters->int0Parameter); +} + +int GameScript::HP(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* scr = GetActorFromObject( Sender, parameters->objectParameter ); + if (!scr) { + return 0; + } + if (scr->Type != ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) scr; + if ((signed) actor->GetBase( IE_HITPOINTS ) == parameters->int0Parameter) { + return 1; + } + return 0; +} + +int GameScript::HPGT(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* scr = GetActorFromObject( Sender, parameters->objectParameter ); + if (!scr) { + return 0; + } + if (scr->Type != ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) scr; + if ( (signed) actor->GetBase( IE_HITPOINTS ) > parameters->int0Parameter) { + return 1; + } + return 0; +} + +int GameScript::HPLT(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* scr = GetActorFromObject( Sender, parameters->objectParameter ); + if (!scr) { + return 0; + } + if (scr->Type != ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) scr; + if ( (signed) actor->GetBase( IE_HITPOINTS ) < parameters->int0Parameter) { + return 1; + } + return 0; +} + +//these triggers work on the current damage (not the last damage) +/* they are identical to HPLost +int GameScript::DamageTaken(Scriptable* Sender, Trigger* parameters) +{ + if (Sender->Type!=ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) Sender; + int damage = actor->GetStat(IE_MAXHITPOINTS)-actor->GetBase(IE_HITPOINTS); + if (damage==(int) parameters->int0Parameter) { + return 1; + } + return 0; +} + +int GameScript::DamageTakenGT(Scriptable* Sender, Trigger* parameters) +{ + if (Sender->Type!=ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) Sender; + int damage = actor->GetStat(IE_MAXHITPOINTS)-actor->GetBase(IE_HITPOINTS); + if (damage>(int) parameters->int0Parameter) { + return 1; + } + return 0; +} + +int GameScript::DamageTakenLT(Scriptable* Sender, Trigger* parameters) +{ + if (Sender->Type!=ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) Sender; + int damage = actor->GetStat(IE_MAXHITPOINTS)-actor->GetBase(IE_HITPOINTS); + if (damage<(int) parameters->int0Parameter) { + return 1; + } + return 0; +} +*/ + +int GameScript::HPLost(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* scr = GetActorFromObject( Sender, parameters->objectParameter ); + if (!scr) { + return 0; + } + if (scr->Type != ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) scr; + //max-current + if ( (signed) actor->GetStat(IE_MAXHITPOINTS)-(signed) actor->GetBase( IE_HITPOINTS ) == (signed) parameters->int0Parameter) { + return 1; + } + return 0; +} + +int GameScript::HPLostGT(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* scr = GetActorFromObject( Sender, parameters->objectParameter ); + if (!scr) { + return 0; + } + if (scr->Type != ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) scr; + //max-current + if ( (signed) actor->GetStat(IE_MAXHITPOINTS)-(signed) actor->GetBase( IE_HITPOINTS ) > (signed) parameters->int0Parameter) { + return 1; + } + return 0; +} + +int GameScript::HPLostLT(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* scr = GetActorFromObject( Sender, parameters->objectParameter ); + if (!scr) { + return 0; + } + if (scr->Type != ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) scr; + //max-current + if ( (signed) actor->GetStat(IE_MAXHITPOINTS)-(signed) actor->GetBase( IE_HITPOINTS ) < (signed) parameters->int0Parameter) { + return 1; + } + return 0; +} + +int GameScript::HPPercent(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* scr = GetActorFromObject( Sender, parameters->objectParameter ); + if (!scr) { + return 0; + } + if (GetHPPercent( scr ) == parameters->int0Parameter) { + return 1; + } + return 0; +} + +int GameScript::HPPercentGT(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* scr = GetActorFromObject( Sender, parameters->objectParameter ); + if (!scr) { + return 0; + } + if (GetHPPercent( scr ) > parameters->int0Parameter) { + return 1; + } + return 0; +} + +int GameScript::HPPercentLT(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* scr = GetActorFromObject( Sender, parameters->objectParameter ); + if (!scr) { + return 0; + } + if (GetHPPercent( scr ) < parameters->int0Parameter) { + return 1; + } + return 0; +} + +int GameScript::XP(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* scr = GetActorFromObject( Sender, parameters->objectParameter ); + if (!scr) { + return 0; + } + if (scr->Type != ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) scr; + if (actor->GetStat( IE_XP ) == (unsigned) parameters->int0Parameter) { + return 1; + } + return 0; +} + +int GameScript::XPGT(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* scr = GetActorFromObject( Sender, parameters->objectParameter ); + if (!scr) { + return 0; + } + if (scr->Type != ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) scr; + if (actor->GetStat( IE_XP ) > (unsigned) parameters->int0Parameter) { + return 1; + } + return 0; +} + +int GameScript::XPLT(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* scr = GetActorFromObject( Sender, parameters->objectParameter ); + if (!scr) { + return 0; + } + if (scr->Type != ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) scr; + if (actor->GetStat( IE_XP ) < (unsigned) parameters->int0Parameter) { + return 1; + } + return 0; +} + +int GameScript::CheckSkill(Scriptable* Sender, Trigger* parameters) +{ + if (parameters->int1Parameter>=SkillCount) { + return 0; + } + Scriptable* target = GetActorFromObject( Sender, parameters->objectParameter ); + if (!target) { + return 0; + } + if (target->Type != ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) target; + if ((signed) actor->GetStat( SkillStats[parameters->int1Parameter] ) == parameters->int0Parameter) { + return 1; + } + return 0; +} +int GameScript::CheckStat(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* target = GetActorFromObject( Sender, parameters->objectParameter ); + if (!target) { + return 0; + } + if (target->Type != ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) target; + if ((signed) actor->GetStat( parameters->int1Parameter ) == parameters->int0Parameter) { + return 1; + } + return 0; +} + +int GameScript::CheckSkillGT(Scriptable* Sender, Trigger* parameters) +{ + if (parameters->int1Parameter>=SkillCount) { + return 0; + } + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar || tar->Type != ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) tar; + if ((signed) actor->GetStat( SkillStats[parameters->int1Parameter] ) > parameters->int0Parameter) { + return 1; + } + return 0; +} + +int GameScript::CheckStatGT(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar || tar->Type != ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) tar; + if ((signed) actor->GetStat( parameters->int1Parameter ) > parameters->int0Parameter) { + return 1; + } + return 0; +} + +int GameScript::CheckSkillLT(Scriptable* Sender, Trigger* parameters) +{ + if (parameters->int1Parameter>=SkillCount) { + return 0; + } + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar || tar->Type != ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) tar; + if ((signed) actor->GetStat( SkillStats[parameters->int1Parameter] ) < parameters->int0Parameter) { + return 1; + } + return 0; +} + +int GameScript::CheckStatLT(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar || tar->Type != ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) tar; + if ((signed) actor->GetStat( parameters->int1Parameter ) < parameters->int0Parameter) { + return 1; + } + return 0; +} + +/* i believe this trigger is the same as 'MarkObject' action + except that if it cannot set the marked object, it returns false */ +int GameScript::SetLastMarkedObject(Scriptable* Sender, Trigger* parameters) +{ + if (Sender->Type != ST_ACTOR) { + return 0; + } + Actor *scr = (Actor *) Sender; + + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar || tar->Type != ST_ACTOR) { + return 0; + } + scr->LastMarked = tar->GetGlobalID(); + return 1; +} + +int GameScript::IsSpellTargetValid(Scriptable* Sender, Trigger* parameters) +{ + if (Sender->Type != ST_ACTOR) { + return 0; + } + Actor *scr = (Actor *) Sender; + + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + Actor *actor = NULL; + if (tar->Type == ST_ACTOR) { + actor = (Actor *) tar; + } + + int flags = parameters->int1Parameter; + if (!(flags & MSO_IGNORE_NULL) && !actor) { + return 0; + } + if (!(flags & MSO_IGNORE_INVALID) && actor && actor->InvalidSpellTarget() ) { + return 0; + } + int splnum = parameters->int0Parameter; + if (!(flags & MSO_IGNORE_HAVE) && !scr->spellbook.HaveSpell(splnum, 0) ) { + return 0; + } + int range; + if ((flags & MSO_IGNORE_RANGE) || !actor) { + range = 0; + } else { + range = Distance(scr, actor); + } + if (!(flags & MSO_IGNORE_INVALID) && actor->InvalidSpellTarget(splnum, scr, range)) { + return 0; + } + return 1; +} + +//This trigger seems to always return true for actors... +//Always manages to set spell to 0, otherwise it sets if there was nothing set earlier +int GameScript::SetMarkedSpell_Trigger(Scriptable* Sender, Trigger* parameters) +{ + if (Sender->Type != ST_ACTOR) { + return 0; + } + Actor *scr = (Actor *) Sender; + if (parameters->int0Parameter) { + if (scr->LastMarkedSpell) { + return 1; + } + if (!scr->spellbook.HaveSpell(parameters->int0Parameter, 0) ) { + return 1; + } + } + + //TODO: check if spell exists (not really important) + scr->LastMarkedSpell = parameters->int0Parameter; + return 1; +} + +int GameScript::ForceMarkedSpell_Trigger(Scriptable* Sender, Trigger* parameters) +{ + if (Sender->Type != ST_ACTOR) { + return 0; + } + Actor *scr = (Actor *) Sender; + scr->LastMarkedSpell = parameters->int0Parameter; + return 1; +} + +int GameScript::IsMarkedSpell(Scriptable* Sender, Trigger* parameters) +{ + if (Sender->Type != ST_ACTOR) { + return 0; + } + Actor *scr = (Actor *) Sender; + return scr->LastMarkedSpell == parameters->int0Parameter; +} + + +int GameScript::See(Scriptable* Sender, Trigger* parameters) +{ + int see = SeeCore(Sender, parameters, 0); + //don't mark LastSeen for clear!!! + if (Sender->Type==ST_ACTOR && see) { + Actor *act = (Actor *) Sender; + //save lastseen as lastmarked + act->LastMarked = act->LastSeen; + //Sender->AddTrigger (&act->LastSeen); + } + return see; +} + +int GameScript::Detect(Scriptable* Sender, Trigger* parameters) +{ + parameters->int0Parameter=1; //seedead/invis + int see = SeeCore(Sender, parameters, 0); + if (!see) { + return 0; + } + return 1; +} + +int GameScript::LOS(Scriptable* Sender, Trigger* parameters) +{ + int see=SeeCore(Sender, parameters, 1); + if (!see) { + return 0; + } + return Range(Sender, parameters); //same as range +} + +int GameScript::NumCreatures(Scriptable* Sender, Trigger* parameters) +{ + int value = GetObjectCount(Sender, parameters->objectParameter); + return value == parameters->int0Parameter; +} + +int GameScript::NumCreaturesAtMyLevel(Scriptable* Sender, Trigger* parameters) +{ + if (Sender->Type != ST_ACTOR) { + return 0; + } + int level = ((Actor *) Sender)->GetXPLevel(true); + int value; + + if (parameters->int0Parameter) { + value = GetObjectLevelCount(Sender, parameters->objectParameter); + } else { + value = GetObjectCount(Sender, parameters->objectParameter); + } + return value == level; +} + +int GameScript::NumCreaturesLT(Scriptable* Sender, Trigger* parameters) +{ + int value = GetObjectCount(Sender, parameters->objectParameter); + return value < parameters->int0Parameter; +} + +int GameScript::NumCreaturesLTMyLevel(Scriptable* Sender, Trigger* parameters) +{ + if (Sender->Type != ST_ACTOR) { + return 0; + } + int level = ((Actor *) Sender)->GetXPLevel(true); + int value; + + if (parameters->int0Parameter) { + value = GetObjectLevelCount(Sender, parameters->objectParameter); + } else { + value = GetObjectCount(Sender, parameters->objectParameter); + } + return value < level; +} + +int GameScript::NumCreaturesGT(Scriptable* Sender, Trigger* parameters) +{ + int value = GetObjectCount(Sender, parameters->objectParameter); + return value > parameters->int0Parameter; +} + +int GameScript::NumCreaturesGTMyLevel(Scriptable* Sender, Trigger* parameters) +{ + if (Sender->Type != ST_ACTOR) { + return 0; + } + int level = ((Actor *) Sender)->GetXPLevel(true); + int value; + + if (parameters->int0Parameter) { + value = GetObjectLevelCount(Sender, parameters->objectParameter); + } else { + value = GetObjectCount(Sender, parameters->objectParameter); + } + return value > level; +} + +int GameScript::NumCreatureVsParty(Scriptable* Sender, Trigger* parameters) +{ + //creating object on the spot + if (!parameters->objectParameter) { + parameters->objectParameter = new Object(); + } + int value = GetObjectCount(Sender, parameters->objectParameter); + value -= core->GetGame()->GetPartySize(true); + return value == parameters->int0Parameter; +} + +int GameScript::NumCreatureVsPartyGT(Scriptable* Sender, Trigger* parameters) +{ + if (!parameters->objectParameter) { + parameters->objectParameter = new Object(); + } + int value = GetObjectCount(Sender, parameters->objectParameter); + value -= core->GetGame()->GetPartySize(true); + return value > parameters->int0Parameter; +} + +int GameScript::NumCreatureVsPartyLT(Scriptable* Sender, Trigger* parameters) +{ + if (!parameters->objectParameter) { + parameters->objectParameter = new Object(); + } + int value = GetObjectCount(Sender, parameters->objectParameter); + value -= core->GetGame()->GetPartySize(true); + return value < parameters->int0Parameter; +} + +int GameScript::Morale(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar || tar->Type != ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) tar; + return (signed) actor->GetStat(IE_MORALEBREAK) == parameters->int0Parameter; +} + +int GameScript::MoraleGT(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar || tar->Type != ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) tar; + return (signed) actor->GetStat(IE_MORALEBREAK) > parameters->int0Parameter; +} + +int GameScript::MoraleLT(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar || tar->Type != ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) tar; + return (signed) actor->GetStat(IE_MORALEBREAK) < parameters->int0Parameter; +} + +int GameScript::CheckSpellState(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar || tar->Type != ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) tar; + if (parameters->int0Parameter>255) { + return 0; + } + unsigned int position = parameters->int0Parameter>>5; + unsigned int bit = 1<<(parameters->int0Parameter&31); + if (actor->GetStat(IE_SPLSTATE_ID1+position) & bit) { + return 1; + } + return 0; +} + +int GameScript::StateCheck(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar || tar->Type != ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) tar; + if (actor->GetStat(IE_STATE_ID) & parameters->int0Parameter) { + return 1; + } + return 0; +} + +int GameScript::ExtendedStateCheck(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar || tar->Type != ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) tar; + if (actor->GetStat(IE_EXTSTATE_ID) & parameters->int0Parameter) { + return 1; + } + return 0; +} + +int GameScript::NotStateCheck(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar || tar->Type != ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) tar; + if (actor->GetStat(IE_STATE_ID) & ~parameters->int0Parameter) { + return 1; + } + return 0; +} + +int GameScript::RandomNum(Scriptable* /*Sender*/, Trigger* parameters) +{ + if (parameters->int0Parameter<0) { + return 0; + } + if (parameters->int1Parameter<0) { + return 0; + } + return parameters->int1Parameter-1 == RandomNumValue%parameters->int0Parameter; +} + +int GameScript::RandomNumGT(Scriptable* /*Sender*/, Trigger* parameters) +{ + if (parameters->int0Parameter<0) { + return 0; + } + if (parameters->int1Parameter<0) { + return 0; + } + return parameters->int1Parameter-1 < RandomNumValue%parameters->int0Parameter; +} + +int GameScript::RandomNumLT(Scriptable* /*Sender*/, Trigger* parameters) +{ + if (parameters->int0Parameter<0) { + return 0; + } + if (parameters->int1Parameter<0) { + return 0; + } + return parameters->int1Parameter-1 > RandomNumValue%parameters->int0Parameter; +} + +int GameScript::OpenState(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar) { + if (InDebug&ID_TRIGGERS) { + printMessage("GameScript"," ",LIGHT_RED); + printf("Couldn't find door/container:%s\n", parameters->objectParameter? parameters->objectParameter->objectName:""); + printf("Sender: %s\n", Sender->GetScriptName() ); + } + return 0; + } + switch(tar->Type) { + case ST_DOOR: + { + Door *door =(Door *) tar; + return !door->IsOpen() == !parameters->int0Parameter; + } + case ST_CONTAINER: + { + Container *cont = (Container *) tar; + return !(cont->Flags&CONT_LOCKED) == !parameters->int0Parameter; + } + default:; //to remove a warning + } + printMessage("GameScript"," ",LIGHT_RED); + printf("Not a door/container:%s\n", tar->GetScriptName()); + return 0; +} + +int GameScript::IsLocked(Scriptable * Sender, Trigger *parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar) { + printMessage("GameScript"," ",LIGHT_RED); + printf("Couldn't find door/container:%s\n", parameters->objectParameter? parameters->objectParameter->objectName:""); + printf("Sender: %s\n", Sender->GetScriptName() ); + return 0; + } + switch(tar->Type) { + case ST_DOOR: + { + Door *door =(Door *) tar; + return !!(door->Flags&DOOR_LOCKED); + } + case ST_CONTAINER: + { + Container *cont = (Container *) tar; + return !!(cont->Flags&CONT_LOCKED); + } + default:; //to remove a warning + } + printMessage("GameScript"," ",LIGHT_RED); + printf("Not a door/container:%s\n", tar->GetScriptName()); + return 0; +} + +int GameScript::Level(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar) { + return 0; + } + if (tar->Type != ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) tar; + // FIXME: what about multiclasses or dualclasses? + return actor->GetStat(IE_LEVEL) == (unsigned) parameters->int0Parameter; +} + +//this is just a hack, actually multiclass should be available +int GameScript::ClassLevel(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar) { + return 0; + } + if (tar->Type != ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) tar; + + if (!ID_Class( actor, parameters->int0Parameter) ) + return 0; + // FIXME: compare the requested level + return actor->GetStat(IE_LEVEL) == (unsigned) parameters->int1Parameter; +} + +// iwd2 and pst have different order of parameters: +// ClassLevelGT(Protagonist,MAGE,89) +// LevelInClass(Myself,10,CLERIC) +int GameScript::LevelInClass(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar) { + return 0; + } + if (tar->Type != ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) tar; + + if (!ID_ClassMask( actor, parameters->int1Parameter) ) + return 0; + // FIXME: compare the requested level + return actor->GetStat(IE_LEVEL) == (unsigned) parameters->int0Parameter; +} + +int GameScript::LevelGT(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar) { + return 0; + } + if (tar->Type != ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) tar; + return actor->GetStat(IE_LEVEL) > (unsigned) parameters->int0Parameter; +} + +//this is just a hack, actually multiclass should be available +int GameScript::ClassLevelGT(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar) { + return 0; + } + if (tar->Type != ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) tar; + if (!ID_Class( actor, parameters->int0Parameter) ) + return 0; + return actor->GetStat(IE_LEVEL) > (unsigned) parameters->int1Parameter; +} + +int GameScript::LevelInClassGT(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar) { + return 0; + } + if (tar->Type != ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) tar; + + if (!ID_ClassMask( actor, parameters->int1Parameter) ) + return 0; + return actor->GetStat(IE_LEVEL) > (unsigned) parameters->int0Parameter; +} + +int GameScript::LevelLT(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar) { + return 0; + } + if (tar->Type != ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) tar; + return actor->GetStat(IE_LEVEL) < (unsigned) parameters->int0Parameter; +} + +int GameScript::ClassLevelLT(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar) { + return 0; + } + if (tar->Type != ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) tar; + if (!ID_Class( actor, parameters->int0Parameter) ) + return 0; + return actor->GetStat(IE_LEVEL) < (unsigned) parameters->int1Parameter; +} + +int GameScript::LevelInClassLT(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar) { + return 0; + } + if (tar->Type != ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) tar; + + if (!ID_ClassMask( actor, parameters->int1Parameter) ) + return 0; + return actor->GetStat(IE_LEVEL) < (unsigned) parameters->int0Parameter; +} + +int GameScript::UnselectableVariable(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar) { + return 0; + } + return tar->UnselectableTimer == (unsigned) parameters->int0Parameter; +} + +int GameScript::UnselectableVariableGT(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar) { + return 0; + } + return tar->UnselectableTimer > (unsigned) parameters->int0Parameter; +} + +int GameScript::UnselectableVariableLT(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar) { + return 0; + } + return tar->UnselectableTimer < (unsigned) parameters->int0Parameter; +} + +int GameScript::AreaCheck(Scriptable* Sender, Trigger* parameters) +{ + if (!strnicmp(Sender->GetCurrentArea()->GetScriptName(), parameters->string0Parameter, 8)) { + return 1; + } + return 0; +} + +int GameScript::AreaCheckObject(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + + if (!tar) { + return 0; + } + if (!strnicmp(tar->GetCurrentArea()->GetScriptName(), parameters->string0Parameter, 8)) { + return 1; + } + return 0; +} + +//lame iwd2 uses a numeric area identifier, this reduces its usability +int GameScript::CurrentAreaIs(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + + if (!tar) { + return 0; + } + ieResRef arearesref; + snprintf(arearesref, 8, "AR%04d", parameters->int0Parameter); + if (!strnicmp(tar->GetCurrentArea()->GetScriptName(), arearesref, 8)) { + return 1; + } + return 0; +} + +//lame bg2 uses a constant areaname prefix, this reduces its usability +//but in the spirit of flexibility, gemrb extension allows arbitrary prefixes +int GameScript::AreaStartsWith(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + + if (!tar) { + return 0; + } + ieResRef arearesref; + if (parameters->string0Parameter[0]) { + strnlwrcpy(arearesref, parameters->string0Parameter, 8); + } else { + strnlwrcpy(arearesref, "AR30", 8); //InWatchersKeep + } + int i = strlen(arearesref); + if (!strnicmp(tar->GetCurrentArea()->GetScriptName(), arearesref, i)) { + return 1; + } + return 0; +} + +int GameScript::EntirePartyOnMap(Scriptable* Sender, Trigger* /*parameters*/) +{ + Map *map = Sender->GetCurrentArea(); + Game *game=core->GetGame(); + int i=game->GetPartySize(true); + while (i--) { + Actor *actor=game->GetPC(i,true); + if (actor->GetCurrentArea()!=map) { + return 0; + } + } + return 1; +} + +int GameScript::AnyPCOnMap(Scriptable* Sender, Trigger* /*parameters*/) +{ + Map *map = Sender->GetCurrentArea(); + Game *game=core->GetGame(); + int i=game->GetPartySize(true); + while (i--) { + Actor *actor=game->GetPC(i,true); + if (actor->GetCurrentArea()==map) { + return 1; + } + } + return 0; +} + +int GameScript::InActiveArea(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar) { + return 0; + } + if (core->GetGame()->GetCurrentArea() == tar->GetCurrentArea()) { + return 1; + } + return 0; +} + +int GameScript::InMyArea(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar) { + return 0; + } + if (Sender->GetCurrentArea() == tar->GetCurrentArea()) { + return 1; + } + return 0; +} + +int GameScript::AreaType(Scriptable* Sender, Trigger* parameters) +{ + Map *map=Sender->GetCurrentArea(); + return (map->AreaType¶meters->int0Parameter)>0; +} + +int GameScript::IsExtendedNight( Scriptable* Sender, Trigger* /*parameters*/) +{ + Map *map=Sender->GetCurrentArea(); + if (map->AreaType&AT_EXTENDED_NIGHT) { + return 1; + } + return 0; +} + +int GameScript::AreaFlag(Scriptable* Sender, Trigger* parameters) +{ + Map *map=Sender->GetCurrentArea(); + return (map->AreaFlags¶meters->int0Parameter)>0; +} + +int GameScript::AreaRestDisabled(Scriptable* Sender, Trigger* /*parameters*/) +{ + Map *map=Sender->GetCurrentArea(); + if (map->AreaFlags&2) { + return 1; + } + return 0; +} + +//new optional parameter: size of actor (to reach target) +int GameScript::TargetUnreachable(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar || tar->Type != ST_ACTOR) { + return 1; //well, if it doesn't exist it is unreachable + } + Map *map=Sender->GetCurrentArea(); + if (!map) { + return 1; + } + unsigned int size = parameters->int0Parameter; + + if (!size) { + if (Sender->Type==ST_ACTOR) { + size = ((Movable *) Sender)->size; + } + else { + size = 1; + } + } + return map->TargetUnreachable( Sender->Pos, tar->Pos, size); +} + +int GameScript::PartyCountEQ(Scriptable* /*Sender*/, Trigger* parameters) +{ + return core->GetGame()->GetPartySize(0)==parameters->int0Parameter; +} + +int GameScript::PartyCountLT(Scriptable* /*Sender*/, Trigger* parameters) +{ + return core->GetGame()->GetPartySize(0)int0Parameter; +} + +int GameScript::PartyCountGT(Scriptable* /*Sender*/, Trigger* parameters) +{ + return core->GetGame()->GetPartySize(0)>parameters->int0Parameter; +} + +int GameScript::PartyCountAliveEQ(Scriptable* /*Sender*/, Trigger* parameters) +{ + return core->GetGame()->GetPartySize(1)==parameters->int0Parameter; +} + +int GameScript::PartyCountAliveLT(Scriptable* /*Sender*/, Trigger* parameters) +{ + return core->GetGame()->GetPartySize(1)int0Parameter; +} + +int GameScript::PartyCountAliveGT(Scriptable* /*Sender*/, Trigger* parameters) +{ + return core->GetGame()->GetPartySize(1)>parameters->int0Parameter; +} + +int GameScript::LevelParty(Scriptable* /*Sender*/, Trigger* parameters) +{ + return core->GetGame()->GetPartyLevel(1)==parameters->int0Parameter; +} + +int GameScript::LevelPartyLT(Scriptable* /*Sender*/, Trigger* parameters) +{ + return core->GetGame()->GetPartyLevel(1)int0Parameter; +} + +int GameScript::LevelPartyGT(Scriptable* /*Sender*/, Trigger* parameters) +{ + return core->GetGame()->GetPartyLevel(1)>parameters->int0Parameter; +} + +int GameScript::PartyGold(Scriptable* /*Sender*/, Trigger* parameters) +{ + return core->GetGame()->PartyGold == (ieDword) parameters->int0Parameter; +} + +int GameScript::PartyGoldGT(Scriptable* /*Sender*/, Trigger* parameters) +{ + return core->GetGame()->PartyGold > (ieDword) parameters->int0Parameter; +} + +int GameScript::PartyGoldLT(Scriptable* /*Sender*/, Trigger* parameters) +{ + return core->GetGame()->PartyGold < (ieDword) parameters->int0Parameter; +} + +int GameScript::OwnsFloaterMessage(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar) { + return 0; + } + return tar->textDisplaying; +} + +int GameScript::InCutSceneMode(Scriptable* /*Sender*/, Trigger* /*parameters*/) +{ + return core->InCutSceneMode(); +} + +int GameScript::Proficiency(Scriptable* Sender, Trigger* parameters) +{ + unsigned int idx = parameters->int0Parameter; + if (idx>31) { + return 0; + } + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar) { + return 0; + } + if (tar->Type != ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) tar; + return (signed) actor->GetStat(IE_PROFICIENCYBASTARDSWORD+idx) == parameters->int1Parameter; +} + +int GameScript::ProficiencyGT(Scriptable* Sender, Trigger* parameters) +{ + unsigned int idx = parameters->int0Parameter; + if (idx>31) { + return 0; + } + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar) { + return 0; + } + if (tar->Type != ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) tar; + return (signed) actor->GetStat(IE_PROFICIENCYBASTARDSWORD+idx) > parameters->int1Parameter; +} + +int GameScript::ProficiencyLT(Scriptable* Sender, Trigger* parameters) +{ + unsigned int idx = parameters->int0Parameter; + if (idx>31) { + return 0; + } + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar) { + return 0; + } + if (tar->Type != ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) tar; + return (signed) actor->GetStat(IE_PROFICIENCYBASTARDSWORD+idx) < parameters->int1Parameter; +} + +//this is a PST specific stat, shows how many free proficiency slots we got +//we use an unused stat for it +int GameScript::ExtraProficiency(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar) { + return 0; + } + if (tar->Type != ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) tar; + return (signed) actor->GetStat(IE_FREESLOTS) == parameters->int0Parameter; +} + +int GameScript::ExtraProficiencyGT(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar) { + return 0; + } + if (tar->Type != ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) tar; + return (signed) actor->GetStat(IE_FREESLOTS) > parameters->int0Parameter; +} + +int GameScript::ExtraProficiencyLT(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar) { + return 0; + } + if (tar->Type != ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) tar; + return (signed) actor->GetStat(IE_FREESLOTS) < parameters->int0Parameter; +} + +int GameScript::Internal(Scriptable* Sender, Trigger* parameters) +{ + unsigned int idx = parameters->int0Parameter; + if (idx>15) { + return 0; + } + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar) { + return 0; + } + if (tar->Type != ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) tar; + return (signed) actor->GetStat(IE_INTERNAL_0+idx) == parameters->int1Parameter; +} + +int GameScript::InternalGT(Scriptable* Sender, Trigger* parameters) +{ + unsigned int idx = parameters->int0Parameter; + if (idx>15) { + return 0; + } + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar) { + return 0; + } + if (tar->Type != ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) tar; + return (signed) actor->GetStat(IE_INTERNAL_0+idx) > parameters->int1Parameter; +} + +int GameScript::InternalLT(Scriptable* Sender, Trigger* parameters) +{ + unsigned int idx = parameters->int0Parameter; + if (idx>15) { + return 0; + } + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar) { + return 0; + } + if (tar->Type != ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) tar; + return (signed) actor->GetStat(IE_INTERNAL_0+idx) < parameters->int1Parameter; +} + +//we check if target is currently in dialog or not +int GameScript::NullDialog(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar) { + return 0; + } + if (tar->Type != ST_ACTOR) { + return 0; + } + GameControl *gc = core->GetGameControl(); + if ( (tar->GetGlobalID() != gc->dialoghandler->targetID) && (tar->GetGlobalID() != gc->dialoghandler->speakerID) ) { + return 1; + } + return 0; +} + +//this one checks scriptname (deathvar), i hope it is right +//IsScriptName depends on this too +//Name is another (similar function) +int GameScript::CalledByName(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar) { + return 0; + } + if (tar->Type != ST_ACTOR) { + return 0; + } + Actor *actor = (Actor *) tar; + if (stricmp(actor->GetScriptName(), parameters->string0Parameter) ) { + return 0; + } + return 1; +} + +//This is checking on the character's name as it was typed in +int GameScript::CharName(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* scr = GetActorFromObject( Sender, parameters->objectParameter ); + if (!scr || scr->Type!=ST_ACTOR) { + return 0; + } + Actor* actor = (Actor *) scr; + if (!strnicmp(actor->ShortName, parameters->string0Parameter, 32) ) { + return 1; + } + return 0; +} + +int GameScript::AnimationID(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar) { + return 0; + } + if (tar->Type != ST_ACTOR) { + return 0; + } + Actor *actor = (Actor *) tar; + if ((ieWord) actor->GetStat(IE_ANIMATION_ID) == (ieWord) parameters->int0Parameter) { + return 1; + } + return 0; +} + +int GameScript::AnimState(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar) { + return 0; + } + if (tar->Type != ST_ACTOR) { + return 0; + } + Actor *actor = (Actor *) tar; + return actor->GetStance() == parameters->int0Parameter; +} + +//this trigger uses hours +int GameScript::Time(Scriptable* /*Sender*/, Trigger* parameters) +{ + return (core->GetGame()->GameTime/AI_UPDATE_TIME)%7200/300 == (ieDword) parameters->int0Parameter; +} + +//this trigger uses hours +int GameScript::TimeGT(Scriptable* /*Sender*/, Trigger* parameters) +{ + return (core->GetGame()->GameTime/AI_UPDATE_TIME)%7200/300 > (ieDword) parameters->int0Parameter; +} + +//this trigger uses hours +int GameScript::TimeLT(Scriptable* /*Sender*/, Trigger* parameters) +{ + return (core->GetGame()->GameTime/AI_UPDATE_TIME)%7200/300 < (ieDword) parameters->int0Parameter; +} + +int GameScript::HotKey(Scriptable* Sender, Trigger* parameters) +{ + if (Sender->Type != ST_ACTOR) { + return 0; + } + Actor *scr = (Actor *) Sender; + // FIXME: this is never going to work on 64 bit archs ... + int ret = (unsigned long) scr->HotKey == (unsigned long) parameters->int0Parameter; + //probably we need to implement a trigger mechanism, clear + //the hotkey only when the triggerblock was evaluated as true + if (ret) { + Sender->AddTrigger (&scr->HotKey); + } + return ret; +} + +int GameScript::CombatCounter(Scriptable* /*Sender*/, Trigger* parameters) +{ + return core->GetGame()->CombatCounter == (ieDword) parameters->int0Parameter; +} + +int GameScript::CombatCounterGT(Scriptable* /*Sender*/, Trigger* parameters) +{ + return core->GetGame()->CombatCounter > (ieDword) parameters->int0Parameter; +} + +int GameScript::CombatCounterLT(Scriptable* /*Sender*/, Trigger* parameters) +{ + return core->GetGame()->CombatCounter < (ieDword) parameters->int0Parameter; +} + +int GameScript::TrapTriggered(Scriptable* Sender, Trigger* parameters) +{ + if (Sender->Type != ST_TRIGGER) { + return 0; + } +/* matchactor would do this, hmm + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar || tar->Type != ST_ACTOR) { + return 0; + } +*/ + if (MatchActor(Sender, Sender->LastTrigger, parameters->objectParameter)) { + Sender->AddTrigger (&Sender->LastTrigger); + return 1; + } + return 0; +} + +int GameScript::InteractingWith(Scriptable* Sender, Trigger* parameters) +{ + if (Sender->Type!=ST_ACTOR) { + return 0; + } + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar || tar->Type != ST_ACTOR) { + return 0; + } + GameControl *gc = core->GetGameControl(); + if (Sender->GetGlobalID() != gc->dialoghandler->targetID && Sender->GetGlobalID() != gc->dialoghandler->speakerID) { + return 0; + } + if (tar->GetGlobalID() != gc->dialoghandler->targetID && tar->GetGlobalID() != gc->dialoghandler->speakerID) { + return 0; + } + return 1; +} + +int GameScript::LastPersonTalkedTo(Scriptable* Sender, Trigger* parameters) +{ + if (Sender->Type!=ST_ACTOR) { + return 0; + } + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar || tar->Type != ST_ACTOR) { + return 0; + } + Actor *scr = (Actor *) Sender; + if (MatchActor(Sender, scr->LastTalkedTo, parameters->objectParameter)) { + return 1; + } + return 0; +} + +int GameScript::IsRotation(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar || tar->Type != ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) tar; + if ( actor->GetOrientation() == parameters->int0Parameter ) { + return 1; + } + return 0; +} + +//GemRB currently stores the saved location in a local variable, but it is +//actually stored in the .gam structure (only for PCs) +int GameScript::IsFacingSavedRotation(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar || tar->Type!=ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) tar; + if (actor->GetOrientation() == actor->GetStat(IE_SAVEDFACE) ) { + return 1; + } + return 0; +} + +int GameScript::IsFacingObject(Scriptable* Sender, Trigger* parameters) +{ + if (Sender->Type != ST_ACTOR) { + return 0; + } + Scriptable* target = GetActorFromObject( Sender, parameters->objectParameter ); + if (!target) { + return 0; + } + Actor* actor = ( Actor* ) Sender; + if (actor->GetOrientation()==GetOrient( target->Pos, actor->Pos ) ) { + return 1; + } + return 0; +} + +int GameScript::AttackedBy(Scriptable* Sender, Trigger* parameters) +{ + if (Sender->Type!=ST_ACTOR) { + return 0; + } + Actor *scr = (Actor *) Sender; + Targets *tgts = GetAllObjects(Sender->GetCurrentArea(), Sender, parameters->objectParameter, GA_NO_DEAD); + int ret = 0; + int AStyle = parameters->int0Parameter; + //iterate through targets to get the actor + if (tgts) { + targetlist::iterator m; + const targettype *tt = tgts->GetFirstTarget(m, ST_ACTOR); + while (tt) { + Actor *actor = (Actor *) tt->actor; + //if (actor->LastTarget == scr->GetID()) { + if (scr->LastAttacker == actor->GetGlobalID()) { + if (!AStyle || (AStyle==actor->GetAttackStyle()) ) { + scr->AddTrigger(&scr->LastAttacker); + ret = 1; + break; + } + } + tt = tgts->GetNextTarget(m, ST_ACTOR); + } + } + delete tgts; + return ret; +} + +int GameScript::TookDamage(Scriptable* Sender, Trigger* /*parameters*/) +{ + if (Sender->Type!=ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) Sender; + //zero damage doesn't count? + if (actor->LastHitter && actor->LastDamage) { + Sender->AddTrigger(&actor->LastHitter); + return 1; + } + return 0; +} + +int GameScript::HitBy(Scriptable* Sender, Trigger* parameters) +{ + if (Sender->Type!=ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) Sender; + if (parameters->int0Parameter) { + if (!(parameters->int0Parameter&actor->LastDamageType) ) { + return 0; + } + } + if (MatchActor(Sender, actor->LastHitter, parameters->objectParameter)) { + Sender->AddTrigger(&actor->LastHitter); + return 1; + } + return 0; +} + +int GameScript::Heard(Scriptable* Sender, Trigger* parameters) +{ + if (Sender->Type!=ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) Sender; + if (parameters->int0Parameter) { + if (parameters->int0Parameter!=actor->LastShout) { + return 0; + } + } + if (MatchActor(Sender, actor->LastHeard, parameters->objectParameter)) { + Sender->AddTrigger(&actor->LastHeard); + return 1; + } + return 0; +} + +int GameScript::LastMarkedObject_Trigger(Scriptable* Sender, Trigger* parameters) +{ + if (Sender->Type!=ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) Sender; + if (MatchActor(Sender, actor->LastMarked, parameters->objectParameter)) { + //don't mark this object for clear + //Sender->AddTrigger(&actor->LastSeen); + return 1; + } + return 0; +} + +int GameScript::HelpEX(Scriptable* Sender, Trigger* parameters) +{ + if (Sender->Type!=ST_ACTOR) { + return 0; + } + int stat; + switch (parameters->int0Parameter) { + case 1: stat = IE_EA; break; + case 2: stat = IE_GENERAL; break; + case 3: stat = IE_RACE; break; + case 4: stat = IE_CLASS; break; + case 5: stat = IE_SPECIFIC; break; + case 6: stat = IE_SEX; break; + case 7: stat = IE_ALIGNMENT; break; + default: return 0; + } + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar || tar->Type!=ST_ACTOR) { + //a non actor checking for help? + return 0; + } + Actor* actor = ( Actor* ) tar; + Actor* help = Sender->GetCurrentArea()->GetActorByGlobalID(actor->LastHelp); + if (!help) { + //no help required + return 0; + } + if (actor->GetStat(stat)==help->GetStat(stat) ) { + Sender->AddTrigger(&actor->LastHelp); + return 1; + } + return 0; +} + +int GameScript::Help_Trigger(Scriptable* Sender, Trigger* parameters) +{ + if (Sender->Type!=ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) Sender; + if (MatchActor(Sender, actor->LastHelp, parameters->objectParameter)) { + Sender->AddTrigger(&actor->LastHelp); + return 1; + } + return 0; +} + +int GameScript::ReceivedOrder(Scriptable* Sender, Trigger* parameters) +{ + if (MatchActor(Sender, Sender->LastOrderer, parameters->objectParameter) && + parameters->int0Parameter==Sender->LastOrder) { + Sender->AddTrigger(&Sender->LastOrderer); + return 1; + } + return 0; +} + +int GameScript::Joins(Scriptable* Sender, Trigger* parameters) +{ + if(Sender->Type!=ST_ACTOR) { + return 0; + } + Actor * actor = ( Actor* ) Sender; + //this trigger is sent only to PCs in a party + if(!actor->PCStats) { + return 0; + } + if (MatchActor(Sender, actor->PCStats->LastJoined, parameters->objectParameter)) { + Sender->AddTrigger(&actor->PCStats->LastJoined); + return 1; + } + return 0; +} + +int GameScript::Leaves(Scriptable* Sender, Trigger* parameters) +{ + if(Sender->Type!=ST_ACTOR) { + return 0; + } + Actor * actor = ( Actor* ) Sender; + //this trigger is sent only to PCs in a party + if(!actor->PCStats) { + return 0; + } + if (MatchActor(Sender, actor->PCStats->LastLeft, parameters->objectParameter)) { + Sender->AddTrigger(&actor->PCStats->LastLeft); + return 1; + } + return 0; +} + +int GameScript::FallenPaladin(Scriptable* Sender, Trigger* /*parameters*/) +{ + if (Sender->Type!=ST_ACTOR) { + return 0; + } + Actor* act = ( Actor* ) Sender; + return (act->GetStat(IE_MC_FLAGS) & MC_FALLEN_PALADIN)!=0; +} + +int GameScript::FallenRanger(Scriptable* Sender, Trigger* /*parameters*/) +{ + if (Sender->Type!=ST_ACTOR) { + return 0; + } + Actor* act = ( Actor* ) Sender; + return (act->GetStat(IE_MC_FLAGS) & MC_FALLEN_RANGER)!=0; +} + +int GameScript::NightmareModeOn(Scriptable* /*Sender*/, Trigger* /*parameters*/) +{ + ieDword diff; + + core->GetDictionary()->Lookup("Nightmare Mode", diff); + if (diff) { + return 1; + } + return 0; +} + +int GameScript::Difficulty(Scriptable* /*Sender*/, Trigger* parameters) +{ + ieDword diff; + + core->GetDictionary()->Lookup("Difficulty Level", diff); + int mode = parameters->int1Parameter; + //hack for compatibility + if (!mode) { + mode = EQUALS; + } + return DiffCore(diff, (ieDword) parameters->int0Parameter, mode); +} + +int GameScript::DifficultyGT(Scriptable* /*Sender*/, Trigger* parameters) +{ + ieDword diff; + + core->GetDictionary()->Lookup("Difficulty Level", diff); + return diff>(ieDword) parameters->int0Parameter; +} + +int GameScript::DifficultyLT(Scriptable* /*Sender*/, Trigger* parameters) +{ + ieDword diff; + + core->GetDictionary()->Lookup("Difficulty Level", diff); + return diff<(ieDword) parameters->int0Parameter; +} + +int GameScript::Vacant(Scriptable* Sender, Trigger* /*parameters*/) +{ + if (Sender->Type!=ST_AREA) { + return 0; + } + Map *map = (Map *) Sender; + if ( map->CanFree() ) { + return 1; + } + return 0; +} + +//this trigger always checks the right hand weapon? +int GameScript::InWeaponRange(Scriptable* Sender, Trigger* parameters) +{ + if (Sender->Type!=ST_ACTOR) { + return 0; + } + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar) { + return 0; + } + Actor *actor = (Actor *) Sender; + WeaponInfo wi; + unsigned int wrange = 0; + ITMExtHeader *header = actor->GetWeapon(wi, false); + if (header) { + wrange = wi.range; + } + header = actor->GetWeapon(wi, true); + if (header && (wi.range>wrange)) { + wrange = wi.range; + } + if ( PersonalDistance( Sender, tar ) <= wrange * 10 ) { + return 1; + } + return 0; +} + +//this implementation returns only true if there is a bow wielded +//but there is no ammo for it +//if the implementation should sign 'no ranged attack possible' +//then change some return values +//in bg2/iwd2 it doesn't accept an object (the object parameter is gemrb ext.) +int GameScript::OutOfAmmo(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* scr = Sender; + if (parameters->objectParameter) { + scr = GetActorFromObject( Sender, parameters->objectParameter ); + } + if ( !scr || scr->Type!=ST_ACTOR) { + return 0; + } + Actor *actor = (Actor *) scr; + WeaponInfo wi; + ITMExtHeader *header = actor->GetWeapon(wi, false); + //no bow wielded? + if (!header || header->AttackType!=ITEM_AT_BOW) { + return 0; + } + //we either have a projectile (negative) or an empty bow (positive) + //so we should find a negative slot, positive slot means: OutOfAmmo + if (actor->inventory.GetEquipped()<0) { + return 0; + } + //out of ammo + return 1; +} + +//returns true if a weapon is equipped (with more than 0 range) +//if a bow is equipped without projectile, it is useless! +//please notice how similar is this to OutOfAmmo +int GameScript::HaveUsableWeaponEquipped(Scriptable* Sender, Trigger* /*parameters*/) +{ + if (Sender->Type!=ST_ACTOR) { + return 0; + } + Actor *actor = (Actor *) Sender; + WeaponInfo wi; + ITMExtHeader *header = actor->GetWeapon(wi, false); + + //bows are not usable (because if they are loaded, the equipped + //weapon is the projectile) + if (!header || header->AttackType==ITEM_AT_BOW) { + return 0; + } + //only fist we have, it is not qualified as weapon? + if (actor->inventory.GetEquippedSlot() == actor->inventory.GetFistSlot()) { + return 0; + } + return 1; +} + +int GameScript::HasWeaponEquipped(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar || tar->Type!=ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) tar; + if (actor->inventory.GetEquippedSlot() == IW_NO_EQUIPPED) { + return 0; + } + return 1; +} + +int GameScript::PCInStore( Scriptable* /*Sender*/, Trigger* /*parameters*/) +{ + if (core->GetCurrentStore()) { + return 1; + } + return 0; +} + +//this checks if the launch point is onscreen, a more elaborate check +//would see if any piece of the Scriptable is onscreen, what is the original +//behaviour? +int GameScript::OnScreen( Scriptable* Sender, Trigger* /*parameters*/) +{ + Region vp = core->GetVideoDriver()->GetViewport(); + if (vp.PointInside(Sender->Pos) ) { + return 1; + } + return 0; +} + +int GameScript::IsPlayerNumber( Scriptable* Sender, Trigger* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar || tar->Type!=ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) tar; + if (actor->InParty == parameters->int0Parameter) { + return 1; + } + return 0; +} + +int GameScript::PCCanSeePoint( Scriptable* /*Sender*/, Trigger* parameters) +{ + Map* map = core->GetGame()->GetCurrentArea(); + if (map->IsVisible(parameters->pointParameter, false) ) { + return 1; + } + return 0; +} + +//i'm clueless about this trigger +int GameScript::StuffGlobalRandom( Scriptable* Sender, Trigger* parameters) +{ + unsigned int max=parameters->int0Parameter+1; + ieDword Value; + if (max) { + Value = RandomNumValue%max; + } else { + Value = RandomNumValue; + } + SetVariable( Sender, parameters->string0Parameter, Value ); + if (Value) { + return 1; + } + return 0; +} + +int GameScript::IsCreatureAreaFlag( Scriptable* Sender, Trigger* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar || tar->Type!=ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) tar; + if (actor->GetStat(IE_MC_FLAGS) & parameters->int0Parameter) { + return 1; + } + return 0; +} + +int GameScript::IsPathCriticalObject( Scriptable* Sender, Trigger* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar || tar->Type!=ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) tar; + if (actor->GetStat(IE_MC_FLAGS) & MC_PLOT_CRITICAL) { + return 1; + } + return 0; +} + +// 0 - ability, 1 - number, 2 - mode +int GameScript::ChargeCount( Scriptable* Sender, Trigger* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar || tar->Type!=ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) tar; + int Slot = actor->inventory.FindItem(parameters->string0Parameter,0); + if (Slot<0) { + return 0; + } + CREItem *item = actor->inventory.GetSlotItem (Slot); + if (!item) {//bah + return 0; + } + if (parameters->int0Parameter>2) { + return 0; + } + int charge = item->Usages[parameters->int0Parameter]; + switch (parameters->int2Parameter) { + case DM_EQUAL: + if (charge == parameters->int1Parameter) + return 1; + break; + case DM_LESS: + if (charge < parameters->int1Parameter) + return 1; + break; + case DM_GREATER: + if (charge > parameters->int1Parameter) + return 1; + break; + default: + return 0; + } + return 0; +} + +// no idea if it checks only alive partymembers +int GameScript::CheckPartyLevel( Scriptable* /*Sender*/, Trigger* parameters) +{ + if (core->GetGame()->GetPartyLevel(false)int0Parameter) { + return 0; + } + return 1; +} + +// no idea if it checks only alive partymembers +int GameScript::CheckPartyAverageLevel( Scriptable* /*Sender*/, Trigger* parameters) +{ + int level = core->GetGame()->GetPartyLevel(false); + switch (parameters->int1Parameter) { + case DM_EQUAL: + if (level ==parameters->int0Parameter) { + return 1; + } + break; + case DM_LESS: + if (level < parameters->int0Parameter) { + return 1; + } + break; + case DM_GREATER: + if (level > parameters->int0Parameter) { + return 1; + } + break; + default: + return 0; + } + return 1; +} + +int GameScript::CheckDoorFlags( Scriptable* Sender, Trigger* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar || tar->Type!=ST_DOOR) { + return 0; + } + Door* door = ( Door* ) tar; + if (door->Flags¶meters->int0Parameter) { + return 1; + } + return 0; +} + +// works only on animations? +// Be careful when converting to GetActorFromObject, it won't return animations (those are not scriptable) +int GameScript::Frame( Scriptable* Sender, Trigger* parameters) +{ + //to avoid a crash + if (!parameters->objectParameter) { + return 0; + } + AreaAnimation* anim = Sender->GetCurrentArea()->GetAnimation(parameters->objectParameter->objectName); + if (!anim) { + return 0; + } + int frame = anim->frame; + if ((frame>=parameters->int0Parameter) && + (frame<=parameters->int1Parameter) ) { + return 1; + } + return 0; +} + +//Modalstate in IWD2 allows specifying an object +int GameScript::ModalState( Scriptable* Sender, Trigger* parameters) +{ + Scriptable *scr; + + if (parameters->objectParameter) { + scr = GetActorFromObject( Sender, parameters->objectParameter ); + } else { + scr = Sender; + } + if (scr->Type!=ST_ACTOR) { + return 0; + } + Actor *actor = (Actor *) scr; + + if (actor->ModalState==(ieDword) parameters->int0Parameter) { + return 1; + } + return 0; +} + +/* a special redundant trigger for iwd2 - could do something extra */ +int GameScript::IsCreatureHiddenInShadows( Scriptable* Sender, Trigger* /*parameters*/) +{ + if (Sender->Type!=ST_ACTOR) { + return 0; + } + Actor *actor = (Actor *) Sender; + + if (actor->ModalState==MS_STEALTH) { + return 1; + } + return 0; +} + +int GameScript::IsWeather( Scriptable* /*Sender*/, Trigger* parameters) +{ + Game *game = core->GetGame(); + ieDword weather = game->WeatherBits & parameters->int0Parameter; + if (weather == (ieDword) parameters->int1Parameter) { + return 1; + } + return 0; +} + +int GameScript::Delay( Scriptable* Sender, Trigger* parameters) +{ + ieDword delay = (ieDword) parameters->int0Parameter; + if (delay<=1) { + return 1; + } + ieDword time1=Sender->lastDelay/1000/delay; + ieDword time2=Sender->lastRunTime/1000/delay; + + if (time1!=time2) { + return 1; + } + return 0; +} + +int GameScript::TimeOfDay(Scriptable* /*Sender*/, Trigger* parameters) +{ + ieDword timeofday = (core->GetGame()->GameTime/AI_UPDATE_TIME)%7200/1800; + + if (timeofday==(ieDword) parameters->int0Parameter) { + return 1; + } + return 0; +} + +//this is a PST action, it's using delta, not diffmode +int GameScript::RandomStatCheck(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar || tar->Type!=ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) tar; + + ieDword stat = actor->GetStat(parameters->int0Parameter); + ieDword value = Bones(parameters->int2Parameter); + switch(parameters->int1Parameter) { + case DM_SET: + if (stat==value) + return 1; + break; + case DM_LOWER: + if (statvalue) + return 1; + break; + } + return 0; +} + +int GameScript::PartyRested(Scriptable* Sender, Trigger* /*parameters*/) +{ + if (Sender->GetInternalFlag()&IF_PARTYRESTED) { + Sender->SetBitTrigger(BT_PARTYRESTED); + return 1; + } + return 0; +} + +int GameScript::IsWeaponRanged(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar || tar->Type!=ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) tar; + if (actor->inventory.GetEquipped()<0) { + return 1; + } + return 0; +} + +//HoW applies sequence on area animations +int GameScript::Sequence(Scriptable* Sender, Trigger* parameters) +{ + //to avoid a crash, check if object is NULL + if (parameters->objectParameter) { + AreaAnimation *anim = Sender->GetCurrentArea()->GetAnimation(parameters->objectParameter->objectName); + if (anim) { + //this is the cycle count for the area animation + //very much like stance for avatar anims + if (anim->sequence==parameters->int0Parameter) { + return 1; + } + return 0; + } + } + + Scriptable *tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar || tar->Type != ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) tar; + if (actor->GetStance()==parameters->int0Parameter) { + return 1; + } + return 0; +} + +int GameScript::TimerExpired(Scriptable* Sender, Trigger* parameters) +{ + if (Sender->TimerExpired(parameters->int0Parameter) ) { + return 1; + } + return 0; +} + +int GameScript::TimerActive(Scriptable* Sender, Trigger* parameters) +{ + if (Sender->TimerActive(parameters->int0Parameter) ) { + return 1; + } + return 0; +} + +int GameScript::ActuallyInCombat(Scriptable* /*Sender*/, Trigger* /*parameters*/) +{ + Game *game=core->GetGame(); + if (game->AnyPCInCombat()) return 1; + return 0; +} + +int GameScript::InMyGroup(Scriptable* Sender, Trigger* parameters) +{ + if (Sender->Type!=ST_ACTOR) { + return 0; + } + + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar || tar->Type!=ST_ACTOR) { + return 0; + } +/* IESDP SUCKS + if (GetGroup( (Actor *) tar)==GetGroup( (Actor *) Sender) ) { + return 1; + } +*/ + if ( ((Actor *) tar)->GetStat(IE_SPECIFIC)==((Actor *) tar)->GetStat(IE_SPECIFIC) ) { + return 1; + } + return 0; +} + +int GameScript::AnyPCSeesEnemy(Scriptable* /*Sender*/, Trigger* /*parameters*/) +{ + Game *game = core->GetGame(); + unsigned int i = (unsigned int) game->GetLoadedMapCount(); + while(i--) { + Map *map = game->GetMap(i); + if (map->AnyPCSeesEnemy()) { + return 1; + } + } + return 0; +} + +int GameScript::Unusable(Scriptable* Sender, Trigger* parameters) +{ + if (Sender->Type!=ST_ACTOR) { + return 0; + } + Actor *actor = (Actor *) Sender; + + Item *item = gamedata->GetItem(parameters->string0Parameter); + int ret; + if (actor->Unusable(item)) { + ret = 0; + } else { + ret = 1; + } + gamedata->FreeItem(item, parameters->string0Parameter, true); + return ret; +} + +int GameScript::HasBounceEffects(Scriptable* Sender, Trigger* parameters) +{ + Scriptable *tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar || tar->Type != ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) tar; + if (actor->GetStat(IE_BOUNCE)) return 1; + return 0; +} + +int GameScript::HasImmunityEffects(Scriptable* Sender, Trigger* parameters) +{ + Scriptable *tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar || tar->Type != ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) tar; + if (actor->GetStat(IE_IMMUNITY)) return 1; + return 0; +} + +// this is a GemRB specific trigger, to transfer some system variables +// to a global (game variable), it will always return true, and the +// variable could be checked in a subsequent trigger (like triggersetglobal) + +#define SYSV_SCREENFLAGS 0 +#define SYSV_CONTROLSTATUS 1 +#define SYSV_REPUTATION 2 +#define SYSV_PARTYGOLD 3 + +int GameScript::SystemVariable_Trigger(Scriptable* Sender, Trigger* parameters) +{ + ieDword value; + + switch (parameters->int0Parameter) { + case SYSV_SCREENFLAGS: + value = core->GetGameControl()->GetScreenFlags(); + break; + case SYSV_CONTROLSTATUS: + value = core->GetGame()->ControlStatus; + break; + case SYSV_REPUTATION: + value = core->GetGame()->Reputation; + break; + case SYSV_PARTYGOLD: + value = core->GetGame()->PartyGold; + break; + default: + return 0; + } + + SetVariable(Sender, parameters->string0Parameter, value); + return 1; +} + +int GameScript::SpellCast(Scriptable* Sender, Trigger* parameters) +{ + if(parameters->int0Parameter) { + unsigned int param = 2000+parameters->int0Parameter%1000; + if (param!=Sender->LastSpellSeen) { + return 0; + } + } + if(MatchActor(Sender, Sender->LastCasterSeen, parameters->objectParameter)) { + Sender->AddTrigger(&Sender->LastCasterSeen); + return 1; + } + return 0; +} + +int GameScript::SpellCastPriest(Scriptable* Sender, Trigger* parameters) +{ + if(parameters->int0Parameter) { + unsigned int param = 1000+parameters->int0Parameter%1000; + if (param!=Sender->LastSpellSeen) { + return 0; + } + } + if(MatchActor(Sender, Sender->LastCasterSeen, parameters->objectParameter)) { + Sender->AddTrigger(&Sender->LastCasterSeen); + return 1; + } + return 0; +} + +int GameScript::SpellCastInnate(Scriptable* Sender, Trigger* parameters) +{ + if(parameters->int0Parameter) { + unsigned int param = 3000+parameters->int0Parameter%1000; + if (param!=Sender->LastSpellSeen) { + return 0; + } + } + if(MatchActor(Sender, Sender->LastCasterSeen, parameters->objectParameter)) { + Sender->AddTrigger(&Sender->LastCasterSeen); + return 1; + } + return 0; +} + +int GameScript::SpellCastOnMe(Scriptable* Sender, Trigger* parameters) +{ + if(parameters->int0Parameter) { + if ((ieDword) parameters->int0Parameter!=Sender->LastSpellOnMe) { + return 0; + } + } + if(MatchActor(Sender, Sender->LastCasterOnMe, parameters->objectParameter)) { + Sender->AddTrigger(&Sender->LastCasterOnMe); + return 1; + } + return 0; +} + +int GameScript::CalendarDay(Scriptable* /*Sender*/, Trigger* parameters) +{ + int day = core->GetCalendar()->GetCalendarDay(core->GetGame()->GameTime/AI_UPDATE_TIME/7200); + if(day == parameters->int0Parameter) { + return 1; + } + return 0; +} + +int GameScript::CalendarDayGT(Scriptable* /*Sender*/, Trigger* parameters) +{ + int day = core->GetCalendar()->GetCalendarDay(core->GetGame()->GameTime/AI_UPDATE_TIME/7200); + if(day > parameters->int0Parameter) { + return 1; + } + return 0; +} + +int GameScript::CalendarDayLT(Scriptable* /*Sender*/, Trigger* parameters) +{ + int day = core->GetCalendar()->GetCalendarDay(core->GetGame()->GameTime/AI_UPDATE_TIME/7200); + if(day < parameters->int0Parameter) { + return 1; + } + return 0; +} + +//NT Returns true only if the active CRE was turned by the specified priest or paladin. +int GameScript::TurnedBy(Scriptable* Sender, Trigger* /*parameters*/) +{ + if (Sender->Type!=ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) Sender; + if (MatchActor(Sender, actor->LastTurner, NULL)) { + Sender->AddTrigger(&actor->LastTurner); + return 1; + } + return 0; +} diff --git a/project/jni/application/gemrb/src/core/GlobalTimer.cpp b/project/jni/application/gemrb/src/core/GlobalTimer.cpp new file mode 100644 index 000000000..1f71723d2 --- /dev/null +++ b/project/jni/application/gemrb/src/core/GlobalTimer.cpp @@ -0,0 +1,339 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003-2005 The GemRB Project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * + */ + +#include "GlobalTimer.h" + +#include "ControlAnimation.h" +#include "Game.h" +#include "Interface.h" +#include "Video.h" +#include "GUI/GameControl.h" + +GlobalTimer::GlobalTimer(void) +{ + //AI_UPDATE_TIME: how many AI updates in a second + interval = ( 1000 / AI_UPDATE_TIME ); + Init(); +} + +GlobalTimer::~GlobalTimer(void) +{ + std::vector::iterator i; + for(i = animations.begin(); i != animations.end(); ++i) { + delete (*i); + } +} + +void GlobalTimer::Init() +{ + fadeToCounter = 0; + fadeFromCounter = 0; + fadeFromMax = 0; + fadeToMax = 0; + waitCounter = 0; + shakeCounter = 0; + startTime = 0; //forcing an update + speed = 0; + ClearAnimations(); +} + +void GlobalTimer::Freeze() +{ + unsigned long thisTime; + unsigned long advance; + + GetTime( thisTime ); + advance = thisTime - startTime; + if ( advance < interval) { + return; + } + startTime = thisTime; + Game* game = core->GetGame(); + if (!game) { + return; + } + game->RealTime+=advance; + + ieDword count = advance/interval; + // pst/bg2 do this, if you fix it for another game, wrap it in a check + DoFadeStep(count); + + // show scrolling cursor while paused + GameControl* gc = core->GetGameControl(); + if (gc) + gc->UpdateScrolling(); +} + +bool GlobalTimer::ViewportIsMoving() +{ + return (goal.x!=currentVP.x) || (goal.y!=currentVP.y); +} + +void GlobalTimer::SetMoveViewPort(ieDword x, ieDword y, int spd, bool center) +{ + speed=spd; + currentVP=core->GetVideoDriver()->GetViewport(); + if (center) { + x-=currentVP.w/2; + y-=currentVP.h/2; + } + goal.x=(short) x; + goal.y=(short) y; +} + +void GlobalTimer::DoStep(int count) +{ + Video *video = core->GetVideoDriver(); + + int x = currentVP.x; + int y = currentVP.y; + if ( (x != goal.x) || (y != goal.y)) { + if (speed) { + if (xgoal.x) x=goal.x; + } else { + x-=speed; + if (xgoal.y) y=goal.y; + } else { + y-=speed; + if (y>1); + y += (rand()%shakeY) - (shakeY>>1); + } + } + video->MoveViewportTo(x,y); +} + +void GlobalTimer::Update() +{ + Map *map; + Game *game; + GameControl* gc; + unsigned long thisTime; + unsigned long advance; + + gc = core->GetGameControl(); + if (gc) + gc->UpdateScrolling(); + + UpdateAnimations(); + + GetTime( thisTime ); + + if (!startTime) { + startTime = thisTime; + return; + } + + advance = thisTime - startTime; + if ( advance < interval) { + return; + } + ieDword count = advance/interval; + DoStep(count); + DoFadeStep(count); + if (!gc) { + goto end; + } + game = core->GetGame(); + if (!game) { + goto end; + } + map = game->GetCurrentArea(); + if (!map) { + goto end; + } + //do spell effects expire in dialogs? + //if yes, then we should remove this condition + if (!(gc->GetDialogueFlags()&DF_IN_DIALOG) ) { + map->UpdateFog(); + map->UpdateEffects(); + if (thisTime) { + //this measures in-world time (affected by effects, actions, etc) + game->AdvanceTime(count); + } + } + //this measures time spent in the game (including pauses) + if (thisTime) { + game->RealTime+=advance; + } +end: + startTime = thisTime; +} + + +void GlobalTimer::DoFadeStep(ieDword count) { + Video *video = core->GetVideoDriver(); + if (fadeToCounter) { + fadeToCounter-=count; + if (fadeToCounter<0) { + fadeToCounter=0; + } + video->SetFadePercent( ( ( fadeToMax - fadeToCounter ) * 100 ) / fadeToMax ); + //bug/patch #1837747 made this unneeded + //goto end; //hmm, freeze gametime? + } + //i think this 'else' is needed now because of the 'goto' cut above + else if (fadeFromCounter!=fadeFromMax) { + if (fadeFromCounter>fadeFromMax) { + fadeFromCounter-=count; + if (fadeFromCounterfadeFromMax) { + fadeToCounter=fadeFromMax; + } + video->SetFadePercent( ( ( fadeFromMax - fadeFromCounter ) * 100 ) / fadeFromMax ); + //bug/patch #1837747 made this unneeded + //goto end; //freeze gametime? + } + } + if (fadeFromCounter==fadeFromMax) { + video->SetFadePercent( 0 ); + } +} + +void GlobalTimer::SetFadeToColor(unsigned long Count) +{ + if(!Count) { + Count = 64; + } + fadeToCounter = Count; + fadeToMax = fadeToCounter; + //stay black for a while + fadeFromCounter = 128; + fadeFromMax = 0; +} + +void GlobalTimer::SetFadeFromColor(unsigned long Count) +{ + if(!Count) { + Count = 64; + } + fadeFromCounter = 0; + fadeFromMax = Count; +} + +void GlobalTimer::SetWait(unsigned long Count) +{ + waitCounter = Count; +} + +void GlobalTimer::AddAnimation(ControlAnimation* ctlanim, unsigned long time) +{ + AnimationRef* anim; + unsigned long thisTime; + + GetTime( thisTime ); + time += thisTime; + + // if there are no free animation reference objects, + // alloc one, else take the first free one + if (first_animation == 0) + anim = new AnimationRef; + else { + anim = animations.front (); + animations.erase (animations.begin()); + first_animation--; + } + + // fill in data + anim->time = time; + anim->ctlanim = ctlanim; + + // and insert it into list of other anim refs, sorted by time + for (std::vector::iterator it = animations.begin() + first_animation; it != animations.end (); it++) { + if ((*it)->time > time) { + animations.insert( it, anim ); + anim = NULL; + break; + } + } + if (anim) + animations.push_back( anim ); +} + +void GlobalTimer::RemoveAnimation(ControlAnimation* ctlanim) +{ + // Animation refs for given control are not physically removed, + // but just marked by erasing ptr to the control. They will be + // collected when they get to the front of the vector + for (std::vector::iterator it = animations.begin() + first_animation; it != animations.end (); it++) { + if ((*it)->ctlanim == ctlanim) { + (*it)->ctlanim = NULL; + } + } +} + +void GlobalTimer::UpdateAnimations() +{ + unsigned long thisTime; + GetTime( thisTime ); + while (animations.begin() + first_animation != animations.end()) { + AnimationRef* anim = animations[first_animation]; + if (anim->ctlanim == NULL) { + first_animation++; + continue; + } + + if (anim->time <= thisTime) { + anim->ctlanim->UpdateAnimation(); + first_animation++; + continue; + } + break; + } +} + +void GlobalTimer::ClearAnimations() +{ + first_animation = (unsigned int) animations.size(); +} + +void GlobalTimer::SetScreenShake(unsigned long shakeX, unsigned long shakeY, + unsigned long Count) +{ + this->shakeX = shakeX; + this->shakeY = shakeY; + shakeCounter = Count+1; +} diff --git a/project/jni/application/gemrb/src/core/GlobalTimer.h b/project/jni/application/gemrb/src/core/GlobalTimer.h new file mode 100644 index 000000000..ebf955c82 --- /dev/null +++ b/project/jni/application/gemrb/src/core/GlobalTimer.h @@ -0,0 +1,78 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * + */ +#ifndef GLOBALTIMER_H +#define GLOBALTIMER_H + +#include "exports.h" +#include "win32def.h" + +#include "Region.h" + +#include + +class ControlAnimation; + +struct AnimationRef +{ + ControlAnimation *ctlanim; + unsigned long time; +}; + + +class GEM_EXPORT GlobalTimer { +private: + unsigned long startTime; + unsigned long interval; + + int fadeToCounter, fadeToMax; + int fadeFromCounter, fadeFromMax; + unsigned long waitCounter; + int shakeCounter; + unsigned long shakeX, shakeY; + unsigned int first_animation; + std::vector animations; + //move viewport to this coordinate + Point goal; + int speed; + Region currentVP; + + void DoFadeStep(ieDword count); +public: + GlobalTimer(void); + ~GlobalTimer(void); +public: + void Init(); + void Freeze(); + void Update(); + bool ViewportIsMoving(); + void DoStep(int count); + void SetMoveViewPort(ieDword x, ieDword y, int spd, bool center); + void SetFadeToColor(unsigned long Count); + void SetFadeFromColor(unsigned long Count); + void SetWait(unsigned long Count); + void SetScreenShake(unsigned long shakeX, unsigned long shakeY, + unsigned long Count); + void AddAnimation(ControlAnimation* ctlanim, unsigned long time); + void RemoveAnimation(ControlAnimation* ctlanim); + void ClearAnimations(); + void UpdateAnimations(); +}; + +#endif diff --git a/project/jni/application/gemrb/src/core/Holder.h b/project/jni/application/gemrb/src/core/Holder.h new file mode 100644 index 000000000..170a4c87e --- /dev/null +++ b/project/jni/application/gemrb/src/core/Holder.h @@ -0,0 +1,95 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * + */ + +#ifndef HOLDER_H +#define HOLDER_H + +#include +#include + +template +class Held { +public: + Held() : RefCount(0) {} + void acquire() { ++RefCount; } + void release() { assert(RefCount && "Broken Held usage."); + if (!--RefCount) delete static_cast(this); } + size_t GetRefCount() { return RefCount; } +private: + size_t RefCount; +}; + +/** + * @class Holder + * Intrusive smart pointer. + * + * The class T must have member function acquire and release, such that + * acquire increases the refcount, and release decreses the refcount and + * frees the object if needed. + * + * Derived class of Holder shouldn't add member variables. That way, + * they can freely converted to Holder without slicing. + */ + +template +class Holder { +public: + Holder(T* ptr = NULL) + : ptr(ptr) + { + if (ptr) + ptr->acquire(); + } + ~Holder() + { + if (ptr) + ptr->release(); + } + Holder(const Holder& rhs) + : ptr(rhs.ptr) + { + if (ptr) + ptr->acquire(); + } + Holder& operator=(const Holder& rhs) + { + if (rhs.ptr) + rhs.ptr->acquire(); + if (ptr) + ptr->release(); + ptr = rhs.ptr; + return *this; + } + T& operator*() const { return *ptr; } + T* operator->() const { return ptr; } + bool operator!() const { return !ptr; } +#include "operatorbool.h" + OPERATOR_BOOL(Holder,T,ptr) + T* get() const { return ptr; } + void release() { + if (ptr) + ptr->release(); + ptr = NULL; + } +protected: + T *ptr; +}; + +#endif diff --git a/project/jni/application/gemrb/src/core/Image.cpp b/project/jni/application/gemrb/src/core/Image.cpp new file mode 100644 index 000000000..8b03e095b --- /dev/null +++ b/project/jni/application/gemrb/src/core/Image.cpp @@ -0,0 +1,47 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2007 The GemRB Project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "Image.h" + +#include "Interface.h" +#include "Video.h" + +Image::Image(unsigned int w, unsigned int h) + : height(h), width(w), data(new Color[height*width]) +{ +} + +Image::~Image() +{ + delete[] data; +} + +Sprite2D* Image::GetSprite2D() +{ + union { + Color color; + ieDword Mask; + } r = {{ 0xFF, 0x00, 0x00, 0x00 }}, + g = {{ 0x00, 0xFF, 0x00, 0x00 }}, + b = {{ 0x00, 0x00, 0xFF, 0x00 }}, + a = {{ 0x00, 0x00, 0x00, 0xFF }}; + void *pixels = malloc(sizeof(Color) * height*width); + memcpy(pixels, data, sizeof(Color)*height*width); + return core->GetVideoDriver()->CreateSprite(width, height, 32, + r.Mask, g.Mask, b.Mask, a.Mask, pixels); +} diff --git a/project/jni/application/gemrb/src/core/Image.h b/project/jni/application/gemrb/src/core/Image.h new file mode 100644 index 000000000..9628d6561 --- /dev/null +++ b/project/jni/application/gemrb/src/core/Image.h @@ -0,0 +1,61 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2007 The GemRB Project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef IMAGE_H +#define IMAGE_H + +#include "RGBAColor.h" +#include "exports.h" + +class Sprite2D; + +class GEM_EXPORT Image { +public: + Image(unsigned int height, unsigned int width); + ~Image(); + Color GetPixel(unsigned int x, unsigned int y) const + { + if (x >= width || y >= height) { + static const Color black = { 0, 0, 0, 0 }; + return black; + } + return data[width*y+x]; + + } + void SetPixel(unsigned int x, unsigned int y, Color idx) + { + if (x >= width || y >= height) + return; + data[width*y+x] = idx; + + } + unsigned int GetHeight() const + { + return height; + } + unsigned int GetWidth() const + { + return width; + } + Sprite2D *GetSprite2D(); +private: + unsigned int height, width; + Color *data; +}; + +#endif diff --git a/project/jni/application/gemrb/src/core/ImageFactory.cpp b/project/jni/application/gemrb/src/core/ImageFactory.cpp new file mode 100644 index 000000000..17ad0d0c6 --- /dev/null +++ b/project/jni/application/gemrb/src/core/ImageFactory.cpp @@ -0,0 +1,42 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2007 The GemRB Project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * + */ + +#include "ImageFactory.h" + +#include "Interface.h" +#include "Video.h" + +ImageFactory::ImageFactory(const char* ResRef, Sprite2D* bitmap_) + : FactoryObject( ResRef, IE_BMP_CLASS_ID ), bitmap(bitmap_) +{ + +} + +ImageFactory::~ImageFactory(void) +{ + core->GetVideoDriver()->FreeSprite( bitmap ); +} + +Sprite2D* ImageFactory::GetSprite2D() const +{ + bitmap->acquire(); + return bitmap; +} + diff --git a/project/jni/application/gemrb/src/core/ImageFactory.h b/project/jni/application/gemrb/src/core/ImageFactory.h new file mode 100644 index 000000000..c0576d99f --- /dev/null +++ b/project/jni/application/gemrb/src/core/ImageFactory.h @@ -0,0 +1,40 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2007 The GemRB Project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * + */ + +#ifndef IMAGEFACTORY_H +#define IMAGEFACTORY_H + +#include "exports.h" +#include "globals.h" + +#include "FactoryObject.h" +#include "Sprite2D.h" + +class GEM_EXPORT ImageFactory : public FactoryObject { +private: + Sprite2D* bitmap; +public: + ImageFactory(const char* ResRef, Sprite2D* bitmap); + ~ImageFactory(void); + + Sprite2D* GetSprite2D() const; +}; + +#endif diff --git a/project/jni/application/gemrb/src/core/ImageMgr.cpp b/project/jni/application/gemrb/src/core/ImageMgr.cpp new file mode 100644 index 000000000..c38c4866f --- /dev/null +++ b/project/jni/application/gemrb/src/core/ImageMgr.cpp @@ -0,0 +1,92 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * + */ + +#include "ImageMgr.h" + +#include "win32def.h" + +#include "ImageFactory.h" +#include "Interface.h" +#include "Video.h" + +const TypeID ImageMgr::ID = { "ImageMgr" }; + +ImageMgr::ImageMgr(void) +{ +} + +ImageMgr::~ImageMgr(void) +{ +} + +Bitmap* ImageMgr::GetBitmap() +{ + unsigned int height = GetHeight(); + unsigned int width = GetWidth(); + Bitmap *data = new Bitmap(width, height); + + printMessage("ImageMgr", "Don't know how to handle 24bit bitmap from ", WHITE); + printf( "%s...", str->filename ); + printStatus( "ERROR", LIGHT_RED ); + + Sprite2D *spr = GetSprite2D(); + + for (unsigned int y = 0; y < height; y++) { + for (unsigned int x = 0; x < width; x++) { + data->SetAt(x,y, spr->GetPixel(x,y).r); + } + } + + core->GetVideoDriver()->FreeSprite(spr); + + return data; +} + +Image* ImageMgr::GetImage() +{ + unsigned int height = GetHeight(); + unsigned int width = GetWidth(); + Image *data = new Image(width, height); + + Sprite2D *spr = GetSprite2D(); + + for (unsigned int y = 0; y < height; y++) { + for (unsigned int x = 0; x < width; x++) { + data->SetPixel(x,y, spr->GetPixel(x,y)); + } + } + + core->GetVideoDriver()->FreeSprite(spr); + + return data; +} + +void ImageMgr::GetPalette(int /*colors*/, Color* /*pal*/) +{ + printMessage("ImageMgr", "Can't get non-existant palette from ", WHITE); + printf("%s... ", str->filename); + printStatus("ERROR", LIGHT_RED); +} + +ImageFactory* ImageMgr::GetImageFactory(const char* ResRef) +{ + ImageFactory* fact = new ImageFactory( ResRef, GetSprite2D() ); + return fact; +} diff --git a/project/jni/application/gemrb/src/core/ImageMgr.h b/project/jni/application/gemrb/src/core/ImageMgr.h new file mode 100644 index 000000000..40d4709eb --- /dev/null +++ b/project/jni/application/gemrb/src/core/ImageMgr.h @@ -0,0 +1,68 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * + */ + +#ifndef IMAGEMGR_H +#define IMAGEMGR_H + +#include "exports.h" + +#include "Bitmap.h" +#include "Image.h" +#include "Resource.h" +#include "Sprite2D.h" +#include "System/DataStream.h" + +class ImageFactory; + +/** + * Base class for Image plugins. + */ +class GEM_EXPORT ImageMgr : public Resource { +public: + static const TypeID ID; +public: + ImageMgr(void); + virtual ~ImageMgr(void); + /** Returns a \ref Sprite2D containing the image. */ + virtual Sprite2D* GetSprite2D() = 0; + virtual Image* GetImage(); + virtual Bitmap* GetBitmap(); + /** + * Returns image palette. + * + * @param[in] colors Number of colors to return. + * @param[out] pal Array to fill with colors. + * + * This does nothing if there is no palette. + */ + virtual void GetPalette(int colors, Color* pal); + /** Returns the width of the image */ + virtual int GetWidth() = 0; + /** Returns the height of the image */ + virtual int GetHeight() = 0; + /** + * Returns a \ref ImageFactory for the current image. + * + * @param[in] ResRef name of image represented by factory. + */ + ImageFactory* GetImageFactory(const char* ResRef); +}; + +#endif diff --git a/project/jni/application/gemrb/src/core/ImageWriter.cpp b/project/jni/application/gemrb/src/core/ImageWriter.cpp new file mode 100644 index 000000000..4c2eae78b --- /dev/null +++ b/project/jni/application/gemrb/src/core/ImageWriter.cpp @@ -0,0 +1,27 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2007 The GemRB Project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "ImageWriter.h" + +ImageWriter::ImageWriter(void) +{ +} + +ImageWriter::~ImageWriter(void) +{ +} diff --git a/project/jni/application/gemrb/src/core/ImageWriter.h b/project/jni/application/gemrb/src/core/ImageWriter.h new file mode 100644 index 000000000..a02a44b7b --- /dev/null +++ b/project/jni/application/gemrb/src/core/ImageWriter.h @@ -0,0 +1,35 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2007 The GemRB Project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef IMAGEWRITER_H +#define IMAGEWRITER_H + +#include "Plugin.h" +#include "Sprite2D.h" +#include "System/DataStream.h" + +class GEM_EXPORT ImageWriter : public Plugin { +public: + ImageWriter(void); + ~ImageWriter(void); + + /** Writes an Sprite2D to a stream and frees the sprite. */ + virtual void PutImage(DataStream *output, Sprite2D *sprite) = 0; +}; + +#endif diff --git a/project/jni/application/gemrb/src/core/IniSpawn.cpp b/project/jni/application/gemrb/src/core/IniSpawn.cpp new file mode 100644 index 000000000..b7c1ee4d6 --- /dev/null +++ b/project/jni/application/gemrb/src/core/IniSpawn.cpp @@ -0,0 +1,716 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2007 The GemRB Project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * + */ + +// This class handles the special spawn structures of planescape torment +// (stored in .ini format) + +#include "IniSpawn.h" + +#include "win32def.h" + +#include "Game.h" +#include "GameData.h" +#include "Interface.h" +#include "Map.h" +#include "GameScript/GSUtils.h" +#include "GameScript/Matching.h" +#include "Scriptable/Actor.h" + +static const int StatValues[9]={ +IE_EA, IE_FACTION, IE_TEAM, IE_GENERAL, IE_RACE, IE_CLASS, IE_SPECIFIC, +IE_SEX, IE_ALIGNMENT }; + +IniSpawn::IniSpawn(Map *owner) +{ + map = owner; + NamelessSpawnArea[0] = 0; + NamelessState = 35; + NamelessVar = NULL; + namelessvarcount = 0; + Locals = NULL; + localscount = 0; + eventspawns = NULL; + eventcount = 0; + last_spawndate = 0; +} + +IniSpawn::~IniSpawn() +{ + if (eventspawns) { + delete[] eventspawns; + } +} + +Holder GetIniFile(const ieResRef DefaultArea) +{ + //the lack of spawn ini files is not a serious problem, happens all the time + if (!gamedata->Exists( DefaultArea, IE_INI_CLASS_ID)) { + return NULL; + } + + DataStream* inifile = gamedata->GetResource( DefaultArea, IE_INI_CLASS_ID ); + if (!inifile) { + return NULL; + } + if (!core->IsAvailable( IE_INI_CLASS_ID )) { + printStatus( "ERROR", LIGHT_RED ); + printMessage( "IniSpawn","No INI Importer Available.\n",LIGHT_RED ); + return NULL; + } + + PluginHolder ini(IE_INI_CLASS_ID); + ini->Open(inifile, true ); //autofree + return ini; +} + +/*** initializations ***/ + +inline int CountElements(const char *s, char separator) +{ + int ret = 1; + while(*s) { + if (*s==separator) ret++; + s++; + } + return ret; +} + +inline void GetElements(const char *s, ieResRef *storage, int count) +{ + while(count--) { + ieResRef *field = storage+count; + strnuprcpy(*field, s, sizeof(ieResRef)-1); + for(size_t i=0;iGetKeyAsString(crittername,"spec_var",NULL); + if (s) { + if ((strlen(s)>9) && s[6]==':' && s[7]==':') { + strnuprcpy(critter.SpecContext, s, 6); + strnlwrcpy(critter.SpecVar, s+8, 32); + } else { + strnuprcpy(critter.SpecContext, "GLOBAL", 6); + strnlwrcpy(critter.SpecVar, s, 32); + } + } + + //add this to specvar at each spawn + ps = inifile->GetKeyAsInt(crittername,"spec_var_inc", 0); + critter.SpecVarInc=ps; + + //use this value with spec_var_operation to determine spawn + ps = inifile->GetKeyAsInt(crittername,"spec_var_value",0); + critter.SpecVarValue=ps; + //this operation uses DiffCore + s = inifile->GetKeyAsString(crittername,"spec_var_operation",""); + critter.SpecVarOperator=GetDiffMode(s); + //the amount of critters to spawn + critter.TotalQuantity = inifile->GetKeyAsInt(crittername,"spec_qty",1); + critter.SpawnCount = inifile->GetKeyAsInt(crittername,"create_qty",critter.TotalQuantity); + + //the creature resource(s) + s = inifile->GetKeyAsString(crittername,"cre_file",NULL); + if (s) { + critter.creaturecount = CountElements(s,','); + critter.CreFile=new ieResRef[critter.creaturecount]; + GetElements(s, critter.CreFile, critter.creaturecount); + } else { + printMessage( "IniSpawn"," ", LIGHT_RED); + printf("Invalid spawn entry: %s\n", crittername); + } + + s = inifile->GetKeyAsString(crittername,"point_select",NULL); + + if (s) { + ps=s[0]; + } else { + ps=0; + } + + s = inifile->GetKeyAsString(crittername,"spawn_point",NULL); + if (s) { + //expect more than one spawnpoint + if (ps=='r') { + //select one of the spawnpoints randomly + int count = core->Roll(1,CountElements(s,']'),-1); + //go to the selected spawnpoint + while(count--) { + while(*s++!=']') ; + } + } + //parse the selected spawnpoint + int x,y,o; + if (sscanf(s,"[%d.%d:%d]", &x, &y, &o)==3) { + critter.SpawnPoint.x=(short) x; + critter.SpawnPoint.y=(short) y; + critter.Orientation=o; + } else { + if (sscanf(s,"[%d.%d]", &x, &y)==2) { + critter.SpawnPoint.x=(short) x; + critter.SpawnPoint.y=(short) y; + critter.Orientation=core->Roll(1,16,-1); + } + } + } + + //store or retrieve spawn point + s = inifile->GetKeyAsString(crittername,"spawn_point_global", NULL); + if (s) { + switch (ps) { + case 'e': + critter.SpawnPoint.fromDword(CheckVariable(map, s+8,s)); + break; + default: + //see save_selected_point + //SetVariable(map, s+8, s, critter.SpawnPoint.asDword()); + break; + } + } + + //take facing from variable + s = inifile->GetKeyAsString(crittername,"spawn_facing_global", NULL); + if (s) { + switch (ps) { + case 'e': + critter.Orientation=(int) CheckVariable(map, s+8,s); + break; + default: + //see save_selected_point + //SetVariable(map, s+8, s, (ieDword) critter.Orientation); + break; + } + } + + s = inifile->GetKeyAsString(crittername,"save_selected_point",NULL); + if (s) { + if ((strlen(s)>9) && s[6]==':' && s[7]==':') { + SetVariable(map, s+8, s, critter.SpawnPoint.asDword()); + } else { + SetVariable(map, s, "GLOBAL", critter.SpawnPoint.asDword()); + } + } + s = inifile->GetKeyAsString(crittername,"save_selected_facing",NULL); + if (s) { + if ((strlen(s)>9) && s[6]==':' && s[7]==':') { + SetVariable(map, s+8, s, (ieDword) critter.Orientation); + } else { + SetVariable(map, s, "GLOBAL", (ieDword) critter.Orientation); + } + } + + //sometimes only the orientation is given, the point is stored in a variable + ps = inifile->GetKeyAsInt(crittername,"facing",-1); + if (ps!=-1) critter.Orientation = ps; + ps = inifile->GetKeyAsInt(crittername, "ai_ea",-1); + if (ps!=-1) critter.SetSpec[AI_EA] = (ieByte) ps; + ps = inifile->GetKeyAsInt(crittername, "ai_team",-1); + if (ps!=-1) critter.SetSpec[AI_TEAM] = (ieByte) ps; + ps = inifile->GetKeyAsInt(crittername, "ai_general",-1); + if (ps!=-1) critter.SetSpec[AI_GENERAL] = (ieByte) ps; + ps = inifile->GetKeyAsInt(crittername, "ai_race",-1); + if (ps!=-1) critter.SetSpec[AI_RACE] = (ieByte) ps; + ps = inifile->GetKeyAsInt(crittername, "ai_class",-1); + if (ps!=-1) critter.SetSpec[AI_CLASS] = (ieByte) ps; + ps = inifile->GetKeyAsInt(crittername, "ai_specifics",-1); + if (ps!=-1) critter.SetSpec[AI_SPECIFICS] = (ieByte) ps; + ps = inifile->GetKeyAsInt(crittername, "ai_gender",-1); + if (ps!=-1) critter.SetSpec[AI_GENDER] = (ieByte) ps; + ps = inifile->GetKeyAsInt(crittername, "ai_alignment",-1); + if (ps!=-1) critter.SetSpec[AI_ALIGNMENT] = (ieByte) ps; + + s = inifile->GetKeyAsString(crittername,"spec",NULL); + if (s) { + int x[9]; + + ps = sscanf(s,"[%d.%d.%d.%d.%d.%d.%d.%d.%d]", x, x+1, x+2, x+3, x+4, x+5, + x+6, x+7, x+8); + if (ps == 0) { + strnuprcpy(critter.ScriptName, s, 32); + critter.Flags|=CF_CHECK_NAME; + memset(critter.Spec,-1,sizeof(critter.Spec)); + } else { + while(ps--) { + critter.Spec[ps]=(ieByte) x[ps]; + } + } + } + + s = inifile->GetKeyAsString(crittername,"script_name",NULL); + if (s) { + strnuprcpy(critter.ScriptName, s, 32); + } + + //iwd2 script names (override remains the same) + //special 1 == area + s = inifile->GetKeyAsString(crittername,"script_special_1",NULL); + if (s) { + strnuprcpy(critter.AreaScript,s, 8); + } + //special 2 == class + s = inifile->GetKeyAsString(crittername,"script_special_2",NULL); + if (s) { + strnuprcpy(critter.ClassScript,s, 8); + } + //special 3 == general + s = inifile->GetKeyAsString(crittername,"script_special_3",NULL); + if (s) { + strnuprcpy(critter.GeneralScript,s, 8); + } + //team == specific + s = inifile->GetKeyAsString(crittername,"script_team",NULL); + if (s) { + strnuprcpy(critter.SpecificScript,s, 8); + } + + //combat == race + s = inifile->GetKeyAsString(crittername,"script_combat",NULL); + if (s) { + strnuprcpy(critter.RaceScript,s, 8); + } + //movement == default + s = inifile->GetKeyAsString(crittername,"script_movement",NULL); + if (s) { + strnuprcpy(critter.DefaultScript,s, 8); + } + + //pst script names + s = inifile->GetKeyAsString(crittername,"script_override",NULL); + if (s) { + strnuprcpy(critter.OverrideScript,s, 8); + } + s = inifile->GetKeyAsString(crittername,"script_class",NULL); + if (s) { + strnuprcpy(critter.ClassScript,s, 8); + } + s = inifile->GetKeyAsString(crittername,"script_race",NULL); + if (s) { + strnuprcpy(critter.RaceScript,s, 8); + } + s = inifile->GetKeyAsString(crittername,"script_general",NULL); + if (s) { + strnuprcpy(critter.GeneralScript,s, 8); + } + s = inifile->GetKeyAsString(crittername,"script_default",NULL); + if (s) { + strnuprcpy(critter.DefaultScript,s, 8); + } + s = inifile->GetKeyAsString(crittername,"script_area",NULL); + if (s) { + strnuprcpy(critter.AreaScript,s, 8); + } + s = inifile->GetKeyAsString(crittername,"script_specifics",NULL); + if (s) { + strnuprcpy(critter.SpecificScript,s, 8); + } + s = inifile->GetKeyAsString(crittername,"dialog",NULL); + if (s) { + strnuprcpy(critter.Dialog,s, 8); + } + + //flags + if (inifile->GetKeyAsBool(crittername,"death_scriptname",false)) { + critter.Flags|=CF_DEATHVAR; + } + //don't spawn when spawnpoint is visible + if (inifile->GetKeyAsBool(crittername,"ignore_can_see",false)) { + critter.Flags|=CF_IGNORECANSEE; + } + //unsure, but could be similar to previous + if (inifile->GetKeyAsBool(crittername,"check_view_port", false)) { + critter.Flags|=CF_CHECKVIEWPORT; + } + //unknown, this is used only in pst + if (inifile->GetKeyAsBool(crittername,"check_crowd", false)) { + critter.Flags|=CF_CHECKCROWD; + } + //unknown, this is used only in pst + if (inifile->GetKeyAsBool(crittername,"find_safest_point", false)) { + critter.Flags|=CF_SAFESTPOINT; + } + //disable spawn based on game difficulty + if (inifile->GetKeyAsBool(crittername,"area_diff_1", false)) { + critter.Flags|=CF_NO_DIFF_1; + } + if (inifile->GetKeyAsBool(crittername,"area_diff_2", false)) { + critter.Flags|=CF_NO_DIFF_2; + } + if (inifile->GetKeyAsBool(crittername,"area_diff_3", false)) { + critter.Flags|=CF_NO_DIFF_3; + } +} + +void IniSpawn::ReadSpawnEntry(DataFileMgr *inifile, const char *entryname, SpawnEntry &entry) +{ + const char *s; + + entry.interval = (unsigned int) inifile->GetKeyAsInt(entryname,"interval",0); + //don't default to NULL here, some entries may be missing in original game + //an empty default string here will create an empty but consistent entry + s = inifile->GetKeyAsString(entryname,"critters",""); + int crittercount = CountElements(s,','); + entry.crittercount=crittercount; + entry.critters=new CritterEntry[crittercount]; + ieVariable *critters = new ieVariable[crittercount]; + GetElements(s, critters, crittercount); + while(crittercount--) { + ReadCreature(inifile, critters[crittercount], entry.critters[crittercount]); + } + delete[] critters; +} + +/* set by action */ +void IniSpawn::SetNamelessDeath(const ieResRef area, Point &pos, ieDword state) +{ + strnuprcpy(NamelessSpawnArea, area, 8); + NamelessSpawnPoint = pos; + NamelessState = state; +} + +void IniSpawn::InitSpawn(const ieResRef DefaultArea) +{ + const char *s; + + Holder inifile = GetIniFile(DefaultArea); + if (!inifile) { + strnuprcpy(NamelessSpawnArea, DefaultArea, 8); + return; + } + + s = inifile->GetKeyAsString("nameless","destare",DefaultArea); + strnuprcpy(NamelessSpawnArea, s, 8); + s = inifile->GetKeyAsString("nameless","point","[0.0]"); + int x,y; + if (sscanf(s,"[%d.%d]", &x, &y)!=2) { + x=0; + y=0; + } + NamelessSpawnPoint.x=x; + NamelessSpawnPoint.y=y; + //35 - already standing + //36 - getting up + NamelessState = inifile->GetKeyAsInt("nameless","state",36); + + namelessvarcount = inifile->GetKeysCount("namelessvar"); + if (namelessvarcount) { + NamelessVar = new VariableSpec[namelessvarcount]; + for (y=0;yGetKeyNameByIndex("namelessvar",y); + strnlwrcpy(NamelessVar[y].Name, Key, 32); + NamelessVar[y].Value = inifile->GetKeyAsInt("namelessvar",Key,0); + } + } + + localscount = inifile->GetKeysCount("locals"); + if (localscount) { + Locals = new VariableSpec[localscount]; + for (y=0;yGetKeyNameByIndex("locals",y); + strnlwrcpy(Locals[y].Name, Key, 32); + Locals[y].Value = inifile->GetKeyAsInt("locals",Key,0); + } + } + + s = inifile->GetKeyAsString("spawn_main","enter",NULL); + if (s) { + ReadSpawnEntry(inifile.get(), s, enterspawn); + } + s = inifile->GetKeyAsString("spawn_main","events",NULL); + if (s) { + eventcount = CountElements(s,','); + eventspawns = new SpawnEntry[eventcount]; + ieVariable *events = new ieVariable[eventcount]; + GetElements(s, events, eventcount); + int ec = eventcount; + while(ec--) { + ReadSpawnEntry(inifile.get(), events[ec], eventspawns[ec]); + } + delete[] events; + } + //maybe not correct + InitialSpawn(); +} + + +/*** events ***/ + +//respawn nameless after he bit the dust +void IniSpawn::RespawnNameless() +{ + Game *game = core->GetGame(); + Actor *nameless = game->GetPC(0, false); + + if (NamelessSpawnPoint.isnull()) { + core->GetGame()->JoinParty(nameless,JP_INITPOS); + NamelessSpawnPoint=nameless->Pos; + strnuprcpy(NamelessSpawnArea, nameless->Area, 8); + } + + nameless->Resurrect(); + //hardcoded!!! + if (NamelessState==36) { + nameless->SetStance(IE_ANI_PST_START); + } + int i; + + for (i=0;iGetPartySize(false);i++) { + MoveBetweenAreasCore(game->GetPC(i, false),NamelessSpawnArea,NamelessSpawnPoint,-1, true); + } + + //certain variables are set when nameless dies + for (i=0;i=0) { + // dunno if this should be negated + if (!DiffCore(specvar, critter.SpecVarValue, critter.SpecVarOperator) ) { + return; + } + } else { + //ar0203 in PST seems to want the check this way. + //if other areas conflict and you want to use (!specvar), + //please research further + //researched further - ar0203 respawns only if specvar is 1 + if (!specvar) { + return; + } + } + } + + if (!(critter.Flags&CF_IGNORECANSEE)) { + if (map->IsVisible(critter.SpawnPoint, false) ) { + return; + } + } + + if (critter.Flags&CF_NO_DIFF_MASK) { + ieDword difficulty; + ieDword diff_bit; + + core->GetDictionary()->Lookup("Difficulty Level", difficulty); + switch (difficulty) + { + case 0: + diff_bit = CF_NO_DIFF_1; + break; + case 1: + diff_bit = CF_NO_DIFF_2; + break; + case 2: + diff_bit = CF_NO_DIFF_3; + break; + default: + diff_bit = 0; + } + if (critter.Flags&diff_bit) { + return; + } + } + + if (critter.ScriptName[0] && (critter.Flags&CF_CHECK_NAME) ) { + //maybe this one needs to be using getobjectcount as well + //currently we cannot count objects with scriptname??? + if (map->GetActor( critter.ScriptName, 0 )) { + return; + } + } else { + //Object *object = new Object(); + Object object; + //objectfields based on spec + object.objectFields[0]=critter.Spec[0]; + object.objectFields[1]=critter.Spec[1]; + object.objectFields[2]=critter.Spec[2]; + object.objectFields[3]=critter.Spec[3]; + object.objectFields[4]=critter.Spec[4]; + object.objectFields[5]=critter.Spec[5]; + object.objectFields[6]=critter.Spec[6]; + object.objectFields[7]=critter.Spec[7]; + object.objectFields[8]=critter.Spec[8]; + int cnt = GetObjectCount(map, &object); + if (cnt>=critter.TotalQuantity) { + return; + } + } + + int x = core->Roll(1,critter.creaturecount,-1); + Actor* cre = gamedata->GetCreature(critter.CreFile[x]); + if (!cre) { + return; + } + + SetVariable(map, critter.SpecVar, critter.SpecContext, specvar+(ieDword) critter.SpecVarInc); + map->AddActor(cre); + for (x=0;x<9;x++) { + if (critter.SetSpec[x]) { + cre->SetBase(StatValues[x], critter.SetSpec[x]); + } + } + cre->SetPosition( critter.SpawnPoint, 0, 0);//maybe critters could be repositioned + cre->SetOrientation(critter.Orientation,false); + if (critter.ScriptName[0]) { + cre->SetScriptName(critter.ScriptName); + } + if (critter.OverrideScript[0]) { + cre->SetScript(critter.OverrideScript, SCR_OVERRIDE); + } + if (critter.ClassScript[0]) { + cre->SetScript(critter.ClassScript, SCR_CLASS); + } + if (critter.RaceScript[0]) { + cre->SetScript(critter.RaceScript, SCR_RACE); + } + if (critter.GeneralScript[0]) { + cre->SetScript(critter.GeneralScript, SCR_GENERAL); + } + if (critter.DefaultScript[0]) { + cre->SetScript(critter.DefaultScript, SCR_DEFAULT); + } + if (critter.AreaScript[0]) { + cre->SetScript(critter.AreaScript, SCR_AREA); + } + if (critter.SpecificScript[0]) { + cre->SetScript(critter.SpecificScript, SCR_SPECIFICS); + } + if (critter.Dialog[0]) { + cre->SetDialog(critter.Dialog); + } +} + +void IniSpawn::SpawnGroup(SpawnEntry &event) +{ + if (!event.critters) { + return; + } + unsigned int interval = event.interval; + if (interval) { + if(core->GetGame()->GameTime/interval<=last_spawndate/interval) { + return; + } + } + last_spawndate=core->GetGame()->GameTime; + + for(int i=0;iSpawnCount;j++) { + SpawnCreature(*critter); + } + } +} + +//execute the initial spawn +void IniSpawn::InitialSpawn() +{ + SpawnGroup(enterspawn); + //these variables are set when entering first + for (int i=0;i.ini files + * @author The GemRB Project + */ + +#ifndef INISPAWN_H +#define INISPAWN_H + +#include "exports.h" +#include "ie_types.h" + +#include "DataFileMgr.h" +#include "Region.h" + +class Map; + +/** + * @struct CritterEntry + */ + +//critter flags +#define CF_IGNORECANSEE 1 +#define CF_DEATHVAR 2 +#define CF_NO_DIFF_1 4 +#define CF_NO_DIFF_2 8 +#define CF_NO_DIFF_3 16 +#define CF_CHECKVIEWPORT 32 +#define CF_CHECKCROWD 64 +#define CF_SAFESTPOINT 128 +#define CF_NO_DIFF_MASK 28 +#define CF_CHECK_NAME 256 +//spec ids flags +#define AI_EA 0 +#define AI_FACTION 1 +#define AI_TEAM 2 +#define AI_GENERAL 3 +#define AI_RACE 4 +#define AI_CLASS 5 +#define AI_SPECIFICS 6 +#define AI_GENDER 7 +#define AI_ALIGNMENT 8 + +//spawn point could be: +// s - single +// r - random +// e - preset +// save_select_point saves the spawnpoint + +struct CritterEntry { + int creaturecount; + ieResRef *CreFile; //spawn one of these creatures + ieByte Spec[9]; //existance check IDS qualifier + ieByte SetSpec[9]; //set IDS qualifier + ieVariable ScriptName; //existance check scripting name + ieVariable SpecVar; //condition variable + ieResRef SpecContext; //condition variable context + ieResRef OverrideScript; //override override script + ieResRef ClassScript; //overrride class script + ieResRef RaceScript; //override race script + ieResRef GeneralScript; //override general script + ieResRef DefaultScript; //override default script + ieResRef AreaScript; //override area script + ieResRef SpecificScript; //override specific script + ieResRef Dialog; //override dialog + ieVariable SpawnPointVar; //spawn point saved location + Point SpawnPoint; //spawn point + int SpecVarOperator; //operation performed on spec var + int SpecVarValue; //using this value with the operation + int SpecVarInc; //add this to spec var at each spawn + int Orientation; //spawn orientation + int Flags; //CF_IGNORENOSEE, CF_DEATHVAR, etc + int TotalQuantity; //total number + int SpawnCount; //create quantity +}; + +/** + * @class SpawnEntry + */ +class SpawnEntry { +public: + ieDword interval; + int crittercount; + CritterEntry *critters; + SpawnEntry() { + interval = 0; + crittercount = 0; + critters = NULL; + } + ~SpawnEntry() { + if (critters) { + for (int i=0;i +#endif + +#include "Interface.h" + +#include "exports.h" +#include "globals.h" +#include "strrefs.h" +#include "win32def.h" + +#include "ActorMgr.h" +#include "AmbientMgr.h" +#include "AnimationMgr.h" +#include "ArchiveImporter.h" +#include "Audio.h" +#include "Calendar.h" +#include "DataFileMgr.h" +#include "DialogHandler.h" +#include "DialogMgr.h" +#include "DisplayMessage.h" +#include "EffectMgr.h" +#include "EffectQueue.h" +#include "Factory.h" +#include "Game.h" +#include "GameData.h" +#include "ImageMgr.h" +#include "ItemMgr.h" +#include "MapMgr.h" +#include "MoviePlayer.h" +#include "MusicMgr.h" +#include "Palette.h" +#include "PluginMgr.h" +#include "PluginMgr.h" +#include "ProjectileServer.h" +#include "SaveGameIterator.h" +#include "SaveGameMgr.h" +#include "ScriptEngine.h" +#include "ScriptedAnimation.h" +#include "SoundMgr.h" +#include "SpellMgr.h" +#include "StoreMgr.h" +#include "StringMgr.h" +#include "TileMap.h" +#include "Video.h" +#include "WorldMapMgr.h" +#include "GUI/Button.h" +#include "GUI/Console.h" +#include "GUI/GameControl.h" +#include "GUI/Label.h" +#include "GUI/MapControl.h" +#include "GUI/WorldMapControl.h" +#include "System/FileStream.h" +#include "System/VFS.h" + +#if defined(__HAIKU__) +#include +#endif + +#include +#include +#include + +GEM_EXPORT Interface* core; + +#ifdef WIN32 +GEM_EXPORT HANDLE hConsole; +#endif + +//use DialogF.tlk if the protagonist is female, that's why we leave space +static const char dialogtlk[] = "dialog.tlk\0"; + +static int MaximumAbility = 25; +static ieWordSigned *strmod = NULL; +static ieWordSigned *strmodex = NULL; +static ieWordSigned *intmod = NULL; +static ieWordSigned *dexmod = NULL; +static ieWordSigned *conmod = NULL; +static ieWordSigned *chrmod = NULL; +static ieWordSigned *lorebon = NULL; +static ieWordSigned *wisbon = NULL; +static int **reputationmod = NULL; +static ieVariable IWD2DeathVarFormat = "_DEAD%s"; +static ieVariable DeathVarFormat = "SPRITE_IS_DEAD%s"; + +Interface::Interface(int iargc, char* iargv[]) +{ + argc = iargc; + argv = iargv; +#ifdef WIN32 + hConsole = GetStdHandle( STD_OUTPUT_HANDLE ); +#endif + textcolor( LIGHT_WHITE ); + printf( "GemRB Core Version v%s Loading...\n", VERSION_GEMRB ); + + // default to the correct endianswitch + ieWord endiantest = 1; + if (((char *)&endiantest)[1] == 1) { + // big-endian + DataStream::SetEndianSwitch(true); + } + + unsigned int i; + for(i=0;i<256;i++) { + pl_uppercase[i]=(ieByte) toupper(i); + pl_lowercase[i]=(ieByte) tolower(i); + } + + projserv = NULL; + VideoDriverName = "sdl"; + AudioDriverName = "openal"; + vars = NULL; + tokens = NULL; + lists = NULL; + RtRows = NULL; + sgiterator = NULL; + game = NULL; + calendar = NULL; + worldmap = NULL; + CurrentStore = NULL; + CurrentContainer = NULL; + UseContainer = false; + InfoTextPalette = NULL; + timer = NULL; + displaymsg = NULL; + evntmgr = NULL; + console = NULL; + slottypes = NULL; + slotmatrix = NULL; + + ModalWindow = NULL; + tooltip_x = 0; + tooltip_y = 0; + tooltip_currtextw = 0; + tooltip_ctrl = NULL; + plugin_flags = NULL; + + pal16 = NULL; + pal32 = NULL; + pal256 = NULL; + + GUIEnhancements = 0; + + CursorCount = 0; + Cursors = NULL; + + mousescrollspd = 10; + + ConsolePopped = false; + CheatFlag = false; + FogOfWar = 1; + QuitFlag = QF_NORMAL; + EventFlag = EF_CONTROL; +#ifndef WIN32 + CaseSensitive = true; //this is the default value, so CD1/CD2 will be resolved +#else + CaseSensitive = false; +#endif + GameOnCD = false; + SkipIntroVideos = false; + DrawFPS = false; + KeepCache = false; + TooltipDelay = 100; + FullScreen = 0; + GUIScriptsPath[0] = 0; + GamePath[0] = 0; + SavePath[0] = 0; + GemRBPath[0] = 0; + PluginsPath[0] = 0; + CachePath[0] = 0; + GemRBOverridePath[0] = 0; + GameName[0] = 0; + + strncpy( GameOverridePath, "override", sizeof(GameOverridePath) ); + strncpy( GameSoundsPath, "sounds", sizeof(GameSoundsPath) ); + strncpy( GameScriptsPath, "scripts", sizeof(GameScriptsPath) ); + strncpy( GamePortraitsPath, "portraits", sizeof(GamePortraitsPath) ); + strncpy( GameCharactersPath, "characters", sizeof(GameCharactersPath) ); + strncpy( GameDataPath, "data", sizeof(GameDataPath) ); + strncpy( INIConfig, "baldur.ini", sizeof(INIConfig) ); + strncpy( ButtonFont, "STONESML", sizeof(ButtonFont) ); + strncpy( TooltipFont, "STONESML", sizeof(TooltipFont) ); + strncpy( MovieFont, "STONESML", sizeof(MovieFont) ); + strncpy( ScrollCursorBam, "CURSARW", sizeof(ScrollCursorBam) ); + strncpy( GlobalScript, "BALDUR", sizeof(GlobalScript) ); + strncpy( WorldMapName[0], "WORLDMAP", sizeof(ieResRef) ); + memset( WorldMapName[1], 0, sizeof(ieResRef) ); + strncpy( Palette16, "MPALETTE", sizeof(Palette16) ); + strncpy( Palette32, "PAL32", sizeof(Palette32) ); + strncpy( Palette256, "MPAL256", sizeof(Palette256) ); + strcpy( TooltipBackResRef, "\0" ); + for (int size = 0; size < MAX_CIRCLE_SIZE; size++) { + strcpy( GroundCircleBam[size], "\0" ); + GroundCircleScale[size] = 0; + } + TooltipColor.r = 0; + TooltipColor.g = 255; + TooltipColor.b = 0; + TooltipColor.a = 255; + TooltipMargin = 10; + + TooltipBack = NULL; + DraggedItem = NULL; + DraggedPortrait = 0; + DefSound = NULL; + DSCount = -1; + memset(GameFeatures, 0, sizeof( GameFeatures )); + //GameFeatures = 0; + //GameFeatures2 = 0; + memset( WindowFrames, 0, sizeof( WindowFrames )); + memset( GroundCircles, 0, sizeof( GroundCircles )); + memset(FogSprites, 0, sizeof( FogSprites )); + AreaAliasTable = NULL; + ItemExclTable = NULL; + ItemDialTable = NULL; + ItemDial2Table = NULL; + ItemTooltipTable = NULL; + update_scripts = false; + + gamedata = new GameData(); +} + +#define FreeResourceVector(type, variable) \ +{ \ + size_t i=variable.size(); \ + while(i--) { \ + if (variable[i]) { \ + delete variable[i]; \ + } \ + } \ + variable.clear(); \ +} + +//2da lists are ieDword lists allocated by malloc +static void Release2daList(void *poi) +{ + free( (ieDword *) poi); +} + +static void ReleaseItemList(void *poi) +{ + delete ((ItemList *) poi); +} + +void FreeAbilityTables() +{ + if (strmod) { + free(strmod); + } + strmod = NULL; + if (strmodex) { + free(strmodex); + } + strmodex = NULL; + if (intmod) { + free(intmod); + } + intmod = NULL; + if (dexmod) { + free(dexmod); + } + dexmod = NULL; + if (conmod) { + free(conmod); + } + conmod = NULL; + if (chrmod) { + free(chrmod); + } + chrmod = NULL; + if (lorebon) { + free(lorebon); + } + lorebon = NULL; + if (wisbon) { + free(wisbon); + } + wisbon = NULL; +} + +void Interface::FreeResRefTable(ieResRef *&table, int &count) +{ + if (table) { + free( table ); + count = -1; + } +} + +static void ReleaseItemTooltip(void *poi) +{ + free(poi); +} + +Interface::~Interface(void) +{ + DragItem(NULL,NULL); + delete AreaAliasTable; + + if (music) { + music->HardEnd(); + } + // stop any ambients which are still enqueued + if (AudioDriver) { + AmbientMgr *ambim = AudioDriver->GetAmbientMgr(); + if (ambim) ambim->deactivate(); + } + //destroy the highest objects in the hierarchy first! + delete game; + delete calendar; + delete worldmap; + + FreeAbilityTables(); + + if (reputationmod) { + for (unsigned int i=0; i<20; i++) { + if (reputationmod[i]) { + free(reputationmod[i]); + } + } + free(reputationmod); + reputationmod=NULL; + } + + PluginMgr::Get()->RunCleanup(); + + ReleaseMemoryActor(); + EffectQueue_ReleaseMemory(); + CharAnimations::ReleaseMemory(); + delete CurrentStore; + + FreeResRefTable(DefSound, DSCount); + + free( slottypes ); + free( slotmatrix ); + + delete sgiterator; + + if (Cursors) { + for (int i = 0; i < CursorCount; i++) { + video->FreeSprite( Cursors[i] ); + } + delete[] Cursors; + } + + FreeResourceVector( Font, fonts ); + FreeResourceVector( Window, windows ); + + size_t i; + for (i = 0; i < musiclist.size(); i++) { + free((void *)musiclist[i]); + } + + DamageInfoMap.clear(); + + ModalStates.clear(); + + delete plugin_flags; + + delete projserv; + + delete console; + + delete pal256; + delete pal32; + delete pal16; + + delete timer; + delete displaymsg; + + if (video) { + + for(i=0;iFreeSprite(FogSprites[i]); + } + + for(i=0;i<4;i++) { + video->FreeSprite(WindowFrames[i]); + } + + for (int size = 0; size < MAX_CIRCLE_SIZE; size++) { + for(i=0;i<6;i++) { + video->FreeSprite(GroundCircles[size][i]); + } + } + + if (TooltipBack) { + for(i=0;i<3;i++) { + //freesprite checks for null pointer + video->FreeSprite(TooltipBack[i]); + } + delete[] TooltipBack; + } + if (InfoTextPalette) { + gamedata->FreePalette(InfoTextPalette); + } + + video->SetDragCursor(NULL); + } + + delete evntmgr; + + delete vars; + delete tokens; + if (lists) { + lists->RemoveAll(Release2daList); + delete lists; + } + + if (RtRows) { + RtRows->RemoveAll(ReleaseItemList); + delete RtRows; + } + if (ItemExclTable) { + ItemExclTable->RemoveAll(NULL); + delete ItemExclTable; + } + if (ItemDialTable) { + ItemDialTable->RemoveAll(NULL); + delete ItemDialTable; + } + if (ItemDial2Table) { + ItemDial2Table->RemoveAll(NULL); + delete ItemDial2Table; + } + if (ItemTooltipTable) { + ItemTooltipTable->RemoveAll(ReleaseItemTooltip); + delete ItemTooltipTable; + } + + Map::ReleaseMemory(); + Actor::ReleaseMemory(); + + gamedata->ClearCaches(); + delete gamedata; + gamedata = NULL; + + // Removing all stuff from Cache, except bifs + if (!KeepCache) DelTree((const char *) CachePath, true); +} + +void Interface::SetWindowFrame(int i, Sprite2D *Picture) +{ + video->FreeSprite(WindowFrames[i]); + WindowFrames[i]=Picture; +} + +GameControl* Interface::StartGameControl() +{ + //making sure that our window is the first one + if (ConsolePopped) { + PopupConsole(); + } + DelAllWindows();//deleting ALL windows + gamedata->DelTable(0xffffu); //dropping ALL tables + Window* gamewin = new Window( 0xffff, 0, 0, (ieWord) Width, (ieWord) Height ); + gamewin->WindowPack[0]=0; + GameControl* gc = new GameControl(); + gc->XPos = 0; + gc->YPos = 0; + gc->Width = (ieWord) Width; + gc->Height = (ieWord) Height; + gc->Owner = gamewin; + gc->ControlID = 0x00000000; + gc->ControlType = IE_GUI_GAMECONTROL; + gamewin->AddControl( gc ); + AddWindow( gamewin ); + SetVisible( 0, WINDOW_VISIBLE ); + //setting the focus to the game control + evntmgr->SetFocused(gamewin, gc); + if (guiscript->LoadScript( "MessageWindow" )) { + guiscript->RunFunction( "MessageWindow", "OnLoad" ); + gc->UnhideGUI(); + } + + return gc; +} + +/* handle main loop events that might destroy or create windows +thus cannot be called from DrawWindows directly +these events are pending until conditions are right +*/ +void Interface::HandleEvents() +{ + GameControl *gc = GetGameControl(); + if (gc && (!gc->Owner || !gc->Owner->Visible)) { + gc=NULL; + } + + if (EventFlag&EF_SELECTION) { + EventFlag&=~EF_SELECTION; + guiscript->RunFunction( "GUICommonWindows", "SelectionChanged", false); + } + + if (EventFlag&EF_UPDATEANIM) { + EventFlag&=~EF_UPDATEANIM; + guiscript->RunFunction( "GUICommonWindows", "UpdateAnimation", false); + } + + if (EventFlag&EF_PORTRAIT) { + ieDword tmp = (ieDword) ~0; + vars->Lookup( "PortraitWindow", tmp ); + if (tmp != (ieDword) ~0) { + EventFlag&=~EF_PORTRAIT; + guiscript->RunFunction( "GUICommonWindows", "UpdatePortraitWindow" ); + } + } + + if (EventFlag&EF_ACTION) { + ieDword tmp = (ieDword) ~0; + vars->Lookup( "ActionsWindow", tmp ); + if (tmp != (ieDword) ~0) { + EventFlag&=~EF_ACTION; + guiscript->RunFunction( "GUICommonWindows", "UpdateActionsWindow" ); + } + } + + if ((EventFlag&EF_CONTROL) && gc) { + EventFlag&=~EF_CONTROL; + guiscript->RunFunction( "MessageWindow", "UpdateControlStatus" ); + //this is the only value we can use here + if (game->ControlStatus & CS_HIDEGUI) + gc->HideGUI(); + else + gc->UnhideGUI(); + return; + } + if ((EventFlag&EF_SHOWMAP) && gc) { + ieDword tmp = (ieDword) ~0; + vars->Lookup( "OtherWindow", tmp ); + if (tmp == (ieDword) ~0) { + EventFlag &= ~EF_SHOWMAP; + guiscript->RunFunction( "GUIMA", "ShowMap" ); + } + return; + } + + if (EventFlag&EF_SEQUENCER) { + EventFlag&=~EF_SEQUENCER; + guiscript->RunFunction( "GUIMG", "OpenSequencerWindow" ); + return; + } + + if (EventFlag&EF_IDENTIFY) { + EventFlag&=~EF_IDENTIFY; + // FIXME: Implement this. + guiscript->RunFunction( "GUICommonWindows", "OpenIdentifyWindow" ); + return; + } + if (EventFlag&EF_OPENSTORE) { + EventFlag&=~EF_OPENSTORE; + guiscript->RunFunction( "GUISTORE", "OpenStoreWindow" ); + return; + } + + if (EventFlag&EF_EXPANSION) { + EventFlag&=~EF_EXPANSION; + guiscript->RunFunction( "MessageWindow", "GameExpansion", false ); + return; + } +} + +/* handle main loop events that might destroy or create windows +thus cannot be called from DrawWindows directly +*/ +void Interface::HandleFlags() +{ + //clear events because the context changed + EventFlag = EF_CONTROL; + + if (QuitFlag&(QF_QUITGAME|QF_EXITGAME) ) { + // when reaching this, quitflag should be 1 or 2 + // if Exitgame was set, we'll set Start.py too + QuitGame (QuitFlag&QF_EXITGAME); + QuitFlag &= ~(QF_QUITGAME|QF_EXITGAME); + } + + if (QuitFlag&QF_LOADGAME) { + QuitFlag &= ~QF_LOADGAME; + LoadGame(LoadGameIndex.get(), VersionOverride ); + LoadGameIndex.release(); + //after loading a game, always check if the game needs to be upgraded + } + + if (QuitFlag&QF_ENTERGAME) { + QuitFlag &= ~QF_ENTERGAME; + if (game) { + EventFlag|=EF_EXPANSION; + timer->Init(); + + //rearrange party slots + game->ConsolidateParty(); + GameControl* gc = StartGameControl(); + //switch map to protagonist + Actor* actor = GetFirstSelectedPC(true); + if (actor) { + gc->ChangeMap(actor, true); + } + } else { + printMessage("Core", "No game to enter...\n", LIGHT_RED); + QuitFlag = QF_QUITGAME; + } + } + + if (QuitFlag&QF_CHANGESCRIPT) { + QuitFlag &= ~QF_CHANGESCRIPT; + guiscript->LoadScript( NextScript ); + guiscript->RunFunction( NextScript, "OnLoad" ); + } +} + +bool GenerateAbilityTables() +{ + FreeAbilityTables(); + + //range is: 0 - maximumability + int tablesize = MaximumAbility+1; + strmod = (ieWordSigned *) malloc (tablesize * 4 * sizeof(ieWordSigned) ); + if (!strmod) + return false; + strmodex = (ieWordSigned *) malloc (101 * 4 * sizeof(ieWordSigned) ); + if (!strmodex) + return false; + intmod = (ieWordSigned *) malloc (tablesize * 3 * sizeof(ieWordSigned) ); + if (!intmod) + return false; + dexmod = (ieWordSigned *) malloc (tablesize * 3 * sizeof(ieWordSigned) ); + if (!dexmod) + return false; + conmod = (ieWordSigned *) malloc (tablesize * 5 * sizeof(ieWordSigned) ); + if (!conmod) + return false; + chrmod = (ieWordSigned *) malloc (tablesize * 1 * sizeof(ieWordSigned) ); + if (!chrmod) + return false; + lorebon = (ieWordSigned *) malloc (tablesize * 1 * sizeof(ieWordSigned) ); + if (!lorebon) + return false; + wisbon = (ieWordSigned *) malloc (tablesize * 1 * sizeof(ieWordSigned) ); + if (!wisbon) + return false; + return true; +} + +bool Interface::ReadAbilityTable(const ieResRef tablename, ieWordSigned *mem, int columns, int rows) +{ + AutoTable tab(tablename); + if (!tab) { + return false; + } + //this is a hack for rows not starting at 0 in some cases + int fix = 0; + const char * tmp = tab->GetRowName(0); + if (tmp && (tmp[0]!='0')) { + fix = atoi(tmp); + for (int i=0;iQueryField(0,j),NULL,0 ); + } + } + } + for (int j=0;jQueryField(i,j),NULL,0 ); + } + } + return true; +} + +bool Interface::ReadAbilityTables() +{ + bool ret = GenerateAbilityTables(); + if (!ret) + return ret; + ret = ReadAbilityTable("strmod", strmod, 4, MaximumAbility + 1); + if (!ret) + return ret; + ret = ReadAbilityTable("strmodex", strmodex, 4, 101); + //3rd ed doesn't have strmodex, but has a maximum of 40 + if (!ret && (MaximumAbility<=25) ) + return ret; + ret = ReadAbilityTable("intmod", intmod, 3, MaximumAbility + 1); + if (!ret) + return ret; + ret = ReadAbilityTable("hpconbon", conmod, 5, MaximumAbility + 1); + if (!ret) + return ret; + if (!HasFeature(GF_3ED_RULES)) { + //no lorebon in iwd2??? + ret = ReadAbilityTable("lorebon", lorebon, 1, MaximumAbility + 1); + if (!ret) + return ret; + //no dexmod in iwd2??? + ret = ReadAbilityTable("dexmod", dexmod, 3, MaximumAbility + 1); + if (!ret) + return ret; + } + //this table is a single row (not a single column) + ret = ReadAbilityTable("chrmodst", chrmod, MaximumAbility + 1, 1); + if (!ret) + return ret; + if (HasFeature(GF_WISDOM_BONUS)) { + ret = ReadAbilityTable("wisxpbon", wisbon, 1, MaximumAbility + 1); + if (!ret) + return ret; + } + return true; +} + +bool Interface::ReadGameTimeTable() +{ + AutoTable table("gametime"); + if (!table) { + return false; + } + + Time.round_sec = atoi(table->QueryField("ROUND_SECONDS", "DURATION")); + Time.turn_sec = atoi(table->QueryField("TURN_SECONDS", "DURATION")); + Time.round_size = Time.round_sec * AI_UPDATE_TIME; + Time.rounds_per_turn = Time.turn_sec / Time.round_sec; + + return true; +} + +bool Interface::ReadAuxItemTables() +{ + int idx; + int table; + bool flag = true; + + if (ItemExclTable) { + ItemExclTable->RemoveAll(NULL); + } else { + ItemExclTable = new Variables(); + ItemExclTable->SetType(GEM_VARIABLES_INT); + } + table = gamedata->LoadTable( "itemexcl" ); + + AutoTable aa; + + //don't report error when the file doesn't exist + if (aa.load("itemexcl")) { + idx = aa->GetRowCount(); + while (idx--) { + ieResRef key; + + strnlwrcpy(key,aa->GetRowName(idx),8); + ieDword value = strtol(aa->QueryField(idx,0),NULL,0); + ItemExclTable->SetAt(key, value); + } + } + if (ItemDialTable) { + ItemDialTable->RemoveAll(NULL); + } else { + ItemDialTable = new Variables(); + ItemDialTable->SetType(GEM_VARIABLES_INT); + } + if (ItemDial2Table) { + ItemDial2Table->RemoveAll(NULL); + } else { + ItemDial2Table = new Variables(); + ItemDial2Table->SetType(GEM_VARIABLES_STRING); + } + + //don't report error when the file doesn't exist + if (aa.load("itemdial")) { + idx = aa->GetRowCount(); + while (idx--) { + ieResRef key, dlgres; + + strnlwrcpy(key,aa->GetRowName(idx),8); + ieDword value = strtol(aa->QueryField(idx,0),NULL,0); + ItemDialTable->SetAt(key, value); + strnlwrcpy(dlgres,aa->QueryField(idx,1),8); + ItemDial2Table->SetAtCopy(key, dlgres); + } + } + + if (ItemTooltipTable) { + ItemTooltipTable->RemoveAll(ReleaseItemTooltip); + } else { + ItemTooltipTable = new Variables(); + ItemTooltipTable->SetType(GEM_VARIABLES_POINTER); + } + + //don't report error when the file doesn't exist + if (aa.load("tooltip")) { + idx = aa->GetRowCount(); + while (idx--) { + ieResRef key; + int *tmppoi = (int *) malloc(sizeof(int)*3); + + strnlwrcpy(key,aa->GetRowName(idx),8); + for (int i=0;i<3;i++) { + tmppoi[i] = atoi(aa->QueryField(idx,i)); + } + ItemTooltipTable->SetAt(key, (void*)tmppoi); + } + } + return flag; +} + +//Static +const char *Interface::GetDeathVarFormat() +{ + return DeathVarFormat; +} + +int Interface::GetItemExcl(const ieResRef itemname) const +{ + ieDword value; + + if (ItemExclTable && ItemExclTable->Lookup(itemname, value)) { + return (int) value; + } + return 0; +} + +int Interface::GetItemTooltip(const ieResRef itemname, int header, int identified) +{ + int *value = NULL; + + if (ItemTooltipTable) { + void* lookup = NULL; + ItemTooltipTable->Lookup(itemname, lookup); + value = (int*)lookup; + } + if (value && (value[header]>=0)) { + return value[header]; + } + Item *item = gamedata->GetItem(itemname); + if (!item) { + return -1; + } + int ret = identified?item->ItemNameIdentified:item->ItemName; + gamedata->FreeItem(item, itemname, 0); + return ret; +} + +int Interface::GetItemDialStr(const ieResRef itemname) const +{ + ieDword value; + + if (ItemDialTable && ItemDialTable->Lookup(itemname, value)) { + return (int) value; + } + return -1; +} + +//second value is the item dialog resource returned by this method +int Interface::GetItemDialRes(const ieResRef itemname, ieResRef retval) const +{ + if (ItemDial2Table && ItemDial2Table->Lookup(itemname, retval, sizeof(ieResRef))) { + return 1; + } + return 0; +} + +bool Interface::ReadAreaAliasTable(const ieResRef tablename) +{ + if (AreaAliasTable) { + AreaAliasTable->RemoveAll(NULL); + } else { + AreaAliasTable = new Variables(); + AreaAliasTable->SetType(GEM_VARIABLES_INT); + } + + AutoTable aa(tablename); + if (!aa) { + //don't report error when the file doesn't exist + return true; + } + + int idx = aa->GetRowCount(); + while (idx--) { + ieResRef key; + + strnlwrcpy(key,aa->GetRowName(idx),8); + ieDword value = atoi(aa->QueryField(idx,0)); + AreaAliasTable->SetAt(key, value); + } + return true; +} + +//this isn't const +int Interface::GetAreaAlias(const ieResRef areaname) const +{ + ieDword value; + + if (AreaAliasTable && AreaAliasTable->Lookup(areaname, value)) { + return (int) value; + } + return -1; +} + +bool Interface::ReadMusicTable(const ieResRef tablename, int col) { + AutoTable tm(tablename); + if (!tm) + return false; + + for (unsigned int i = 0; i < tm->GetRowCount(); i++) { + musiclist.push_back(strdup(tm->QueryField(i, col))); + } + + return true; +} + +bool Interface::ReadDamageTypeTable() { + AutoTable tm("dmgtypes"); + if (!tm) + return false; + + DamageInfoStruct di; + for (ieDword i = 0; i < tm->GetRowCount(); i++) { + di.strref = displaymsg->GetStringReference(atoi(tm->QueryField(i, 0))); + di.resist_stat = TranslateStat(tm->QueryField(i, 1)); + di.value = strtol(tm->QueryField(i, 2), (char **) NULL, 16); + di.iwd_mod_type = atoi(tm->QueryField(i, 3)); + DamageInfoMap.insert(std::make_pair ((ieDword)di.value, di)); + } + + return true; +} + +bool Interface::ReadReputationModTable() { + AutoTable tm("reputati"); + if (!tm) + return false; + + reputationmod = (int **) calloc(21, sizeof(int *)); + int cols = tm->GetColumnCount(); + for (unsigned int i=0; i<20; i++) { + reputationmod[i] = (int *) calloc(cols, sizeof(int)); + for (int j=0; jQueryField(i, j)); + } + } + + return true; +} + +bool Interface::ReadModalStates() +{ + AutoTable table("modal"); + if (!table) + return false; + + ModalStatesStruct ms; + for (unsigned short i = 0; i < table->GetRowCount(); i++) { + strncpy(ms.spell, table->QueryField(i, 0), 8); + strncpy(ms.action, table->QueryField(i, 1), 16); + ms.entering_str = atoi(table->QueryField(i, 2)); + ms.leaving_str = atoi(table->QueryField(i, 3)); + ms.failed_str = atoi(table->QueryField(i, 4)); + ms.aoe_spell = atoi(table->QueryField(i, 5)); + ModalStates.push_back(ms); + } + + return true; +} + +//Not a constant anymore, we let the caller set the entry to zero +char *Interface::GetMusicPlaylist(int SongType) const { + if (SongType < 0 || (unsigned int)SongType >= musiclist.size()) + return NULL; + + return musiclist[SongType]; +} + +static const Color white = {0xff,0xff,0xff,0xff}; +static const Color black = {0x00,0x00,0x00,0xff}; +static const Region bg( 0, 0, 100, 30 ); + +/** this is the main loop */ +void Interface::Main() +{ + ieDword brightness = 10; + ieDword contrast = 5; + ieDword speed = 10; + + vars->Lookup("Full Screen", FullScreen); + video->CreateDisplay( Width, Height, Bpp, FullScreen); + video->SetDisplayTitle( GameName, GameType ); + vars->Lookup("Brightness Correction", brightness); + vars->Lookup("Gamma Correction", contrast); + vars->Lookup("Mouse Scroll Speed", speed); + video->SetGamma(brightness, contrast); + SetMouseScrollSpeed((int) speed); + if (vars->Lookup("Tooltips", TooltipDelay)) { + // the games store the slider position*10, not the actual delay + TooltipDelay *= TOOLTIP_DELAY_FACTOR/10; + } + + Font* fps = GetFont( ( unsigned int ) 0 ); + char fpsstring[40]={"???.??? fps"}; + unsigned long frame = 0, time, timebase; + GetTime(timebase); + double frames = 0.0; + Palette* palette = CreatePalette( white, black ); + do { + //don't change script when quitting is pending + + while (QuitFlag) { + HandleFlags(); + } + //eventflags are processed only when there is a game + if (EventFlag && game) { + HandleEvents(); + } + HandleGUIBehaviour(); + + GameLoop(); + DrawWindows(); + if (DrawFPS) { + frame++; + GetTime( time ); + if (time - timebase > 1000) { + frames = ( frame * 1000.0 / ( time - timebase ) ); + timebase = time; + frame = 0; + sprintf( fpsstring, "%.3f fps", frames ); + } + video->DrawRect( bg, black ); + fps->Print( bg, + ( unsigned char * ) fpsstring, palette, + IE_FONT_ALIGN_LEFT | IE_FONT_ALIGN_MIDDLE, true ); + } + if (TickHook) + TickHook->call(); + } while (video->SwapBuffers() == GEM_OK); + gamedata->FreePalette( palette ); +} + +int Interface::ReadResRefTable(const ieResRef tablename, ieResRef *&data) +{ + int count = 0; + + if (data) { + free(data); + data = NULL; + } + AutoTable tm(tablename); + if (!tm) { + printStatus( "ERROR", LIGHT_RED ); + printf( "Cannot find %s.2da.\n",tablename ); + return 0; + } + count = tm->GetRowCount(); + data = (ieResRef *) calloc( count, sizeof(ieResRef) ); + for (int i = 0; i < count; i++) { + strnlwrcpy( data[i], tm->QueryField( i, 0 ), 8 ); + //* marks an empty resource + if (data[i][0]=='*') { + data[i][0]=0; + } + } + return count; +} + +int Interface::LoadSprites() +{ + ieDword i; + int size; + if (!IsAvailable( IE_2DA_CLASS_ID )) { + printf( "No 2DA Importer Available.\nTermination in Progress...\n" ); + return GEM_ERROR; + } + + //loading cursors + AnimationFactory* anim; + anim = (AnimationFactory*) gamedata->GetFactoryResource("cursors", IE_BAM_CLASS_ID); + if (anim) + { + CursorCount = anim->GetCycleCount(); + Cursors = new Sprite2D * [CursorCount]; + for (int i = 0; i < CursorCount; i++) { + Cursors[i] = anim->GetFrame( 0, (ieByte) i ); + } + } + printMessage( "Core", "Loading Cursors...", WHITE ); + + // this is the last existing cursor type + if (CursorCountSetCursor( Cursors[0], Cursors[1] ); + printStatus( "OK", LIGHT_GREEN ); + + // Load fog-of-war bitmaps + anim = (AnimationFactory*) gamedata->GetFactoryResource("fogowar", IE_BAM_CLASS_ID); + printMessage( "Core", "Loading Fog-Of-War bitmaps...", WHITE ); + if (!anim || anim->GetCycleSize( 0 ) != 8) { + // unknown type of fog anim + printStatus( "ERROR", LIGHT_RED ); + return GEM_ERROR; + } + + FogSprites[0] = NULL; + FogSprites[1] = anim->GetFrame( 0, 0 ); + FogSprites[2] = anim->GetFrame( 1, 0 ); + FogSprites[3] = anim->GetFrame( 2, 0 ); + + FogSprites[4] = video->MirrorSpriteVertical( FogSprites[1], false ); + + FogSprites[5] = NULL; + + FogSprites[6] = video->MirrorSpriteVertical( FogSprites[3], false ); + + FogSprites[7] = NULL; + + FogSprites[8] = video->MirrorSpriteHorizontal( FogSprites[2], false ); + + FogSprites[9] = video->MirrorSpriteHorizontal( FogSprites[3], false ); + + FogSprites[10] = NULL; + FogSprites[11] = NULL; + + FogSprites[12] = video->MirrorSpriteHorizontal( FogSprites[6], false ); + + FogSprites[16] = anim->GetFrame( 3, 0 ); + FogSprites[17] = anim->GetFrame( 4, 0 ); + FogSprites[18] = anim->GetFrame( 5, 0 ); + FogSprites[19] = anim->GetFrame( 6, 0 ); + + FogSprites[20] = video->MirrorSpriteVertical( FogSprites[17], false ); + + FogSprites[21] = NULL; + + FogSprites[23] = NULL; + + FogSprites[24] = video->MirrorSpriteHorizontal( FogSprites[18], false ); + + FogSprites[25] = anim->GetFrame( 7, 0 ); + + { + Sprite2D *tmpsprite = video->MirrorSpriteVertical( FogSprites[25], false ); + FogSprites[22] = video->MirrorSpriteHorizontal( tmpsprite, false ); + video->FreeSprite( tmpsprite ); + } + + FogSprites[26] = NULL; + FogSprites[27] = NULL; + + { + Sprite2D *tmpsprite = video->MirrorSpriteVertical( FogSprites[19], false ); + FogSprites[28] = video->MirrorSpriteHorizontal( tmpsprite, false ); + video->FreeSprite( tmpsprite ); + } + + i = 0; + vars->Lookup("3D Acceleration", i); + if (i) { + for(i=0;iCreateAlpha( FogSprites[i] ); + video->FreeSprite ( FogSprites[i] ); + FogSprites[i] = alphasprite; + } + } + } + + printStatus( "OK", LIGHT_GREEN ); + + // Load ground circle bitmaps (PST only) + //block required due to msvc6.0 incompatibility + for (size = 0; size < MAX_CIRCLE_SIZE; size++) { + if (GroundCircleBam[size][0]) { + anim = (AnimationFactory*) gamedata->GetFactoryResource(GroundCircleBam[size], IE_BAM_CLASS_ID); + if (!anim || anim->GetCycleCount() != 6) { + // unknown type of circle anim + printMessage( "Core", "Loading Ground circle bitmaps...", WHITE ); + printStatus( "ERROR", LIGHT_RED ); + return GEM_ERROR; + } + + for (int i = 0; i < 6; i++) { + Sprite2D* sprite = anim->GetFrame( 0, (ieByte) i ); + if (GroundCircleScale[size]) { + GroundCircles[size][i] = video->SpriteScaleDown( sprite, GroundCircleScale[size] ); + video->FreeSprite( sprite ); + } else { + GroundCircles[size][i] = sprite; + } + } + } + } + + printMessage( "Core", "Loading Ground circle bitmaps...", WHITE ); + printStatus( "OK", LIGHT_GREEN ); + + printMessage( "Core", "Loading Fonts...\n", WHITE ); + AutoTable tab("fonts"); + if (!tab) { + printStatus( "ERROR", LIGHT_RED ); + printf( "Cannot find fonts.2da.\nTermination in Progress...\n" ); + return GEM_ERROR; + } else { + PluginHolder bamint(IE_BAM_CLASS_ID); + if (!bamint) { + printStatus( "ERROR", LIGHT_RED ); + printf( "No BAM Importer Available.\nTermination in Progress...\n" ); + return GEM_ERROR; + } + DataStream* str = NULL; + + int count = tab->GetRowCount(); + for (int i = 0; i < count; i++) { + const char* ResRef = tab->QueryField( i, 0 ); + int needpalette = atoi( tab->QueryField( i, 1 ) ); + int first_char = atoi( tab->QueryField( i, 2 ) ); + str = gamedata->GetResource( ResRef, IE_BAM_CLASS_ID ); + if (!bamint->Open( str, true )) { + continue; + } + Font* fnt = bamint->GetFont(); + if (!fnt) { + continue; + } + strnlwrcpy( fnt->ResRef, ResRef, 8 ); + if (needpalette) { + + Color fore = {0xff, 0xff, 0xff, 0}; + Color back = {0x00, 0x00, 0x00, 0}; + if (!strnicmp( TooltipFont, ResRef, 8) ) { + if (TooltipColor.a==0xff) { + fore = TooltipColor; + } else { + fore = back; + back = TooltipColor; + } + } + Palette* pal = CreatePalette( fore, back ); + pal->CreateShadedAlphaChannel(); + fnt->SetPalette(pal); + gamedata->FreePalette( pal ); + } + fnt->SetFirstChar( (ieByte) first_char ); + fonts.push_back( fnt ); + } + + if (fonts.size() == 0) { + printMessage( "Core", "No default font loaded! ", WHITE ); + printStatus( "ERROR", LIGHT_RED ); + return GEM_ERROR; + } + if (GetFont( ButtonFont ) == NULL) { + printMessage( "Core", "ButtonFont not loaded: ", WHITE ); + printf("%s ", ButtonFont); + printStatus( "WARNING", YELLOW ); + } + if (GetFont( MovieFont ) == NULL) { + printMessage( "Core", "MovieFont not loaded: ", WHITE ); + printf("%s ", MovieFont); + printStatus( "WARNING", YELLOW ); + } + if (GetFont( TooltipFont ) == NULL) { + printMessage( "Core", "TooltipFont not loaded: ", WHITE ); + printf("%s ", TooltipFont); + printStatus( "WARNING", YELLOW ); + } + } + printMessage( "Core", "Fonts Loaded...", WHITE ); + printStatus( "OK", LIGHT_GREEN ); + + if (TooltipBackResRef[0]) { + anim = (AnimationFactory*) gamedata->GetFactoryResource(TooltipBackResRef, IE_BAM_CLASS_ID); + printMessage( "Core", "Initializing Tooltips...", WHITE ); + if (!anim) { + printStatus( "ERROR", LIGHT_RED ); + return GEM_ERROR; + } + TooltipBack = new Sprite2D * [3]; + for (int i = 0; i < 3; i++) { + TooltipBack[i] = anim->GetFrame( 0, (ieByte) i ); + TooltipBack[i]->XPos = 0; + TooltipBack[i]->YPos = 0; + } + printStatus( "OK", LIGHT_GREEN ); + } + + return GEM_OK; +} + +int Interface::Init() +{ + plugin_flags = new Variables(); + plugin_flags->SetType( GEM_VARIABLES_INT ); + + printMessage( "Core", "Initializing the Event Manager...", WHITE ); + evntmgr = new EventMgr(); + + printMessage( "Core", "Initializing Lists Dictionary...", WHITE ); + lists = new Variables(); + if (!lists) { + printStatus( "ERROR", LIGHT_RED ); + return GEM_ERROR; + } + lists->SetType( GEM_VARIABLES_POINTER ); + + printMessage( "Core", "Initializing Variables Dictionary...", WHITE ); + vars = new Variables(); + if (!vars) { + printStatus( "ERROR", LIGHT_RED ); + return GEM_ERROR; + } + vars->SetType( GEM_VARIABLES_INT ); + vars->ParseKey(true); + + vars->SetAt( "Volume Ambients", 100 ); + vars->SetAt( "Volume Movie", 100 ); + vars->SetAt( "Volume Music", 100 ); + vars->SetAt( "Volume SFX", 100 ); + vars->SetAt( "Volume Voices", 100 ); + printStatus( "OK", LIGHT_GREEN ); + + if (!LoadConfig()) { + return GEM_ERROR; + } + printMessage( "Core", "Starting Plugin Manager...\n", WHITE ); + PluginMgr *plugin = PluginMgr::Get(); + plugin->LoadPlugins(PluginsPath); + if (plugin && plugin->GetPluginCount()) { + printMessage( "Core", "Plugin Loading Complete...", WHITE ); + printStatus( "OK", LIGHT_GREEN ); + } else { + printMessage( "Core", "Plugin Loading Failed, check path...", YELLOW); + printStatus( "ERROR", LIGHT_RED ); + return GEM_ERROR; + } + plugin->RunInitializers(); + + time_t t; + t = time( NULL ); + srand( ( unsigned int ) t ); +#ifdef _DEBUG + FileStreamPtrCount = 0; + CachedFileStreamPtrCount = 0; +#endif + printMessage( "Core", "GemRB Core Initialization...\n", WHITE ); + printStatus( "OK", LIGHT_GREEN ); + printMessage( "Core", "Initializing Video Driver...", WHITE ); + video = ( Video * ) PluginMgr::Get()->GetDriver(&Video::ID, VideoDriverName.c_str()); + if (!video) { + printStatus( "ERROR", LIGHT_RED ); + printf( "No Video Driver Available.\nTermination in Progress...\n" ); + return GEM_ERROR; + } + if (video->Init() == GEM_ERROR) { + printStatus( "ERROR", LIGHT_RED ); + printf( "Cannot Initialize Video Driver.\nTermination in Progress...\n" ); + return GEM_ERROR; + } + Color defcolor={255,255,255,200}; + SetInfoTextColor(defcolor); + printStatus( "OK", LIGHT_GREEN ); + + { + printMessage( "Core", "Initializing Search Path...", WHITE ); + if (!IsAvailable( PLUGIN_RESOURCE_DIRECTORY )) { + printf( "no DirectoryImporter! " ); + printStatus( "ERROR", LIGHT_RED ); + return GEM_ERROR; + } + + char path[_MAX_PATH]; + + PathJoin( path, CachePath, NULL); + gamedata->AddSource(path, "Cache", PLUGIN_RESOURCE_DIRECTORY); + + PathJoin( path, GemRBOverridePath, "override", GameType, NULL); + gamedata->AddSource(path, "GemRB Override", PLUGIN_RESOURCE_DIRECTORY); + + size_t i; + for (i = 0; i < ModPath.size(); ++i) + gamedata->AddSource(ModPath[i].c_str(), "Mod paths", PLUGIN_RESOURCE_DIRECTORY); + + PathJoin( path, GemRBOverridePath, "override", "shared", NULL); + gamedata->AddSource(path, "shared GemRB Override", PLUGIN_RESOURCE_DIRECTORY); + + PathJoin( path, GamePath, GameOverridePath, NULL); + gamedata->AddSource(path, "Override", PLUGIN_RESOURCE_DIRECTORY); + + PathJoin( path, GamePath, GameSoundsPath, NULL); + gamedata->AddSource(path, "Sounds", PLUGIN_RESOURCE_DIRECTORY); + + PathJoin( path, GamePath, GameScriptsPath, NULL); + gamedata->AddSource(path, "Scripts", PLUGIN_RESOURCE_DIRECTORY); + + PathJoin( path, GamePath, GamePortraitsPath, NULL); + gamedata->AddSource(path, "Portraits", PLUGIN_RESOURCE_DIRECTORY); + + PathJoin( path, GamePath, GameDataPath, NULL); + gamedata->AddSource(path, "Data", PLUGIN_RESOURCE_DIRECTORY); + + //IWD2 movies are on the CD but not in the BIF + for (i = 0; i < MAX_CD; i++) { + for (size_t j=0;jAddSource(path, description, PLUGIN_RESOURCE_DIRECTORY); + } + } + + printStatus( "OK", LIGHT_GREEN ); + } + + { + printMessage( "Core", "Initializing KEY Importer...", WHITE ); + char ChitinPath[_MAX_PATH]; + PathJoin( ChitinPath, GamePath, "chitin.key", NULL ); + if (!gamedata->AddSource(ChitinPath, "chitin.key", PLUGIN_RESOURCE_KEY)) { + printStatus( "ERROR", LIGHT_RED ); + return GEM_ERROR; + } + printStatus( "OK", LIGHT_GREEN ); + } + + printMessage( "Core", "Reading Game Options...\n", WHITE ); + if (!LoadGemRBINI()) + { + printf( "Cannot Load INI\nTermination in Progress...\n" ); + return GEM_ERROR; + } + + //loading baldur.ini + { + char ini_path[_MAX_PATH]; + PathJoin( ini_path, GamePath, INIConfig, NULL ); + LoadINI( ini_path ); + int i; + for (i = 0; i < 8; i++) { + if (INIConfig[i] == '.') + break; + GameNameResRef[i] = INIConfig[i]; + } + GameNameResRef[i] = 0; + } + + printMessage( "Core", "Creating Projectile Server...\n", WHITE ); + projserv = new ProjectileServer(); + if (!projserv->GetHighestProjectileNumber()) { + printStatus( "ERROR", LIGHT_RED ); + printf( "No projectiles are available...\n" ); + } + + printMessage( "Core", "Checking for Dialogue Manager...", WHITE ); + if (!IsAvailable( IE_TLK_CLASS_ID )) { + printStatus( "ERROR", LIGHT_RED ); + printf( "No TLK Importer Available.\nTermination in Progress...\n" ); + return GEM_ERROR; + } + printStatus( "OK", LIGHT_GREEN ); + strings = PluginHolder(IE_TLK_CLASS_ID); + printMessage( "Core", "Loading Dialog.tlk file...", WHITE ); + char strpath[_MAX_PATH]; + PathJoin( strpath, GamePath, dialogtlk, NULL ); + FileStream* fs = new FileStream(); + if (!fs->Open( strpath, true )) { + printStatus( "ERROR", LIGHT_RED ); + printf( "Cannot find Dialog.tlk.\nTermination in Progress...\n" ); + delete fs; + return GEM_ERROR; + } + printStatus( "OK", LIGHT_GREEN ); + strings->Open( fs, true ); + + { + printMessage( "Core", "Loading Palettes...\n", WHITE ); + ResourceHolder pal16im(Palette16); + if (pal16im) + pal16 = pal16im->GetImage(); + ResourceHolder pal32im(Palette32); + if (pal32im) + pal32 = pal32im->GetImage(); + ResourceHolder pal256im(Palette256); + if (pal256im) + pal256 = pal256im->GetImage(); + if (!pal16 || !pal32 || !pal256) { + printStatus( "ERROR", LIGHT_RED ); + return GEM_ERROR; + } + printMessage( "Core", "Palettes Loaded\n", WHITE ); + } + + if (!IsAvailable( IE_BAM_CLASS_ID )) { + printStatus( "ERROR", LIGHT_RED ); + printf( "No BAM Importer Available.\nTermination in Progress...\n" ); + return GEM_ERROR; + } + + printMessage( "Core", "Initializing stock sounds...\n", WHITE ); + DSCount = ReadResRefTable ("defsound", DefSound); + if (DSCount == 0) { + printStatus( "ERROR", LIGHT_RED ); + printf( "Cannot find defsound.2da.\nTermination in Progress...\n" ); + return GEM_ERROR; + } + + printStatus( "OK", LIGHT_GREEN ); + printMessage( "Core", "Broadcasting Event Manager...", WHITE ); + video->SetEventMgr( evntmgr ); + printStatus( "OK", LIGHT_GREEN ); + printMessage( "Core", "Initializing Window Manager...", WHITE ); + windowmgr = PluginHolder(IE_CHU_CLASS_ID); + if (windowmgr == NULL) { + printStatus( "ERROR", LIGHT_RED ); + return GEM_ERROR; + } + printStatus( "OK", LIGHT_GREEN ); + printMessage( "Core", "Initializing GUI Script Engine...", WHITE ); + guiscript = PluginHolder(IE_GUI_SCRIPT_CLASS_ID); + if (guiscript == NULL) { + printStatus( "ERROR", LIGHT_RED ); + return GEM_ERROR; + } + if (!guiscript->Init()) { + printStatus( "ERROR", LIGHT_RED ); + return GEM_ERROR; + } + printStatus( "OK", LIGHT_GREEN ); + strcpy( NextScript, "Start" ); + + int ret = LoadSprites(); + if (ret) return ret; + + printMessage( "Core", "Setting up the Console...", WHITE ); + QuitFlag = QF_CHANGESCRIPT; + console = new Console(); + console->XPos = 0; + console->YPos = (ieWord) (Height - 25); + console->Width = (ieWord) Width; + console->Height = 25; + if (fonts.size() > 0) { + console->SetFont( fonts[0] ); + } + + Sprite2D *tmpsprite = GetCursorSprite(); + if (!tmpsprite) { + printStatus( "ERROR", LIGHT_RED ); + return GEM_ERROR; + } + console->SetCursor (tmpsprite); + printStatus( "OK", LIGHT_GREEN ); + + printMessage( "Core", "Starting up the Sound Driver...", WHITE ); + AudioDriver = ( Audio * ) PluginMgr::Get()->GetDriver(&Audio::ID, AudioDriverName.c_str()); + if (AudioDriver == NULL) { + printStatus( "ERROR", LIGHT_RED ); + return GEM_ERROR; + } + if (!AudioDriver->Init()) { + printStatus( "ERROR", LIGHT_RED ); + return GEM_ERROR; + } + printStatus( "OK", LIGHT_GREEN ); + + printMessage( "Core", "Allocating SaveGameIterator...", WHITE ); + sgiterator = new SaveGameIterator(); + if (sgiterator == NULL) { + printStatus( "ERROR", LIGHT_RED ); + return GEM_ERROR; + } + printStatus( "OK", LIGHT_GREEN ); + + //no need of strdup, variables do copy the key! + vars->SetAt( "SkipIntroVideos", (unsigned long)SkipIntroVideos ); + vars->SetAt( "GUIEnhancements", (unsigned long)GUIEnhancements ); + + printMessage( "Core", "Initializing Token Dictionary...", WHITE ); + tokens = new Variables(); + if (!tokens) { + printStatus( "ERROR", LIGHT_RED ); + return GEM_ERROR; + } + tokens->SetType( GEM_VARIABLES_STRING ); + printStatus( "OK", LIGHT_GREEN ); + + printMessage( "Core", "Initializing Music Manager...", WHITE ); + music = PluginHolder(IE_MUS_CLASS_ID); + if (!music) { + printStatus( "ERROR", LIGHT_RED ); + return GEM_ERROR; + } + printStatus( "OK", LIGHT_GREEN ); + + printMessage("Core", "Loading music list...\n", WHITE ); + if (HasFeature( GF_HAS_SONGLIST )) { + ret = ReadMusicTable("songlist", 1); + } else { + /*since bg1 and pst has no .2da for songlist, + we must supply one in the gemrb/override folder. + It should be: music.2da, first column is a .mus filename*/ + ret = ReadMusicTable("music", 0); + } + if (ret) { + printStatus( "OK", LIGHT_GREEN ); + } else { + printStatus( "NOT FOUND", YELLOW ); + } + + if (HasFeature( GF_RESDATA_INI )) { + printMessage( "Core", "Loading resource data File...", WHITE ); + INIresdata = PluginHolder(IE_INI_CLASS_ID); + DataStream* ds = gamedata->GetResource("resdata", IE_INI_CLASS_ID); + if (!INIresdata->Open( ds, true )) { + printStatus( "ERROR", LIGHT_RED ); + } else { + printStatus( "OK", LIGHT_GREEN ); + } + } + + if (HasFeature( GF_HAS_PARTY_INI )) { + printMessage( "Core", "Loading precreated teams setup...\n", + WHITE ); + INIparty = PluginHolder(IE_INI_CLASS_ID); + FileStream* fs = new FileStream(); + char tINIparty[_MAX_PATH]; + PathJoin( tINIparty, GamePath, "Party.ini", NULL ); + fs->Open( tINIparty, true ); + if (!INIparty->Open( fs, true )) { + printStatus( "ERROR", LIGHT_RED ); + } else { + printStatus( "OK", LIGHT_GREEN ); + } + } + + if (HasFeature(GF_IWD2_DEATHVARFORMAT)) { + memcpy(DeathVarFormat, IWD2DeathVarFormat, sizeof(ieVariable)); + } + + if (HasFeature( GF_HAS_BEASTS_INI )) { + printMessage( "Core", "Loading beasts definition File...\n", + WHITE ); + INIbeasts = PluginHolder(IE_INI_CLASS_ID); + FileStream* fs = new FileStream(); + char tINIbeasts[_MAX_PATH]; + PathJoin( tINIbeasts, GamePath, "beast.ini", NULL ); + // FIXME: crashes if file does not open + fs->Open( tINIbeasts, true ); + if (!INIbeasts->Open( fs, true )) { + printStatus( "ERROR", LIGHT_RED ); + } else { + printStatus( "OK", LIGHT_GREEN ); + } + + printMessage( "Core", "Loading quests definition File...\n", + WHITE ); + INIquests = PluginHolder(IE_INI_CLASS_ID); + FileStream* fs2 = new FileStream(); + char tINIquests[_MAX_PATH]; + PathJoin( tINIquests, GamePath, "quests.ini", NULL ); + // FIXME: crashes if file does not open + fs2->Open( tINIquests, true ); + if (!INIquests->Open( fs2, true )) { + printStatus( "ERROR", LIGHT_RED ); + } else { + printStatus( "OK", LIGHT_GREEN ); + } + } + game = NULL; + calendar = NULL; + + timer = new GlobalTimer(); + printMessage( "Core", "Bringing up the Global Timer...", WHITE ); + if (!timer) { + printStatus( "ERROR", LIGHT_RED ); + return GEM_ERROR; + } + printStatus( "OK", LIGHT_GREEN ); + + ret = Init_EffectQueue(); + printMessage( "Core", "Initializing effects...", WHITE ); + if (!ret) { + printStatus( "ERROR", LIGHT_RED ); + return GEM_ERROR; + } + printStatus( "OK", LIGHT_GREEN ); + + ret = InitItemTypes(); + printMessage( "Core", "Initializing Inventory Management...", WHITE ); + if (!ret) { + printStatus( "ERROR", LIGHT_RED ); + return GEM_ERROR; + } + printStatus( "OK", LIGHT_GREEN ); + + displaymsg = new DisplayMessage(); + printMessage( "Core", "Initializing string constants...", WHITE ); + if (!displaymsg) { + printStatus( "ERROR", LIGHT_RED ); + return GEM_ERROR; + } + printStatus( "OK", LIGHT_GREEN ); + + ret = ReadRandomItems(); + printMessage( "Core", "Initializing random treasure...", WHITE ); + if (ret) { + printStatus( "OK", LIGHT_GREEN ); + } + else { + printStatus( "ERROR", LIGHT_RED ); + } + + ret = ReadAbilityTables(); + printMessage( "Core", "Initializing ability tables...", WHITE ); + if (!ret) { + printStatus( "ERROR", LIGHT_RED ); + return GEM_ERROR; + } + printStatus( "OK", LIGHT_GREEN ); + + ret = ReadReputationModTable(); + printMessage( "Core", "Reading reputation mod table...", WHITE); + if (ret) { + printStatus( "OK", LIGHT_GREEN ); + } else { + printStatus( "NOT FOUND", LIGHT_RED ); + } + + if ( gamedata->Exists("WMAPLAY", IE_2DA_CLASS_ID) ) { + ret = ReadAreaAliasTable( "WMAPLAY" ); + printMessage( "Core", "Initializing area aliases...", WHITE ); + if (ret) { + printStatus( "OK", LIGHT_GREEN ); + } + else { + printStatus( "NOT FOUND", YELLOW ); + } + } + + ret = ReadGameTimeTable(); + printMessage( "Core", "Reading game time table...", WHITE); + if (!ret) { + printStatus( "ERROR", LIGHT_RED ); + return GEM_ERROR; + } + printStatus( "OK", LIGHT_GREEN ); + + ret = ReadAuxItemTables(); + printMessage( "Core", "Reading item tables...", WHITE); + if (!ret) { + printStatus( "ERROR", LIGHT_RED ); + return GEM_ERROR; + } + printStatus( "OK", LIGHT_GREEN ); + + ret = ReadDamageTypeTable(); + printMessage( "Core", "Reading damage type table...", WHITE); + if (!ret) { + printStatus( "ERROR", LIGHT_RED ); + } else { + printStatus( "OK", LIGHT_GREEN ); + } + + ret = ReadModalStates(); + printMessage( "Core", "Reading modal states table...", WHITE); + if (!ret) { + printStatus( "ERROR", LIGHT_RED ); + return GEM_ERROR; + } else { + printStatus( "OK", LIGHT_GREEN ); + } + + printMessage( "Core", "Reading game script tables...", WHITE); + InitializeIEScript(); + printStatus( "OK", LIGHT_GREEN ); + + printMessage( "Core", "Core Initialization Complete!\n", WHITE ); + + return GEM_OK; +} + +bool Interface::IsAvailable(SClass_ID filetype) const +{ + return PluginMgr::Get()->IsAvailable( filetype ); +} + +WorldMap *Interface::GetWorldMap(const char *map) +{ + int index = worldmap->FindAndSetCurrentMap(map?map:game->CurrentArea); + return worldmap->GetWorldMap(index); +} + +ProjectileServer* Interface::GetProjectileServer() const +{ + return projserv; +} + +Video* Interface::GetVideoDriver() const +{ + return video.get(); +} + +Audio* Interface::GetAudioDrv(void) const { + return AudioDriver.get(); +} + +const char* Interface::TypeExt(SClass_ID type) const +{ + switch (type) { + case IE_2DA_CLASS_ID: + return ".2da"; + + case IE_ACM_CLASS_ID: + return ".acm"; + + case IE_ARE_CLASS_ID: + return ".are"; + + case IE_BAM_CLASS_ID: + return ".bam"; + + case IE_BCS_CLASS_ID: + return ".bcs"; + + case IE_BS_CLASS_ID: + return ".bs"; + + case IE_BIF_CLASS_ID: + return ".bif"; + + case IE_BIO_CLASS_ID: + if (HasFeature(GF_BIOGRAPHY_RES)) { + return ".res"; + } + return ".bio"; + + case IE_BMP_CLASS_ID: + return ".bmp"; + + case IE_PNG_CLASS_ID: + return ".png"; + + case IE_CHR_CLASS_ID: + return ".chr"; + + case IE_CHU_CLASS_ID: + return ".chu"; + + case IE_CRE_CLASS_ID: + return ".cre"; + + case IE_DLG_CLASS_ID: + return ".dlg"; + + case IE_EFF_CLASS_ID: + return ".eff"; + + case IE_GAM_CLASS_ID: + return ".gam"; + + case IE_IDS_CLASS_ID: + return ".ids"; + + case IE_INI_CLASS_ID: + return ".ini"; + + case IE_ITM_CLASS_ID: + return ".itm"; + + case IE_MOS_CLASS_ID: + return ".mos"; + + case IE_MUS_CLASS_ID: + return ".mus"; + + case IE_MVE_CLASS_ID: + return ".mve"; + + case IE_OGG_CLASS_ID: + return ".ogg"; + + case IE_PLT_CLASS_ID: + return ".plt"; + + case IE_PRO_CLASS_ID: + return ".pro"; + + case IE_SAV_CLASS_ID: + return ".sav"; + + case IE_SPL_CLASS_ID: + return ".spl"; + + case IE_SRC_CLASS_ID: + return ".src"; + + case IE_STO_CLASS_ID: + return ".sto"; + + case IE_TIS_CLASS_ID: + return ".tis"; + + case IE_TLK_CLASS_ID: + return ".tlk"; + + case IE_TOH_CLASS_ID: + return ".toh"; + + case IE_TOT_CLASS_ID: + return ".tot"; + + case IE_VAR_CLASS_ID: + return ".var"; + + case IE_VVC_CLASS_ID: + return ".vvc"; + + case IE_WAV_CLASS_ID: + return ".wav"; + + case IE_WED_CLASS_ID: + return ".wed"; + + case IE_WFX_CLASS_ID: + return ".wfx"; + + case IE_WMP_CLASS_ID: + return ".wmp"; + } + return NULL; +} + +void Interface::FreeString(char *&str) const +{ + if (str) { + strings->FreeString(str); + } + str = NULL; +} + +ieStrRef Interface::UpdateString(ieStrRef strref, const char *text) const +{ + return strings->UpdateString( strref, text ); +} + +char* Interface::GetString(ieStrRef strref, ieDword options) const +{ + ieDword flags = 0; + + if (!(options & IE_STR_STRREFOFF)) { + vars->Lookup( "Strref On", flags ); + } + return strings->GetString( strref, flags | options ); +} + +void Interface::SetFeature(int flag, int position) +{ + if (flag) { + GameFeatures[position>>5] |= 1<<(position&31); + } else { + GameFeatures[position>>5] &= ~(1<<(position&31) ); + } +/* + if (position>=32) { + position-=32; + if (flag) { + GameFeatures2 |= 1 << position; + } else { + GameFeatures2 &= ~( 1 << position ); + } + return; + } + if (flag) { + GameFeatures |= 1 << position; + } else { + GameFeatures &= ~( 1 << position ); + } +*/ +} + +ieDword Interface::HasFeature(int position) const +{ + return GameFeatures[position>>5] & (1<<(position&31)); +/* + if (position>=64) { + return GameFeatures3 & ( 1 << (position-64) ); + } + if (position>=32) { + return GameFeatures2 & ( 1 << (position-32) ); + } + return GameFeatures & ( 1 << position ); +*/ +} + +/** Search directories and load a config file */ +bool Interface::LoadConfig(void) +{ +#ifndef WIN32 + char path[_MAX_PATH]; + char name[_MAX_PATH]; + + // Find directory where user stores GemRB configurations (~/.gemrb). + // FIXME: Create it if it does not exist + // Use current dir if $HOME is not defined (or bomb out??) + + char* s = getenv( "HOME" ); + if (s) { + strcpy( UserDir, s ); + strcat( UserDir, "/."PACKAGE"/" ); + } else { + strcpy( UserDir, "./" ); + } + + // Find basename of this program. It does the same as basename (3), + // but that's probably missing on some archs + s = strrchr( argv[0], PathDelimiter ); + if (s) { + s++; + } else { + s = argv[0]; + } + + strcpy( name, s ); + //if (!name[0]) // FIXME: could this happen? + // strcpy (name, PACKAGE); // ugly hack + + // If we were called as $0 -c , load config from filename + if (argc > 2 && ! strcmp("-c", argv[1])) { + if (LoadConfig( argv[2] )) { + return true; + } else { + // Explicitly specified cfg file HAS to be present + return false; + } + } + + // FIXME: temporary hack, to be deleted?? + if (LoadConfig( "GemRB.cfg" )) { + return true; + } + + PathJoin( path, UserDir, name, NULL ); + strcat( path, ".cfg" ); + + if (LoadConfig( path )) { + return true; + } + +#ifdef SYSCONFDIR + PathJoin( path, SYSCONFDIR, name, NULL ); + strcat( path, ".cfg" ); + + if (LoadConfig( path )) { + return true; + } +#endif + + // Don't try with default binary name if we have tried it already + if (!strcmp( name, PACKAGE )) { + return false; + } + + PathJoin( path, UserDir, PACKAGE, NULL ); + strcat( path, ".cfg" ); + + if (LoadConfig( path )) { + return true; + } + +#ifdef SYSCONFDIR + PathJoin( path, SYSCONFDIR, PACKAGE, NULL ); + strcat( path, ".cfg" ); + + if (LoadConfig( path )) { + return true; + } +#endif + + return false; +#else // WIN32 + // If we were called as $0 -c , load config from filename + if (argc > 2 && ! strcmp("-c", argv[1])) { + return LoadConfig( argv[2] ); + // Explicitly specified cfg file HAS to be present + } + strcpy( UserDir, ".\\" ); + return LoadConfig( "GemRB.cfg" ); +#endif// WIN32 +} + +bool Interface::LoadConfig(const char* filename) +{ + FILE* config; + size_t i; + + printMessage("Config","Trying to open ", WHITE); + textcolor(LIGHT_WHITE); + printf("%s ", filename); + config = fopen( filename, "rb" ); + if (config == NULL) { + printStatus("NOT FOUND", LIGHT_RED); + return false; + } + char name[65], value[_MAX_PATH + 3]; + + //once GemRB own format is working well, this might be set to 0 + SaveAsOriginal = 1; + + while (!feof( config )) { + char rem; + + if (fread( &rem, 1, 1, config ) != 1) + break; + + if (rem == '#') { + //it should always return 0 + if (fscanf( config, "%*[^\r\n]%*[\r\n]" )!=0) + break; + continue; + } + fseek( config, -1, SEEK_CUR ); + memset(value,'\0',_MAX_PATH + 3); + //the * element is not counted + if (fscanf( config, "%64[^= ] = %[^\r\n]%*[\r\n]", name, value )!=2) + continue; + for (i=_MAX_PATH + 2; i > 0; i--) { + if (value[i] == '\0') continue; + if (value[i] == ' ') { + value[i] = '\0'; + } else { + break; + } + } + + if (false) { +#define CONFIG_INT(str, var) \ + } else if (stricmp(name, str) == 0) { \ + var ( atoi(value) ) + CONFIG_INT("Bpp", Bpp = ); + CONFIG_INT("CaseSensitive", CaseSensitive = ); + CONFIG_INT("DoubleClickDelay", evntmgr->SetDCDelay); + CONFIG_INT("DrawFPS", DrawFPS = ); + CONFIG_INT("EnableCheatKeys", EnableCheatKeys); + CONFIG_INT("EndianSwitch", DataStream::SetEndianSwitch); + CONFIG_INT("FogOfWar", FogOfWar = ); + CONFIG_INT("FullScreen", FullScreen = ); + CONFIG_INT("GUIEnhancements", GUIEnhancements = ); + CONFIG_INT("GameOnCD", GameOnCD = ); + CONFIG_INT("Height", Height = ); + CONFIG_INT("KeepCache", KeepCache = ); + CONFIG_INT("MultipleQuickSaves", GameControl::MultipleQuickSaves); + CONFIG_INT("RepeatKeyDelay", evntmgr->SetRKDelay); + CONFIG_INT("SaveAsOriginal", SaveAsOriginal = ); + CONFIG_INT("ScriptDebugMode", SetScriptDebugMode); + CONFIG_INT("SkipIntroVideos", SkipIntroVideos = ); + CONFIG_INT("TooltipDelay", TooltipDelay = ); + CONFIG_INT("Width", Width = ); +#undef CONFIG_INT +#define CONFIG_STRING(str, var) \ + } else if (stricmp(name, str) == 0) { \ + strncpy(var, value, sizeof(var)) + CONFIG_STRING("GameCharactersPath", GameCharactersPath); + CONFIG_STRING("GameDataPath", GameDataPath); + CONFIG_STRING("GameName", GameName); + CONFIG_STRING("GameOverridePath", GameOverridePath); + CONFIG_STRING("GamePortraitsPath", GamePortraitsPath); + CONFIG_STRING("GameScriptsPath", GameScriptsPath); + CONFIG_STRING("GameSoundsPath", GameSoundsPath); + CONFIG_STRING("GameType", GameType); +#undef CONFIG_STRING +#define CONFIG_STRING(str, var) \ + } else if (stricmp(name, str) == 0) { \ + var = value + CONFIG_STRING("AudioDriver", AudioDriverName); + CONFIG_STRING("VideoDriver", VideoDriverName); +#undef CONFIG_STRING +#define CONFIG_PATH(str, var) \ + } else if (stricmp(name, str) == 0) { \ + strncpy(var, value, sizeof(var)); + CONFIG_PATH("CachePath", CachePath); + CONFIG_PATH("GUIScriptsPath", GUIScriptsPath); + CONFIG_PATH("GamePath", GamePath); + CONFIG_PATH("GemRBOverridePath", GemRBOverridePath); + CONFIG_PATH("GemRBPath", GemRBPath); + CONFIG_PATH("PluginsPath", PluginsPath); + CONFIG_PATH("SavePath", SavePath); +#undef CONFIG_PATH + } else if (stricmp( name, "ModPath" ) == 0) { + for (char *path = strtok(value,SPathListSeparator); + path; + path = strtok(NULL,SPathListSeparator)) { + ModPath.push_back(path); + } + } else if (stricmp( name, "SkipPlugin" ) == 0) { + plugin_flags->SetAt( value, PLF_SKIP ); + } else if (stricmp( name, "DelayPlugin" ) == 0) { + plugin_flags->SetAt( value, PLF_DELAY ); + } else { + for(i=0;iGetResource( "gemrb", IE_INI_CLASS_ID ); + if (! inifile) { + printStatus( "ERROR", LIGHT_RED ); + return false; + } + + printMessage( "Core", "Loading game type-specific GemRB setup...\n", WHITE ); + printf( "%s",inifile->originalfile); + + if (!IsAvailable( IE_INI_CLASS_ID )) { + printStatus( "ERROR", LIGHT_RED ); + printf( "[Core]: No INI Importer Available.\n" ); + return false; + } + PluginHolder ini(IE_INI_CLASS_ID); + ini->Open( inifile, true ); //autofree + + printStatus( "OK", LIGHT_GREEN ); + + const char *s; + + // Resrefs are already initialized in Interface::Interface() + s = ini->GetKeyAsString( "resources", "CursorBAM", NULL ); + if (s) + strnlwrcpy( CursorBam, s, 8 ); //console cursor + + s = ini->GetKeyAsString( "resources", "ScrollCursorBAM", NULL ); + if (s) + strnlwrcpy( ScrollCursorBam, s, 8 ); + + s = ini->GetKeyAsString( "resources", "ButtonFont", NULL ); + if (s) + strnlwrcpy( ButtonFont, s, 8 ); + + s = ini->GetKeyAsString( "resources", "TooltipFont", NULL ); + if (s) + strnlwrcpy( TooltipFont, s, 8 ); + + s = ini->GetKeyAsString( "resources", "MovieFont", NULL ); + if (s) + strnlwrcpy( MovieFont, s, 8 ); + + s = ini->GetKeyAsString( "resources", "TooltipBack", NULL ); + if (s) + strnlwrcpy( TooltipBackResRef, s, 8 ); + + s = ini->GetKeyAsString( "resources", "TooltipColor", NULL ); + if (s) { + if (s[0] == '#') { + unsigned long c = strtoul (s + 1, NULL, 16); + // FIXME: check errno + TooltipColor.r = (unsigned char) (c >> 24); + TooltipColor.g = (unsigned char) (c >> 16); + TooltipColor.b = (unsigned char) (c >> 8); + TooltipColor.a = (unsigned char) (c); + } + } + + //which stat determines the fist weapon (defaults to class) + Actor::SetFistStat(ini->GetKeyAsInt( "resources", "FistStat", IE_CLASS)); + + TooltipMargin = ini->GetKeyAsInt( "resources", "TooltipMargin", TooltipMargin ); + + // The format of GroundCircle can be: + // GroundCircleBAM1 = wmpickl/3 + // to denote that the bitmap should be scaled down 3x + for (int size = 0; size < MAX_CIRCLE_SIZE; size++) { + char name[30]; + sprintf( name, "GroundCircleBAM%d", size+1 ); + s = ini->GetKeyAsString( "resources", name, NULL ); + if (s) { + const char *pos = strchr( s, '/' ); + if (pos) { + GroundCircleScale[size] = atoi( pos+1 ); + strncpy( GroundCircleBam[size], s, pos - s ); + GroundCircleBam[size][pos - s] = '\0'; + } else { + strcpy( GroundCircleBam[size], s ); + } + } + } + + s = ini->GetKeyAsString( "resources", "NoteString", NULL ); + TextArea::SetNoteString(s); + + s = ini->GetKeyAsString( "resources", "INIConfig", NULL ); + if (s) + strcpy( INIConfig, s ); + + s = ini->GetKeyAsString( "resources", "Palette16", NULL ); + if (s) + strcpy( Palette16, s ); + + s = ini->GetKeyAsString( "resources", "Palette32", NULL ); + if (s) + strcpy( Palette32, s ); + + s = ini->GetKeyAsString( "resources", "Palette256", NULL ); + if (s) + strcpy( Palette256, s ); + + unsigned int i = (unsigned int) ini->GetKeyAsInt ("charset", "CharCount", 0); + if (i>99) i=99; + while(i--) { + char key[10]; + snprintf(key,9,"Letter%d", i+1); + s = ini->GetKeyAsString( "charset", key, NULL ); + if (s) { + const char *s2 = strchr(s,','); + if (s2) { + upperlower(atoi(s), atoi(s2+1) ); + } + } + } + + MaximumAbility = ini->GetKeyAsInt ("resources", "MaximumAbility", 25 ); + + RedrawTile = ini->GetKeyAsInt( "resources", "RedrawTile", 0 )!=0; + + for (i=0;iGetKeyAsInt( "resources", game_flags[i], 0 ), i ); + //printMessage("Option", "", GREEN); + //printf("%s = %s\n", game_flags[i], HasFeature(i)?"yes":"no"); + } + + ForceStereo = ini->GetKeyAsInt( "resources", "ForceStereo", 0 ); + + return true; +} + +Palette* Interface::CreatePalette(const Color &color, const Color &back) +{ + Palette* pal = new Palette(); + pal->col[0].r = 0; + pal->col[0].g = 0xff; + pal->col[0].b = 0; + pal->col[0].a = 0; + for (int i = 1; i < 256; i++) { + pal->col[i].r = back.r + + ( unsigned char ) ( ( ( color.r - back.r ) * ( i ) ) / 255 ); + pal->col[i].g = back.g + + ( unsigned char ) ( ( ( color.g - back.g ) * ( i ) ) / 255 ); + pal->col[i].b = back.b + + ( unsigned char ) ( ( ( color.b - back.b ) * ( i ) ) / 255 ); + pal->col[i].a = back.a + + ( unsigned char ) ( ( ( color.a - back.a ) * ( i ) ) / 255 ); + } + return pal; +} + +/** No descriptions */ +Color* Interface::GetPalette(unsigned index, int colors, Color *pal) const +{ + Image *img; + if (colors == 32) { + img = pal32; + } else if (colors <= 32) { + img = pal16; + } else if (colors == 256) { + img = pal256; + } else { + return pal; + } + if (index >= img->GetHeight()) { + index = 0; + } + for (int i = 0; i < colors; i++) { + pal[i] = img->GetPixel(i, index); + } + return pal; +} +/** Returns a preloaded Font */ +Font* Interface::GetFont(const char *ResRef) const +{ + for (unsigned int i = 0; i < fonts.size(); i++) { + if (strnicmp( fonts[i]->ResRef, ResRef, 8 ) == 0) { + return fonts[i]; + } + } + return NULL; +} + +Font* Interface::GetFont(unsigned int index) const +{ + if (index >= fonts.size()) { + return NULL; + } + return fonts[index]; +} + +Font* Interface::GetButtonFont() const +{ + return GetFont( ButtonFont ); +} + +/** Returns the Event Manager */ +EventMgr* Interface::GetEventMgr() const +{ + return evntmgr; +} + +/** Returns the Window Manager */ +WindowMgr* Interface::GetWindowMgr() const +{ + return windowmgr.get(); +} + +/** Get GUI Script Manager */ +ScriptEngine* Interface::GetGUIScriptEngine() const +{ + return guiscript.get(); +} + +static EffectRef fx_summon_disable_ref={"AvatarRemovalModifier",NULL,-1}; + +//NOTE: if there were more summoned creatures, it will return only the last +Actor *Interface::SummonCreature(const ieResRef resource, const ieResRef vvcres, Scriptable *Owner, Actor *target, const Point &position, int eamod, int level, Effect *fx, bool sexmod) +{ + //maximum number of monsters summoned + int cnt=10; + Actor * ab = NULL; + + //TODO: + //decrease the number of summoned creatures with the number of already summoned creatures here + //the summoned creatures have a special IE_SPECIFIC + + while(cnt--) { + ab = gamedata->GetCreature(resource); + if (!ab) { + return NULL; + } + + if (Owner && Owner->Type==ST_ACTOR) { + ab->LastSummoner = Owner->GetGlobalID(); + } + //Always use Base stats for the recently summoned creature + + int enemyally; + + if (eamod==EAM_SOURCEALLY || eamod==EAM_SOURCEENEMY) { + if (Owner && Owner->Type==ST_ACTOR) { + enemyally = ((Actor *) Owner)->GetStat(IE_EA)>EA_GOODCUTOFF; + } else { + enemyally = true; + } + } else { + if (target) { + enemyally = target->GetBase(IE_EA)>EA_GOODCUTOFF; + } else { + enemyally = true; + } + } + + switch (eamod) { + case EAM_SOURCEALLY: + case EAM_ALLY: + if (enemyally) { + ab->SetBase(IE_EA, EA_ENEMY); //is this the summoned EA? + } else { + ab->SetBase(IE_EA, EA_CONTROLLED); //is this the summoned EA? + } + break; + case EAM_SOURCEENEMY: + case EAM_ENEMY: + if (enemyally) { + ab->SetBase(IE_EA, EA_CONTROLLED); //is this the summoned EA? + } else { + ab->SetBase(IE_EA, EA_ENEMY); //is this the summoned EA? + } + break; + case EAM_NEUTRAL: + ab->SetBase(IE_EA, EA_NEUTRAL); + break; + default: + break; + } + + // mark the summon, but only if they don't have a special sex already + if (sexmod && ab->BaseStats[IE_SEX] < SEX_EXTRA) { + ab->SetBase(IE_SEX, SEX_SUMMON); + } + + Map *map; + if (target) { + map = target->GetCurrentArea(); + } else { + map = Owner->GetCurrentArea(); + } + map->AddActor(ab); + ab->SetPosition(position, true, 0); + ab->RefreshEffects(NULL); + + if (vvcres[0]) { + ScriptedAnimation* vvc = gamedata->GetScriptedAnimation(vvcres, false); + if (vvc) { + //This is the final position of the summoned creature + //not the original target point + vvc->XPos=ab->Pos.x; + vvc->YPos=ab->Pos.y; + //force vvc to play only once + vvc->PlayOnce(); + map->AddVVCell( vvc ); + + //set up the summon disable effect + Effect *newfx = EffectQueue::CreateEffect(fx_summon_disable_ref, 0, 1, FX_DURATION_ABSOLUTE); + if (newfx) { + newfx->Duration = vvc->GetSequenceDuration(AI_UPDATE_TIME)*9/10 + core->GetGame()->GameTime; + ApplyEffect(newfx, ab, ab); + } + } + } + + //remove the xp value of friendly summons + if (ab->BaseStats[IE_EA]SetBase(IE_XPVALUE, 0); + } + if (fx) { + ApplyEffect(fx, ab, Owner); + } + + //this check should happen after the fact + level -= ab->GetBase(IE_XP); + if(level<0 || ab->GetBase(IE_XP) == 0) { + break; + } + + } + return ab; +} + +void Interface::RedrawControls(const char *varname, unsigned int value) +{ + for (unsigned int i = 0; i < windows.size(); i++) { + Window *win = windows[i]; + if (win != NULL && win->Visible!=WINDOW_INVALID) { + win->RedrawControls(varname, value); + } + } +} + +void Interface::RedrawAll() +{ + for (unsigned int i = 0; i < windows.size(); i++) { + Window *win = windows[i]; + if (win != NULL && win->Visible!=WINDOW_INVALID) { + win->Invalidate(); + } + } +} + +/** Loads a WindowPack (CHUI file) in the Window Manager */ +bool Interface::LoadWindowPack(const char* name) +{ + DataStream* stream = gamedata->GetResource( name, IE_CHU_CLASS_ID ); + if (stream == NULL) { + printMessage( "Interface", "Error: Cannot find ", LIGHT_RED ); + printf( "%s.chu\n", name ); + return false; + } + if (!GetWindowMgr()->Open( stream, true )) { + printMessage( "Interface", "Error: Cannot Load ", LIGHT_RED ); + printf( "%s.chu\n", name ); + return false; + } + + strncpy( WindowPack, name, sizeof( WindowPack ) ); + WindowPack[sizeof( WindowPack ) - 1] = '\0'; + + return true; +} + +/** Loads a Window in the Window Manager */ +int Interface::LoadWindow(unsigned short WindowID) +{ + unsigned int i; + + for (i = 0; i < windows.size(); i++) { + Window *win = windows[i]; + if (win == NULL) + continue; + if (win->Visible==WINDOW_INVALID) { + continue; + } + if (win->WindowID == WindowID && + !strnicmp( WindowPack, win->WindowPack, sizeof(WindowPack) )) { + SetOnTop( i ); + win->Invalidate(); + return i; + } + } + Window* win = windowmgr->GetWindow( WindowID ); + if (win == NULL) { + return -1; + } + memcpy( win->WindowPack, WindowPack, sizeof(WindowPack) ); + + int slot = -1; + for (i = 0; i < windows.size(); i++) { + if (windows[i] == NULL) { + slot = i; + break; + } + } + if (slot == -1) { + windows.push_back( win ); + slot = ( int ) windows.size() - 1; + } else { + windows[slot] = win; + } + win->Invalidate(); + return slot; +} +// FIXME: it's a clone of LoadWindow +/** Creates a Window in the Window Manager */ +int Interface::CreateWindow(unsigned short WindowID, int XPos, int YPos, unsigned int Width, unsigned int Height, char* Background) +{ + unsigned int i; + + for (i = 0; i < windows.size(); i++) { + if (windows[i] == NULL) + continue; + if (windows[i]->WindowID == WindowID && !stricmp( WindowPack, + windows[i]->WindowPack )) { + SetOnTop( i ); + windows[i]->Invalidate(); + return i; + } + } + + Window* win = new Window( WindowID, (ieWord) XPos, (ieWord) YPos, (ieWord) Width, (ieWord) Height ); + if (Background[0]) { + ResourceHolder mos(Background); + if (mos != NULL) { + win->SetBackGround( mos->GetSprite2D(), true ); + } else { + printf( "[Core]: Cannot Load BackGround, skipping\n" ); + } + } + + strcpy( win->WindowPack, WindowPack ); + + int slot = -1; + for (i = 0; i < windows.size(); i++) { + if (windows[i] == NULL) { + slot = i; + break; + } + } + if (slot == -1) { + windows.push_back( win ); + slot = ( int ) windows.size() - 1; + } else { + windows[slot] = win; + } + win->Invalidate(); + return slot; +} + +/** Sets a Window on the Top */ +void Interface::SetOnTop(int Index) +{ + std::vector::iterator t; + for(t = topwin.begin(); t != topwin.end(); ++t) { + if((*t) == Index) { + topwin.erase(t); + break; + } + } + if(topwin.size() != 0) + topwin.insert(topwin.begin(), Index); + else + topwin.push_back(Index); +} +/** Add a window to the Window List */ +void Interface::AddWindow(Window * win) +{ + int slot = -1; + for(unsigned int i = 0; i < windows.size(); i++) { + Window *w = windows[i]; + + if(w==NULL) { + slot = i; + break; + } + } + if(slot == -1) { + windows.push_back(win); + slot=(int)windows.size()-1; + } + else + windows[slot] = win; + win->Invalidate(); +} + +/** Get a Control on a Window */ +int Interface::GetControl(unsigned short WindowIndex, unsigned long ControlID) const +{ + if (WindowIndex >= windows.size()) { + return -1; + } + Window* win = windows[WindowIndex]; + if (win == NULL) { + return -1; + } + int i = 0; + while (true) { + Control* ctrl = win->GetControl( (unsigned short) i ); + if (ctrl == NULL) + return -1; + if (ctrl->ControlID == ControlID) + return i; + i++; + } +} +/** Adjust the Scrolling factor of a control (worldmap atm) */ +int Interface::AdjustScrolling(unsigned short WindowIndex, + unsigned short ControlIndex, short x, short y) +{ + if (WindowIndex >= windows.size()) { + return -1; + } + Window* win = windows[WindowIndex]; + if (win == NULL) { + return -1; + } + Control* ctrl = win->GetControl( ControlIndex ); + if (ctrl == NULL) { + return -1; + } + switch(ctrl->ControlType) { + case IE_GUI_WORLDMAP: + ((WorldMapControl *) ctrl)->AdjustScrolling(x,y); + break; + default: //doesn't work for these + return -1; + } + return 0; +} + +/** Set the Text of a Control */ +int Interface::SetText(unsigned short WindowIndex, + unsigned short ControlIndex, const char* string) +{ + if (WindowIndex >= windows.size()) { + return -1; + } + Window* win = windows[WindowIndex]; + if (win == NULL) { + return -1; + } + Control* ctrl = win->GetControl( ControlIndex ); + if (ctrl == NULL) { + return -1; + } + return ctrl->SetText( string ); +} +/** Set the Tooltip text of a Control */ +int Interface::SetTooltip(unsigned short WindowIndex, + unsigned short ControlIndex, const char* string) +{ + if (WindowIndex >= windows.size()) { + return -1; + } + Window* win = windows[WindowIndex]; + if (win == NULL) { + return -1; + } + Control* ctrl = win->GetControl( ControlIndex ); + if (ctrl == NULL) { + return -1; + } + return ctrl->SetTooltip( string ); +} + +void Interface::DisplayTooltip(int x, int y, Control *ctrl) +{ + if (tooltip_ctrl && tooltip_ctrl == ctrl && tooltip_x == x && tooltip_y == y) + return; + tooltip_x = x; + tooltip_y = y; + tooltip_currtextw = 0; + tooltip_ctrl = ctrl; +} + +int Interface::GetVisible(unsigned short WindowIndex) const +{ + if (WindowIndex >= windows.size()) { + return -1; + } + Window* win = windows[WindowIndex]; + if (win == NULL) { + return -1; + } + return win->Visible; +} +/** Set a Window Visible Flag */ +int Interface::SetVisible(unsigned short WindowIndex, int visible) +{ + if (WindowIndex >= windows.size()) { + return -1; + } + Window* win = windows[WindowIndex]; + if (win == NULL) { + return -1; + } + if (visible!=WINDOW_FRONT) { + win->Visible = (char) visible; + } + switch (visible) { + case WINDOW_GRAYED: + win->Invalidate(); + //here is a fallthrough + case WINDOW_INVISIBLE: + //hiding the viewport if the gamecontrol window was made invisible + if (win->WindowID==65535) { + video->SetViewport( 0,0,0,0 ); + } + evntmgr->DelWindow( win ); + break; + + case WINDOW_VISIBLE: + if (win->WindowID==65535) { + video->SetViewport( win->XPos, win->YPos, win->Width, win->Height); + } + //here is a fallthrough + case WINDOW_FRONT: + if (win->Visible==WINDOW_VISIBLE) { + evntmgr->AddWindow( win ); + } + win->Invalidate(); + SetOnTop( WindowIndex ); + break; + } + return 0; +} + + +/** Set the Status of a Control in a Window */ +int Interface::SetControlStatus(unsigned short WindowIndex, + unsigned short ControlIndex, unsigned long Status) +{ + //don't set the status of an already invalidated window + Window* win = GetWindow(WindowIndex); + if (win == NULL) { + return -1; + } + Control* ctrl = win->GetControl( ControlIndex ); + if (ctrl == NULL) { + return -1; + } + if (Status&IE_GUI_CONTROL_FOCUSED) { + evntmgr->SetFocused( win, ctrl); + } + if (ctrl->ControlType != ((Status >> 24) & 0xff) ) { + return -2; + } + switch (ctrl->ControlType) { + case IE_GUI_BUTTON: + //Button + { + Button* btn = ( Button* ) ctrl; + btn->SetState( ( unsigned char ) ( Status & 0x7f ) ); + } + break; + default: + ctrl->Value = Status & 0x7f; + break; + } + return 0; +} + +/** Show a Window in Modal Mode */ +int Interface::ShowModal(unsigned short WindowIndex, int Shadow) +{ + if (WindowIndex >= windows.size()) { + printMessage( "Core", "Window not found", LIGHT_RED ); + return -1; + } + Window* win = windows[WindowIndex]; + if (win == NULL) { + printMessage( "Core", "Window already freed", LIGHT_RED ); + return -1; + } + win->Visible = WINDOW_FRONT; + //don't destroy the other window handlers + //evntmgr->Clear(); + SetOnTop( WindowIndex ); + evntmgr->AddWindow( win ); + evntmgr->SetFocused( win, NULL ); + + ModalWindow = NULL; + DrawWindows(); + win->Invalidate(); + + Color gray = { + 0, 0, 0, 128 + }; + Color black = { + 0, 0, 0, 255 + }; + + Region r( 0, 0, Width, Height ); + + if (Shadow == MODAL_SHADOW_GRAY) { + video->DrawRect( r, gray ); + } else if (Shadow == MODAL_SHADOW_BLACK) { + video->DrawRect( r, black ); + } + + ModalWindow = win; + return 0; +} + +bool Interface::IsFreezed() +{ + return !update_scripts; +} + +void Interface::GameLoop(void) +{ + update_scripts = false; + GameControl *gc = GetGameControl(); + if (gc) { + update_scripts = !(gc->GetDialogueFlags() & DF_FREEZE_SCRIPTS); + } + + GSUpdate(update_scripts); + + //i'm not sure if this should be here + + //in multi player (if we ever get to it), only the server must call this + if (update_scripts) { + if ( game->selected.size() > 0 ) { + gc->ChangeMap(GetFirstSelectedPC(true), false); + } + // the game object will run the area scripts as well + game->UpdateScripts(); + } +} + +/** handles hardcoded gui behaviour */ +void Interface::HandleGUIBehaviour(void) +{ + GameControl *gc = GetGameControl(); + if (gc) { + //this variable is used all over in the following hacks + int flg = gc->GetDialogueFlags(); + + //the following part is a series of hardcoded gui behaviour + + //initiating dialog + if (flg & DF_IN_DIALOG) { + // -3 noaction + // -2 close + // -1 open + // choose option + ieDword var = (ieDword) -3; + vars->Lookup("DialogChoose", var); + if ((int) var == -2) { + gc->dialoghandler->EndDialog(); + } else if ( (int)var !=-3) { + gc->dialoghandler->DialogChoose(var); + if (!(gc->GetDialogueFlags() & (DF_OPENCONTINUEWINDOW | DF_OPENENDWINDOW))) + guiscript->RunFunction( "GUIWORLD", "NextDialogState" ); + + // the last node of a dialog can have a new-dialog action! don't interfere in that case + ieDword newvar = 0; vars->Lookup("DialogChoose", newvar); + if (var == (ieDword) -1 || newvar != (ieDword) -1) { + vars->SetAt("DialogChoose", (ieDword) -3); + } + } + if (flg & DF_OPENCONTINUEWINDOW) { + guiscript->RunFunction( "GUIWORLD", "OpenContinueMessageWindow" ); + gc->SetDialogueFlags(DF_OPENCONTINUEWINDOW|DF_OPENENDWINDOW, BM_NAND); + } else if (flg & DF_OPENENDWINDOW) { + guiscript->RunFunction( "GUIWORLD", "OpenEndMessageWindow" ); + gc->SetDialogueFlags(DF_OPENCONTINUEWINDOW|DF_OPENENDWINDOW, BM_NAND); + } + } + + //handling container + if (CurrentContainer && UseContainer) { + if (!(flg & DF_IN_CONTAINER) ) { + gc->SetDialogueFlags(DF_IN_CONTAINER, BM_OR); + guiscript->RunFunction( "CommonWindow", "OpenContainerWindow" ); + } + } else { + if (flg & DF_IN_CONTAINER) { + gc->SetDialogueFlags(DF_IN_CONTAINER, BM_NAND); + guiscript->RunFunction( "CommonWindow", "CloseContainerWindow" ); + } + } + //end of gui hacks + } +} + +void Interface::DrawWindows(void) +{ + //here comes the REAL drawing of windows + if (ModalWindow) { + ModalWindow->DrawWindow(); + return; + } + size_t i = topwin.size(); + while(i--) { + unsigned int t = topwin[i]; + + if ( t >=windows.size() ) + continue; + + //visible ==1 or 2 will be drawn + Window* win = windows[t]; + if (win != NULL) { + if (win->Visible == WINDOW_INVALID) { + topwin.erase(topwin.begin()+i); + evntmgr->DelWindow( win ); + delete win; + windows[t]=NULL; + } else if (win->Visible) { + win->DrawWindow(); + } + } + } +} + +void Interface::DrawTooltip () +{ + if (! tooltip_ctrl || !tooltip_ctrl->Tooltip) + return; + + Font* fnt = GetFont( TooltipFont ); + char *tooltip_text = tooltip_ctrl->Tooltip; + + int w1 = 0; + int w2 = 0; + int strw = fnt->CalcStringWidth( tooltip_text ) + 8; + int w = strw; + int h = fnt->maxHeight; + + if (TooltipBack) { + // animate BG tooltips + // TODO: make tooltip animation an option instead + // of following hard-coded check! + if (TooltipMargin == 5) { + // TODO: make speed an option + int tooltip_anim_speed = 15; + if (tooltip_currtextw < strw) { + tooltip_currtextw += tooltip_anim_speed; + } + if (tooltip_currtextw > strw) { + tooltip_currtextw = strw; + } + w = tooltip_currtextw; + } + + h = TooltipBack[0]->Height; + w1 = TooltipBack[1]->Width; + w2 = TooltipBack[2]->Width; + w += TooltipMargin*2; + strw += TooltipMargin*2; + //multiline in case of too much text + if (w>TooltipBack[0]->Width) + strw=w=TooltipBack[0]->Width; + else if (strw>TooltipBack[0]->Width) + strw=TooltipBack[0]->Width; + } + + int strx = tooltip_x - strw / 2; + int y = tooltip_y - h / 2; + // Ensure placement within the screen + if (strx < 0) strx = 0; + else if (strx + strw + w1 + w2 > Width) + strx = Width - strw - w1 - w2; + if (y < 0) y = 0; + else if (y + h > Height) + y = Height - h; + + int x = strx + ((strw - w) / 2); + + // FIXME: take back[0] from center, not from left end + Region r2 = Region( x, y, w, h ); + if (TooltipBack) { + video->BlitSprite( TooltipBack[0], x + TooltipMargin, y, true, &r2 ); + video->BlitSprite( TooltipBack[1], x, y, true ); + video->BlitSprite( TooltipBack[2], x + w, y, true ); + } + + if (TooltipBack) { + r2.x+=TooltipMargin; + strx+=TooltipMargin; + } + Region textr = Region( strx, y, strw, h ); + fnt->Print( r2, textr, (ieByte *) tooltip_text, NULL, + IE_FONT_ALIGN_CENTER | IE_FONT_ALIGN_MIDDLE, true ); +} + +//interface for higher level functions, if the window was +//marked for deletion it is not returned +Window* Interface::GetWindow(unsigned short WindowIndex) const +{ + if (WindowIndex < windows.size()) { + Window *win = windows[WindowIndex]; + if (win && (win->Visible!=WINDOW_INVALID) ) { + return win; + } + } + return NULL; +} + +// this function will determine if wnd is a valid window pointer +// by checking if its WindowID is the same as the reference +bool Interface::IsValidWindow(unsigned short WindowID, Window *wnd) const +{ + size_t WindowIndex = windows.size(); + while (WindowIndex--) { + if (windows[WindowIndex] == wnd) { + return wnd->WindowID == WindowID; + } + } + return false; +} + +//this function won't delete the window, just mark it for deletion +//it will be deleted in the next DrawWindows cycle +//regardless, the window deleted is inaccessible for gui scripts and +//other high level functions from now +int Interface::DelWindow(unsigned short WindowIndex) +{ + if (WindowIndex >= windows.size()) { + return -1; + } + Window* win = windows[WindowIndex]; + if ((win == NULL) || (win->Visible==WINDOW_INVALID) ) { + printMessage( "Core", "Window deleted again", LIGHT_RED ); + return -1; + } + if (win == ModalWindow) { + ModalWindow = NULL; + RedrawAll(); //marking windows for redraw + } + evntmgr->DelWindow( win ); + win->release(); + //re-capturing new (old) modal window if any + size_t tw = topwin.size(); + for(size_t i=0;iVisible==WINDOW_FRONT) { + ModalWindow = tmp; + break; + } + } + return 0; +} + +void Interface::DelAllWindows() +{ + vars->SetAt("MessageWindow", (ieDword) ~0); + vars->SetAt("OptionsWindow", (ieDword) ~0); + vars->SetAt("PortraitWindow", (ieDword) ~0); + vars->SetAt("ActionsWindow", (ieDword) ~0); + vars->SetAt("TopWindow", (ieDword) ~0); + vars->SetAt("OtherWindow", (ieDword) ~0); + vars->SetAt("FloatWindow", (ieDword) ~0); + for(unsigned int WindowIndex=0; WindowIndexClear(); + ModalWindow = NULL; +} + +/** Popup the Console */ +void Interface::PopupConsole() +{ + ConsolePopped = !ConsolePopped; + RedrawAll(); + console->Changed = true; +} + +/** Draws the Console */ +void Interface::DrawConsole() +{ + console->Draw( 0, 0 ); +} + +/** Get the Sound Manager */ +SaveGameIterator* Interface::GetSaveGameIterator() const +{ + return sgiterator; +} +/** Sends a termination signal to the Video Driver */ +bool Interface::Quit(void) +{ + return video->Quit(); +} +/** Returns the variables dictionary */ +Variables* Interface::GetDictionary() const +{ + return vars; +} +/** Returns the token dictionary */ +Variables* Interface::GetTokenDictionary() const +{ + return tokens; +} +/** Get the Music Manager */ +MusicMgr* Interface::GetMusicMgr() const +{ + return music.get(); +} +/** Loads an IDS Table, returns -1 on error or the Symbol Table Index on success */ +int Interface::LoadSymbol(const char* ResRef) +{ + int ind = GetSymbolIndex( ResRef ); + if (ind != -1) { + return ind; + } + DataStream* str = gamedata->GetResource( ResRef, IE_IDS_CLASS_ID ); + if (!str) { + return -1; + } + PluginHolder sm(IE_IDS_CLASS_ID); + if (!sm) { + delete str; + return -1; + } + if (!sm->Open( str, true )) { + return -1; + } + Symbol s; + strncpy( s.ResRef, ResRef, 8 ); + s.sm = sm; + ind = -1; + for (size_t i = 0; i < symbols.size(); i++) { + if (!symbols[i].sm) { + ind = ( int ) i; + break; + } + } + if (ind != -1) { + symbols[ind] = s; + return ind; + } + symbols.push_back( s ); + return ( int ) symbols.size() - 1; +} +/** Gets the index of a loaded Symbol Table, returns -1 on error */ +int Interface::GetSymbolIndex(const char* ResRef) const +{ + for (size_t i = 0; i < symbols.size(); i++) { + if (!symbols[i].sm) + continue; + if (strnicmp( symbols[i].ResRef, ResRef, 8 ) == 0) + return ( int ) i; + } + return -1; +} +/** Gets a Loaded Symbol Table by its index, returns NULL on error */ +Holder Interface::GetSymbol(unsigned int index) const +{ + if (index >= symbols.size()) { + return Holder(); + } + if (!symbols[index].sm) { + return Holder(); + } + return symbols[index].sm; +} +/** Frees a Loaded Symbol Table, returns false on error, true on success */ +bool Interface::DelSymbol(unsigned int index) +{ + if (index >= symbols.size()) { + return false; + } + if (!symbols[index].sm) { + return false; + } + symbols[index].sm.release(); + return true; +} +/** Plays a Movie */ +int Interface::PlayMovie(const char* ResRef) +{ + ResourceHolder mp(ResRef); + if (!mp) { + return -1; + } + + ieDword subtitles = 0; + Font *SubtitleFont = NULL; + Palette *palette = NULL; + ieDword *frames = NULL; + ieDword *strrefs = NULL; + int cnt = 0; + int offset = 0; + + //one of these two should exist (they both mean the same thing) + vars->Lookup("Display Movie Subtitles", subtitles); + if (subtitles) { + //HoW flag + cnt=-3; + offset = 3; + } else { + //ToB flag + vars->Lookup("Display Subtitles", subtitles); + } + AutoTable sttable; + if (subtitles && sttable.load(ResRef)) { + cnt += sttable->GetRowCount(); + if (cnt>0) { + frames = (ieDword *) malloc(cnt * sizeof(ieDword) ); + strrefs = (ieDword *) malloc(cnt * sizeof(ieDword) ); + } else { + cnt = 0; + } + if (frames && strrefs) { + for (int i=0;iQueryField(i+offset, 0) ); + strrefs[i] = atoi (sttable->QueryField(i+offset, 1) ); + } + } + int r = atoi(sttable->QueryField("red", "frame")); + int g = atoi(sttable->QueryField("green", "frame")); + int b = atoi(sttable->QueryField("blue", "frame")); + SubtitleFont = GetFont (MovieFont); //will change + if (r || g || b) { + if (SubtitleFont) { + Color fore = {(unsigned char) r,(unsigned char) g,(unsigned char) b, 0x00}; + Color back = {0x00, 0x00, 0x00, 0x00}; + palette = CreatePalette( fore, back ); + } + } + } + + //shutting down music and ambients before movie + if (music) + music->HardEnd(); + AmbientMgr *ambim = AudioDriver->GetAmbientMgr(); + if (ambim) ambim->deactivate(); + video->SetMovieFont(SubtitleFont, palette ); + mp->CallBackAtFrames(cnt, frames, strrefs); + mp->Play(); + gamedata->FreePalette( palette ); + if (frames) + free(frames); + if (strrefs) + free(strrefs); + //restarting music + if (music) + music->Start(); + if (ambim) ambim->activate(); + //this will fix redraw all windows as they looked like + //before the movie + RedrawAll(); + + //Setting the movie name to 1 + vars->SetAt( ResRef, 1 ); + return 0; +} + +int Interface::Roll(int dice, int size, int add) const +{ + if (dice < 1) { + return add; + } + if (size < 1) { + return add; + } + if (dice > 100) { + return add + dice * size / 2; + } + for (int i = 0; i < dice; i++) { + add += rand() % size + 1; + } + return add; +} + +static char bmp_suffix[6]="M.BMP"; +static char png_suffix[6]="M.PNG"; + +int Interface::GetPortraits(TextArea* ta, bool smallorlarge) +{ + int count = 0; + char Path[_MAX_PATH]; + + if (smallorlarge) { + bmp_suffix[0]='S'; + png_suffix[0]='S'; + } else { + bmp_suffix[0]='M'; + png_suffix[0]='M'; + } + PathJoin( Path, GamePath, GamePortraitsPath, NULL ); + DirectoryIterator dir(Path); + if (!dir) { + return -1; + } + printf( "Looking in %s\n", Path ); + do { + char *name = dir.GetName(); + if (name[0] == '.') + continue; + if (dir.IsDirectory()) + continue; + strupr(name); + char *pos = strstr(name,bmp_suffix); + if (!pos && IsAvailable(IE_PNG_CLASS_ID) ) { + pos = strstr(name,png_suffix); + } + if (!pos) continue; + pos[1]=0; + count++; + ta->AppendText( name, -1 ); + } while (++dir); + return count; +} + +int Interface::GetCharSounds(TextArea* ta) +{ + bool hasfolders; + int count = 0; + char Path[_MAX_PATH]; + + PathJoin( Path, GamePath, GameSoundsPath, NULL ); + hasfolders = ( HasFeature( GF_SOUNDFOLDERS ) != 0 ); + DirectoryIterator dir(Path); + if (!dir) { + return -1; + } + printf( "Looking in %s\n", Path ); + do { + char *name = dir.GetName(); + if (name[0] == '.') + continue; + if (hasfolders == !dir.IsDirectory()) + continue; + if (!hasfolders) { + strupr(name); + char *pos = strstr(name,"A.WAV"); + if (!pos) continue; + *pos=0; + } + count++; + ta->AppendText( name, -1 ); + } while (++dir); + return count; +} + +int Interface::GetCharacters(TextArea* ta) +{ + int count = 0; + char Path[_MAX_PATH]; + + PathJoin( Path, GamePath, GameCharactersPath, NULL ); + DirectoryIterator dir(Path); + if (!dir) { + return -1; + } + printf( "Looking in %s\n", Path ); + do { + char *name = dir.GetName(); + if (name[0] == '.') + continue; + if (dir.IsDirectory()) + continue; + strupr(name); + char *pos = strstr(name,".CHR"); + if (!pos) continue; + *pos=0; + count++; + ta->AppendText( name, -1 ); + } while (++dir); + return count; +} + +bool Interface::LoadINI(const char* filename) +{ + FILE* config; + config = fopen( filename, "rb" ); + if (config == NULL) { + return false; + } + char name[65], value[_MAX_PATH + 3]; + while (!feof( config )) { + name[0] = 0; + value[0] = 0; + char rem; + + if (fread( &rem, 1, 1, config ) != 1) + break; + + if (( rem == '#' ) || + ( rem == '[' ) || + ( rem == '\r' ) || + ( rem == '\n' ) || + ( rem == ';' )) { + if (rem == '\r') { + fgetc( config ); + continue; + } else if (rem == '\n') + continue; + + //it should always return zero + if (fscanf( config, "%*[^\r\n]%*[\r\n]" )!=0) + break; + continue; + } + fseek( config, -1, SEEK_CUR ); + //the * element is not counted + if (fscanf( config, "%[^=]=%[^\r\n]%*[\r\n]", name, value )!=2) + continue; + if (( value[0] >= '0' ) && ( value[0] <= '9' )) { + vars->SetAt( name, atoi( value ) ); + } + } + fclose( config ); + return true; +} + +/** Enables/Disables the Cut Scene Mode */ +void Interface::SetCutSceneMode(bool active) +{ + GameControl *gc = GetGameControl(); + if (gc) { + gc->SetCutSceneMode( active ); + } + if (game) { + if (active) { + game->ControlStatus |= CS_HIDEGUI; + } else { + game->ControlStatus &= ~CS_HIDEGUI; + } + SetEventFlag(EF_CONTROL); + } + video->SetMouseEnabled(!active); +} + +bool Interface::InCutSceneMode() const +{ + return (GetGameControl()->GetScreenFlags()&SF_DISABLEMOUSE)!=0; +} + +void Interface::QuitGame(int BackToMain) +{ + SetCutSceneMode(false); + if (timer) { + //clear cutscenes + //clear fade/screenshake effects + timer->Init(); + timer->SetFadeFromColor(0); + } + + DelAllWindows(); //delete all windows, including GameControl + + //shutting down ingame music + //(do it before deleting the game) + if (music) { + music->HardEnd(); + } + // stop any ambients which are still enqueued + if (AudioDriver) { + AmbientMgr *ambim = AudioDriver->GetAmbientMgr(); + if (ambim) ambim->deactivate(); + } + //delete game, worldmap + if (game) { + delete game; + game=NULL; + } + if (worldmap) { + delete worldmap; + worldmap=NULL; + } + if (BackToMain) { + strcpy(NextScript, "Start"); + QuitFlag |= QF_CHANGESCRIPT; + } + GSUpdate(true); +} + +void Interface::SetupLoadGame(Holder sg, int ver_override) +{ + LoadGameIndex = sg; + VersionOverride = ver_override; + QuitFlag |= QF_LOADGAME; +} + +void Interface::LoadGame(SaveGame *sg, int ver_override) +{ + // This function has rather painful error handling, + // as it should swap all the objects or none at all + // and the loading can fail for various reasons + + // Yes, it uses goto. Other ways seemed too awkward for me. + + strings->CloseAux(); + tokens->RemoveAll(NULL); //clearing the token dictionary + + if(calendar) delete calendar; + calendar = new Calendar; + + DataStream* gam_str = NULL; + DataStream* sav_str = NULL; + DataStream* wmp_str1 = NULL; + DataStream* wmp_str2 = NULL; + + Game* new_game = NULL; + WorldMapArray* new_worldmap = NULL; + + LoadProgress(10); + if (!KeepCache) DelTree((const char *) CachePath, true); + LoadProgress(15); + + if (sg == NULL) { + //Load the Default Game + gam_str = gamedata->GetResource( GameNameResRef, IE_GAM_CLASS_ID ); + sav_str = NULL; + wmp_str1 = gamedata->GetResource( WorldMapName[0], IE_WMP_CLASS_ID ); + if (WorldMapName[1][0]) { + wmp_str2 = gamedata->GetResource( WorldMapName[1], IE_WMP_CLASS_ID ); + } + } else { + gam_str = sg->GetGame(); + sav_str = sg->GetSave(); + wmp_str1 = sg->GetWmap(0); + if (WorldMapName[1][0]) { + wmp_str2 = sg->GetWmap(1); + if (!wmp_str2) { + //upgrade an IWD game to HOW + wmp_str2 = gamedata->GetResource( WorldMapName[1], IE_WMP_CLASS_ID ); + } + } + } + + // These are here because of the goto + PluginHolder gam_mgr(IE_GAM_CLASS_ID); + PluginHolder wmp_mgr(IE_WMP_CLASS_ID); + + if (!gam_str || !(wmp_str1 || wmp_str2) ) + goto cleanup; + + // Load GAM file + if (!gam_mgr) + goto cleanup; + + if (!gam_mgr->Open( gam_str, true )) + goto cleanup; + + new_game = gam_mgr->LoadGame(new Game(), ver_override); + if (!new_game) + goto cleanup; + + gam_str = NULL; + + // Load WMP (WorldMap) file + if (!wmp_mgr) + goto cleanup; + + if (!wmp_mgr->Open( wmp_str1, wmp_str2, true )) + goto cleanup; + + new_worldmap = wmp_mgr->GetWorldMapArray( ); + + wmp_str1 = NULL; + wmp_str2 = NULL; + + LoadProgress(20); + // Unpack SAV (archive) file to Cache dir + if (sav_str) { + PluginHolder ai(IE_BIF_CLASS_ID); + if (ai) { + if (ai->DecompressSaveGame(sav_str) != GEM_OK) { + goto cleanup; + } + } + delete sav_str; + sav_str = NULL; + } + + // Let's assume that now is everything loaded OK and swap the objects + + delete game; + delete worldmap; + + game = new_game; + worldmap = new_worldmap; + + strings->OpenAux(); + LoadProgress(70); + return; +cleanup: + // Something went wrong, so try to clean after itself + + delete new_game; + delete new_worldmap; + + delete gam_str; + delete wmp_str1; + delete wmp_str2; + delete sav_str; +} + +/* swapping out old resources */ +void Interface::UpdateMasterScript() +{ + if (game) { + game->SetScript( GlobalScript, 0 ); + } + + PluginHolder wmp_mgr(IE_WMP_CLASS_ID); + if (! wmp_mgr) + return; + + if (worldmap) { + DataStream *wmp_str1 = gamedata->GetResource( WorldMapName[0], IE_WMP_CLASS_ID ); + DataStream *wmp_str2 = gamedata->GetResource( WorldMapName[1], IE_WMP_CLASS_ID ); + + if (!wmp_mgr->Open( wmp_str1, wmp_str2, true )) { + delete wmp_str1; + delete wmp_str2; + } + + delete worldmap; + worldmap = wmp_mgr->GetWorldMapArray(); + } +} + +bool Interface::HideGCWindow() +{ + Window *window = GetWindow( 0 ); + // in the beginning, there's no window at all + if (! window) + return false; + + Control* gc = window->GetControl(0); + if (gc->ControlType!=IE_GUI_GAMECONTROL) { + return false; + } + SetVisible(0, WINDOW_INVISIBLE); + return true; +} + +void Interface::UnhideGCWindow() +{ + Window *window = GetWindow( 0 ); + if (!window) + return; + Control* gc = window->GetControl(0); + if (gc->ControlType!=IE_GUI_GAMECONTROL) + return; + SetVisible(0, WINDOW_VISIBLE); +} + +GameControl *Interface::GetGameControl() const +{ + Window *window = GetWindow( 0 ); + // in the beginning, there's no window at all + if (! window) + return NULL; + + Control* gc = window->GetControl(0); + if (gc == NULL) { + return NULL; + } + if (gc->ControlType!=IE_GUI_GAMECONTROL) { + return NULL; + } + return (GameControl *) gc; +} + +bool Interface::InitItemTypes() +{ + if (slotmatrix) { + free(slotmatrix); + } + AutoTable it("itemtype"); + ItemTypes = 0; + if (it) { + ItemTypes = it->GetRowCount(); //number of itemtypes + if (ItemTypes<0) { + ItemTypes = 0; + } + int InvSlotTypes = it->GetColumnCount(); + if (InvSlotTypes > 32) { //bit count limit + InvSlotTypes = 32; + } + //make sure unsigned int is 32 bits + slotmatrix = (ieDword *) malloc(ItemTypes * sizeof(ieDword) ); + for (int i=0;iQueryField(i,j),NULL,0) ) { + value |= k; + } + k <<= 1; + } + //we let any items in the inventory + slotmatrix[i] = (ieDword) value | SLOT_INVENTORY; + } + } + + //slottype describes the inventory structure + Inventory::Init(HasFeature(GF_MAGICBIT)); + AutoTable st("slottype"); + if (slottypes) { + free(slottypes); + slottypes = NULL; + } + SlotTypes = 0; + if (st) { + SlotTypes = st->GetRowCount(); + //make sure unsigned int is 32 bits + slottypes = (SlotType *) malloc(SlotTypes * sizeof(SlotType) ); + memset(slottypes, -1, SlotTypes * sizeof(SlotType) ); + for (unsigned int row = 0; row < SlotTypes; row++) { + bool alias; + unsigned int i = (ieDword) strtol(st->GetRowName(row),NULL,0 ); + if (i>=SlotTypes) continue; + if (slottypes[i].sloteffects!=0xffffffffu) { + slottypes[row].slot = i; + i=row; + alias = true; + } else { + slottypes[row].slot = i; + alias = false; + } + slottypes[i].slottype = (ieDword) strtol(st->QueryField(row,0),NULL,0 ); + slottypes[i].slotid = (ieDword) strtol(st->QueryField(row,1),NULL,0 ); + strnlwrcpy( slottypes[i].slotresref, st->QueryField(row,2), 8 ); + slottypes[i].slottip = (ieDword) strtol(st->QueryField(row,3),NULL,0 ); + slottypes[i].slotflags = (ieDword) strtol(st->QueryField(row,5),NULL,0 ); + //don't fill sloteffects for aliased slots (pst) + if (alias) { + continue; + } + slottypes[i].sloteffects = (ieDword) strtol(st->QueryField(row,4),NULL,0 ); + //setting special slots + if (slottypes[i].slottype&SLOT_ITEM) { + if (slottypes[i].slottype&SLOT_INVENTORY) { + Inventory::SetInventorySlot(i); + } else { + Inventory::SetQuickSlot(i); + } + } + switch (slottypes[i].sloteffects) { + //fist slot, not saved, default weapon + case SLOT_EFFECT_FIST: Inventory::SetFistSlot(i); break; + //magic weapon slot, overrides all weapons + case SLOT_EFFECT_MAGIC: Inventory::SetMagicSlot(i); break; + //weapon slot, Equipping marker is relative to it + case SLOT_EFFECT_MELEE: Inventory::SetWeaponSlot(i); break; + //ranged slot + case SLOT_EFFECT_MISSILE: Inventory::SetRangedSlot(i); break; + //right hand + case SLOT_EFFECT_LEFT: Inventory::SetShieldSlot(i); break; + //head (for averting critical hit) + case SLOT_EFFECT_HEAD: Inventory::SetHeadSlot(i); break; + default:; + } + } + } + return (it && st); +} + +ieDword Interface::FindSlot(unsigned int idx) const +{ + ieDword i; + + for (i=0;i=SlotTypes) { + return 0; + } + return slottypes[idx].slot; +} + +ieDword Interface::QuerySlotType(unsigned int idx) const +{ + if (idx>=SlotTypes) { + return 0; + } + return slottypes[idx].slottype; +} + +ieDword Interface::QuerySlotID(unsigned int idx) const +{ + if (idx>=SlotTypes) { + return 0; + } + return slottypes[idx].slotid; +} + +ieDword Interface::QuerySlottip(unsigned int idx) const +{ + if (idx>=SlotTypes) { + return 0; + } + return slottypes[idx].slottip; +} + +ieDword Interface::QuerySlotEffects(unsigned int idx) const +{ + if (idx>=SlotTypes) { + return 0; + } + return slottypes[idx].sloteffects; +} + +ieDword Interface::QuerySlotFlags(unsigned int idx) const +{ + if (idx>=SlotTypes) { + return 0; + } + return slottypes[idx].slotflags; +} + +const char *Interface::QuerySlotResRef(unsigned int idx) const +{ + if (idx>=SlotTypes) { + return ""; + } + return slottypes[idx].slotresref; +} + +// checks the itemtype vs. slottype, and also checks the usability flags +// vs. Actor's stats (alignment, class, race, kit etc.) +int Interface::CanUseItemType(int slottype, Item *item, Actor *actor, bool feedback, bool equipped) const +{ + //inventory is a special case, we allow any items to enter it + if ( slottype==-1 ) { + return SLOT_INVENTORY; + } + //if we look for ALL slot types, then SLOT_SHIELD shouldn't interfere + //with twohandedness + //As long as this is an Item, use the ITEM constant + //switch for IE_INV_ITEM_* if it is a CREItem + if (item->Flags&IE_ITEM_TWO_HANDED) { + //if the item is twohanded and there are more slots, drop the shield slot + if (slottype&~SLOT_SHIELD) { + slottype&=~SLOT_SHIELD; + } + if (slottype&SLOT_SHIELD) { + //cannot equip twohanded in offhand + if (feedback) displaymsg->DisplayConstantString(STR_NOT_IN_OFFHAND, 0xf0f0f0); + return 0; + } + } + + if ( (unsigned int) item->ItemType>=(unsigned int) ItemTypes) { + //invalid itemtype + if (feedback) displaymsg->DisplayConstantString(STR_WRONGITEMTYPE, 0xf0f0f0); + return 0; + } + + //if actor is supplied, check its usability fields + if (actor) { + //constant strings + int idx = actor->Unusable(item); + if (idx) { + if (feedback) displaymsg->DisplayConstantString(idx, 0xf0f0f0); + return 0; + } + //custom strings + ieStrRef str = actor->Disabled(item->Name, item->ItemType); + if (str && !equipped) { + if (feedback) displaymsg->DisplayString(str, 0xf0f0f0, 0); + return 0; + } + } + + //if any bit is true, the answer counts as true + int ret = (slotmatrix[item->ItemType]&slottype); + + if (!ret) { + if (feedback) displaymsg->DisplayConstantString(STR_WRONGITEMTYPE, 0xf0f0f0); + return 0; + } + + //this warning comes only when feedback is enabled + if (feedback) { + //this was, but that disabled equipping of amber earrings in PST + //if (slotmatrix[item->ItemType]&(SLOT_QUIVER|SLOT_WEAPON|SLOT_ITEM)) { + if (ret&(SLOT_QUIVER|SLOT_WEAPON|SLOT_ITEM)) { + //don't ruin the return variable, it contains the usable slot bits + int flg = 0; + if (ret&SLOT_QUIVER) { + if (item->GetWeaponHeader(true)) flg = 1; + } + + if (ret&SLOT_WEAPON) { + //melee + if (item->GetWeaponHeader(false)) flg = 1; + //ranged + if (item->GetWeaponHeader(true)) flg = 1; + } + + if (ret&SLOT_ITEM) { + if (item->GetEquipmentHeaderNumber(0)!=0xffff) flg = 1; + } + + if (!flg) { + displaymsg->DisplayConstantString(STR_UNUSABLEITEM, 0xf0f0f0); + return 0; + } + } + } + + return ret; +} + +Label *Interface::GetMessageLabel() const +{ + ieDword WinIndex = (ieDword) -1; + ieDword TAIndex = (ieDword) -1; + + vars->Lookup( "OtherWindow", WinIndex ); + if (( WinIndex != (ieDword) -1 ) && + ( vars->Lookup( "MessageLabel", TAIndex ) )) { + Window* win = GetWindow( (unsigned short) WinIndex ); + if (win) { + Control *ctrl = win->GetControl( (unsigned short) TAIndex ); + if (ctrl && ctrl->ControlType==IE_GUI_LABEL) + return (Label *) ctrl; + } + } + return NULL; +} + +TextArea *Interface::GetMessageTextArea() const +{ + ieDword WinIndex = (ieDword) -1; + ieDword TAIndex = (ieDword) -1; + + vars->Lookup( "MessageWindow", WinIndex ); + if (( WinIndex != (ieDword) -1 ) && + ( vars->Lookup( "MessageTextArea", TAIndex ) )) { + Window* win = GetWindow( (unsigned short) WinIndex ); + if (win) { + Control *ctrl = win->GetControl( (unsigned short) TAIndex ); + if (ctrl && ctrl->ControlType==IE_GUI_TEXTAREA) + return (TextArea *) ctrl; + } + } + return NULL; +} + +static const char *saved_extensions[]={".are",".sto",0}; +static const char *saved_extensions_last[]={".tot",".toh",0}; + +//returns the priority of the file to be saved +//2 - save +//1 - save last +//0 - don't save +int Interface::SavedExtension(const char *filename) +{ + const char *str=strchr(filename,'.'); + if (!str) return 0; + int i=0; + while(saved_extensions[i]) { + if (!stricmp(saved_extensions[i], str) ) return 2; + i++; + } + i=0; + while(saved_extensions_last[i]) { + if (!stricmp(saved_extensions_last[i], str) ) return 1; + i++; + } + return 0; +} + +static const char *protected_extensions[]={".exe",".dll",".so",0}; + +//returns true if file should be saved +bool Interface::ProtectedExtension(const char *filename) +{ + const char *str=strchr(filename,'.'); + if (!str) return false; + int i=0; + while(protected_extensions[i]) { + if (!stricmp(protected_extensions[i], str) ) return true; + i++; + } + return false; +} + +void Interface::RemoveFromCache(const ieResRef resref, SClass_ID ClassID) +{ + char filename[_MAX_PATH]; + + snprintf(filename, _MAX_PATH, "%s%.8s%s", CachePath, resref, TypeExt( ClassID ) ); + unlink ( filename); +} + +//this function checks if the path is eligible as a cache +//if it contains a directory, or suspicious file extensions +//we bail out, because the cache will be purged regularly. +bool Interface::StupidityDetector(const char* Pt) +{ + char Path[_MAX_PATH]; + strcpy( Path, Pt ); + DirectoryIterator dir(Path); + if (!dir) { + printf("\n**cannot open**\n"); + return true; + } + do { + const char *name = dir.GetName(); + if (dir.IsDirectory()) { + if (name[0] == '.') { + if (name[1] == '\0') + continue; + if (name[1] == '.' && name[2] == '\0') + continue; + } + printf("\n**contains another dir**\n"); + return true; //a directory in there??? + } + if (ProtectedExtension(name) ) { + printf("\n**contains alien files**\n"); + return true; //an executable file in there??? + } + } while (++dir); + //ok, we got a good conscience + return false; +} + +void Interface::DelTree(const char* Pt, bool onlysave) +{ + char Path[_MAX_PATH]; + + if (!Pt[0]) return; //Don't delete the root filesystem :) + strcpy( Path, Pt ); + DirectoryIterator dir(Path); + if (!dir) { + return; + } + do { + char *name = dir.GetName(); + if (dir.IsDirectory()) + continue; + if (name[0] == '.') + continue; + if (!onlysave || SavedExtension(name) ) { + char dtmp[_MAX_PATH]; + dir.GetFullPath(dtmp); + unlink( dtmp ); + } + } while (++dir); +} + +void Interface::LoadProgress(int percent) +{ + vars->SetAt("Progress", percent); + RedrawControls("Progress", percent); + RedrawAll(); + DrawWindows(); + video->SwapBuffers(); +} + +void Interface::ReleaseDraggedItem() +{ + DraggedItem=NULL; //shouldn't free this + video->SetDragCursor (NULL); +} + +void Interface::DragItem(CREItem *item, const ieResRef Picture) +{ + //We should drop the dragged item and pick this up, + //we shouldn't have a valid DraggedItem at this point. + //Anyway, if there is still a dragged item, it will be destroyed. + if (DraggedItem) { + printMessage("Core","Forgot to call ReleaseDraggedItem when leaving inventory (item destroyed)!\n",YELLOW); + delete DraggedItem; + } + DraggedItem = item; + if (video) { + Sprite2D* DraggedCursor = NULL; + if (item) { + DraggedCursor = gamedata->GetBAMSprite( Picture, 0, 0 ); + } + video->SetDragCursor (DraggedCursor); + } +} + +void Interface::SetDraggedPortrait(int dp, int idx) +{ + if (idx<0) idx=14; + DraggedPortrait = dp; + if (dp) { + //hmm this might work? + Cursors[idx]->acquire(); + video->SetDragCursor(Cursors[idx]); + } else { + video->SetDragCursor(NULL); + } +} + +bool Interface::ReadItemTable(const ieResRef TableName, const char * Prefix) +{ + ieResRef ItemName; + int i,j; + + AutoTable tab(TableName); + if (!tab) { + return false; + } + i=tab->GetRowCount(); + for(j=0;jGetRowName(j), 8); + } + //Variable elements are free'd, so we have to use malloc + //well, not anymore, we can use ReleaseFunction + int l=tab->GetColumnCount(j); + if (l<1) continue; + int cl = atoi(tab->GetColumnName(0)); + ItemList *itemlist = new ItemList(l, cl); + for(int k=0;kResRefs[k],tab->QueryField(j,k), 8); + } + RtRows->SetAt(ItemName, (void*)itemlist); + } + return true; +} + +bool Interface::ReadRandomItems() +{ + ieResRef RtResRef; + int i; + + ieDword difflev=0; //rt norm or rt fury + vars->Lookup("Nightmare Mode", difflev); + if (RtRows) { + RtRows->RemoveAll(ReleaseItemList); + } + else { + RtRows=new Variables(10, 17); //block size, hash table size + if (!RtRows) { + return false; + } + RtRows->SetType( GEM_VARIABLES_POINTER ); + } + AutoTable tab("randitem"); + if (!tab) { + return false; + } + if (difflev>=tab->GetColumnCount()) { + difflev = tab->GetColumnCount()-1; + } + + //the gold item + strnlwrcpy( GoldResRef, tab->QueryField((unsigned int) 0,(unsigned int) 0), 8); + if ( GoldResRef[0]=='*' ) { + return false; + } + strnlwrcpy( RtResRef, tab->QueryField( 1, difflev ), 8); + i=atoi( RtResRef ); + if (i<1) { + ReadItemTable( RtResRef, 0 ); //reading the table itself + return true; + } + if (i>5) { + i=5; + } + while(i--) { + strnlwrcpy( RtResRef, tab->QueryField(2+i,difflev), 8); + ReadItemTable( RtResRef,tab->GetRowName(2+i) ); + } + return true; +} + +CREItem *Interface::ReadItem(DataStream *str) +{ + CREItem *itm = new CREItem(); + + str->ReadResRef( itm->ItemResRef ); + str->ReadWord( &itm->Expired ); + str->ReadWord( &itm->Usages[0] ); + str->ReadWord( &itm->Usages[1] ); + str->ReadWord( &itm->Usages[2] ); + str->ReadDword( &itm->Flags ); + if (ResolveRandomItem(itm) ) { + return itm; + } + delete itm; + return NULL; +} + +#define MAX_LOOP 10 + +//This function generates random items based on the randitem.2da file +//there could be a loop, but we don't want to freeze, so there is a limit +bool Interface::ResolveRandomItem(CREItem *itm) +{ + if (!RtRows) return true; + for(int loop=0;loopLookup( itm->ItemResRef, lookup ) ) { + return true; + } + ItemList *itemlist = (ItemList*)lookup; + if (itemlist->WeightOdds) { + //instead of 1d19 we calculate with 2d10 (which also has 19 possible values) + i=Roll(2,(itemlist->Count+1)/2,-2); + } else { + i=Roll(1,itemlist->Count,-1); + } + strnlwrcpy( NewItem, itemlist->ResRefs[i], 8); + char *p=(char *) strchr(NewItem,'*'); + if (p) { + *p=0; //doing this so endptr is ok + k=strtol(p+1,NULL,10); + } else { + k=1; + } + j=strtol(NewItem,&endptr,10); + if (j<1) { + j=1; + } + if (*endptr) { + strnlwrcpy(itm->ItemResRef, NewItem, 8); + } else { + strnlwrcpy(itm->ItemResRef, GoldResRef, 8); + } + if ( !memcmp( itm->ItemResRef,"no_drop",8 ) ) { + itm->ItemResRef[0]=0; + } + if (!itm->ItemResRef[0]) { + return false; + } + itm->Usages[0]=(ieWord) Roll(j,k,0); + } + printMessage("Interface"," ",LIGHT_RED); + printf("Loop detected while generating random item:%s\n",itm->ItemResRef); + return false; +} + +//now that we store spell name in spl, i guess, we shouldn't pass 'ieResRef name' +//these functions are needed because Win32 doesn't allow freeing memory from +//another dll. So we allocate all commonly used memories from core +ITMExtHeader *Interface::GetITMExt(int count) +{ + return new ITMExtHeader[count]; +} + +SPLExtHeader *Interface::GetSPLExt(int count) +{ + return new SPLExtHeader[count]; +} + +Effect *Interface::GetEffect(ieDword opcode) +{ + if (opcode==0xffffffff) { + return NULL; + } + Effect *fx = new Effect(); + if (!fx) { + return NULL; + } + memset(fx,0,sizeof(Effect)); + fx->Opcode=opcode; + return fx; +} + +Effect *Interface::GetFeatures(int count) +{ + return new Effect[count]; +} + +/* +void Interface::FreeITMExt(ITMExtHeader *p, Effect *e) +{ + delete [] p; + delete [] e; +} + +void Interface::FreeSPLExt(SPLExtHeader *p, Effect *e) +{ + delete [] p; + delete [] e; +} +*/ + +WorldMapArray *Interface::NewWorldMapArray(int count) +{ + return new WorldMapArray(count); +} + +Container *Interface::GetCurrentContainer() +{ + return CurrentContainer; +} + +int Interface::CloseCurrentContainer() +{ + UseContainer = false; + if ( !CurrentContainer) { + return -1; + } + //remove empty ground piles on closeup + CurrentContainer->GetCurrentArea()->TMap->CleanupContainer(CurrentContainer); + CurrentContainer = NULL; + return 0; +} + +void Interface::SetCurrentContainer(Actor *actor, Container *arg, bool flag) +{ + //abort action if the first selected PC isn't the original actor + if (actor!=GetFirstSelectedPC(false)) { + CurrentContainer = NULL; + return; + } + CurrentContainer = arg; + UseContainer = flag; +} + +Store *Interface::GetCurrentStore() +{ + return CurrentStore; +} + +int Interface::CloseCurrentStore() +{ + if ( !CurrentStore ) { + return -1; + } + PluginHolder sm(IE_STO_CLASS_ID); + if (sm == NULL) { + return -1; + } + int size = sm->GetStoredFileSize (CurrentStore); + if (size > 0) { + //created streams are always autofree (close file on destruct) + //this one will be destructed when we return from here + FileStream str; + + str.Create( CurrentStore->Name, IE_STO_CLASS_ID ); + int ret = sm->PutStore (&str, CurrentStore); + if (ret <0) { + printMessage("Core"," ", YELLOW); + printf("Store removed: %s\n", CurrentStore->Name); + RemoveFromCache(CurrentStore->Name, IE_STO_CLASS_ID); + } + } else { + printMessage("Core"," ", YELLOW); + printf("Store removed: %s\n", CurrentStore->Name); + RemoveFromCache(CurrentStore->Name, IE_STO_CLASS_ID); + } + //make sure the stream isn't connected to sm, or it will be double freed + delete CurrentStore; + CurrentStore = NULL; + return 0; +} + +Store *Interface::SetCurrentStore(const ieResRef resname, ieDword owner) +{ + if ( CurrentStore ) { + if ( !strnicmp(CurrentStore->Name, resname, 8) ) { + return CurrentStore; + } + + //not simply delete the old store, but save it + CloseCurrentStore(); + } + + DataStream* str = gamedata->GetResource( resname, IE_STO_CLASS_ID ); + PluginHolder sm(IE_STO_CLASS_ID); + if (sm == NULL) { + delete ( str ); + return NULL; + } + if (!sm->Open( str, true )) { + return NULL; + } + + // FIXME - should use some already allocated in core + // not really, only one store is open at a time, then it is + // unloaded, we don't really have to cache it, it will be saved in + // Cache anyway! + CurrentStore = sm->GetStore( new Store() ); + if (CurrentStore == NULL) { + return NULL; + } + strnlwrcpy(CurrentStore->Name, resname, 8); + if (owner) { + CurrentStore->SetOwnerID(owner); + } + return CurrentStore; +} + +void Interface::SetMouseScrollSpeed(int speed) { + mousescrollspd = (speed+1)*2; +} + +int Interface::GetMouseScrollSpeed() { + return mousescrollspd; +} + +ieStrRef Interface::GetRumour(const ieResRef dlgref) +{ + PluginHolder dm(IE_DLG_CLASS_ID); + dm->Open( gamedata->GetResource( dlgref, IE_DLG_CLASS_ID ), true ); + Dialog *dlg = dm->GetDialog(); + + if (!dlg) { + printMessage("Interface"," ", LIGHT_RED); + printf( "Cannot load dialog: %s\n", dlgref ); + return (ieStrRef) -1; + } + Scriptable *pc=game->GetPC( game->GetSelectedPCSingle(), false ); + + ieStrRef ret = (ieStrRef) -1; + int i = dlg->FindRandomState( pc ); + if (i>=0 ) { + ret = dlg->GetState( i )->StrRef; + } + delete dlg; + return ret; +} + +void Interface::DoTheStoreHack(Store *s) +{ + size_t size = s->PurchasedCategoriesCount * sizeof( ieDword ); + s->purchased_categories=(ieDword *) malloc(size); + + size = s->CuresCount * sizeof( STOCure ); + s->cures=(STOCure *) malloc(size); + + size = s->DrinksCount * sizeof( STODrink ); + s->drinks=(STODrink *) malloc(size); + + for(size=0;sizeItemsCount;size++) { + STOItem *si = new STOItem(); + memset(si, 0, sizeof(STOItem) ); + s->items.push_back( si ); + } +} + +//plays stock sound listed in defsound.2da +void Interface::PlaySound(int index) +{ + if (index<=DSCount) { + AudioDriver->Play(DefSound[index]); + } +} + +Actor *Interface::GetFirstSelectedPC(bool forced) +{ + int partySize = game->GetPartySize( false ); + if (!partySize) return NULL; + for (int i = 0; i < partySize; i++) { + Actor* actor = game->GetPC( i,false ); + if (actor->IsSelected()) { + return actor; + } + } + + if (forced) { + return game->GetPC(0,false); + } + return NULL; +} + +Actor *Interface::GetFirstSelectedActor() +{ + if (game->selected.size()) { + return game->selected[0]; + } + return NULL; +} + +//this is used only for the console +Sprite2D *Interface::GetCursorSprite() +{ + Sprite2D *spr = gamedata->GetBAMSprite(CursorBam, 0, 0); + if (spr) + { + if(HasFeature(GF_OVERRIDE_CURSORPOS)) + { + spr->XPos=1; + spr->YPos=spr->Height-1; + } + } + return spr; +} + +Sprite2D *Interface::GetScrollCursorSprite(int frameNum, int spriteNum) +{ + return gamedata->GetBAMSprite(ScrollCursorBam, frameNum, spriteNum); +} + +/* we should return -1 if it isn't gold, otherwise return the gold value */ +int Interface::CanMoveItem(const CREItem *item) const +{ + //This is an inventory slot, switch to IE_ITEM_* if you use Item + if (!HasFeature(GF_NO_DROP_CAN_MOVE) ) { + if (item->Flags & IE_INV_ITEM_UNDROPPABLE) + return 0; + } + //not gold, we allow only one single coin ResRef, this is good + //for all of the original games + if (strnicmp(item->ItemResRef, GoldResRef, 8 ) ) + return -1; + //gold, returns the gold value (stack size) + return item->Usages[0]; +} + +// dealing with applying effects +void Interface::ApplySpell(const ieResRef resname, Actor *actor, Scriptable *caster, int level) +{ + Spell *spell = gamedata->GetSpell(resname); + if (!spell) { + return; + } + + level = spell->GetHeaderIndexFromLevel(level); + EffectQueue *fxqueue = spell->GetEffectBlock(caster, actor->Pos, level); + + ApplyEffectQueue(fxqueue, actor, caster, actor->Pos); + delete fxqueue; +} + +void Interface::ApplySpellPoint(const ieResRef resname, Map* area, const Point &pos, Scriptable *caster, int level) +{ + Spell *spell = gamedata->GetSpell(resname); + if (!spell) { + return; + } + level = spell->GetHeaderIndexFromLevel(level); + Projectile *pro = spell->GetProjectile(caster, level, pos); + pro->SetCaster(caster->GetGlobalID()); + area->AddProjectile(pro, caster->Pos, pos); +} + +//-1 means the effect was reflected back to the caster +//0 means the effect was resisted and should be removed +//1 means the effect was applied +int Interface::ApplyEffect(Effect *effect, Actor *actor, Scriptable *caster) +{ + if (!effect) { + return 0; + } + + EffectQueue *fxqueue = new EffectQueue(); + //AddEffect now copies the fx data, please delete your effect reference + //if you created it. (Don't delete cached references) + fxqueue->AddEffect( effect ); + int res = ApplyEffectQueue(fxqueue, actor, caster); + delete fxqueue; + return res; +} + +int Interface::ApplyEffectQueue(EffectQueue *fxqueue, Actor *actor, Scriptable *caster) +{ + Point p; + p.empty(); //the effect should have all its coordinates already set + return ApplyEffectQueue(fxqueue, actor, caster, p); +} + +int Interface::ApplyEffectQueue(EffectQueue *fxqueue, Actor *actor, Scriptable *caster, Point p) +{ + int res = fxqueue->CheckImmunity ( actor ); + if (res) { + if (res == -1 ) { + //bounced back at a nonliving caster + if (caster->Type!=ST_ACTOR) { + return 0; + } + actor = (Actor *) caster; + } + fxqueue->SetOwner( caster ); + + if (fxqueue->AddAllEffects( actor, p )==FX_NOT_APPLIED) { + res=0; + } + } + return res; +} + +Effect *Interface::GetEffect(const ieResRef resname, int level, const Point &p) +{ + //Don't free this reference, it is cached! + Effect *effect = gamedata->GetEffect(resname); + if (!effect) { + return NULL; + } + if (!level) { + level = 1; + } + effect->Power = level; + effect->PosX=p.x; + effect->PosY=p.y; + return effect; +} + +// dealing with saved games +int Interface::SwapoutArea(Map *map) +{ + PluginHolder mm(IE_ARE_CLASS_ID); + if (mm == NULL) { + return -1; + } + int size = mm->GetStoredFileSize (map); + if (size > 0) { + //created streams are always autofree (close file on destruct) + //this one will be destructed when we return from here + FileStream str; + + str.Create( map->GetScriptName(), IE_ARE_CLASS_ID ); + int ret = mm->PutArea (&str, map); + if (ret <0) { + printMessage("Core"," ", YELLOW); + printf("Area removed: %s\n", map->GetScriptName()); + RemoveFromCache(map->GetScriptName(), IE_ARE_CLASS_ID); + } + } else { + printMessage("Core"," ", YELLOW); + printf("Area removed: %s\n", map->GetScriptName()); + RemoveFromCache(map->GetScriptName(), IE_ARE_CLASS_ID); + } + //make sure the stream isn't connected to sm, or it will be double freed + return 0; +} + +int Interface::WriteCharacter(const char *name, Actor *actor) +{ + char Path[_MAX_PATH]; + + PathJoin( Path, GamePath, GameCharactersPath, NULL ); + if (!actor) { + return -1; + } + PluginHolder gm(IE_CRE_CLASS_ID); + if (gm == NULL) { + return -1; + } + + //str is freed + { + FileStream str; + + if (!str.Create( Path, name, IE_CHR_CLASS_ID )) + return -1; + + int ret = gm->PutActor(&str, actor, true); + if (ret <0) { + printMessage("Core"," ", YELLOW); + printf("Character cannot be saved: %s\n", name); + return -1; + } + } + + //write the BIO string + if (!HasFeature(GF_NO_BIOGRAPHY)) { + FileStream str; + + str.Create( Path, name, IE_BIO_CLASS_ID ); + //never write the string reference into this string + char *tmp = GetString(actor->GetVerbalConstant(VB_BIO),IE_STR_STRREFOFF); + str.Write (tmp, strlen(tmp)); + free(tmp); + } + return 0; +} + +int Interface::WriteGame(const char *folder) +{ + PluginHolder gm(IE_GAM_CLASS_ID); + if (gm == NULL) { + return -1; + } + + int size = gm->GetStoredFileSize (game); + if (size > 0) { + //created streams are always autofree (close file on destruct) + //this one will be destructed when we return from here + FileStream str; + + str.Create( folder, GameNameResRef, IE_GAM_CLASS_ID ); + int ret = gm->PutGame (&str, game); + if (ret <0) { + printMessage("Core"," ", YELLOW); + printf("Game cannot be saved: %s\n", folder); + return -1; + } + } else { + printMessage("Core"," ", YELLOW); + printf("Internal error, game cannot be saved: %s\n", folder); + return -1; + } + return 0; +} + +int Interface::WriteWorldMap(const char *folder) +{ + PluginHolder wmm(IE_WMP_CLASS_ID); + if (wmm == NULL) { + return -1; + } + + if (WorldMapName[1][0]) { + worldmap->SetSingle(false); + } + + int size1 = wmm->GetStoredFileSize (worldmap, 0); + int size2 = 1; //just a dummy value + + //if size is 0 for the first worldmap, then there is a problem + if (!worldmap->IsSingle() && (size1>0) ) { + size2=wmm->GetStoredFileSize (worldmap, 1); + } + + int ret = 0; + if ((size1 < 0) || (size2<0) ) { + ret=-1; + } else { + //created streams are always autofree (close file on destruct) + //this one will be destructed when we return from here + FileStream str1; + FileStream str2; + + str1.Create( folder, WorldMapName[0], IE_WMP_CLASS_ID ); + if (!worldmap->IsSingle()) { + str2.Create( folder, WorldMapName[1], IE_WMP_CLASS_ID ); + } + ret = wmm->PutWorldMap (&str1, &str2, worldmap); + } + if (ret <0) { + printMessage("Core"," ", YELLOW); + printf("Internal error, worldmap cannot be saved: %s\n", folder); + return -1; + } + return 0; +} + +int Interface::CompressSave(const char *folder) +{ + FileStream str; + + str.Create( folder, GameNameResRef, IE_SAV_CLASS_ID ); + DirectoryIterator dir(CachePath); + if (!dir) { + return -1; + } + //BIF and SAV are the same + PluginHolder ai(IE_BIF_CLASS_ID); + ai->CreateArchive( &str); + + //.tot and .toh should be saved last, because they are updated when an .are is saved + int priority=2; + while(priority) { + do { + const char *name = dir.GetName(); + if (dir.IsDirectory()) + continue; + if (name[0] == '.') + continue; + if (SavedExtension(name)==priority) { + char dtmp[_MAX_PATH]; + dir.GetFullPath(dtmp); + FileStream fs; + fs.Open(dtmp, true); + ai->AddToSaveGame(&str, &fs); + } + } while (++dir); + //reopen list for the second round + priority--; + if (priority>0) { + dir.Rewind(); + } + } + return 0; +} + +int Interface::GetMaximumAbility() const { return MaximumAbility; } + +int Interface::GetStrengthBonus(int column, int value, int ex) const +{ + //to hit, damage, open doors, weight allowance + if (column<0 || column>3) + return -9999; + + if (value<0) + value = 0; + else if (value>25) + value = 25; + + if (ex<0) + ex=0; + else if (ex>100) + ex=100; + + return strmod[column*(MaximumAbility+1)+value]+strmodex[column*101+ex]; +} + +// we don't use the stuff maze yet +// IE: bonus skill points are ignored and the plain int mod used! +int Interface::GetIntelligenceBonus(int column, int value) const +{ + if (HasFeature(GF_3ED_RULES)) { + //learn spell, max spell level, max spell number on level, bonus skill points + if (column<0 || column>2) return -9999; + } else { + //learn spell, max spell level, max spell number on level, maze duration dice, maze duration dice size + if (column<0 || column>4) return -9999; + } + + return intmod[column*(MaximumAbility+1)+value]; +} + +int Interface::GetDexterityBonus(int column, int value) const +{ + //no dexmod in iwd2 and only one type of modifier + if (HasFeature(GF_3ED_RULES)) { + return (value-10)/2; + } + + //reaction, missile, ac + if (column<0 || column>2) + return -9999; + + return dexmod[column*(MaximumAbility+1)+value]; +} + +int Interface::GetConstitutionBonus(int column, int value) const +{ + //no conmod in iwd2 + if (HasFeature(GF_3ED_RULES)) { + return (value-10)/2; + } + + //normal, warrior, minimum, regen hp, regen fatigue + if (column<0 || column>4) + return -9999; + + return conmod[column*(MaximumAbility+1)+value]; +} + +int Interface::GetCharismaBonus(int column, int /*value*/) const +{ + // store price reduction + if (column<0 || column>(MaximumAbility-1)) + return -9999; + + return chrmod[column]; +} + +int Interface::GetLoreBonus(int column, int value) const +{ + //no lorebon in iwd2 - lore is a skill + if (HasFeature(GF_3ED_RULES)) return 0; + + if (column<0 || column>0) + return -9999; + + return lorebon[value]; +} + +int Interface::GetWisdomBonus(int column, int value) const +{ + //no wismod in iwd2 + if (HasFeature(GF_3ED_RULES)) { + return (value-10)/2; + } + + if (!HasFeature(GF_WISDOM_BONUS)) return 0; + + // xp bonus + if (column<0 || column>0) + return -9999; + + return wisbon[value]; +} + +int Interface::GetReputationMod(int column) const +{ + int reputation = game->Reputation / 10 - 1; + + if (column<0 || column>8) { + return -9999; + } + if (reputation > 19) { + reputation = 19; + } + if (reputation < 0) { + reputation = 0; + } + + return reputationmod[reputation][column]; +} + +// -3, -2 if request is illegal or in cutscene +// -1 if pause is already active +// 0 if pause was not allowed +// 1 if autopause happened +int Interface::Autopause(ieDword flag) +{ + GameControl *gc = GetGameControl(); + if (!gc) { + return -3; + } + if (InCutSceneMode()) { + return -2; + } + if (gc->GetDialogueFlags()&DF_FREEZE_SCRIPTS) { + return -1; + } + ieDword autopause_flags = 0; + + vars->Lookup("Auto Pause State", autopause_flags); + if (autopause_flags & (1<DisplayConstantString(STR_AP_UNUSABLE+flag, 0xff0000); + gc->SetDialogueFlags(DF_FREEZE_SCRIPTS, BM_OR); + return 1; + } + return 0; +} + +void Interface::RegisterOpcodes(int count, const EffectRef *opcodes) +{ + EffectQueue_RegisterOpcodes(count, opcodes); +} + +void Interface::SetInfoTextColor(const Color &color) +{ + if (InfoTextPalette) { + gamedata->FreePalette(InfoTextPalette); + } + InfoTextPalette = CreatePalette(color, black); +} + +//todo row? +void Interface::GetResRefFrom2DA(const ieResRef resref, ieResRef resource1, ieResRef resource2, ieResRef resource3) +{ + if (!resource1) { + return; + } + resource1[0]=0; + if (resource2) { + resource2[0]=0; + } + if (resource3) { + resource3[0]=0; + } + AutoTable tab(resref); + if (tab) { + unsigned int cols = tab->GetColumnCount(); + unsigned int row = (unsigned int) Roll(1,tab->GetRowCount(),-1); + strnuprcpy(resource1, tab->QueryField(row,0), 8); + if (resource2 && cols>1) + strnuprcpy(resource2, tab->QueryField(row,1), 8); + if (resource3 && cols>2) + strnuprcpy(resource3, tab->QueryField(row,2), 8); + } +} + +ieDword *Interface::GetListFrom2DAInternal(const ieResRef resref) +{ + ieDword *ret; + + AutoTable tab(resref); + if (tab) { + ieDword cnt = tab->GetRowCount(); + ret = (ieDword *) malloc((1+cnt)*sizeof(ieDword)); + ret[0]=cnt; + while(cnt) { + ret[cnt]=strtol(tab->QueryField(cnt-1, 0),NULL, 0); + cnt--; + } + return ret; + } + ret = (ieDword *) malloc(sizeof(ieDword)); + ret[0]=0; + return ret; +} + +ieDword* Interface::GetListFrom2DA(const ieResRef tablename) +{ + ieDword *list; + + if (!lists->Lookup(tablename, (void *&) list)) { + list = GetListFrom2DAInternal(tablename); + lists->SetAt(tablename, list); + } + + return list; +} + +//returns a numeric value associated with a stat name (symbol) from stats.ids +ieDword Interface::TranslateStat(const char *stat_name) +{ + long tmp; + + if (valid_number(stat_name, tmp)) { + return (ieDword) tmp; + } + + int symbol = LoadSymbol( "stats" ); + Holder sym = GetSymbol( symbol ); + ieDword stat = (ieDword) sym->GetValue( stat_name ); + if (stat==(ieDword) ~0) { + printMessage("Core"," ",YELLOW); + printf("Cannot translate symbol: %s\n", stat_name); + } + return stat; +} + +void Interface::WaitForDisc(int disc_number, const char* path) +{ + GetDictionary()->SetAt( "WaitForDisc", (ieDword) disc_number ); + + GetGUIScriptEngine()->RunFunction( "GUICommonWindows", "OpenWaitForDiscWindow" ); + do { + DrawWindows(); + for (size_t i=0;iRunFunction( "GUICommonWindows", "OpenWaitForDiscWindow" ); + return; + } + } + + } while (video->SwapBuffers() == GEM_OK); +} + +// remove the extraneus EOL newline and carriage return +void Interface::StripLine(char * string, size_t size) { + if (size >= 2 && string[size-2] == '\n') { + string[size-2] = '\0'; + } + if (size >= 3 && string[size-3] == '\r') { + string[size-3] = '\0'; // remove the carriage return too + } +} + +void Interface::SetTickHook(EventHandler hook) +{ + TickHook = hook; +} + +void Interface::SetNextScript(const char *script) +{ + strncpy( NextScript, script, sizeof(NextScript) ); + QuitFlag |= QF_CHANGESCRIPT; +} + +void Interface::SanityCheck(const char *ver) { + if (strcmp(ver, VERSION_GEMRB)) { + printf("version check failed: core version %s doesn't match caller's version %s\n", VERSION_GEMRB, ver); + abort(); + } +} diff --git a/project/jni/application/gemrb/src/core/Interface.h b/project/jni/application/gemrb/src/core/Interface.h new file mode 100644 index 000000000..3965efc37 --- /dev/null +++ b/project/jni/application/gemrb/src/core/Interface.h @@ -0,0 +1,786 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * + */ + +/** + * @file Interface.h + * Declaration of Interface class, central interconnect for various GemRB parts + */ + +#ifndef INTERFACE_H +#define INTERFACE_H + +//skip messy warnings in MSVC6 +#include "win32def.h" + +#include "SClassID.h" +#include "exports.h" + +#include "Cache.h" +#include "Callback.h" +#include "GlobalTimer.h" +#include "Holder.h" + +#include +#include + +#ifdef _MSC_VER // No SFINAE +#include "Audio.h" +#include "DataFileMgr.h" +#include "MusicMgr.h" +#include "SaveGame.h" +#include "ScriptEngine.h" +#include "StringMgr.h" +#include "SymbolMgr.h" +#include "Video.h" +#endif + +class Actor; +class Audio; +class CREItem; +class Calendar; +class Console; +class Container; +class Control; +class DataFileMgr; +struct Effect; +class EffectQueue; +struct EffectRef; +class EventMgr; +class Factory; +class Font; +class Game; +class GameControl; +class ITMExtHeader; +class Image; +class Item; +class Label; +class Map; +class MusicMgr; +class Palette; +class ProjectileServer; +class Resource; +class SPLExtHeader; +class SaveGame; +class SaveGameIterator; +class ScriptEngine; +class ScriptedAnimation; +class Spell; +class Store; +class StringMgr; +class SymbolMgr; +class TableMgr; +class TextArea; +class Variables; +class Video; +class Window; +class WindowMgr; +class WorldMap; +class WorldMapArray; + +struct Symbol { + Holder sm; + char ResRef[8]; +}; + +struct SlotType { + ieDword slot; + ieDword slottype; + ieDword slottip; + ieDword slotid; + ieDword sloteffects; + ieDword slotflags; + ieResRef slotresref; +}; + +struct DamageInfoStruct { + unsigned int strref; + unsigned int resist_stat; + unsigned int value; + int iwd_mod_type; + // maybe also add the ac bonus and/or the DL_ constants +}; + +struct ModalStatesStruct { + ieResRef spell; + char action[16]; + unsigned int entering_str; + unsigned int leaving_str; + unsigned int failed_str; + bool aoe_spell; +}; + +struct TimeStruct { + unsigned int round_sec; + unsigned int turn_sec; + unsigned int round_size; // in ticks + unsigned int rounds_per_turn; +}; + +class ItemList { +public: + ieResRef *ResRefs; + unsigned int Count; + //if count is odd and the column titles start with 2, the random roll should be 2d((c+1)/2)-1 + bool WeightOdds; + + ItemList(unsigned int size, int label) { + ResRefs = (ieResRef *) calloc(size, sizeof(ieResRef) ); + Count = size; + if ((size&1) && (label==2)) { + WeightOdds=true; + } else { + WeightOdds=false; + } + } + ~ItemList() { + if (ResRefs) { + free(ResRefs); + } + } +}; + +// Colors of modal window shadow +// !!! Keep these synchronized with GUIDefines.py !!! +#define MODAL_SHADOW_NONE 0 +#define MODAL_SHADOW_GRAY 1 +#define MODAL_SHADOW_BLACK 2 + +#define WINDOW_INVALID -1 +#define WINDOW_INVISIBLE 0 +#define WINDOW_VISIBLE 1 +#define WINDOW_GRAYED 2 +#define WINDOW_FRONT 3 + +//quitflags +#define QF_NORMAL 0 +#define QF_QUITGAME 1 +#define QF_EXITGAME 2 +#define QF_CHANGESCRIPT 4 +#define QF_LOADGAME 8 +#define QF_ENTERGAME 16 + +//events that are called out of drawwindow +//they wait until the condition is right +#define EF_CONTROL 1 //updates the game window statuses +#define EF_SHOWMAP 2 //starts worldmap +#define EF_PORTRAIT 4 //updates portraits +#define EF_ACTION 8 //updates the actions bar +#define EF_UPDATEANIM 16 //updates avatar animation +#define EF_SEQUENCER 32 //starts sequencer/contingency creation +#define EF_IDENTIFY 64 //starts identify screen +#define EF_SELECTION 128 //selection changed +#define EF_OPENSTORE 256 //open store window +#define EF_EXPANSION 512 //upgrade game request + +//autopause +#define AP_UNUSABLE 0 +#define AP_ATTACKED 1 +#define AP_HIT 2 +#define AP_WOUNDED 3 +#define AP_DEAD 4 +#define AP_NOTARGET 5 +#define AP_ENDROUND 6 +#define AP_ENEMY 7 +#define AP_TRAP 8 +#define AP_SPELLCAST 9 + +/** ea relations (derivated from 2 actor's EA value) */ +#define EAR_FRIEND 0 +#define EAR_NEUTRAL 1 +#define EAR_HOSTILE 2 + +/** Max size of actor's ground circle (PST) */ +#define MAX_CIRCLE_SIZE 3 + +/** Summoning */ +#define EAM_SOURCEALLY 0 +#define EAM_SOURCEENEMY 1 +#define EAM_ENEMY 2 +#define EAM_ALLY 3 +#define EAM_NEUTRAL 4 +#define EAM_DEFAULT 5 +// +#define STAT_CON_HP_NORMAL 0 +#define STAT_CON_HP_WARRIOR 1 +#define STAT_CON_HP_MIN 2 +#define STAT_CON_HP_REGEN 3 +#define STAT_CON_FATIGUE 4 + +#define STAT_DEX_REACTION 0 +#define STAT_DEX_MISSILE 1 +#define STAT_DEX_AC 2 + +#define STAT_INT_LEARN 0 +#define STAT_INT_MAXLEVEL 1 +#define STAT_INT_MAXNUMBER 2 + +//sloteffects (querysloteffect returns it) +#define SLOT_EFFECT_NONE 0 +#define SLOT_EFFECT_ITEM 1 //normal equipped item +#define SLOT_EFFECT_FIST 2 //fist slot +#define SLOT_EFFECT_MAGIC 3 //magic weapon slot +#define SLOT_EFFECT_MELEE 4 //normal weapon slot +#define SLOT_EFFECT_MISSILE 5 //quiver slots +#define SLOT_EFFECT_LEFT 6 //shield (left hand) slot +#define SLOT_EFFECT_HEAD 7 //head slot + +//fog of war bits +#define FOG_DRAWFOG 1 +#define FOG_DRAWSEARCHMAP 2 +#define FOG_DITHERSPRITES 4 + +enum PluginFlagsType { + PLF_NORMAL, + PLF_SKIP, + PLF_DELAY +}; + +/** + * @class Interface + * Central interconnect for all GemRB parts, driving functions and utility functions possibly belonging to a better place + */ + +class GEM_EXPORT Interface +{ +private: + Holder