hide results

    Mod Developer Guide by rezzzman

    Version: 1.0 | Updated: 10/03/08 | Printable Version | Search This Guide

                       _________________________________________
                       ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
                         Vampire the Masquerade Bloodlines(PC)
    
                                 Mod Development Guide
    
                       _________________________________________
                       ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
    
                                    May 25, 2008
                                     Version 1.0
    
                            Written by:  Dheu
                                 Email:  Dheuster@gmail.com
                           Use subject:  BloodlinesDevGuide 1.0
    
              Living Document: (read before emailing anyone)
              ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
              This is a living document.
    
              http://docs.google.com/Doc?id=dhgs89mq_12hbmgkpd9
    
              If you see any mistakes, or have anything that you want to add, email
              me and I will add you to the update list. (You will need a google
              account)
    
              If you make additions/corrections, be certain to give yourself
              credit in the Contributing Authors section at the end of the
              guide.
    
    
    ______________________________________Notes____________________________________
    This Tutorial\FAQ was also published at:
    
      http://www.gamefaqs.com/
      http://www.gamewinners.com/
      http://www.cheatcc.com/
    
    Official 1.2 Patch : vtmb_1_2.exe 15.2 MB (15,973,576 bytes)
    
      http://www.vampirebloodlines.com/patch/
    
    This Document looks best in a fixed-width font, such as Courier New.
    
    Vampire The Masquerade Bloodlines is Copyright © 2004 by Activision
    
    I am not affiliated with Activision or anyone who had anything to do with the
    creation of this game.  This Document may be posted on any site. You may not
    charge for, or in any way profit from this Document.
    
    -------------------------------------------------------------------------------
                                  Table of Contents:
    -------------------------------------------------------------------------------
    
      I.       Basics
        I.1       Game Architecture
        I.2       Enabling the Game Console
        I.3       Standard Tools
        I.4       VPK Files
      II.      Getting Started
        II.1      Creating a baseline
        II.2      Unofficial Patches
        II.3      Uncompressing Game Files\Change Management
      III.     Introduction to Python
        III.1     The __main__ Object
        III.2     Executing Python commands from Console.
        III.3     Executing Console commands from Python
        III.4     Entities
        III.5     Creating Entities with Python
        III.6     Events
        III.7     Input\Output
        III.8     Bringing it all Together
        III.9     Known Bugs
      IV.      Dialogs
       IV.1       Dialog Basics
       IV.2       DLG File Format
       IV.3       Special Conditions and Colored Responses
       IV.4       Dialog Engine Commands and Flow Control
       IV.5       Considerations and Scripting
       IV.6       Dialog Engine Bugs
      V.       Dialog Audio Synchronization
        V.1       DLG, VCD and LIP Files
        V.2       VCD Internals
        V.3       LIP Internals
        V.4       Creating Custom Audio
      VI.      Animating NPCs with Python
       VI.1       Skeletal Animations
       VI.2       Facial Expressions
       VI.3       Dispositions
       VI.4       Schedules and ScriptedDisciplines
       VI.5       Gestures and interesting_place
       VI.6       scripted_sequence and logic_choreographed_scene
      VII.     Editing Models/Skins
       VII.1      Basics
       VII.2      MDL File Details
       VII.3      Working with downloaded Skins
       VII.4      Editing Skins yourself
      VIII.    Cameras and Cut scenes
      IX.      Custom Items
      X.       Miscellaneous
      XI.      Legalities
      XII.     Common Scenarios and Examples
    
    
    Frequently Asked Questions
    
    VTMB Links
    
    Appendices
    
      A.       Entity Classes
      B.       Map Names
      C.       Item Name Summary
      D.       Game States
    
    External
    
      E.      Common Models  : http://docs.google.com/Doc?id=dhgs89mq_3gtwn2chb
      F.      VCLAN Values   : http://docs.google.com/Doc?id=dhgs89mq_4fgq4nrfg
      F.5.6   VCLAN Values   : http://docs.google.com/Doc?id=dhgs89mq_5cxmzw4vg
      G       Entity Details : http://docs.google.com/Doc?id=dhgs89mq_6fmx3cxgt
      H       Animations     : http://docs.google.com/Doc?id=dhgs89mq_7dbfdbwdh
      I       Gestures       : http://docs.google.com/Doc?id=dhgs89mq_8fpssv86r
      J       Console Vars   : http://docs.google.com/Doc?id=dhgs89mq_10cfs83dqp
    
    Contributing Authors
    
    Final Words....
    
    ===============================================================================
    I. > > > >  Basics
    ===============================================================================
    
      These notes make certain assumptions about the person reading them. In short,
      I assume the reader is a programmer of some ilk, who either has python
      experience or has enough general programming experience that you can pick up
      Python from a good tutorial in about 30 min.
    
    -------------------------------------------------------------------------------
    I.1  > > > >  Game Architecture
    -------------------------------------------------------------------------------
    
      The game is composed of 5 Major parts:
    
      1) The game engine:
    
           You generally don’t touch this part. It handles rendering
           the 3D environment, managing input, output and events. VTMB
           uses an early prototype version of the Valve Source Engine for
           rendering and event management. (Basically fully functional
           except Enemy AI).
    
      2) Map files:
    
           Map files (.bsp) serve 2 purposes. They define the visual environment
           and also provide an initial set of objects within those environments.
           Eembedded objects include characters, trigger zones, sound emitters,
           invisible cameras and patrol path nodes. (If you have experience with
           Never Winter Nights editing, some of this may sound familiar.)
    
           For those that are new to this, I will simply say that there is a lot
           of invisible objects hidden on maps that you can’t see that help
           make things easier for the game engine.
    
      3) Models:
    
           Model files (.mdl) provide the look and feel of objects when rendered
           within the Map. These are read in at run time and injected into the map.
           (maps contain "pointers" to these models, but not the model data
           itself.)
    
           Some MDL files don’t contain any model data, but only contain
           animations. This is discussed more below.
    
      4) Sounds:
    
           Ever watch a movie with the sound turned off? Gets real boring real
           fast.  On the other hand, you can take a crappy looking 16 color sprite
           and give it a human voice and it adds a whole new dimension of
           interaction.  VTMB uses wav files for background music and ambient
           sounds. It uses mp3s for dialogs.
    
      5) Scripts
    
           A good scripting language is what separates RPGs from Action games.
           Scripts allow developers to compose cut scenes, conversations, remember
           choices and edit the environment based on user actions.  The scripting
           language for bloodlines is python, and it is the glue that holds the
           game together.
    
    -------------------------------------------------------------------------------
    I.2  > > > >  Enabling the Game Console
    -------------------------------------------------------------------------------
    
      Since VTMB doesn’t come with a slick Map Editor like Neverwinter Nights,
      modding the game will involve a lot of work within the game itself. For
      example, you may need to scout out locations, characters and animation
      sequences for re-use. To this end, you will need the console.
    
      To Enable the console, Right click the icon you use to start the game. Go to
      "Properties" and add –console to the target. The new launch target should
      look something like:
    
      "C:\Program Files\Activision\Vampire - Bloodlines\vampire.exe" -console
    
      STEAM USERS : Right Click VTMB link in Steam, select Properties and
                    the Set launch options. Add "-console" (without quotes)
                    to the input box. You may have problems if running VISTA.
    
      Now when you start the game, the console will appear. Hit the tilde (~) to
      Hide/unhide the console.
    
      The console comes with hundreds of commands to help developers and players
      alike. Simply hit a letter to see all commands that start with that letter.
      Type "HELP <COMMAND>" to see more info on a specific command. A full
      listing of commands and variables is included in attached Appendix J.
    
      I found the following commands invaluable during the development of my MOD.
    
          notarget [0/1]
    
              Enemies do not see you (invisibility)
    
          draw_hud 0:
    
              Removes the hud.
    
          noclip [0/1]
    
              Walk through walls. More importantly, disables the activation of
              triggers.
    
          cl_showfps 1:
    
              Shows your frame rate (important when testing models)
    
          cl_showpos 1:
    
              Shows your position and map name.
    
          picker:
    
              Quite possibly the most useful console command the game has to offer.
              Displays a bounding box around any entities you approach which show
              additional entity information such as its base class and instance
              name.
    
          maps
    
              Shows list of game maps
    
          map <map name> (no bsp)
    
              Teleport to a map. Be warned that scripts often assume a certain game
              state, so if you teleport somewhere before you would normally have
              access to it, things may break. Even starting a conversation may
              cause the game to crash.
    
          report_enties:
    
              logs a list of all entity classes currently in memory to the console.
              Unfortunately, it doesn't tell you the instance name. ( you will need
              to use some python for that)
    
          ent_info:
    
              give the name of an entity class (typically found using the previous
              command), this function will list the entities "inputs" and
              "outputs". The inputs translate to functions that you can call on
              the entity. Some entities inherit functions from their parents. In
              these situations, not all functions are seen. But it’s a good
              starting point.
    
             The outputs translate to events that the entity can detect (and
             then re-throw). You can only detect events with embedded entities.
    
             See Appendix G for a full listing of entity functions and events
    
          ent_dump <instance_name>:
    
              Given the name of an entity, prints all properties and attributes
              associated with it to console. Sometimes you have to name the
              instance first.
    
              > FindPlayer().SetName("pc")
              > ent_dump pc
    
          createplayer :
    
              Even though you may think you are starting the game over, you are in
              fact only rebuilding your own character. Once creation process is
              over, the game resumes in your last location. You will lose all
              attributes an XP.
    
          vstat get "attribute" value
    
              console method of increasing stats. Negative values are ignored.
    
      Less useful, but still interesting:
    
          ai_show_interesting
    
              Displays "interesting places" as phone-booth sized cubes scattered
              about the map. These are like magnets and pull NPCs toward them. When
              the NPC enters, they perform an action associated with the booth. The
              booths have info overlaid; typically the default action that an NPC
              will perform when they enter the "booth".
    
          cl_entityreport 1
    
              Displays a list of all entity instances. Unfortunately, most of the
              info is C++ related: C classes and vector data that we can't touch
              from python.
    
          cl_pdump #
    
              Display additional entity info on screen. Pass in the entity number
              retrieved using either picker or cl_entityreport. set to -1 to
              deactivate.
    
      For a full list of commands use : cmdlist
      For a list of both variables and commands use : cvarlist
    
      The console is connected directly to the game engine. If a command is entered
      that the engine does not recognize, it relays the command to the python
      shell. This means you can access python objects and methods from console. As
      you learn more about python, you will be able to do even more from the
      console. However trying to call python functions immediately from
      autoexec.cfg or user.cfg wont work as the shell isn’t initialized when those
      modules load. You can however define aliases and bind keys that
      will execute python functions at a later time.
    
      FREEZE/LOCKING ISSUES:
    
          Using the console outside of the game menus can make the game unstable.
          Specifically, displaying the console normally pauses the game and when
          you click on the [X] in the top right corner to remove the console,
          sometimes the game will remain paused. This behavior is mostly
          unpredictable.
    
          If this happens to you; in order to get the game to un-pause you need to
          hide the console from a game menu. IE : hit ESC, enable/hide the console,
          then hit ESC to return the game. I have also been told that going to the
          character stats window will un-pause the game.
    
          If you wish to avoid the possibility of Locking/Freezing, simply make a
          habit of going to the game menu before activating console.
    
    -------------------------------------------------------------------------------
    I.3  > > > >  Standard Tools
    -------------------------------------------------------------------------------
    
      VPKTool :
    
          === THE === modding tool for VTMB. The most recent release is version
          3.9a and you can access it here:
    
          www.strategyinformer.com/pc/vampirethemasqueradebloodlines/tool/7142.html
    
          or from Turfster's website:
    
          http://turfster.cjb.net/
    
          ** It is also included with all of WESP's patches
    
          VPKTool is really all you need for basic modding. However there are other
          tools that help with specific aspects.
    
      Python 2.1.2
    
          The scripting language that the original game used. You can download the
          original shell here. It may also have been distributed with this Dev
          Guide.
    
          http://www.python.org/ftp/python/2.1.2/
    
          The main thing here is the IDLE editor, which provides indention and
          Color coding support.
    
      DialogEditor
    
          DialogEditor is handy for visualizing character dialogs. Unfortunately
          it doesn’t allow you to edit conditions or actions, making it almost
          useless for creating dialogs. But if your goal is to update existing
          lines,  Or review spelling in new text that you did by hand, then it is a
          godsend.
    
          Bear in mind when NPCs talk, they are playing pre-recorded audio. So
          While you can change player responses, you can’t change what the NPCs
          say.  There is room for limited ADDITIONS and new material).
    
      ZVTools
    
          A set of Python scripts by ZylonBane that allow you to grab objects and
          move them around within a map. More importantly, it has the ability to
          save off all the map data. Basically the scripts allow runtime editing of
          the maps. Very cool if your mod will involve editing the maps.
    
      Now, if you want to actually EDIT models or Skins or introduce new custom
      content into the game, you may need some additional software. The original
      game was built using Maya for texturing and 3D Studio Max for models.
    
      The best free stuff that I know of is Blender3D for Model editing and Gimp
      for Image editing. There is a blender import plug-in for importing the models
      into Blender. It requires the installation of python.
    
      If you own Half Life 2, you can download the Steam Software Development Kit
      via Steam. The SDK offers tools for editing MDL models. While steams tools
      are easier to use than Blender, editing models is a complicated task for
      common users regardless of the tool.
    
      The general editing and re-skinning of models is outside the scope of this
      tutorial, however I will touch on some of the basics.
    
    -------------------------------------------------------------------------------
    I.4  > > > >  VPK Files
    -------------------------------------------------------------------------------
    
      If you look inside the Bloodlines installation directory, you will notice
      some large files with the extension .vpk. All the resources for the game are
      compressed within these files. This serves 3 purposes:
    
      1) Faster Loading:
    
         Easier to ask the OS to retrieve 1 big file than 300 small files.
    
      2) Smaller Footprint:
    
         Hey, who wants the game to take up another 5 gigs?
    
      3) User Overrides:
    
         When a user extracts a resource from the .vpk file and places it within
         the Vampires installation directory, (directory structure intact), the
         game engine will load the users uncompress version on startup instead of
         the one in the VPK file. While this design allows user mods, the downside
         is that it only allows 1 User Mod to be installed at a time.
    
    
    ===============================================================================
    II  > > > >  Getting Started
    ===============================================================================
    
      There are some pre-emptive steps that need to be taken care of before you can
      really begin modding. At a bare minimum, you must uncompress the files into a
      state in which you can make changes to them. This may be all you need if you
      are just playing around and feeling out what you can do. However, if you wish
      to share your mod with other people, you will eventually need to keep track
      of your changes, build a zip file for distribution and provide instructions
      for installing it.
    
      If you are a forward thinker, you can take some steps now to make the
      distribution and installation instructions easier to create and manage.
    
    -------------------------------------------------------------------------------
    II.1  > > > >  Creating a baseline
    -------------------------------------------------------------------------------
    
      A baseline is a snapshot of all the files in your game folder BEFORE you
      start editing them. If a user has a different version of the game than the
      one you build your mod upon, they may break their game if they install your
      mod. For this reason, Most mod developers will provide very specific
      instructions for installing their mod. These instructions generally go
      something like:
    
        1) Install a fresh copy of VTMB (you MUST uninstall if it exists)
        2) Install the Official 1.2 patch from <website> (Steam Users Ignore)
        3) Extract zip file to <installation dir>/Vampire
    
      The official 1.2 patch is supported by the Distributor, so despite being
      an external dependency (not included in your mod distribution), it is
      relatively safe to assume it will be around for many years.
    
      Unfortunately, Troika went out of business shortly after releasing VTMB so
      there is no expectation that new official patches will be release. Luckily,
      the Official 1.2 patch is still very much playable. A few misspelled words, a
      few side quests that may not open up if you don’t do things in the right
      order. However, the main quest is solid.
    
      That said, if you search around the Internet you will likely see Fan
      supported "Unofficial Patches". Read below for more info:
    
    -------------------------------------------------------------------------------
    II.2  > > > >  Unofficial Patches
    -------------------------------------------------------------------------------
    
      Unofficial patches are user built mods (much like what you may be endeavoring
      to create). Some simply fix scripting and timing issues with the original
      game. Some re-introduce content that wasn't linked into the original game.
      Some of the more advanced patches actually update the game engine and/or add
      support for higher resolutions and textures.
    
      Problems with Unofficial Patches:
    
      - Unofficial patches are NOT supported by a distributor. They are supported
        by an every day guy like yourself.  Unlike companies, human beings are
        fickle. Relying on someone else’s work creates a high-risk external
        dependency. Will the patch still be around when you are done? You must
        think forward. Not just weeks and months, but years into the future.
    
      - There is more than 1 mod out there with the term "Patch" in it. If you tell
        someone to download an unofficial "patch", you must also be certain to
        specify the AUTHOR and hope users get the right one. The safest bet is to
        use an actual url and hope the site is still up in 12 months.
    
      - Some Unofficial Patches come in the form of an installable executable. It
        is dangerous to ask users to install an executable, period. Taking the
        risk yourself is one thing, asking someone else to do the same is another.
    
      - The more installation requirements you place on users, the less likely
        someone is to take the time out to install your mod.
    
      Most of the issues above can be solved through MOD EXTENSION:
    
        Basically, you use the 1.2 patch as your baseline, but then install an
        unofficial patch/mod on top of your baseline, keeping track of all the
        files it installs/touches. When you package up your mod, you include the
        patch files as part of your distribution. By distributing the patch with
        your mod, you remove the risk of your baseline disappearing and decrease
        the work required by the user.
    
        If you DISTRIBUTE someone else’s work with yours, you should ask for
        permission. Furthermore, you should make it clear when you ask for
        permission that your mod will likely introduce modifications to their
        patch.
    
      My recommendation:
    
        I advise checking out some of the Unofficial patches. You want to avoid
        requiring users to install ANYTHING other than the official patches and
        your mod. As such, you are looking for permission to include the desired
        patch in your mod's distribution. If you can’t find an author willing to
        grant permission. Then use the Official 1.2 patch.
    
        Before I get swamped with emails, I personally used Wesp’s 5.6 "Basic"
        patch as Wesp kindly granted permission to distribute the patch contents
        with my mod.
    
        http://www.patches-scrolls.de/vampire_bloodlines.php
    
        Note that his permission was not granted for you and your mod. If you wish
        to use an unofficial patch, you must contact the author.
    
      NOTES:
    
        You CAN and SHOULD install this game twice on the same computer. To do so,
        simply install the game (and any patches), rename the directory and then
        use the games uninstaller. Now install again. You will be able to run
        both copies independently. (If you are a steam user, you may need to make
        a dos based batch script to rename the base directory so that the version
        you wish to run gets executed.)
    
    -------------------------------------------------------------------------------
    II.3  > > > >  Uncompressing Game Files\Change Management
    -------------------------------------------------------------------------------
    
      In the introduction to this section, we discussed the concept of change
      management: keeping track of what you change now so that you can create a
      distribution later.
    
      You can approach change management in one of two ways
    
      - Install the game and manually keep track of the files that you edit
    
          Typical strategy for single man teams making "small" mods that don’t
          touch many files. Unfortunately, most people don’t know how many files
          they will be touching when they begin development.
    
      - Set up software to track the changes for you.
    
          With 76,000 support files, this is recommended for anyone planning on
          making SUBSTANTIAL changes to the game. It is also a must if you plan
          on sharing development tasks with other people.
    
          Unfortunately, setting up software change management can take up to 3
          hours even with my step by step instructions. If you are unsure,
          I recommend installing 2 copies of the game and START by using
          manual change management.
    
      A) Manual change management
    
        1) Install a FRESH COPY of VTMB
           - Yes, you should uninstall first if it is already installed.
           - DO NOT run the game after install
    
        2) Install the Official 1.2 Patch
           - Go to http://www.vampirebloodlines.com/patch/
           - Download latest Patch (1.2)
           - Install, use defaults
           - DO NOT run the game after install
    
           You don’t need to keep track of changes made by the 1.2 patch,
           but just for your general information and amusement, the 1.2
           patch updates the following files:
    
               /bin/engine.dll
               /Vampire/python/vamputil.py
               /Vampire/python/warrens/warrens.py
               /Vampire/dlls/vampire.dll
               /Vampire/cl_dlls/client.dll
    
           It also adds the following vpk files:
    
               /Vampire/pack102.vpk
                 - Empty
    
               /Vampire/pack103.vpk
                 - dlg\Hollywood\isaac.dlg
                 - dlg\main characters\regent.dlg
                 - dlg\santa monica\e.dlg
                 - vdata\hackerterminals\haven_pc.txt
                 - vdata\hackerterminals\shrekhub2_terminal.txt
                 - vdata\system\infobartypes.txt
                 - vdata\system\strings.txt
    
        3) Uncompress VPK files
           - Run VPKTool
           - Goto VPK Extractor Tab
           - Check "0 length wav fix", open "<install_dir>\Vampire\pack000.vpk"
           - Right Click -> Select All
           - Right Click -> Extract
           - Repeat extraction for all VPK files IN ORDER from smallest to largest.
             ORDER IS IMPORTANT! Some files will report errors, don’t worry.
    
        4) Remove Compiled Python files (.pyc)
    
           Python source code is stored in ".pk" files. When a pk file is loaded by
           the game engine, it automatically compiles it into a .pyc file. The game
           engine knows to do this based on the time stamp of the files. If the PK
           file is newer than the PYC file, then the PYC is out of date and needs
           to be recompiled.
    
           The original VPK files don’t contain any .py scripts, but they DO
           contain .pyc files. The 1.2 Patch installs .pk files, but not
           pyc. As a result, you have a mix match: 1.2 pk files and 1.0
           pyc files. The issue is that the pyc files are 1.0, but have a
           newer timestamp, therefore the game engine will not compile the
           fixes.
    
           If I have lost you, don’t worry about it. To resolve this mix up, we
           simply delete all .pyc files under the Vampire/python subdirectory
    
    
        5) [optional] Install Unofficial Patch (that you have permission to use)
    
           - Note, whether it came as a zip or an exe,  keep a copy of the original
             patch around for later reference.
    
        6) [optional] Extract Metadata From Map(bsp) : 3-4 hours
             To add entities and other items to maps, you will need to edit the map
             data.  This is done by opening a map using VPK Tool, extracting
             its data (in text form),  changing it and then writing it back.
    
             If you extract the text data from all the maps into text versions of
             the maps upfront, you can search the data for examples of how to set
             up entities/ camera shots, event handlers etc...
    
             There are a lot of maps and it takes VPKTool a while to extract the
             data. So even streamlined, this can take 3 to 4 hours.
    
             To streamline the process, I have included a directory called meta
             with this guide that contains all the map names with the extension
             .txt. Copy the meta directory to your Vampire/maps directory.
    
             Whether you use my included files or make them yourself, once you
             have a directory of mirrored empty text files, use VPK Tool to
             open up the binary versions (in the parent directory) one at a time,
             copying the contents to the text version under meta.
    
             If you don’t have the patience, you don’t have to do this step right
             now. You can extract/save off to the meta directory AS NEEDED.
    
      B) Setting up Software Change Management
    
        1) Install tortoiseSVN
           - Go to http://tortoisesvn.tigris.org/
           - Download correct version
           - Install, use defaults
           - Restart Computer
    
        2) Create Repository
           - Open Windows Explorer
           - Create a new folder and name it. For example: C:\svnrepo (Drive should
             have at least 10 Gigs of free space)
           - Right-click on the newly created folder and select:
             TortoiseSVN -> Create repository here...
           - Choose a repo type. I used native. Berkeley wont work over a Network
             drive if you plan on splitting up development tasks with other team
             members later.
    
           A repository is then created inside the new folder. DO NOT EDIT THOSE
           FILES YOUSELF!
    
        3) Install a FRESH COPY of VTMB
           - Yes, you should uninstall first if it is already installed.
           - DO NOT run the game after install
    
        4) Install 1.2 Official Patch
           - Go to http://www.vampirebloodlines.com/patch/
           - Download latest Patch (1.2)
           - Install, use defaults
           - DO NOT run the game after install
    
        5) Uncompress VPK files
           - Run VPKTool
           - Goto VPK Extractor Tab
           - Check "0 length wav fix", open "<install_dir>\Vampire\pack000.vpk"
           - Right Click -> Select All
           - Right Click -> Extract
           - Repeat extraction for all VPK files IN ORDER from smallest to largest.
             ORDER IS IMPORTANT! Some files will report errors, don’t worry.
    
        6) Remove Compiled Python scripts
           - Traverse <install_dir>/Vampire/python directories and DELETE any .pyc
             files. (pyc = compiled python files) See Manual Version Control step 4
             above if you would like to see explanation
    
        7) Import the VTMB directory into the Repository:
           - Using Explorer, browser to C:\Program Files\Activision (or equivalent)
           - Right Click "Vampire – Bloodlines" and select: TortoiseSVN -> Import
           - Use the browse button [...] to select the REPO : (file:///C:/svnrepo)
           - Take a nap or something. Import takes about 1.5 hours.
    
        8) Check Out the Code Base:
           - Right Click "Vampire – Bloodlines" and DELETE it.
           - Right Click the C:\Program Files\Activision
           - Select "SVN checkout..."
           - Update Checkout Directory :
               C:\Program Files\Activision\Vampire - Bloodlines
           - It will begin the checkout process. Find something else to do for 1.5
             hours.
    
        9) [optional] Install Unofficial Patch (that you have permission to use)
    
           - Note, whether it came as a zip or an exe,  keep a copy of the original
             patch around for later reference.
           - If you install an unofficial patch, you need to commit the changes
             after it is installed. Commit is like telling the computer to take a
             "snapshot" of the directory. You can restore to any snapshot at a
             later time (or compare what has changed)
           - Revisit the installation directory (C:\Program Files\Activision)
           - You should see an explanation point icon over "Vampire - Bloodlines"
             (This means changes have been detected).
           - Right Click "Vampire – Bloodlines", Select:
               Tortoise SVN -> Check For Modifications. (Depending on the patch it
               may take  while.)
           - Click on the "Text Status" Table Header to re-order by that.
           - Anything marked "non-versioned" is new and has been ADDED by the
             patch.
           - Use SHIFT_CLICK to highlight all non-versioned items. Right click your
             hi-lighted list and select "Add" (Then close dialog with [OK]
           - Right Click "Vampire - Bloodlines" and select "SVN Commit..."
           - As message, put name or version of unofficial patch. Hit [OK]
    
        10) [optional] Extract Metadata From Map Files : 3-4 hours
    
             To add entities and other items to maps, you will need to edit the map
             data.  This is done by opening a map using VPK Tool, extracting
             its data (in text form), changing it and then writing it back.
    
             If you extract the text data from all the maps into text versions of
             the maps upfront, you can baseline your map data and use "Diff"
             functionality to compare changes made to maps. Furthermore, you can
             search the text based data for examples of how to set up entities/
             camera shots, event handlers etc...
    
             There are a lot of maps and it takes VPKTool a while to extract the
             data. Even streamlined, this can take 3 to 4 hours.
    
             To streamline the process, I have included a directory called meta
             with this guide that contains all the map names with the extension
             .txt. Copy the meta directory to your Vampire/maps directory.
    
             Whether you use my included files or make them yourself, once you
             have a directory of mirrored empty text files, use VPK Tool to
             open up the binary versions (in the parent directory) one at a time,
             copying the contents to the text version under meta.
    
             If you don’t have the patience, you don’t have to do this step right
             now. You can extract/save off to the meta directory AS NEEDED.
    
      C) Developer Notes : How did I create the meta directory:
    
        I installed cygwin and then:
        $ cd "/cygdrive/c/Program Files/[...]/Vampire/maps/meta"
        $ touch `ls .. –l | awk '{print $9}'`
        $ for f in *.bsp ; do mv $f `echo $f | sed 's/\(.*\.\)bsp/\1txt/'` ; done
    
    ===============================================================================
    III  > > > >  Introduction to Python
    ===============================================================================
    
      VTMB uses a stripped down version of Python 2.1.2. Like JavaScript, Python
      is a high level programming language that is relatively easy to pick up.
      Python is used to "glue" the game together. Its role is similar to that of
      an orchestra conductor. That is, it mostly conducts already existing
      objects into doing things at the right time. For example, orchestrating a
      cut scene.
    
      I recommend downloading python 2.1.2 if you will be editing the scripts in
      this game. The Python distribution will install IDLE, a nice Python editor
      and will also include documentation for that version of the language so
      that you know what other commands are available to you.
    
      However, you do not HAVE to download python. The game comes with a minimal
      python shell built in. From the console, you can type "import <filename>.py"
      and the game will automatically compile any non-compiled python scripts.
      If there are any errors, it displays the errors to the console. If you
      edited one of the python scripts that came with the game (and errors were
      detected), the game reverts to the original script distributed in the .vpk
      file.
    
      Basic python functionality is included, but advanced python modules such as
      threading and OS are not. You must be careful not to use the more advanced
      Python modules from your game scrtips (such as "re") or the game will crash.
    
      NOTES:
    
        - Python has a special object called None which represents, you guessed
          it: NOTHING. If used within a conditional, None acts the same way as a
          Boolean value of false.
    
        - Python only receives input from the C++ engine. You cannot capture input
          directly from the user (without hacky workarounds).
    
        - Console commands go strait to the C++ Engine.  Only unrecognized commands
          get sent along to the Python Shell. Be careful when defining console
          aliases.
    
          e.g.:
    
          ]a=2                    <- python assignment to new variable a
          ]alias a "echo Hello"   <- console alias a executes "echo Hello"
          ]a=3                    <- intercepted by console, results in error.
    
      For a quick tutorial on Python (lists, maps, tuples, for loops,
      method definitions, etc...) I recommend:
    
          http://www.diveintopython.org/toc/index.html
    
    -------------------------------------------------------------------------------
    III.1  > > > >  The __main__ Object
    -------------------------------------------------------------------------------
    
        The VTMB python shell is loaded when you start VTMB. At start up, the
        system initializes a standard python root level object called "__main__"
    
        Here is a list of the VTMB methods made available via the __main__ object.
    
          Entity    __main__.FindEntityByName(str name)
    
            Searches for a single entity on the map with the name specified. If
            a single entity is found, returns the Entity. If more than one Entity
            by the same name is found, throws an exception. If no entity is found
            by that name, returns None.
    
          Entity[]  __main__.FindEntitiesByName(str name)
    
            Searches for any entity by the name specified on the current map. If
            found, returns an array of Entities. If none are found, returns None.
    
            Note : The string may contain the wild card "*":
    
            Example: FindEntitiesByName("cop_*")
    
          Entity[]  __main__.FindEntitiesByClass(str class)
    
            Searches for any entity belonging to the CLASS specified on the current
            map. If found, returns an array of Entities. If none are found, returns
            None.
    
          Character __main__.FindPlayer()
    
            Returns an object handle to the Player. If the player does not exist
            (because a game has not been loaded), returns None.
    
          void      __main__.ChangeMap(string MapName)
    
            Changes Map. See Appendix B for map name listing. You can also type
            "maps" in the console for a list of maps.
    
          Entity    __main__.CreateEntityNoSpawn(str class,tpl loc,tpl facing )
    
            First step of creating a new Entity Using Python. This allows you
            to set the entity up (model, name, location, etc) before actually
            spawning it with the CallEntitySpawn() method.
    
          void      __main__.CallEntitySpawn(Entity ptr)
    
            Spawns an unspawned entity defined with CreateEntityNoSpawn.
    
          void      __main__.ScheduleTask(float delay, String Command)
    
            Threading support. Executes command in parallel to current thread. You
            can use a delay of 0.0 if you simply want to fork, or you can use
            another delay if you want to give the engine time to do something.
    
          void      __main__.SquadSeesPlayer
    
            The idea is that you enter an area with hostiles who don’t attack
            (because they cant see you).  In practice, enemies normally don’t go
            hostile till a conversation ends or you enter a trigger area. This
            command is relatively unused.
    
          bool      __main__.OneOfSet
    
            This command is used by dialogs to prevent choices from showing up
            several times. For example, maybe you have a line that can show up if
            the person has Persuasion 5 OR Seduction 5. But what if the PC has
            both? You don’t want the option showing up twice. See the dialog
            section below (VIII) for more info and an example of use.
    
        Here is a list of the variables\properties made available via the
        __main__ object:
    
          __main__.ccmd   <- Access to console commands
          __main__.cvar   <- Access to console variables
          __main__.G      <- Global storage (Remembered by Save game).
    
          NOTE : __main__.G does not exist until the user starts a new
                 game or loads a save game.
    
    -------------------------------------------------------------------------------
    III.2  > > > >  Executing Python commands from Console.
    -------------------------------------------------------------------------------
    
        So how do we access this "__main__" object? As mentioned in section I.2,
        the console is connected directly to the game engine. However, if a command
        is entered that the engine does not recognize, it is passed along to the
        python shell.
    
        This means you can access python objects and methods from console:
    
          ]pc=__main__.FindPlayer()
          ]pc.GetCenter()
    
        Furthermore, if the method is a child of __main__, you do not have to
        explicitly specify __main__. Though it is a good habit to get into and
        helps to ensure you don’t conflict with console objects.
    
          ]pc=FindPlayer()
          ]pc.GetCenter()
    
        You can also define simple 1 line python methods.
    
          ]def hello(s): print "Hello [%s]" % (s)
    
        Then you can run your function:
    
          ]hello("World")
          Hello [World]
    
        If you use the Python introspection command "dir" to examine __main__’s
        methods:
    
          ]dir(__main__)
    
        You will now see hello() as one of __main__’s methods. You will also likely
        notice a lot more methods than the small list in III.1. This is because
        most maps in VTMB load a city specific python script with helper functions.
    
        Python uses the line return to separate commands and indentation to
        indicate function blocks (scope). In other words, to create anything beyond
        a simple 1 line method, you must define your function in an external file
        and import it.
    
            |--------------------------------------------------------------------|
            |Ex: Filename = [<install root>\Vampire\python\custom.py]            |
            |--------------------------------------------------------------------|
            |import __main__                                                     |
            |                                                                    |
            |def showInstances(prefix="npc_V"):                                   |
            |    entities = __main__.FindEntitiesByClass(prefix+"*")             |
            |    print "Class                                  Name"             |
            |    print "--------------------------------------------------------"|
            |    for ent in entities:                                            |
            |        name=""                                                     |
            |        try: name=ent.GetName()                                     |
            |        except: pass                                                |
            |        if name != "":                                              |
            |            print "%s %s" % (ent.classname.ljust(35),ent.GetName()) |
            |--------------------------------------------------------------------|
    
        Once created, you can import the file using the import command:
    
        ]import custom
        ]custom.showInstances()
        Class                                  Name"
        ---------------------------------------------------------"
        ...
        ...
    
    -------------------------------------------------------------------------------
    III.3  > > > >  Executing Console commands from Python
    -------------------------------------------------------------------------------
    
        So now that you know how to use console to execute Python, how do you
        execute one of those handy console commands from python?
    
        Console commands are accessed through the ccmd variable:
    
          __main__.ccmd.<command_name>()
    
        Console variables are accessed through the cvar variable:
    
          __main__.cvar.<variable_name>=value
    
        e.g. :
    
    
            |--------------------------------------------------------------------|
            |Ex: Filename = [<install root>\Vampire\python\custom.py]            |
            |--------------------------------------------------------------------|
            |import __main__                                                     |
            |                                                                    |
            |def debugMode():                                                    |
            |  __main__.cvar.draw_hud=0                                          |
            |  __main__.cvar.cl_showfps=1                                        |
            |  __main__.cvar.cl_showpos=1                                        |
            |  try: __main__.ccmd.notarget()                                     |
            |  except: pass                                                      |
            |  try: __main__.ccmd.noclip()                                       |
            |  except: pass                                                      |
            |  try: __main__.ccmd.picker()                                       |
            |  except: pass                                                      |
            |--------------------------------------------------------------------|
    
    
        Bugs and Limitations:
    
          The cvar pointer is very useful, however the ccmd pointer is less
          useful. I have played around with the command and it does not
          appear as though the C++ handler recognizes parameters. Furthermore,
          while you can call methods that do not take parameters, any method
          called always throws an unspecified exception (however, it still
          works). Therefore you must either place the call within a try
          catch as I did above, or you can avoid the error message by assigning
          the function name to a string. This also results in the function being
          called but avoids sending the error message to console.
    
            __main__.ccmd.picker=""
    
        Hack/Work Around:
    
          Luckily, the ccmd command can be used to invoke custom aliases. If you
          define a custom alias within Vampire/cfg/autoexec.cfg that executes the
          contents of a .cfg file:
    
          alias execonsole "exec console.cfg"
    
          You can use python file io to write commands to the console.cfg file and
          then execute them using your alias.
    
            |--------------------------------------------------------------------|
            |Ex: Filename = [<install root>\Vampire\python\custom.py]            |
            |--------------------------------------------------------------------|
            |import __main__                                                     |
            |                                                                    |
            | def console(data=""):                                              |
            |   if data=="": return                                              |
            |   cfg=open('Vampire/cfg/console.cfg', 'w')                         |
            |   try: cfg.write(data)                                             |
            |   finally: cfg.close()                                             |
            |   __main__.ccmd.execonsole=""                                      |
            |--------------------------------------------------------------------|
    
            ] oldname=__main__.cvar.name
            ] __main__.cvar.name="Yukie"
            ] import custom
            ] custom.console("vclan 124")
    
            NOTES: vclan can crash the game if the appropriate model is not
                   In precache. Use with caution.
    
    -------------------------------------------------------------------------------
    III.4  > > > >  Entities
    -------------------------------------------------------------------------------
    
      From Python's perspective, all game objects are Entities: characters,
      triggers, cameras, even some script sequences are grouped together as
      entities. They come in 2 flavors: Embedded and Dynamic. Python can be used to
      manipulate either.
    
      A) Embedded Entities
    
          As the name implies, Embedded entities are embedded into the map data.
          These entities must exist before the game is started. You can remove, add
          or edit embedded entities using the map editing tools provided by
          VPKTool.
    
          After uncompressing VPK files, you will find the Map data under the
          directory:
    
              Vampire/maps.
    
          The Maps themselves are also compressed as .bsp files. Using VPKTool,
          we can futher uncompress the map data into META Data.
    
          NOTES: Generally speaking, I will open the map in question, highlight
                 all the metadata and paste it into notepad. As mentioned in the
                 Getting Started chapter, I recommend saving a new file, named
                 after the map (but with .txt extension), possibly within the sub
                 directly "/Vampire/maps/meta".
    
          The metadata is basically a laundry list of object declarations. It
          is pretty easy to read, but it is MASSIVE and can be overwhelming at
          first.
    
          What is so special about Embedded Entities? Embedded entities provide
          FULL ACCESS to all the properties and events that an entity supports.
    
          The game engine does some things when a map loads that can only be
          done at that time. For example, hooking up events. For this reason,
          you MUST USE embedded entities if you need to receive events or set some
          non-accessible properties/attributes. If you need user Interaction
          (Dialog), you MUST use Embedded Entities.
    
      B) Dynamic Entities
    
          Dynamic entities are not known to the engine when the map loads.
          Since some things can only be done when the map loads (setting up
          events), there are limitations on what can be done with dynamic
          entities.
    
          When you create a dynamic entity, it will receive default values for all
          of its' properties. Some of these can be changed. For example, you can
          change the model of a dynamically created NPC because there is a method
          called "SetModel" which provides access to that property/attribute.
    
          However, you can NOT change the dialog that an NPC speaks because there
          is no method for changing an NPC's associated dialog file.
    
          For those properties that can not be changed, you are stuck with the
          default values. The inability to change/set all properties is irritating
          and in some cases make things impossible.
    
          For example, since you can't set the dialog of a dynamic NPC, you can't
          dynamically create NPC's that the PC can talk to. If you want dialog
          interaction, you have to use embedded entities.
    
          Other notable limitations/irritations:
    
            - Dynamically spawned npc_V* classes have no clipping area (you walk
              through them like ghosts). I believe this is because the default
              "solid" property is 0 or SOLID_NONE. Luckily other entity types such
              as props ARE solid by default.
    
            - You can’t define event handlers for Dynamic entities such as
              "OnDeath"
    
            - Some entities have properties that must have valid values in order to
              successfully spawn. If the default value is invalid and you can't
              set it, then you effectively can't create that entity dynamically.
    
              Example : npc_maker
    
      C) Entity Scope
    
          Embedded or Dynamic, entities are inherently local to the map they are
          defined/created on. The GetName\SetName method mentioned in III.4 implies
          all Entities have a name. All entities CAN have a name, but they do not
          necessarily have a name, nor are their names necessarily unique.
    
          A game/map designer can guarantee that within a particular map, a
          specific entity name exists and is unique, but that is about it. Luckily,
          that is generally all you need to ensure your camera sequences and cut
          scenes work correctly.
    
          That said, if you are modding the game, you must be careful not to create
          a new Entity with the same name as an existing entity within the map,
          else you may cause Unexpected behavior.
    
    -------------------------------------------------------------------------------
    III.5   > > > >  Creating Entities with Python
    -------------------------------------------------------------------------------
    
      Spawning items from Python is a 3 step process: Create, Set Properties, Spawn
    
      A) Create :
    
         The "Creation" step is done with the method:
    
         __main__.CreateEntityNoSpawn(String classname, Location, Facing)
    
         The function takes 3 parameters:
    
           classname :  This is the main parameter. It is a string value which
                        determines what additional attributes, properties and
                        methods the entity INSTANCE will have. See Appendix A
                        for a list of known classname Strings. For this example,
                        we will use npc_VVampire.
    
           Location : A 3 float tuple. representing x,y and z coordinates. Most
                      People use the Player’s current location for the value.
    
                        ex: myloc = (1.0,2.0,3.0)
    
                            or
    
                        myloc = __main__.FindPlayer().GetOrigin()
    
           Facing : Yet another 3 float tuple. The first float Represents angle,
                    up or down, that the character is looking. (are you looking up
                    at the ceiling or down at the floor). The second is what
                    direction they are facing right to left. The 3rd is
                    forwards/backwards motion.
    
                        ex : myFacing = (0.0, 180.00, 0.00)
    
                            or
    
                        myFacing = __main__.FindPlayer().GetAngles()
    
         Code Example:
    
           pc = __main__.FindPlayer()
           loc = pc.GetOrigin()
           ent = __main__.CreateEntityNoSpawn("npc_VVampire", loc, (0,0,0) )
    
      B) Set Properties
    
         Some Entities have REQUIRED properties or the call to spawn will fail.
         NPCs require that you set a model value for example.
    
           ent.SetModel("models/character/npc/unique/downtown/vv/vv.mdl")
           ent.SetName("myEnt")
    
         In making this, I didn’t have the time to go through and test each
         entity type for required parameters, so you will just have to figure
         it out as you go. When you try to spawn an entity, you will receive
         errors messages telling you what must be set to proceed.
    
         In attached Appendix G, I provide a list of properties and methods for
         each specific classname. However, those methods do not include methods
         inherited from parents. For example, npc_VVampire extends a C++
         base Class (referred to as the Proxy Class) which extends the
         Python Character class which extends the Entity Class: Each of
         these "layers" adds more methods to the final instance.
    
         Methods provided by the npc_VVampire classname:
    
            AllowAlertLookaround        AllowKickHintUse    BarterBegin
            ChangeMasqueradeLevel       AllowOpenDoors      BarterEnd
            ClearActiveDisciplines      ChangeSchedule      Bloodgain
            FadeHeadAsCameraTarget      ClearPatrolPath     BloodHeal
            FadeBodyAsCameraTarget      FollowPatrolPath    Bloodloss
            LookAtEntityCenter          FrenzyTrigger       DisableThink
            LookAtEntityDefault         FrenzyUpdate        Faint
            LookAtEntityOrigin          Inventory_Remove    FadeToSkin
            pl_criminal_attack          LookAtEntityEye     FleeAndDie
            pl_supernatural_attack      MakeInvincible      FrenzyCheck
            pl_supernatural_flee        pl_criminal_flee    HumanityAdd
            SetBloodShieldDiscipline    pl_investigate      HungerCheck
            SetBodyAsCameraTarget       physdamagescale     MoneyAdd
            SetDefaultDialogCamera      PlayDialogFile      MoneyRemove
            SetDontFacePlayerInDialog   SetBodygroup        MoveToDest
            SetHeadAsCameraTarget       SetBossMonster      MoveToHome
            SetInvestigateMode          SetFallToGround     PlayFloat
            SetInvestigateModeCombat    SetFollowerBoss     RotateToDest
            SetMovementMultiplier       SetFollowerType     RotateToHome
            SetScriptedDiscipline       SetRelationship     skin
            SpawnTempParticle           SetSkinFadeTime     TakeDamage
            StartPlayerDialog           SetSpeechVolume     TweakParam
            StartPlayerDialogRemote     SetupPatrolType     WalkToNode
            StartPlayerDialogUnforced   StayEntrenched      WillTalk
            TeleportToEntity            UseInteresting
    
         Methods provided b the Proxy base class:
    
             Alpha                           ClearParent
             Kill                            Color
             ScriptHide                      SetFakeSilence
             SetParent                       ScriptUnhide
             SetSoundOverrideEnt             Use
    
         Methods provided by the Character base class: NOTE: From this point
         down, ALL npcs have these methods:
    
             GiveAmmo                         AmmoCount
             GiveItem                         BumpStat
             HasItem                          DialogDiscipline
             IsFollowerOf                     GetQuestState
             RemoveItem                       HasWeaponEquipped
             SeductiveFeed                    IsMale
             SetCamera                        React
             SetDisposition                   SetQuest
             SetExpression                    StartBarter
             SetGesture                       WorldMap
    
         Methods Provided by the Entity base class: NOTE: From this point
         down, ALL entities have these methods:
    
             GetAngles                        GetCenter
             GetAngleVectors                  GetModelName
             GetName                          GetOrigin
             IsAlive                          SetModel
             SetAngles                        SetOrigin
             SetName
    
         Methods Provided by the Python PyObject base object (part of the
         language spec). NOTE: All python objects support these methods:
    
             __init__()
             __getattr__()                    __setattr__()
             __dict__()                       __doc__()
    
         Attributes:
    
             All entity INSTANCES have attributes, however attributes are read-
             only. If you spawn an entity and set its name. e.g. :
    
                 ent.SetName("test"),
    
            You can use the console to see its attributes :
    
                 ent_dump "test"
    
             This will dump all attributes to the console. You can then read
             the attributes using dot notation.
    
                 ]ent.classname
                 ‘npc_VVampire’
    
             Some entities have a method called TweakParam() which will allow you
             to edit select attributes. If you scan the source code, you will see
             it only used to change vision, hearing and squad.
    
      C) Spawn:
    
          Once all desired/required properties are set, we spawn the object with
          the command:
    
              __main__.CallEntitySpawn(ent)
    
          It is important to note that despite all the methods above, not all
          npc_VVampire class attributes are being exposed. For example, there is a
          "dialogname" attribute which allows dialogs, but no method for setting it
          from python. Therefore, we can not dynamically create an NPC that can
          have a dialog with the PC using the standard dialog engine.
    
          In the case of npc_maker, the class requires you to set an NPCType
          attribute to spawn, however there is no setter from python. Therefore the
          entity can’t be created dynamically at all.
    
          See Section XII : Common Scenarios,  for a more robust spawn example
          function.
    
    -------------------------------------------------------------------------------
    III.6  > > > >  Events
    -------------------------------------------------------------------------------
    
      The game engine supports hundreds of events. However, only embedded entities
      receive events. I will tell you upfront that there is no way to receive or
      handle game events from python ALONE. If you wish to receive/handle events
      you will HAVE to work with events already established or embed new entities
      into the maps.
    
      A) ENTITIES:
    
          The vast majority of entities only handle events meant for the entity
          itself. For example, if you embed an npc_Vhuman (see Attached Appendix
          G), you can set it up so that it receives an OnDialogBegin event and
          calls a python method:
    
          {
          "classname" "npc_VHuman"
          "targetname" "customNPC"
          ...
          "OnDialogBegin" ",,,0,-1,OnBeginDialog('customNPC'),"
          }
    
          That is rather strait forward, but what about the awkward string with
          commas? Here is a break down of the event protocol;
    
          "<entity name>,<method>,<param>,<delay>,<max fires>,<python script>,"
    
          <entity name> : Can be any named entity within the same map. The name
                          should NOT be in quotes.
          <method>      : Name of a valid method on the target entity to fire. Each
                          entity supports different methods. See attached Appendix
                          G for a full listing.
          <param>       : Optional parameter to pass into the method. If the method
                          requires more than 1 parameter, you have to use the
                          <python script> option.
          <delay>       : Delay in seconds before calling the method. Can be
                          floating point value.
          <max fires>   : As the name implies, maximum number of times the event
                          will fire.  A value of –1 means there is no maximum and
                          it will always fire.
          <python script> Call a global or module specific python method.
    
          Example: To create an opponent that begins a conversation with you when
                   They Lose half of their health the first time, but not a second
                   time:
          {
          "classname" "npc_VVampire"
          "targetname" "customNPC"
          ...
          "OnHalfHealth" " customNPC, StartPlayerDialogRemote,,0,1,,"
          }
    
          To activate multiple methods, you simply embed the event handler multiple
          Times. Here we disable any vampire disciplines when we start the convo.
    
          {
          "classname" "npc_VVampire"
          "targetname" "customNPC"
          ...
          "OnHalfHealth" " customNPC, StartPlayerDialogRemote,,0,1,,"
          "OnHalfHealth" " customNPC, ClearActiveDisciplines,,0,-1,,"
          }
    
      B) Player Events:
    
          It is cool that you can receive events about entities you create, but
          what about the player? You don’t exactly create the player? There is a
          special entity called events_player that you can plug into a map to
          receive player events. (From Appendix G):
    
          events_player
    
          EVENTS:
    
          output: OnFrenzyBegin
          output: OnFrenzyEnd
          output: OnWolfMorphBegin           (Animalism War Form)
          output: OnWolfMorphEnd             (Animalism War Form)
          output: OnPlayerTookDamage
          output: OnPlayerKilled
          output: OnPlayerSoundLoud
          output: OnActivateAuspex
          output: OnActivateCelerity
          output: OnActivateCorpusVampirus   (Blood Buff)
          output: OnActivateFortitude
          output: OnActivateObfuscate
          output: OnActivatePotence
          output: OnActivatePresense
          output: OnActivateProtean
          output: OnActivateAnimalismLvl1
          output: OnActivateAnimalismLvl2
          output: OnActivateDementationLvl1
          output: OnActivateDementationLvl2
          output: OnActivateDominateLvl1
          output: OnActivateDominateLvl2
          output: OnActivateThaumaturgyLvl1
          output: OnActivateThaumaturgyLvl2
    
          METHODS:
    
          input: EnableOutputs
          input: DisableOutputs
          input: CreateControllerNPC
          input: RemoveControllerNPC
          input: AwardExp
          input: ClearDialogCombatTimers
          input: ImmobilizePlayer
          input: MobilizePlayer
          input: RemoveDisciplines
          input: RemoveDisciplinesNow
          input: MakePlayerUnkillable
          input: MakePlayerKillable
    
      C) World Events
    
          Like events_player, the events_world object lets you key into certain
          global events: (From Appendix G):
    
          events_world
    
          EVENTS:
    
          output: OnCopsOutside
          output: OnCopsComing
          output: OnStartCopPursuitMode
          output: OnEndCopPursuitMode
          output: OnStartCopAlertMode
          output: OnEndCopAlertMode
          output: OnStartHunterPursuitMode
          output: OnEndHunterPursuitMode
          output: OnMasqueradeLevel1
          output: OnMasqueradeLevel2
          output: OnMasqueradeLevel3
          output: OnMasqueradeLevel4
          output: OnMasqueradeLevel5
          output: OnMasqueradeLevelChanged
          output: OnPlayerHasNoBlood
          output: OnCombatMusicStart
          output: OnCombatMusicEnd
          output: OnAlertMusicStart
          output: OnAlertMusicEnd
          output: OnNormalMusicStart
          output: OnNormalMusicEnd
          output: OnUseBegin
          output: OnUseEnd
    
          input: SetSafeArea
          input: SetCopWaitArea
          input: SetCopGrace
          input: SetNosferatuTolerant
          input: SetNoFrenzyArea
          input: AIEnable
          input: FadeGlobalWetness
          input: HideCutsceneInterferingEntities
          input: UnhideCutsceneInterferingEntities
          input: PlayEndCredits
          input: ClearDialogCombatTimers
    
          Of all the events, the two I personally found the most useful were
          OnCombatMusicStart and OnNormalMusicStart. These (not so well named
          events) basically signify the start and end of combat. If you maintain
          an "InCombat" flag, you can also use OnBeginNormalMusic to indicate
          when a map has been loaded from a save game.
    
      D) Other/Misc Events
    
          You may have noticed there are a few events missing, like entering a
          map, the player going stealth or the player picking up an item. I wont
          pretend to possess a comprehensive knowledge of all events, but I can
          tell you about what I have discovered and some workarounds I have
          created.
    
          For entering maps, you can embed a logic_auto entity:
    
          {
          "classname" "logic_auto"
          " spawnflags" "0"
          "OnMapLoad" ",,,0,-1,OnEnterMap('sm_hub_1'),"
          "origin" "-2420.54 -2558.76 -111.97"
          }
    
          ** ALL Maps (.bsp) already have a logic_auto entity embedded into them
             which loads a python script associated with the city that the map is
             located in. Hence the subdirectories under Vampire/Python.
    
          Note that OnEnterMap will only fire when actually traversing into a map
          from another map. Loading a save game will not cause the event to fire.
          However, when you load a save game, OnBeginNormalMusic will still fire.
    
          For detecting item pickup, you can use a trigger_inventory_check:
          {
          "classname" "trigger_inventory_check"
          "targetname" "inventory_check"
          "StartDisabled" "0"
          "spawnflags" "1"
          "itemname" "item_w_tire_iron"
          "OnPlayerHasItem" "inventory_check,Disable,,0,-1,,"
          "OnPlayerHasItem" " popup_35,OpenWindow,,0.5,-1, ,"
          }
    
          Some events are not supported. For example, detecting that the player
          has gone stealth (crouches).  You can design workarounds for some. For
          stealth, you can poll certain states using a logic_timer entity:
    
         {
         "classname" "logic_timer"
         "StartDisabled" "0"
         "UseRandomTime" "0"
         "RefireTime" "15.0"
         "OnTimer" ",,,0,-1,OnPollEvent(),"
         "origin" "-2420.54 -2558.76 -111.97"
         }
    
         (In vamputil.py)
         stealth=0
         def OnPollEvent():
             global stealth
             pc = __main__.FindPlayer()
             crouched = ((pc.GetCenter()[2] - pc.GetOrigin()[2]) == 18)
             if not stealth:
                 if (pc.active_obfuscate or crouched):
                     stealth=1
                     OnStealthBegin()
             else:
                 if (not pc.active_obfuscate and not crouched):
                     stealth=0
                     OnStealthEnd()
    
      E) Events to Avoid:
    
          There isn’t an easy to detect leaving a map as most maps have multiple
          exit points. The PC may be teleported away to a different map by a door,
          an area trigger, a conversation. There about half a dozen ways to exit an
          area and you would have to search and update every entity on the map and
          every dialog associated with every entity on the map. Even then you may
          miss something embedded in a python event script. In general, I would
          avoid designing a mod that relies on the ability to detect when the
          player leaves an area.
    
          Globally detecting events that are normally defined on a per-entity basis
          is generally a no no.  For example, detecting when things die. That would
          involve lots of map edits. The more stuff you edit, the greater the
          chance for bugs.
    
      D) Companion Mod
    
          I don’t mean to plug my mod, but as I built my companion mod it
          occurred to me that others may want to make mods as well (hence this
          guide). I added the events described above to pretty much every map.
          But instead of having them call my specific functions, I had them
          call global methods that I defined in vamputil.py. That way other
          mod developers could hook into those events easily.
    
          If you are doing something small that only impacts 1 map, you will
          probably want to keep your mod small and only make the edits to that map
          But if your goals are larger and you need events like I describe above
          throughout the entire game, you may consider downloading my mod
          and starting from there as a lot of the groundwork has already been
          done.
    
    -------------------------------------------------------------------------------
    III.7  > > > >  Input\Output
    -------------------------------------------------------------------------------
    
      In general, direct user input/output from python is a no-no. I describe some
      workarounds below, but most of these are theoretical and have not been
      tested.
    
      A) INPUT:
    
         Within VTMB, input is generally done using dialogs. You enter a
         conversation with someone, make some choices and the dialog system
         fires some of your python methods as a result.
    
         VTMB does not support receiving user input DIRECTLY into python. There
         is a hacky work around, but I don’t recommend it. Still I will describe it
         below for completeness:
    
         Building upon the console hack I described in section III.3, you can save
         off the users current configuration, rebind keys to python methods to pass
         key press events into python
    
         data  ="host_writeconfig backup.cfg\n"
         data+="unbindall\n"
         data+=’bind "1" "OnInput(\’1\’)"\n’
         data+=’bind "2" "OnInput(\’2\’)"\n’
         data+=’bind "3" "OnInput(\’3\’)"\n’
         data+=’bind "4" "OnInput(\’4\’)"\n’
         data+=’bind "ENTER" "OnInputFinish()"\n’
         data+=’bind "ESC" "OnInputCancel()"\n’
         console(data)
    
         and then later, when you are done, restore the keyboard config:
    
         data="exec backup.cfg"
         console(data)
    
         The primary danger with this is if the user exits the game before
         finishing your home-made user input prompt, they may lose their
         configuration. There are ways to mitigate the danger (look for backup.cfg
         within vamputils and restore/delete if found).
    
         Even with a mitigation strategy, ultimately this is a kludge. Hopefully
         you can design your mod so that you do not need user input beyond what can
         be gained using a normal dialog file.
    
      B) OUTPUT:
    
         I am aware of 4 ways of getting data to the user.
    
         1) hud_timer objects can be used to show time or any number to the
            user that can fit within a clock like sequence (00:00:00). For
            example, I used this in my companion mod to show hits remaining
            when you possess a traveling companion or how much time is
            left before you can re-feed. FYI : There is a HUD Counter, but
            I could never get it to work.
    
         2) game_sign objects rely on files which describe a background
            and text. Typically, the files are pre-fabricated within your
            vdata directory and game_sign entities that reference the files are
            embedded into maps. You call the OpenWindow() method on them to make
            them appear.
    
            One downside of a game sign is that the game pauses while the sign is
            up.
    
         3) If you look up the game_sign entity in Appendix G, you will notice that
            it has a ChangeFile() method on it. Using Python file io, you can
            dynamically construct a sign file, save it off, create a game_sign
            entity on the fly (doesn’t have to be embedded), Change the file to the
            temp file you created and then call open window. This elaborate work
            around would allow you to open professional looking text presentation
            windows to the user from python without edits to the map files.
            Combined with the (mitigated) input hack above, you could simulate a
            decent looking user prompt.
    
            Bear in mind that game signs pause the game. So If you were to use this
            hack to create your own dialog system, you wouldn't be able to animate
            NPCs for emotion, nor would their mouths move if you told them to
            speak a dialog line. There may be other uses for the hack, but I
            haven’t thought of any yet.
    
         4) For simple feedback, you can use the console hack in conjunction with
            the "say" command to have text print to the upper left corner of the
            hud. The text will appear to have been said by the current value of
            __main__.cvar.name.
    
    -------------------------------------------------------------------------------
    III.8  > > > >  Bringing it all together
    -------------------------------------------------------------------------------
    
      So we can call both python AND console commands. We can access both python
      and console variables. This means you can tackle most tasks using one
      approach or the other. If you get stuck or run into a bug, you have a
      backup plan.
    
      For example, take the vclan console command. If you issue that command and
      the clan tries to load a model that isn’t currently in memory, you get
      a pre-cache error and the game crashes. This holds true for other commands
      like npc_create and the console version of SetModel. However, if you change
      the pc model using Python, the model is loaded into memory and the crash does
      not occur.
    
      In the case of creating new objects, you can use python to be more precise
      about What you want. You can assign a name, location, facing, initial model,
      hearing,  vision, squad and health. (health is done by changing the cvar
      sk_basenpctroika_health variable before and after spawn)
    
          A) Useful Console Commands (to call from python)
    
              autosave
    
                  self explanatory
    
              shake
    
                  brief earthquake (think explosion)
    
              player_immobilize/player_mobilize
    
                  self explanatory
    
              fadein/fadeout
    
                  self explanatory
    
              vclan <clan number>  :
    
                  change clan of protagonist. Also resets stats to 1/0
    
              npc_freeze :
    
                 "Freezes" npc under crosshair.  You can then search all npcs
                 (examine npc.playbackrate) to discover which one the PC is looking
                  at Nice work around for "grappling" NPCs from python.
    
              teleport_player "entity_name"
    
                  Needed when you wish to teleport the PC somewhere and set their
                  facing. Bear in mind that pc.SetOrigin() also works for
                  teleportation within a map, but pc.SetAngles() does not. (all the
                  time) This console command correctly changes the facing.
    
          B) Useful Player Objects and Commands
    
              ]pc=__main__.FindPlayer()
    
              Enables the following commands:
    
              ]pc.WorldMap(int WorldMap_State)
    
                  Opens up the world map city chooser for teleporting to
                  various cities.  WorldMap states vary thoughout the game. The
                  "up to date" value is generally maintained by
                  __main__.G.WorldMap_State , however you can enter whatever
                  value you want.
    
              ]pc.BumpStat(string "attribute_name" , int value)
    
                  Can be used to RAISE pc stats. Negative values are ignored.
    
                  NOTE: In order to lower a stat, you must "reset" the player
                        using the console command vclan and then restore stats.
                        You can get the players current clan number using
                        "pc.clan"
    
              ]pc.GiveItem(string "item_name")
    
                  Give yourself an item, provided you have the patients to look up
                  its item code. The console version "give" is actually nicer as it
                  auto-lists all the built in item codes.
    
                  Examples:
    
                      item_w_deserteagle
                      item_w_flamethrower
                      item_w_rocketlauncher
                      item_w_fireaxe
                      item_p_occult_dexterity
    
              ]pc.giveAmmo(string  "item_w_...",int amount)
    
                  Name pretty much says it all. It should be noted that giveAmmo
                  works on stackable items such as bloodpacks.
    
              ]pc.SetModel(string "model_path")
    
                  Unlike the console version, this one wont crash the game if the
                  model is not already in memory.
    
              ]pc.HumanityAdd(#)
    
                  Works for both positive and negative numbers.
    
              ]pc.Bloodgain(#)
    
                  Gives PC blood. When the game starts, the PC has a maximum blood
                  pool of 15
    
              ]pc.Bloodloss(#)
    
                  Take blood away from PC.
    
              ]pc.MoneyAdd(#)
    
                  Gives PC money
    
              ]pc.BloodHeal
    
                  Activates the bloodheal discipline even if you don’t have it.
                  Heals a small amount of health for some of your blood pool
                  points.
    
              ]pc.ChangeMasqueradeLevel(#)
    
                  Accepts both positive and negative values. Negative is good.
                  Positive is bad.
    
              ]pc.CalcFeat(string type)
    
                  Feat values are the result of skills, attributes and items. This
                  function does the calculation for you.
    
                  Example:
                      if pc.CalcFeat("haggle") == 3
    
      NOTES:
    
        Additional pc controls can be installed on a per map basis by injecting
        an events_player object into a map (if one is not found). See III.5.B
        above.
    
        If you name the PC:
    
        ]__main__.FindPlayer().SetName("pc")
    
        you can retrieve a list of PC attributes using the ent_dump command:
    
        ]ent_dump "pc"
    
    -------------------------------------------------------------------------------
    III.9  > > > >  Known Bugs
    -------------------------------------------------------------------------------
    
      Make no mistake, VTMB is a C++ based game. The engine is not written in
      Python and the objects exposed to python are merely proxies to the actual
      objects in C++. Your control over the objects is limited to what the game
      engine exposes to you (via the input methods). This confused me at first as I
      thought I could set/change every attribute I saw using the ent_dump command
      from Python. This simply isn’t true. (Though you can use vstats to change
      many of the player attributes).
    
      Bugs
    
        So I built a mod and then during testing, the game kept crashing when
        the player would change maps. 2 days later, I traced the bug down. Here
        was the problem:
    
        __main__.G.somestring=FindPlayer().GetName()        <-- OK
        __main__.G.someint=FindPlayer().clan                <-- OK
        __main__.G.somefloat=FindPlayer().GetOrigin()[2]    <-- OK
        __main__.G.someinstance=FindPlayer()                <-- NOT OK
    
       The issue here is that FindPlayer() returns a runtime instance. The same
       can be said for FindEntityByName, FindEntitiesByName and
       FindEntitiesByClass.
    
       __main__.G gets saved with your save game. While you can store properties of
       entities as part of a save game, you can not save off any instance pointers
       or you will crash the game.
    
       This rule holds true for G and sub attributes of a G (hash maps, lists, etc)
       or Entities attributes as they are saved off as part of the save game.
    
       Module scope global variables and local scope variables are NOT
       saved as part of the save Game and are therefore safe: You temporarily
       store instances references.
    
       The "G" Delimma:
    
       Vamputil.py gets loaded with the game and its methods are available from
       every map, so it makes sense to add your module imports there. However,
       there is a snag. Vamputil gets imported by the game before the game has set
       up the persistent "G" variable.
    
       This causes 2 issues.
    
       ISSUE 1:
    
       Take this scenario:
    
          VAMPUTIL.PY:
    
            import foo
    
          FOO.PY:
          from __main__ import *
    
          def somefunction():
              G.persist = "1"
    
      The issue is that when the game imported foo, G didn’t exist. So when foo
      imported * from __main__, it didn’t get G. when the function gets called,
      an exception will be thrown. Two possible workarounds. One is to import G
      locally from methods that access it.
    
          def somefunction():
              from __main__ import G
              G.persist = "1"
    
      The other work around is to ONLY import __main__ and not import anything from
      It. Reference elements of __main__  explicitly from the code. This causes a
      runtime lookup and avoids the exception.
    
          FOO.PY:
          import __main__
    
          def somefunction():
              __main__.G.persist = "1"
    
      ISSUE 2:
    
      If a variable does not exist on G, that is fine. You can test for the
      variables using a simple if statement:
    
      If not __main__.G.somevariable:
          __main__.G.somevariable=somethingelse
    
      However, complex data structures will throw exceptions. For example
    
      If not __main__.G.somehasmap["valiue"]: exception thrown
    
      Many times a module needs to initialize and setup lists and
      hashmaps managed by the module when the player first starts the
      game. Of course we have the issue above: G doesn’t exist when
      vamputils loads. So initialization needs to happen from a secondary
      method that happens after vamputils loads.
    
      There are 2 solutions.
    
      First solution is to embed a logic_auto entity in one of the early maps
      (sm_pawnshop_1.bsp -> Your apartment at the beginning of the game)
      and have it call your initialization method there.
    
      {
      "classname" "logic_auto"
      "spawnflags" "0"
      "OnMapLoad" ",,,0,1,yourmodule.initialize(),"
      "origin" "-2420.54 -2558.76 -111.97"
      }
    
      The second solution is similar to the first. But instead of installing your
      own logic_auto entity, you simply add your code to
      python/santimonica/santimonica.py which basically gets loaded by a logic_auto
      entity that is already installed in the map.
    
      Minor Invincibility bugs:
    
      There are 4 ways of making the pc invincible. You can use the console command
      "buddha". This allows the PC to get down to 1 hit point, but not actually
      die. There is an events_player method "MakePlayerUnkillable()". This allows
      The PC to get down to 1/4 their total hit points, but they will not go lower.
      There is the console command "god" which prevents the player from taking any
      damage period. And then there is the console variable debug_no_damage, which
      when set to true prevents anyone from taking damage (even enemies).
    
      Of these four methods, the first two only work if the PC is one of the 7
      standard clans. If you use vclan to change to a non-standard clan, then
      buddha and MakePlayerUnkillable will not stop the PC from dying.
    
    
    ===============================================================================
    IV > > > > Dialogs
    ===============================================================================
    
    -------------------------------------------------------------------------------
    IV.1 Dialog Basics
    -------------------------------------------------------------------------------
    
      So we have talked about Python and writing scripts, but how do we activate
      those scripts? The most common way within this game is a dialog.
    
      A) Where are they?
    
          Dialog files end with the extension ".dlg". Once you unpack the VPK
          files, you will find the Dialog files for the various game characters
          located under Vampire/dlg/...
    
      B) How are dialogs made accessible in game?
    
          Dialogs must be connected to an embedded game entity. The only way to
          add a new dialog to the game is to embed a new entity that links to your
          new dialog.
    
          Alternatively, you can edit an existing dialog that is already connected
          to an existing game entity. If you are new to this and you aren't
          comfortable messing with map files just yet, the second approach is a
          good way to get started.
    
      C) Can I change dialogs while the game is running?
    
          YES! You can!. While python scripts are compiled once per game (and then
          cached), dialogs are loaded into a special dialog engine each time you
          start a dialog. So if you make edits to a dialog, the next time you start
          the dialog, the edits will immediately be seen.
    
    -------------------------------------------------------------------------------
    IV.2 DLG File Format
    -------------------------------------------------------------------------------
    
      If you open up a dialog file, you will see a 13 column table defined with
      "{" and "}":
    
          { }{ }{ }{ }{ }{ }{ }{ }{ }{ }{ }{ }{ }
    
      The FIRST column is the row index. The first row should always
      have an index value of 1:
    
          {1}{ }{ }{ }{ }{ }{ }{ }{ }{ }{ }{ }{ }
          {2}{ }{ }{ }{ }{ }{ }{ }{ }{ }{ }{ }{ }
          {11}{ }{ }{ }{ }{ }{ }{ }{ }{ }{ }{ }{ }
    
          Not all row numbers have to be accounted for (There can be skips
          to allow people to add responses), however, all rows should appear in
          descending order.
    
      The SECOND column is what the NPC/PC says if the Protagonist is Male
    
          {1}{ Male : NPC Statement}{ }{ }{ }{ }{ }{ }{ }{ }{ }{ }{ }
          {2}{ Male : PC Response 1}{ }{ }{ }{ }{ }{ }{ }{ }{ }{ }{ }
          {3}{ Male : PC Response 2}{ }{ }{ }{ }{ }{ }{ }{ }{ }{ }{ }
    
      The THIRD column is what the NPC/PC says if the Protagonist is Female
           #  MALE   FEMALE
          {1}{Male}{ Female : NPC Statement}{ }{ }{ }{ }{ }{ }{ }{ }{ }{ }
          {2}{Male}{ Female : PC Response 1}{ }{ }{ }{ }{ }{ }{ }{ }{ }{ }
          {3}{Male}{ Female : PC Response 2}{ }{ }{ }{ }{ }{ }{ }{ }{ }{ }
    
      The FOURTH column contains an integer value which redirects to another
      row within the dialog file if the PC chooses that response. The special
      character # means it is an NPC statement
    
           #  MALE      FEMALE      NEXT
          {1}{NPC Male}{NPC Female}{ # }{ }{ }{ }{ }{ }{ }{ }{ }{ }
          {2}{PC Male }{PC Female }{ 5 }{ }{ }{ }{ }{ }{ }{ }{ }{ }
          {3}{PC Male }{PC Female }{ 7 }{ }{ }{ }{ }{ }{ }{ }{ }{ }
          {4}{        }{          }{   }{ }{ }{ }{ }{ }{ }{ }{ }{ }
          {5}{NPC Male}{NPC Female}{ # }{ }{ }{ }{ }{ }{ }{ }{ }{ }
          {6}{        }{          }{   }{ }{ }{ }{ }{ }{ }{ }{ }{ }
          {7}{NPC Male}{NPC Female}{ # }{ }{ }{ }{ }{ }{ }{ }{ }{ }
    
          When scanning for responses, the engine stops when it hits a row with a
          "#". The blank row is not necessary, but has been added to improve
          readability.
    
      The FIFTH column is used for conditions. You can use python or one of the
      built in special conditions (See IV.2 below for Special Conditionals)
    
          The engine supports "and" logic operations using "and"
    
              {1}{M}{F}{5}{pc.armor==3 and npc.classname=="npc_VVampire"}
    
          The dialog engine supports "or" logic operations using "or", but the
          entire condition must be contained in parenthesis:
    
              {1}{M}{F}{5}{(pc.armor==3 or npc.classname=="npc_VVampire")}
    
          You wont see many "or" statements throughout the dialogs because you can
          achieve the same effect by using 2 lines.
    
              {1}{M}{F}{5}{(pc.armor==3}
              {2}{M}{F}{5}{(npc.classname=="npc_VVampire"}
    
          But what if both conditions are true? You don't want the same dialog line
          appearing twice. To solve this issue, the game engine provides a special
          command called "OneOfSet)". It is made specifically for the dialog engine
          and ensures only 1 dialog out of several will appear (the first one to
          evaluate true).
    
              {1}{M}{F}{5}{(pc.armor==3 and OneOfSet(1,2)}
              {2}{M}{F}{5}{(npc.classname=="npc_VVampire" and OneOfSet(2,2)}
    
    
      The SIXTH column allows you to execute 1 line of python script. If it is an
      NPC statement, the script will execute when the NPC says the statement. If it
      is within a choice, the script will only execute if the player chooses the
      choice.
    
          Example Script: (In vamputils.py)
    
              def pcFollow(npc):
                  npc.SetFollowerBoss("!player")
    
              def pcNFollow(npc):
                  npc.SetFollowerBoss("")
    
          Example Dialog:
    
              {1}{NPC}{NPC}{#}{                     }{              }{}{}{}{}{}{}{}
              {2}{sit}{sit}{1}{npc.IsFollowerOf(pc) }{pcNFollow(npc)}{}{}{}{}{}{}{}
              {3}{go }{go }{1}{!npc.IsFollowerOf(pc)}{pcFollow(npc) }{}{}{}{}{}{}{}
              {4}{end}{end}{}{                      }{              }{}{}{}{}{}{}{}
              {5}{   }{   }{ }{                     }{              }{}{}{}{}{}{}{}
    
      The REMAINING COLUMNS (7-13) provide specific responses based on the PC's
      class.
    
          Column 7 = Brujah
          Column 8 = Gangrel
          Column 9 = Nosferatu
          Column 10 = Toreador
          Column 11 = Tremere
          Column 12 = Ventrue
          Column 13 = Malkavian
    
          Note that using these columns does not allow gender specific responses
          Which is one of the reasons they are not utilized more throughout the
          game. (If you want to use these, you can still SIMULATE gender specific
          responses by using starting conditionals. See IV.4).
    
    -------------------------------------------------------------------------------
    IV.3  Special Conditions and Colored Responses
    -------------------------------------------------------------------------------
    
      The special conditionals include:
    
          F_Seduction #
          M_Seduction #
          Seduction #
          Intimidate #
          Dominate #
          Persuasion #
          Dementation #
          Thaumaturgy #
          Humanity #
    
      Special conditions mean that the ability is equal to or greater than
      the number. Most result in responses with special colors/fonts. If
      the number is negative, then it means less than (the positive value
      of) the number and the font/color is not applied.
    
      You can perform "and" Boolean logic with the special conditionals,
      but you must use the special "&" operator:
    
          {1}{M}{F}{5}{Seduction 3 & pc.armor==3 and npc.class=="npc_VVampire"}
    
    
      You can NOT use "or" logic conditionals with the special conditions,
      however you can use separate lines and the "OneOfSet" command to
      simulate or conditionals:
    
          {1}{M}{F}{5}{(Seduction 5 & OneOfSet(1,2)}
          {2}{M}{F}{5}{(Persuation 5 & OneOfSet(2,2)}
    
    
      NOTES :
    
          The Dominate condition normally only appears if the player is Ventrue. If
          you wish to have dominate options appear for Tremere as well, there are
          two approaches:
    
           1) The best approach is to simply include a Thaumaturgy based
              response that also depends on a minimum dominate ability. You wont
              hear the domination sound and the blood points wont be subtracted,
              but the response will still be red.
    
              --------------------------------------------------------------------
              {5}{M}{F}{9}{ Dominate 4                                    }{...
              {6}{M}{F}{9}{ Thaumaturgy 1 & getattr(pc,"base_dominate")>3 }{...
    
           2) An alternate approach uses a dialog engine bug you can exploit:
    
              --------------------------------------------------------------------
              {5}{M}{F}{9}{ Dominate 4
              {5}{M}{F}{9}{ getattr(pc,"base_dominate")>3 and IsClan(pc,"Tremere"}
    
              The main 2 things to note here is that BOTH LINES HAVE THE SAME LINE
              NUMBER BUT ONLY 1 LINE EVALUATES TO TRUE. When the game engine
              parses the file, it scans the conditions and the text Content in two
              separate scans. When 2 lines have the same number it confuses the
              engine and treats both lines the same.
    
              From my experience, it seems the number of lines from the top of the
              file (not the line number or which condition is actually true)
              dictate which line controls how the text displays for both responses.
              Sometimes it is the top and sometimes it is the bottom. You have to
              test and possibly swap to get this trick to work.
    
              Unlike the previous approach, not only will the response be red, it
              will execute the dominate animation and produce the dominate sound
              when selected. It will even subtract the correct number of blood
              points. The really interesting thing about this bug is that it
              doesn't matter if the PC even has the dominate ability. All that
              matters is that only 1 condition evaluates to true and both lines
              have the same number (this can be used with any of the special
              abilities to color lines).
    
              I hesitated to even mention this exploit because the problem with it
              is that overuse quickly makes a mod un-maintainable. Dialogs
              containing this exploit are super-sensitive to editing. If someone
              adds new lines above your condition, the engine may suddenly begin
              using the wrong line to display. Having to re-test ALL the conditions
              every time the dialog is edited is cumbersome to say the least. So I
              recommend the first method, unless you are squeezed for space and
              only have room for 1 additional line number.
    
    -------------------------------------------------------------------------------
    IV.4  Dialog Engine Commands and Flow Control
    -------------------------------------------------------------------------------
    
      A) AUTO-END
    
         The auto-end command will automatically end the dialog if the command
         is reached. It is embedded in the response section surrounded by
         brackets.
    
             Below, there nothing but a single AUTO-END in the "Everyone" column,
             so the dialog will always end after the NPC's statement.
    
                 {201}{(STATEMENT MALE)}{STATEMENT FEMALE }{#}{ }{}{}{}{}{}{}{}{}
                 {202}{(AUTO-END)}{}{0}{}{}{}{}{}{}{}{}{}
    
    
             In the second example below, the conversation will end unless the PC
             is a female.
    
                 {201}{(STATEMENT MALE)}{STATEMENT FEMALE }{#}{ }{}{}{}{}{}{}{}{}
                 {202}{(AUTO-END)}{I Don't Know}{0}{}{}{}{}{}{}{}{}{}
    
    
      B) AUTO-LINK
    
         The auto-link command will automatically link to a new line in the dialog
         when it is reached.
    
             Below, there nothing but a single AUTO-LINK in the "Everyone" column,
             so the dialog will link to the next NPC statement.
    
                 {201}{(STATEMENT MALE)}{STATEMENT FEMALE }{#}{ }{}{}{}{}{}{}{}{}
                 {202}{(AUTO-LINK)}{}{211}{}{}{}{}{}{}{}{}{}
    
             Here is an advanced example. If the npc is following the pc and
             female, you will see a "no" response. If the npc is following the pc
             and male, the dialog forwards to 211. If the npc is NOT following
             the pc and the pc is male, you autolink to 221, otherwise you end.
    
                 {201}{(STATEMENT MALE)}{STATEMENT FEMALE }{#}{ }{}{}{}{}{}{}{}{}
                 {202}{(AUTO-LINK)}{No}{211}{pc.IsFollower()}{}{}{}{}{}{}{}{}
                 {202}{(AUTO-LINK)}{}{221}{pc.IsMale()}{}{}{}{}{}{}{}{}
                 {202}{(AUTO-END)}{}{0}{ }{}{}{}{}{}{}{}{}
    
    
      C) "..."
    
         "..." is a reserved NPC statement that basically represents no response.
         It mostly comes into play when an NPC statement does not have an
         associated audio file (discussed later).
    
         Normally when an NPC statement does not have an associated audio file,
         the text displays on the screen. Essentially the engine turns on
         sub-titles for the remainder of the dialog. To be certain that the
         player had a chance to read the text, the engine will prompt the
         user even if the dialog would normally end.
    
         For example, the following would show the NPC saying ".." and prompt
         the user to hit 1) to continue (if line 201 did not have any associated
         audio):
    
                 {201}{.. }{.. }{#}{ }{}{}{}{}{}{}{}{}
                 {202}{(AUTO-END)}{}{0}{ }{}{}{}{}{}{}{}{}
    
    
         However, 3 periods and the dialog would end without prompting the user,
         even if there was no audio.
    
                 {201}{... }{... }{#}{ }{}{}{}{}{}{}{}{}
                 {202}{(AUTO-END)}{}{0}{ }{}{}{}{}{}{}{}{}
    
    
      D) STARTING CONDITION
    
          At the bottom of most dialog files, you will find many lines marked as
          (STARTING CONDITION). When a dialog is loaded, the dialog engine searches
          for a block of STARTING CONDITIONALS at the end of the dialog. They are
          evaluated from top to bottom. As soon as the first condition evaluates to
          true, the engine is redirected to the NPCs first line. Note that you can
          not chain STARTING CONDITIONS (they can not redirect to another starting
          condition block.
    
          Example:
    
              ...
              {2000}{(STARTING CONDITION)}{}{10}{ not pc.IsMale()}{}{}{}{}{}{}{}{}
              {2001}{(STARTING CONDITION)}{}{20}{ pc.Charisma>2()}{}{}{}{}{}{}{}{}
    
          Above, if the pc is female, you go to dialog at 10. If the PC is male and
          has decent charisma, you go to dialog at 20. If none of the starting
          conditions is true, the dialog attempts to start at the beginning of the
          file (line 1). Using a system like this, you could design clan specific
          responses for both genders.
    
          Code placed in the 6th column does NOT get executed with starting
          conditionals. HOWEVER, the conditional supports python and there is
          nothing to stop you from executing code in the conditional block
    
          This is mostly useful for fixing npcs before dialogs start:
    
             {200}{(STARTING CONDITION)}{}{0 }{ mySetup(npc)   }{}{}{}{}{}{}{}{}
             {201}{(STARTING CONDITION)}{}{10}{ not pc.IsMale()}{}{}{}{}{}{}{}{}
             {202}{(STARTING CONDITION)}{}{20}{ pc.Charisma>2()}{}{}{}{}{}{}{}{}
    
          (In Vamputils.py)
          def mySetup(npc):
              # do whatever
              return 0
    
          Notice that the line number of the first STARTING CONDITION is 0. When
          this is the case, the Starting Condition is evaluated but its result is
          ignored and it moves on. In other words, it doesn’t matter if
          mySetup(npc) returns 0 or 1, however we return 0 to be on the safe side.
    
      E) NOTES
    
          You can simulate secondary starting conditional blocks by creating a
          silent audio response (The line MUST have an associated sound file
          that plays nothing) followed by a group of (Auto-Link) lines with
          conditionals.
    
              {10}{[silence] ...}{[silence] ... }{#}{}{}{}{}{}{}{}{}{}
              {11}{(Auto-Link)}{(Auto-Link)}{20}{pc.Charisma==1}{}{}{}{}{}{}{}{}
              {12}{(Auto-Link)}{(Auto-Link)}{30}{pc.Charisma==2}{}{}{}{}{}{}{}{}
              {13}{(Auto-Link)}{(Auto-Link)}{40}{pc.Charisma==3}{}{}{}{}{}{}{}{}
              ...
    
              {2000}{(STARTING CONDITION)}{}{10}{ not pc.IsMale()}{}{}{}{}{}{}{}{}
              {2001}{(STARTING CONDITION)}{}{50}{ pc.IsMale()()}{}{}{}{}{}{}{}{}
    
          You can force what amounts to closed captioning for a specific
          dialog by first redirecting to a line with no associated audio file.
          When a line has no audio, close captioning is automatically turned on.
          It remains on until the dialog is over, even if the next response has
          audio.
    
              {10}{...        }{...        }{#}{}{}{}{}{}{}{}{}{}
              {11}{(Auto-Link)}{(Auto-Link)}{20}{pc.Charisma==1}{}{}{}{}{}{}{}{}
              {12}{(Auto-Link)}{(Auto-Link)}{30}{pc.Charisma==2}{}{}{}{}{}{}{}{}
              {13}{(Auto-Link)}{(Auto-Link)}{40}{pc.Charisma==3}{}{}{}{}{}{}{}{}
              ...
    
            {2000}{(STARTING CONDITION)}{}{10}{ not pc.IsMale()}{}{}{}{}{}{}{}{}
            {2001}{(STARTING CONDITION)}{}{50}{ pc.IsMale()()}{}{}{}{}{}{}{}{}
    
    -------------------------------------------------------------------------------
    IV.5  Considerations and Scripting
    -------------------------------------------------------------------------------
    
      A) Four choices per NPC Statement
    
          The dialog engine will display at most 4 responses. So you must be
          careful with conditionals to make sure you don’t introduce a bug. For
          example, a high level character could meet your Intimidation, Persuasion,
          Seduction and Domination criteria, making other responses unavailable.
          People also cheat.  Don’t assume just because it is the first
          conversation of the game that they will have "at most" some specific
          value.
    
          General rule of thumb : When possible, try to handle both branches of a
          conditional response (see below) so that you can accurately
          control/predict the number of responses and link to "<More...>" as
          a 4th response if more than 4 options may be possible.
    
              {1}{NPC Male}{NPC Female}{# }{                }{}{}{}{}{}{}{}{}
              {2}{PC Male }{PC Female }{5 }{ Persuasion 4   }{}{}{}{}{}{}{}{}
              {3}{PC Male }{PC Female }{5 }{ Persuasion -4  }{}{}{}{}{}{}{}{}
              {4}{        }{          }{  }{                }{}{}{}{}{}{}{}{}
              {5}{NPC Male}{NPC Female}{# }{                }{}{}{}{}{}{}{}{}
    
          You can also control the number of responses using the OneOfSet()
          command discussed earlier.
    
          The common scenario : You have a default way of getting through a
          dialog, but you want to offer a shortcut or special reward to someone
          who has invested into a character with Dialog Skills.
    
          You could display and handle lines for all possibilities (like above),
          or you could allow the user to take advantage of their most powerful
          dialog skill and not bother displaying the other options.
    
              {1}{Male }{Female}{#}{                            }{}{}{}{}{}{}{}{}
              {2}{max1 }{max 1}{10}{                            }{}{}{}{}{}{}{}{}
              {3}{max 2}{max 2}{20}{Dominate 3   & OneOfSet(1,4)}{}{}{}{}{}{}{}{}
              {4}{max 2}{max 2}{30}{Persuasion 5 & OneOfSet(2,4)}{}{}{}{}{}{}{}{}
              {5}{max 2}{max 2}{40}{Seduction 4  & OneOfSet(3,4)}{}{}{}{}{}{}{}{}
              {6}{max 2}{max 2}{50}{Intimidate 3 & OneOfSet(4,4)}{}{}{}{}{}{}{}{}
              {7}{max 3}{max 3}{60}{ }{}{}{}{}{}{}{}{}
    
          This allows you to keep track of when you might need to use a "<More>".
          Again, these are just ideas of how to help manage potential dialog
          bugs. In practice, you will normally end up using some combination
          of the examples above.
    
      B) Scripts and Fail fast execution
    
          When the conditional contains an AND, it uses FAILFAST execution. This
          means if the first condition fails, the second condition is not even
          executed. This is important if your function also performs additional
          hidden tasks. We will talk more about this later.
    
          When dealing with lots of conditionals it is also good practice to make
          the first response a de facto non-conditional response that ensures the
          game can continue.
    
      C) Advanced tricks
    
          For profession grade responses, you can build python functions to help
          guarantee all responses are seen. Basically you make some methods
          that execute with every displayed line to keep track of how many
          lines have displayed. Then, at the right moment, display more and
          link to the remaining choices.
    
          Example Script:
    
              |-------------------------------------------------------------------|
              |Filename = [<vampire root>/python/vamputil.py]                     |
              |-------------------------------------------------------------------|
              |import dialogutil                                                  |
              |-------------------------------------------------------------------|
    
              |-------------------------------------------------------------------|
              |Filename = [<vampire root>/python/dialogutil.py]                   |
              |-------------------------------------------------------------------|
              | from __main__ import Character                                    |
              | def _Reset(self):                                                 |
              |     self.dialogcounter=0                                          |
              | def _Count(self):                                                 |
              |     if self.dialogcounter == 3: return 1                          |
              |     self.dialogcounter += 1                                       |
              |     return 1                                                      |
              | def _IsMax(self):                                                 |
              |     if self.dialogcounter==3:                                     |
              |         self.Reset()                                              |
              |        return 1                                                   |
              |     return 0                                                      |
              | Character.Reset  = _Reset                                         |
              | Character.Count  = _Count                                         |
              | Character.IsMax  = _IsMax                                         |
              |-------------------------------------------------------------------|
    
    
          Example Dialog:
    
              {1}{Opening Line}{Opening Line}{ #  }{npc.Reset()}{}...
              {2}{Response1   }{Response1   }{    }{npc.Count()}{}...
              {3}{<More...>   }{<More...>   }{ 3  }{npc.IsMax()}{}...
              {4}{Response2   }{Response2   }{    }{npc.Count()}{}...
              {5}{<More...>   }{<More...>   }{ 5  }{npc.IsMax()}{}...
              {6}{Response3   }{Response3   }{    }{npc.Count()}{}...
              {7}{<More...>   }{<More...>   }{ 7  }{npc.IsMax()}{}...
              {8}{Response4   }{Response4   }{    }{npc.Count()}{}...
              {9}{<More...>   }{<More...>   }{ 9  }{npc.IsMax()}{}...
              {10}{Response5  }{Response5   }{    }{npc.Count()}{}...
              {11}{<More...>  }{<More...>   }{ 11 }{npc.IsMax()}{}...
              {12}Nevermind   }{Nevermind   }{    }{           }{}...
    
          Thanks to fail fast, as long as you put the npc.Count() method last
          within the conditional (5th) column, it will ensure the line isn't
          counted if an earlier condition fails. IE:
    
          {#}{Response  }{Response   }{    }{npc.IsMale() and npc.Count()}{}...
    
          If the line doesn't display because the npc is female, then it isn't
          counted.
    
          Also notice that there is a <More...> every other line that redirects to
          itself.  And a Reset at the beginning.
    
          This allows you to build large, dynamic and complex dialogs without
          even having to think about weather a dialog option will appear or
          not.
    
    -------------------------------------------------------------------------------
    IV.6 Dialog Engine Bugs
    -------------------------------------------------------------------------------
    
      1) Do not assign to 2 dimensional arrays or 2 dimensional hash maps from
         dialog Files. Bug in the dialog engine seems to reset the array to null.
    
          IE:
    
          module.My2DArray[1][2]="3"  <- will cause engine to set variable
                                           My2DArray to null.
    
          Wrap alterations to complex data structures with python methods. (This
          is a better practice anyway)
    
          module.MyArraySetter(1,2,"3")
    
      2) Calling an npc_maker’s Spawn method will not work while the PC is in a
         dialog. There is no error, it simply will not spawn anything.
    
          Workarounds:
          - Use ScheduleTask() to delay spawn()
          - Call spawn from NPCs OnDialogEnd Event (If it is an NPC that you
            embed/create).
    
      3) Single letter responses are ignored by the dialog engine.
    
    
          Example: None of these lines will display nor will any of their
                   conditions even execute for evaluation.
    
                  {1}{Some Question}{Some Question}{#}{}{}{}{}{}{}{}{}{}
                  {2}{A  }{1  }{10}{IsFollower("E")    }{}{}{}{}{}{}{}{}
                  {3}{B  }{2  }{20}{IsFollower("Lily") }{}{}{}{}{}{}{}{}
                  {4}{C  }{3  }{30}{IsFollower("VV")   }{}{}{}{}{}{}{}{}
                  {5}{D  }{4  }{40}{IsFollower("Knox") }{}{}{}{}{}{}{}{}
    
                  You need at least 2 non-space, non-tab letters for the engine
                  to recognize the line. For example, placing a period at the
                  end of response can fix the bug.
    
                  {1}{Some Question}{Some Question}{#}{}{}{}{}{}{}{}{}{}
                  {2}{A. }{1. }{10}{IsFollower("E")    }{}{}{}{}{}{}{}{}
                  {3}{B. }{2. }{20}{IsFollower("Lily") }{}{}{}{}{}{}{}{}
                  {4}{C. }{3. }{30}{IsFollower("VV")   }{}{}{}{}{}{}{}{}
                  {5}{D. }{4. }{40}{IsFollower("Knox") }{}{}{}{}{}{}{}{}
    
    
    ===============================================================================
    V > > > > Dialog Audio Synchronization
    ===============================================================================
    
    -------------------------------------------------------------------------------
    V.1 DLG, VCD and LIP Files
    -------------------------------------------------------------------------------
    
      So you make your dialog file and text appears on the screen when you begin a
      conversation with your game entity (be it a person or a telephone). But how
      do you make it actually speak?
    
      In reality, the dialog engine involves 3 files:
    
          DLG : DLG files are discussed in the previous section. They link to
                embedded entities and thus invoke the dialog engine. They
                coordinate what NPC say and provide the text for the PC
                response choices. When subtitles are turned on, normally
                the text appearing in the DLG file appears for the NPCs as
                well.
    
          VCD : VCD files are general purpose sequence and synchronization files.
                They are used by several VTMB game components including cut scenes,
                animations and dialogs. They can do almost anything, but in
                regards to dialogs, they are mostly used to specify what audio file
                plays when an NPC makes a statement.
    
          LIP : Lip files break out how to move the mouth as the associated mp3 is
                playing audio. They also provides subtitles when the player is not
                interacting with the dialog engine. (This is most common during
                cut scenes that involve spoken audio. The introduction is a good
                example).
    
      DLG files implicitly look for VCD files based on their file path.
    
      Suppose the dialog file is located at:
    
          <VTMB DIR>/Vampire/dlg/goo/foo.dlg
    
      The dialog engine will look for VCD files under the directory:
    
          <VTMB DIR>/Vampire/sound/character/dlg/goo/foo/
    
      Furthermore, if the dialog engine is displaying an NPC statement at line
      10 of foo.dlg, it will specifically look for the file :
    
          <VTMB DIR>/Vampire/sound/character/dlg/goo/foo/line10_col_e.vcd
    
      Hopefully the file paths are obvious, but just in case I will break it
      down.
    
          An Embedded entity links to the file "dlg/goo/foo.dlg".
          The game engine will never look for files outside the
          Vampire directory. For this reason, "Vampire/" is considered
          the BASE DIRECTORY for dialog files and is not included in
          the derived search path.
    
          After the embedded entity invokes the game engine, the game engine
          searches for a VCD file under "/Vampire/sound/character/".  So in
          this case,  "/Vampire/sound/character/" is considered the BASE
          DIRECTORY for the dialog engine.
    
       The VCD file name will follow the pattern:
    
          line<index>_col_<speaker>.vcd
    
          <index>   : Line number that the NPC response appears on in the
                      corresponding .dlg file. By line number I mean the number
                      that appears in the first column of the line, NOT the
                      physical distance from the top of the file.
    
          <speaker> : Has several possible values:
    
                      e : everyone/default
                      f : female (if PC is female)
                      n : nosferatu (if PC is nosferatu)
    
                      In practice, only the 3 above are ever used (though
                      the engine may support more)
    
        The dialog engine will not actually look for a LIP file directly.
        If a VCD file is found, it looks for a LIP file only if the
        VCD has a speak event, and then the name of the lip file is based
        on the name of the audio file and not on the name of the dialog file.
    
        What this means is, you can add new spoken NPC statements to the game by
        simply making a new line number in the DLG fle and creating a corresponding
        VCD file. If the VCD file points to existing NPC audio, you wont have to
        include the audio (mp3) or the lip file with your mod. This is handy to
        know as it can help to keep your mod size down.
    
    -------------------------------------------------------------------------------
    V.2 VCD Internals
    -------------------------------------------------------------------------------
    
      As mentioned earlier, VCD files provide general sequence and synchronization
      capabilities. There is A LOT that could be said about these files. However,
      all we care about right now is how they help us with dialogs. What is the
      minimum that you have to do to get audio to play?
    
      The first line of a VCD file identifies an actor block.
    
          actor "Vandal"
          {
          }
    
      The actor is the NAME of the in game entity that the VCD data will be applied
      to. This is important. The audio that we set up will play at the location of
      the entity named "Vandal".  However, if the entities name that you are
      talking to is not Vandal, no audio will play. Another problem that can happen
      is if there is more than 1 entity on the map with the same name. The engine
      will play the audio at the first entity it comes across, which may or may not
      be the one standing in front of you.
    
      The VCD files target actor is hard coded into the VCD file and can not be
      changed at runtime. Luckily, you CAN rename entities at runtime using the
      SetName() command.
    
      Within the actor block are many different channels. Channels are groups of
      events that all start at the same time and execute simultaneously. VCD files
      support numerous channels, but the main one we are concerned with here is
      creating a channel with a speak event. The syntax looks like so:
    
          actor "Vandal"
          {
            channel "My unique channel name"
            {
              event speak "My unique event name"
              {
                time 0.000000 14.461179
                param "character/dlg/santa monica/vandal/line551_col_e.wav"
                param2 "70dB"
                fixedlength
              }
            }
          }
    
      The text after channel and event speak is arbitrary. It is whatever you want
      to call the channel. If you wish to stick with game conventions, any channel
      with a speak event is called "Speech" and NPC speak events are called: "NPC
      Line".
    
          time   : When to start and stop within the associated sound file. If you
                   want to use a small inner sound sample from another existing
                   sentence, you can specify the start/stop range here without
                   having to bloat your mod with a new mp3 file.
    
                   You can also use the time range to shave time off a slightly
                   unsynchronized audio file without bloating the upload with large
                   audio files.
    
          param  : In the case of Speech, the event parameter is the name of a wav
                   file. YOU ALWAYS SPECIFY A FILE WITH A >WAV EXTENSION. If an mp3
                   file is detected in the target directory, it will be used in the
                   wav files place,  but if you specify mp3, it will not work.
    
          param2 : The volume level of the sound as it plays.
    
          fixedlength : I have no idea what this is or why it is there (how could
                        audio not be fixed length?). Still. it is there in almost
                        all the VCD files, so we go with the flow.
    
      You can also set up additional channels for executing gestures or facial
      animations on the entity while the audio is playing.
    
          channel "Gestures"
          {
            event gesture "A little something extra"
            {
              time 0.000000 14.666667
              param "ACT_CONVERSE_NORMAL_TALK"
            }
          }
    
          channel "Expressions"
          {
            event expression "A smiling finish"
            {
              time 12.000000 14.666667
              param "vandal"
              param2 "Joy"
              event_ramp
              {
                1.0000 0.0000
              }
            }
          }
    
          Note that expressions and Gestures stop when the VCD is finished
          "playing". If you want an expression/gesture to "stick" (angry woman with
          arms crossed for example), you will want to add SetDisposition to the 6th
          column of the DLG line. See the sections below for more information on
          gestures, expressions and dispositions.
    
      You can also use channels for altering the volume of the audio to create
      inflection or correct audio issues without having to redistribute the audio
      files. (Of course if you are distributing the audio for the first time, you
      should try to get it right so that alterations are not needed).
    
          channel "Speech Triggers"
          {
            event loud "Get louder here"
            {
              time 10.110000 10.240000
              param "0.130"
            }
            event loud " Get louder here as well"
            {
              time 10.320000 10.429999
              param "0.110"
            }
          }
    
      You will occasionally see a bonerename parameter at the end of the actor
      block. I'm not certain why this is, but I suspect that some sequences or
      animations may rename bones for their animation and the bonerename is added
      to vcd files to be certain that the bones are called what the animation
      expects them to be. If you see bonerename in one of the characters existing
      vcd files, I recommend doing it as well in your vcd file. And if you don't
      see it in the characters existing vcd files, you probably don't need to do it
      in your files either.
    
          actor "Vandal"
          {
            ...
            bonerename "Bip01" "Bip01"
          }
    
    -------------------------------------------------------------------------------
    V.3 LIP Internals
    -------------------------------------------------------------------------------
    
      As mentioned in part A above, the LIP file that gets loaded is based on the
      name of the wav file that the speak event is associated with. To keep life
      simple we always place the VCD, LIP and MP3 files into the same directory
      together. (and name them with the same convention). This convention is used
      by the main quest and thus is probably a good idea if you want to keep your
      mod code maintainable.
    
      Here is a small example lip file: I have spaced things for easy reading, but
      in reality you SHOULD NOT add scope spacing.
    
          ------------------------------------------------------
          |VERSION 1.2                                         |
          |PLAINTEXT                                           |
          |{                                                   |
          |    "Whoa"                                          |
          |}                                                   |
          |WORDS                                               |
          |{                                                   |
          |    WORD Whoa 0.000 1.000                           |
          |    {                                               |
          |        119 w 0.000 0.250 1.000 0                   |
          |        652 ah 0.250 0.750 1.000 0                  |
          |        596 ao 0.750 1.000 1.000 0                  |
          |    }                                               |
          |}                                                   |
          |EMPHASIS                                            |
          |{                                                   |
          |}                                                   |
          |CLOSECAPTION                                        |
          |{                                                   |
          |    english                                         |
          |    {                                               |
          |        PHRASE unicode 12 " W h o a "  0.000 1.000  |
          |    }                                               |
          |}                                                   |
          |OPTIONS                                             |
          |{                                                   |
          |    voice_duck 1                                    |
          |    speaker_name Neo                                |
          |}                                                   |
          ------------------------------------------------------
    
      PLAINTTEXT :
    
          A summary of the sentence, likely copied from the dialog file. The
          reality is that the contents of PLAINTEXT are completely arbitrary and
          have no impact on the game at all. The Plaintext block doesn't get used
          by anything nor does it need to match the WORDS block or the
          CLOSECAPTION block. (Though it is convenient when it does).
    
      WORDS :
    
          A breakdown of how the mouth moves with the scene. The text that follows
          the WORD definition doesn't have to match anything (PLAINTEXT OR
          CLOSECAPTIONING), however it normally does in practice to help keep the
          file maintainable.
    
          Each word is described using one of several dozen mouth pose aliases with
          timing info.
    
              119 w 0.000 0.250 1.000 0
    
          The first 2 elements describe the mouth pose. At the time of this writing
          (version 1.0), I do not have a comprehensive list of mouth poses.
          However, they appear to be global and you can use words\mouth poses said
          by any model on any other model.
    
          The third and fourth element are your start and stop time.
    
          I'm not certain what the last 2 numbers are, but some dialogs have a zero
          at the end and some dialogs do not. I find that the presence of the zero
          tends to affect the articulation emphasis of the models mouth. I have
          also found when copying and pasting words from various dialog files that
          mixing and matching lines that end with 0 and lines that don't end with
          zero will normally crash the game at runtime. You can remedy this by
          making them all the same.
    
          Finally, if a dialog is crashing, I found that often times removing or
          adding the zero to the end of the line will stop the crash. (if it had a
          0, remove or if it didn't have a zero, add).
    
          My best guess is that the last 2 numbers are an event_ramp. IE: how
          fast/slow the mouth slides into and out of the pose. When the 0 isn't
          there, it likely takes on a default value.
    
      CLOSECAPTION :
    
          The closecaption block actually does get used. However, it is ONLY used
          when the audio is part of a cut scene. If the lip/audio is being executed
          as a result of an NPC dialog, the NPC Statement within the DLG file takes
          priority over the closecaption block.
    
          As you can see, the CLOSECAPTION block contains a Unicode phrase
          including string length and time signatures for when it should appear.
    
          The most important thing to mention here is the Unicode string itself. It
          may appear that the Unicode is simply a normal string with spaces between
          the characters. But in reality, the spaces are null characters.
    
          When python prints a string, it shows non-printables using their
          escaped ASCII values. So from python the command:
    
              >>> str('"Whoa"').encode("utf-16")[2:]
    
          yields:
    
              "\x00W\x00h\x00o\x00a\x00"\x00
    
               ** NOTICE that the double quotes are also Unicode **
    
          however, in Word or notepad, the string appears as:
    
              " W h o a "
    
    
         To make matters worse, even once you understand what is really there, most
         text editors wont let you insert a null character into your text.
    
         The good news is that you don't NEED close captioning for 95% of the game
         text. If you know for certain that your audio is intended to support a DLG
         conversation, then I recommend that you leave the area blank:
    
             CLOSECAPTION
             {
             }
    
         If you ARE creating audio for a cut scene (or you are obsessive), you have
         two options. You can use a Hex Editor, which will allow you to see and
         insert the null characters, or you can use my lipedit utility.
    
         If you downloaded the zip version of this guide, a file called
         "lipedit.py" should be under the tools directory.
    
         Before running the script, prepare the lip file with a non-Unicode
         sentence:
    
             CLOSECAPTION
             {
             english
             {
             PHRASE unicode "Whoa" 0.000 1.000
             }
             }
    
             NOTE : No indentation and no phrase length. However, the timing info
                    is present.
    
         Once the lip file is prepped, use Explorer to find the lipedit.py script,
         right click it and Select "Edit with IDLE"
    
             ** If you do not see "Edit with IDLE", then you need to install
                python 2.1.2. (See I.3 - Standard Tools)
    
         Two windows will pop up. CLOSE the one that contains the source code.
    
         Now within the empty python window type:
         >>> import lipedit.py
         >>> lipedit.fix('C:\Path\input.lip','C:\Path\output.lip')
    
         Obviously you should replace the path info with the full absolute path of
         your input and output lip files.
    
         This will read in, parse and update the unfinished Unicode block,
         including updating the character count.
    
      OPTIONS :
    
          speaker_name : The name that the subtitle system will place next to
                         spoken text during a cut scene if subtitles are turned
                         on. It does NOT have to actually match the name of the
                         entity. (hence why it is "optional")
    
          voice_duck   : ??? - No idea, but it is present in all the lip files,
                         so I include it.
    
    -------------------------------------------------------------------------------
    V.4 Creating Custom Audio
    -------------------------------------------------------------------------------
    
      This is a little outside the scope of this tutorial, but why not.
    
      A) Tools I used:
    
        - Python 2.1.2 : (if you haven't already installed it)
    
        - WinAmp : (Don't use media player. It locks the files)
    
        - CoolEdit 96 : There are a plethora of other sound editing tools. This
                        just happens to be my favorite. Simple to use and gets the
                        job done without too many extras.
    
        - DbPowerAmp : Mp3 to wave/wave to mp3 conversion (I use release 10)
    
                NOTE : VTMB mp3 audio is 44100Hz Mono at 64Kbps, constant bitrate.
    
      B) Full Audio or Partial Audio?
    
          Having no audio at all is better than having bad audio. Even if the
          script is good, bad audio will detract from the players ability to use
          their imagination. If the player is speaking to a sexy model but it
          sounds like a dude speaking in his falsetto, I don’t care what the girl
          is saying, the person playing the game wont be able to get into it.
    
          A nice middle of the road strategy that I have seen: Have the first few
          words spoken by the character, but not the whole sentence. Most people
          have enough imagination to carry out the rest of the sentence with the
          established voice.
    
              IE: "Yes? How may I help you?"
    
              With just the word "Yes" being spoken at the beginning.
    
         This strategy will save you hours, days, if not weeks and months of dialog
         development time. It will also give you more freedom in designing your
         dialogs. Only the opening line even needs audio. Secondary lines dont need
         anything.
    
         *** And if you use VCD files to point to segments of existing audio, you
             will decrease the size of your mod as you wont be including full
             audio.
    
         There is a catch. If a line has audio, it normally doesn't display. So to
         get your hybrid lines to display you need to activate subtitles.
    
         I find the best way to do this is to direct the dialog engine to an
         intermediate line with "..." (that has no associated audio).  This will
         activate the subtitles. Once subtitles are activated within a dialog, they
         stay on till the end of the dialog. You can then redirect to the real line
         with your segmented audio.
    
         e.g.
    
         {10}{...}{...}{#}{}{}{}{}{}{}{}{}{}
         {11}{(AUTO-LINK)}{}{12}{}{}{}{}{}{}{}{}{}
         {12}{Yes, what... }{Yes, what... }{#}{}{}{}{}{}{}{}{}{}
         {13}{Response 1   }{Response 1}{}{}{}{}{}{}{}{}{}{}
         {14}{Response 2   }{Response 1 }{#}{}{}{}{}{}{}{}{}{}
         ...
         {25}{(STARTING CONDITION)}{ }{10}{1 }{}{}{}{}{}{}{}{}
    
    
      B) Conversation Design
    
          Normally when designing dialog for a game or mod, you would lock yourself
          in a room with a whiteboard or at least a lot of paper and brainstorm on
          what should be said by who and when... And in time you develop your
          scripts. Then you would send your scripts off to a recording studio to be
          spoken, and recorded by professional actors.
    
          However, most mod developers don’t have the luxury of some things like
          funding.  Thus, many people will be looking to make use of existing audio
          and animation data.
    
          The MAIN difference is how you go about designing your dialogs. Instead
          of designing a dialog and then having it recorded, you listen to existing
          audio with a particular conversation or even just a moment in mind. You
          let the existing material guide you. This holds true whether you use a
          hybrid or a full audio approach. The material will provide the
          brainstorming. In fact you may not even know how you are going to get
          from point A to point B, but after hearing a few usable sound bytes, the
          ideas start to form.
    
          It is frustrating for people used to being in full control of their
          conversations, but quality wise, building on top of what is there and
          within its context is generally best.
    
      C) Breaking out Sound Bytes
    
        Start by finding the target characters sound files. After you extract the
        .vpk files. Normally they are found somewhere under :
    
            <VTMB INSTALLATION>/Vampire/sound/character/
    
        Copy all associated files (mp3, lip and vcd) to a working directory. Then
        uncompress all the mp3 audio as wav files and delete the mp3 files.
    
        If you wish, you can create a single text file with all the spoken text.
        The lip files contain the sentences. You can do  this the hard way (by
        hand) or if you have installed cygwin, you can do it the easy way:
    
          - Start cygwin,
          - Use "cd" to change directory to the working directory
              ( /cygdrive/c/... )
          - from the working directory type:
    
            $ for f in *.lip ; do echo $f; sed –n 4p $f; done > compiled.txt
    
        compiled.txt will be created with all the spoken text.
    
        (Be certain to open compiled.txt with WordPad the first time you open it)
    
        By my 3rd conversation, I stopped bothering with the text files because I
        came to realize that word searches rarely helped me as they didn't tell me
        anything about the tonality, the speed, the emotion.  What was more
        effective was opening each wav file up and trimming out re-usable, stand
        alone words and phrases. I used the file name to help describe what the
        audio was. I would also label the line number and the trim mark in the
        file name:
    
            help_141_0.932.wav
            me_311_5.876.wav
            gasp_did_you_hear_that_521_7.234.wav
    
        This will normally take an entire evening.
    
        Now from a single directory, you could double click on files and see if the
        tonality is correct for certain combinations of words. This will allow you
        to form new sentences more effectively. Winamp allows you to queue things
        up easily by CTRL + Clicking wav files.So you can experiment and see how
        the end product might sound before you spend time appending everything
        together with CoolEdit.
    
        If you need a specific word that doesn't exist, you can often piece it
        together by using 2 different words with similar syllables.
    
            Example : subterfuge + admit = submit
    
        But occasionally, you just have to admit defeat and try to think of a
        different word.
    
      D) Re-Composition
    
        This is the hard part. As I mentioned before, when I trimmed the files, I
        kept track of the trim marks. CoolEdit does a good job of showing you the
        selected area in the bottom right corner of the app:
    
        [ -_--_--################-___-_- ]
                  ^              ^
                  |              |
                2.345          6.789
    
        Each wav file has a lip file with data in it that tells the engine how to
        move the mouth with the words. Problem is, the time signatures in the lip
        files will not match up with the timing of your new sentence. If I trimmed
        the example above and I wanted to START a sentence with the word that used
        to reside at 2.345, I would need to subtract 2.345 from all the time
        signatures within the lip file.
    
        And, I actually did that for my first conversation. Took 3 days to make 3
        lines.
    
        So, I decided to make a python script to do it for me. It is named
        lipedit.py (You may recognize this name if you have already read the
        dialog section). The script comes with the zip version of this Guide and
        should be located in the tools/ directory.
    
        To use it, you will have to have installed python (See standard Tools in
        Section I). Right Clip lipedit.py, chose "Edit with idle". Two Python
        windows will pop up. Close the one with the source code in it. In the
        (mostly emtpy) window type:
    
        >>> import lipedit
        >>> lipedit.fix("C:\\input\\file.lip","C:\\output\\file.lip",-2.345)
    
        lipedit uses regular expressions to scan and fix the values.
    
        Use Case Scenario:
    
          Typically, I would use CoolEdit to see at what time the word should be
          spoken. Lets say the word "Help" starts at the 5.0 time signature in
          my wav file.
    
          Next I look at the filename that help came from:
    
              help_141_0.932.wav
    
          Then I would open up line141_col_e.lip and search for the instance of
          the  Word "help" around the 0.932.
    
          WORD help .928 1.115
          {
          113 h  .915 1.010 1.000 0
          213 e 1.010 1.050 1.000 0
          413 l 1.050 1.100 1.000 0
          513 p 1.100 1.115 1.000 0
          }
    
          I would note that the first ACTUAL SYLLABLE of help begins at .915
    
          So to make "help" appear at 5.0, I need to add 4.085: (yes, you will
          still need a calculator).
    
          >>> lipedit.fix('C:\in\line141_col_e.lip','C:\output\help.lip',4.085)
    
          Now I open up help.lip and past the correct time signature for help
          into a new lip file that I am creating for the sentence.
    
        Note that the python script ONLY FIXES the time signatures. You still have
        to go in, copy out the words (with the corrected time signatures) into a
        new lip file and update the lip file with the text that should appear to
        the user in the subtitles.
    
        If this all sounds crazy, then go look at a lip file and then come back
        here and you will know what I mean.
    
        The last step is the vcd file. The easiest thing to do is simply correct
        the file name/path, the audio length and then remove expressions, etc from
        the rest of the file. Of course, you are also free to throw stuff in. Just
        remember that any existing material was thrown off by the trim. And I
        didn't make a py script to autofix vcd files.
    
        It is a tedious process. Even with the help of the lip timing script, once
        you get into the flow, you only average about 1 lip file every 45
        minutes, and that is after you have pieced the wav files together (which
        may take you days).
    
        Alternatively, if you have the means to record your own audio, you can use
        that.  However, your biggest hurdle will be making realistic LIP files. If
        you DO decide to be brave and record your own audio, you may want to
        generate text files of all the conversations. Then you can search for a
        word that you say and use the script to correct the timing (maybe). Or you
        could fudge it by hand. If I was piecing a line together word by word from
        scratch, I  probably wouldn't bother with the python script.
    
        Even if you cant find an exact word, something like it will normally do.
        For example, when muted, you probably cant tell if a models lips are saying
        devious or delicious.
    
    ===============================================================================
    VI  > > > >  Animating NPCs with Python
    ===============================================================================
    
       The animation for NPCs is divided up into 3 categories: skeletal animations
       Facial expressions and Lip synching.
    
       Once you understand what animations and expressions are available to you,
       there are several ways of incorporating them into the game:
    
       Dynamic:
       1) Direct (SetAnimation)
       2) Dispositions
       3) Gestures (Interesting Places)
       4) Schedules
    
       Embedded
       1) Scripted Sequences
       2) Choreographed scenes
    
    -------------------------------------------------------------------------------
    VI.1  > > > >  Skeletal Animations
    -------------------------------------------------------------------------------
    
      Models have a skeleton and a hull. The skeletons attach to the hull and
      control movement of the hull. (The hull is normally painted with a skin).
    
      Being that models are so different, it should not be a surprise that
      skeletal animations are NOT globally standardized. For example, how could
      stubs the zombie with no arms and no legs execute a "standard" animation that
      involves jumping into the air.
    
      The good news is that most HUMAN LIKE NPCS (2 arms, 2 legs and a head)share a
      common skeleton. Thus, there is a set of "standard" animations. This is made
      possible because the MDL format allows models that share the same base
      skeleton to also share animations by linking to an external MDL file were
      common animations are defined.
    
      The vast majority of Human-Like NPCS link to "npc_allsequences.mdl". This
      is defined twice, once for male models and once for female models.
    
      models/character/shared/female/npc_allsequences.mdl
    
      or
    
      models/character/shared/male/npc_allsequences.mdl
    
      in both circumstance however, the master animation file further breaks out
      the common animations into separate animation files:
    
      npc_allsequences.mdl :
         - linkto ->  shared/.../animal_feed.mdl
         - linkto ->  shared/.../baseball.mdl
         - linkto ->  shared/.../bushhook.mdl
         - linkto ->  shared/.../claws.mdl
         - linkto ->  shared/.../finished_moves.mdl
         - linkto ->  shared/.../fists.mdl
         - linkto ->  shared/.../forced_feed.mdl
         - linkto ->  shared/.../katana.mdl
         - linkto ->  shared/.../knife.mdl
         - linkto ->  shared/.../meleeshared_onehand.mdl
         - linkto ->  shared/.../meleeshared_twohand.mdl
         - linkto ->  shared/.../misc.mdl
         - linkto ->  shared/.../move_and_ranged.mdl
         - linkto ->  shared/.../seductive_feed.mdl
         - linkto ->  shared/.../sherifsword.mdl
         - linkto ->  shared/.../sledgehammer.mdl
         - linkto ->  shared/.../stake.mld
         - linkto ->  shared/.../tireiron.mdl
         - linkto ->  shared/.../zombie_feed.mdl
    
      If you take the time to open up each of these files with VPKTool Model Tool
      tab, you can see all the animation names listed in the info window at the
      bottom of the tool. I have taken the liberty of documenting these in Appendix
      H.
    
      Examples:
    
      dance01
      dance02
      dance03
      cower_idle
      cower2_idle
      cower3_idle
    
      Raw animations are generally not applied DIRECTLY within the game. They
      are used by internal mechanisms such as the combat system, gestures,
      and schedules and by embedded elements such as scripted_sequence.
    
      The only time they are used directly within the game is when you have a
      "prop_dynamic" object. For example, in the strip club, there
      are dancers up on stage that you can’t approach. They are in fact not
      npcs but "prop_dynamic" objects with an animation set.
    
      You can create the illusion of applying an animation to an npc by
      Hiding the npc (set the model to models/null.mdl), dynamically
      Create a prop_dynamic where they are standing, and then set the
      Model/Animation.  The downside of applying animations in this
      manner is that you have no control over the model’s facial
      expressions. This may or may not be a big deal for you.
    
      Example:
    
        _EntCreate = __main__.CreateEntityNoSpawn
        _EntSpawn  = __main__.CallEntitySpawn
        npc = __main__.FindEntityByName("targetNPCName")
        e=_EntCreate ("prop_dynamic", npc.GetOrigin(),npc.GetAngles())
        e.SetName("prop_" + npc.GetName)
        e.SetModel(npc.GetModelName())
        e.SetParent(npc)
        npc.SetModel("models/null.mdl")
        _EntSpawn(e)
        e.SetAnimation("dance03")
    
      Some models contain additional internal animations that are specific to that
      model. For example, models/character/npc/common/stripper/Stripper3.mdl
      contains several animations that only that specific model will perform.
    
      Other models link to additional external MDL files for animations. For
      example, models/character/npc/common/lotusblossom_girl/lotusblossom_girl.mdl
      links to character/shared/female/stripper.mdl in addition to the common
      animations.
    
    -------------------------------------------------------------------------------
    VI.2  > > > >  Facial Expressions
    -------------------------------------------------------------------------------
    
      Facial Expressions are basically animations meant specifically for an NPCs
      face. Because faces are more unique than the body, they get special
      attention.  Each model defines their own set of supported expressions. You
      can browse the expressions for a given model\character under the
      "Vampire\expressions" directory.
    
      95% of the models just implement the standard set of expressions (located
      in Vampire\expressions\expressions.txt). The "common" expressions are
      as follows:
    
    
        "Neutral"          "Nearly Crying"              "Nearly Crying_No Deform"
        "Joy"              "Melancholy Smile"           "Meloncholy_NoDeform"
        "Fear"             "Confused"                   "Disgust_NoDeform"
        "Very Frightened"  "Disgust"                    "Sad_NoDeform"
        "Sly Smile"        "Apathy"                     "Fear_NoDeform"
        "Flirtatious"      "Lowered Both"               "Sly Smile_NoDeform"
        "Anger"            "Raised Both"                "Confused_NoDeform"
        "Mad"              "Raised Right"               "Flirtatious_NoDeform"
        "Enraged"          "Raised Left"                "Knockback"
        "Sad"              "Lowered Right"              "Anger_No Deform "
        "Miserable"        "Lowered Left"
    
      Example:
    
        vv=mySpawnNPC("")
        vv.SetExpression("Flirtatious")
    
    
      In the case of VV, her model supports additional expressions.
    
      NOTES:
    
      - I found when executing the SetExpression() method, the next if statement
        (wherever it executed from) would throw an exception saying function
        expects 7 parameters. You can avoid breaking later code by
        preemptively calling if from within a try-catch block after using
        the SetExpression method.
    
        i.e.:
    
        def SetExpression(npc, expression):
            try: npc.SetExpression(expression)
            except:  npc.SetExpression(expression)
            data="dummy"
            try: if data=="": return
            except: pass
            return
    
      - Model specific animations will only work on the original model. If the
        model is a clone of the original (ie: someone copied vv.mdl to vv2.mdl so
        that they could link to a different outfit), the vv specific expressions
        will not work on the clone. The expressions will only work on the original
        vv.mdl.
    
    -------------------------------------------------------------------------------
    VI.3  > > > >  Dispositions
    -------------------------------------------------------------------------------
    
      A disposition is a combination of Body animation and Facial Expression.
      However, dispositions can not include just any Animation. They are limited
      to the STANCE animations defined in stances.mdl.
    
      Dispositions are mostly used to emphasis the facial expression with
      additional body language. For example, if someone is mad at you, they might
      place a hand on the hip or cross their arms. If they are trying to seduce you
      they may lay down on a bed in addition to the sexy smirk they throw at you.
    
      Like expressions, MOST dispositions are common. However, there are a few
      Dispositions that are meant for specific characters.
    
      Common Dispositions:
      --------------------
      When setting a disposition, you specify a name and level. The name
      corresponds to the body animation and the level corresponds to the facial
      mood.
    
          Disposition       Levels
          ---------------------------
          Neutral           [1]
          Anger             [1,2,3]
          Joy               [1,2,3]
          Sad               [1,2,3]
          Fear              [1,2]
          Disgust           [1]
          Apathy            [1]
          Flirtatious       [1]
          Confused          [1]
          Lay               [1,2,3,4]
          Damaged           [1]
          Dead              [1]
          Sitting           [1]
          Bartender         [1,2]
          BehindBack        [1]
    
      Unique Dispositions:
      --------------------
    
          Disposition       Levels
          ---------------------------
          Therese           [1,2,3] -> Therese Only
          Lily              [1,2]   -> Lily Only
          ChairDamaged      [1,2]   -> Lily Only
          PrinceSitting     [1]     -> Chunk and Lacroix Only
    
    
      Fortunately, all NPCs have a method on them called SetDisposition which makes
      Using dispositions much easier than animations.
    
        vv=mySpawnNPC()
        vv.SetDisposition("Apathy",1)
    
    -------------------------------------------------------------------------------
    VI.4  > > > >  Schedules and ScriptedDisciplines
    -------------------------------------------------------------------------------
    
      A schedule is a tiny program that executes on an NPC. Every NPC has a
      Schedule assigned to it. When you cast a spell in the game, under the hood,
      the game assigns your target a schedule that results in the spells execution.
      However schedules are not just used for spells. When you tell an NPC to
      follow you, they execute a special schedule that causes them to follow you
      around the map.
    
      Most NPCs execute the common schedule SCHED_TROIKA_IDLE. This schedule
      includes a laundry list of tasks such as scanning for enemies, the pc,
      looking at the player if they are near, presenting a dialog icon if
      the pc gets close enough, etc.
    
      As you can imagine, changing an NPC’s schedule can cause an animation to
      happen. However, it should be noted that schedules are not JUST animations.
      For example, setting an NPC’s schedule to SCHED_TROIKA_D_BLOODBOIL_EXPLODE
      will show the very cool blood boil explosion animation. It will also kill
      the npc in the process.
    
      FROM : vdata/system/disciplinetgt_xxx
    
      SCHED_TROIKA_D_RAVENS
      SCHED_TROIKA_D_TRANCE
      SCHED_TROIKA_DISORIENTED
      SCHED_TROIKA_D_BURROWING_BEETLE
      SCHED_TROIKA_D_SPECTRAL_WOLVES
      SCHED_TROIKA_D_SPECTRAL_WOLVES_ESCAPE
      SCHED_TROIKA_D_BLOODSUCKERS_COMMUNION
      SCHED_TROIKA_D_PESTILENCE"  // Note: Dies at the end!  (Dmg_Health 100%)
      SCHED_TROIKA_SWAT_INSECTS
      SCHED_TROIKA_D_HYSTERIA
      SCHED_TROIKA_D_HALLUCINATION
      SCHED_TROIKA_D_MASS_HALLUCINATION
      SCHED_TROIKA_D_VISION_OF_DEATH
      SCHED_TROIKA_D_BERSERK
      SCHED_TROIKA_D_SUICIDE
      SCHED_TROIKA_D_VISION_OF_DEATH
      SCHED_TROIKA_D_BRAINWIPE
      SCHED_TROIKA_D_BRAINWIPE_END
      SCHED_TROIKA_D_POSSESSION
      SCHED_TROIKA_D_DAZE
      SCHED_TROIKA_D_BLOODSHOT_KNOCKBACK
      SCHED_TROIKA_D_BLOODBOIL_EXPLODE
      SCHED_TROIKA_D_BLOODBOIL_BOSS
    
      * You can find other schedules by using the "picker" console command from in
        game.
    
      Example:
    
        vv=mySpawnNPC()
        vv.ChangeSchedule("SCHED_TROIKA_D_TRANCE")
    
      You can accomplish the same thing by using SetScriptedDiscipline()
    
        vv=mySpawnNPC()
        vv.SetScriptedDiscipline("dominate 1")
    
    
    -------------------------------------------------------------------------------
    VI.5  > > > >  Gestures and intersting_place
    -------------------------------------------------------------------------------
    
      Interesting places are objects. You can think of them as phone-booth sized
      invisible boxes on a map that draw nearby NPCs to them like a magnet. When
      the NPC enters the box, they perform whatever action the box is configured
      for.
    
      Interesting places generally use gestures to define/refine the activities
      that may occur when a user enters the box. Gestures map to animations,
      however while it is safe to say that every Gesture has a corresponding
      animation, it is not safe to say that every animation has a corresponding
      gesture.
    
      (SAMPLE) FROM : Vampire/vdata/system/interestingplacetypelist.txt
    
      ACT_CONVERSE_NORMAL_TALK
      ACT_CONVERSE_NORMAL_LISTEN
      ACT_COUCH_SIT_INTO
      ACT_COUCH_SIT_IDLE
      ACT_COUCH_SIT_OUTOF
      ACT_COWER_INTO
      ACT_COWER
      ACT_COWER_OUTOF
      ACT_COWER2_INTO
      ACT_COWER2
      ACT_COWER2_OUTOF
      ACT_COWER3_INTO
      ACT_COWER3
      ACT_COWER3_OUTOF
      ACT_DIE
      ACT_DISORIENTED
      ACT_DISPOSITION
      ACT_DISPOSITION_MESMERIZED
      ACT_DOORKNOCK
      ,,,
      (See Appendix I for more)
    
      All NPC’s have a SetGesture() method on them. You can pass in one of the
      ACT_<gesture> strings, or you can pass in an animation name.
    
        vv=mySpawnNPC()
        vv.SetGesture ("ACT_COUCH_SIT_INTO")
        vv.SetGesture ("ACT_COUCH_SIT_OUTOF")
    
      OR
    
        vv=mySpawnNPC()
        vv.SetGesture ("CouchTV_Into")
        vv.SetGesture ("CouchTV_outof")
    
      Single shot animations and gestures will execute only once and then stop.
      However looping animations and gestures will continue to execute over and
      over again as part of the npc’s schedule. Furthermore, the looping
      IDLE_DISPOSITION gesture is hard coded into all the SCHED_TROIKA_IDLE
      schedule so that an npc will always alternate between the idle action and the
      looping gesture you assign them.
    
      This is very annoying and as a result, I recommend avoiding any looping
      gestures or animations with SetGesture.
    
    -------------------------------------------------------------------------------
    VI.6  > > > >  scripted_sequence and logic_choreographed_scene
    -------------------------------------------------------------------------------
    
      Both of these entities allow you to apply an animation to an entity on
      the map. Unlike the limited pure python solutions discussed above, these
      entities must be embedded into the map you wish to execute the animation on
      and hooked up to the entity by name.
    
      A) scripted_sequence
    
          Scripted Sequences are used to execute animations and movements on map
          characeters. By default, they execute once and then self-delete. You
          can override this behavior by setting some of the spawn flags:
    
             bit  value   Meaning
              1     1
              2     2
              3     4      Persistent
              4     8
              5     16     Start Enabled
              6     32
              7     64
              8     128
              9     256     Enable looping Post Idle Animation
             10     512
             11     1024
             12     2048
             13     4096
             14     8192
             15     16384
             16     32768
    
             Using bits : So a spawn flag has 16 options : 0000000000000000
             They are either true or false. To enable an option, you set the bit
             to 1. When you are finished, you convert the binary value to decimal.
             The right most value of the binary number corresponds to bit 1.
    
                 To enable bit 3,5 and 9 = 100010100
    
                     Calculator -> View -> Scientific, check "[*] Bin".
                     Enter the number above and then hit "[*] Dec"
                     Value = 276.
    
             Alternatively, you can add the values up from the value colum of those
             bits you wish to enable.
    
    
             Persistent + Post Idle Animation Enabled = 4 + 256 = 260
    
          Example: Have VV execute a standard animation in your apartment. The
          following entity must be embedded into the apartment:
    
              We use spawn flag 260 so that the animation will loop.
              {
              "classname" "scripted_sequence"
              "targetname" "vv_mesmerized"
              "spawnflags" "260"
              "m_flRadius" "5"
              "m_flRepeat" "0"
              "m_fMoveTo" "0"
              "m_iszEntity" "vv"
              "m_iszPlay" "dance01"
              "m_iszPostIdle" "dance01"
              "origin" "-1768.03 -2646.97 144.03"
              "angles" "0 165 0"
              }
    
              ] import custom
              ] vv=custom.mySpawnNPC("vv")
              ] seq=FindEntityByName("vv_mesmerized")
              ] seq.SetOrigin(vv.GetOrigin())
              ] seq.BeginSequence()
    
    
          Option Details:
    
              "m_flRadius"- Search radius for entities. Depending on the spawn
                            flag, it may or may not care if the Entity matches the
                            name of the m_iszEntity property.
    
              "m_iszIdle" - If set, this is the animation that plays PRIOR to
                            calling BeginSequence. Only has affect if StartEnabled
                            is true.
    
              "m_iszPlay" - When you call BeginSequence, this is the animation that
                            plays
    
          "m_iszPostIdle" - Animaiton that plays after m_iszPlay.
    
              "m_fMoveTo" - 0 : entity plays animations where it is.
                            1 : entity walks to scripted_sequence, then starts
                            2 : entity runs to scripted_sequence, then starts
                            3 : Walk to scripted_sequence, Walk back when finished
                            4 : Walk to scripted_sequence, Teleport back on finish
    
    
      B) logic_choreographed_scene
    
          Some MDL files contain skeletal animations meant for as many as 4 NPC
          models. To use these animations, typically the game uses the
          logic_choreographed_scene entity to identify the animation and the models
          to execute the animations on.
    
          For example, the opening scene of the game uses a scene with 4 npcs to
          have your PC and their sire kneel in front of the theater.
    
          logic_choreographed_scene is not limited to multi-actor animations. You
          can in fact use them to apply any animation from any mdl to any other
          entity model.
    
          Example: Have VV execute the stripper lap dance in your apartment, even
                   though it is not one of the standard animations:
    
              {
              "classname" "logic_choreographed_scene"
              "targetname" "mylapdance"
              "origin" "-1768.03 -2602.09 144.03"
              "angles" "0 300 0"
              "position_start" "1"
              "position_end" "3"
              "hide_ents" "0"
              "force_lod" "0"
              "target1" "noone"
              "target2" "vv"
              "BaseAnim" "models/cinematic/Hollywood/Vesuvius/Lap_danceGroup_4.mdl"
              "SceneFile" "sound/cinematic/Hollywood/Vesuvius/custom.vcd"
              "OnCompletion" " mylapdance,Start,,0,-1,,"
              }
    
              ** The actual targets of the animation are controlled by the
                 SceneFile. The internal target[x] attributes are ignored and used
                 mostly as a local place holder so you don’t have to go open the
                 scene file to figure out what to name your targets.
    
          The Corresponding .vcd file would be updated as follows:
    
            |--------------------------------------------------------------------|
            |Filename = [<vampire root>/sound/cinematic/.../custom.vcd]          |
            |--------------------------------------------------------------------|
            |actor "noone"                                                       |
            |{                                                                   |
            |  channel "Anim"                                                    |
            |  {                                                                 |
            |    event sequence "Lap_dance"                                      |
            |    {                                                               |
            |      time 0.000000 3600                                            |
            |      param "entire_scene"                                          |
            |    }                                                               |
            |  }                                                                 |
            |  bonerename "Bip02" "Bip01"                                        |
            |}                                                                   |
            |actor "vv"                                                          |
            |{                                                                   |
            |  channel "Anim"                                                    |
            |  {                                                                 |
            |    event sequence "Lap_dance"                                      |
            |    {                                                               |
            |      time 0.000000 3600                                            |
            |      param "entire_scene"                                          |
            |    }                                                               |
            |  }                                                                 |
            |  bonerename "Bip01" "Bip01"                                        |
            |}                                                                   |
            |fps 60                                                              |
            |snap off                                                            |
            |--------------------------------------------------------------------|
    
      To run the animation, you would create a vv model named "vv", a null model
      named "noone" and then call the Start() method on the embedded entity.
    
      ] import custom
      ] vv=custom.mySpawnNPC("vv")
      ] noone=custom.mySpawnNPC("noone","models/null.mdl")
      ] scene=FindEntityByName("mylapdance")
      ] scene.Start()
    
      Since the vv model is a real NPC (and not a prop_dynamic), you can set
      her expression:
    
      ] custom.SetExpression(vv,"Sly Smile")
    
    
    ===============================================================================
    VII.   > > > > Editing Models/Skins
    ===============================================================================
    
    -------------------------------------------------------------------------------
    VII.1 Basics
    -------------------------------------------------------------------------------
    
      Models are composed of several files:
    
        <modelname>.mdl : Defines the structure of the model along with animation,
                          bounding box, hit box, material, mesh and LOD info.
    
        <modelname>.vtx : vtx files store hardware optimized material, skinning and
                          triangle strip/fan information for each Level Of Detail
                          (LOD) of each mesh in the MDL. They come in several names
                          for backwards compatibility with older hardware. (DirectX
                          7)
        <modelname.phy> : [optional] contains jointed ragdoll collision model.
    
        <skinname>.vmt  : meta data file that "redirects" to actual texture.
    
        <skinname>.ttz  : ttz and tth work together to define the models
        <skinname>.tth    texture.  When working with skins, you must
                          maintain both.
    
    
        Additionally, MDL files may link to other MDL files internally for
        shared animations.
    
    -------------------------------------------------------------------------------
    VII.2 MDL File Details
    -------------------------------------------------------------------------------
    
      MDL Files contain a lot of information. You can view more of this information
      using VPKTool. Use the Model Tools tab to select a .mdl file and open
      it.
    
      1) Texture Search Paths : Where to search for the VMT files.
    
      2) Texture Names : The name of the VMT files. It is important to note that
         MDL files do not link directly to textures. They link to VMT files. VMT
         files are text based property files that point to the actual textures:
    
                        ##############################     #######################
                    --> # search/path/TEXTURE1(.vmt) # --> # /full/path/TEXTURE1 #
         #########/     ##############################     #######################
         #  MDL  #
         #########\     ##############################     #######################
                    --> # search/path/TEXTURE2(.vmt) # --> # /full/path/TEXTURE2 #
                        ##############################     #######################
    
    
       3) VPKTool has is a window at the bottom that often times goes unnoticed,
          however contains some very cool info if you scroll up.
    
          Aside from the texture info, it lists the models animations. Most
          Animations come from a shared animations mdl file. Normally:
    
          /models/character/shared/[male\female]/npc_allsequences.mdl
    
          However, in addition to this "external" link, the models specific
          animations are also listed. Typically these exist for cut-scenes
          or complex dialogs where the general purpose expression engine wasn't
          good enough.
    
    -------------------------------------------------------------------------------
    VII.3 Working with downloaded Skins
    -------------------------------------------------------------------------------
    
      The instructions below assume you have extracted the contents of all the
      .vpk files somewhere accessible.
    
      A) Replacing the Original Skin
    
         To keep things simple, we will start with the typical scenario: you
         Download someone else’s skin from the internet and want to install it
         into the game. For this example, we will use Dark VV:
    
             http://vh.noirscape.org/files.php?action=showfile&file=174
    
         Most 3rd party skins REPLACE the existing Skin. This is done by
         Clobbering the original skins vmt, ttz and tth files.
    
         Following the chart above, the MDL will find the new VMT files (by the
         same name) which point to the new Texture files (by the same name).
    
         The key here is that the mdl file is unchanged. Thus, when you play the
         game, the new skin will be visible on the original character.
    
      B) Adding new clothing options to existing NPCs
    
         Why replace VV’s skin, when you can install an additional outfit? The main
         reason is because normal people wouldn’t know how to use/access the skin.
         However, As a script savy modder armed with this guide, this shouldn’t
         phase you.
    
         1) Creating the new "outfit" model.
    
            We start by duplicating the original model. You can do this the hard
            way by renaming each file, or you can do this the easy way by simply
            copying the whole directory to a new directory name. Lets go easy!
    
    
            THE MODEL:
            ----------
            Copy the Original MODEL files (mdl, vtx, phy, etc...):
    
              FROM:  \Vampire\models\character\npc\unique\downtown\vv\*
              TO  :  \Vampire\models\character\npc\unique\downtown\vv2\*
    
            Update the Original Model
    
             OPEN: \Vampire\models\character\npc\unique\downtown\vv2\vv.mdl
             UPDATE: "Texture Search Path", change directory to vv2
             COMMIT ALL CHANGES: When you hit this button, it will prompt you for a
                                 place to save. Save as a new name:
    
                      \Vampire\models\character\npc\unique\downtown\vv2\vv2.mdl
    
             NOTE: Internally, the MDL file saves its own name. So it is important
                   not to rename the file (using explorer) or even move it to a
                   different directory after you save. If you decide to rename the
                   file, re-open and save again using VPKTool.
    
    
            Clean up : Delete Original MDL:
    
              FROM:  \Vampire\models\character\npc\unique\downtown\vv2\vv.mdl
    
            THE SKIN:
            ---------
    
            Copy MATERIALS:
    
              FROM:  \Vampire\materials\models\character\npc\unique\downtown\vv
              TO  :  \Vampire\materials\models\character\npc\unique\downtown\vv2
    
              Zip files downloaded off the internet normally don’t contain all
              required skins. For example, it may update the clothes, but not the
              teeth or hair. Most zip files assume that the original directories
              materials are still present. So we must copy everything over.
    
            UNZIP SKIN:
    
              TO  :  \Vampire\materials\models\character\npc\unique\downtown\vv2
    
              Note that the zip file may contain the full directory path. Be
              Certain to unzip the files directly to the taget directory (vv2 in
              this case). You should be prompted about overwriting existing files.
              Say yes.
    
            UPDATE VMT(s)
    
              INSIDE:  \Vampire\materials\models\character\npc\unique\downtown\vv2
    
              The original author likely expected their textures to be in a
              different directory. So you must open the .vmt files (use notepad)
              and update the paths. In this case, to "vv2".
    
         2) [Optional] Optimizatized method (uses less disk space)
    
            The method above is the easy way. The downside is that a lot of
            textures may unnecessarily be duplicated.
    
            A more efficient method is to extract the downloaded skin to its own
            empty directory. Take note of the textures it updates. Rename them
            (say with a 2 somewhere in the name). Copy the renamed texture files
            to the original materials directory (VV) and update the corresponding
            (and renamed) vmt files to point to the new textures. Now duplicate the
            VV model (and associated vtx files) and rename them, but place within
            the original models directly. Finally, edit the newly named mdl file,
            and only update those textures that came with your download (which you
            should have renamed). This allows you to re-use the unchanged textures
            (like teeth).
    
         3) Changing Clothes with a Script (from console)
    
            You can change an NPCs model relatively easily using the SetModel()
            command:
    
              pc=__main__.FindPlayer()
              pc.SetModel("models/character/npc/unique/downtown/vv2/vv2.mdl")
    
            Above, I use the PC as an example, but it works on NPCs as well. Use
            the "picker" console command to get an NPCs instance name, then:
    
              npc=__main__.FindEntityByName("<npc_instance_name>")
              npc.SetModel("models/character/npc/unique/downtown/vv2/vv2.mdl")
    
    
       NOTES ON MULTIPLE SKINS:
    
          MDL files DO support multiple skins. An example is:
    
          Vampire\models\scenery\physics\cube\cube.mdl
    
          If you create this entity and assign it to the variable "cube" you can
          change its skin using the command:
    
              cube.FadeToSkin(1)
    
          Cube only requires 1 texture, but the MDL file has 4 textures. When an
          mdl file has more textures than a model needs, FadeToSkin(#) attempts
          to reload the skin starting with an offset that corresponds to #.
    
          If a model requires 5 textures to map to all of its parts, then all it
          need do to define an additional skin is define 10 internal texture
          links. The function npc.FadeToSkin(1) would offset the starting texture
          accordingly. If there were 15 textures defined, FadeToSkin(2) would
          apply the last 5, etc...
    
          However, VPKTool does not allow you to ADD textures to the MDL file, so
          there is no way to take advantage of the engines multiple skin support
          using VPKTool alone. There may be a way using more advanced external
          tools, but that is outside the scope of this tutorial.
    
    -------------------------------------------------------------------------------
    VII.4 Editing Skins yourself
    -------------------------------------------------------------------------------
    
      The instructions below assume you have extracted the contents of all the
      .vpk files somewhere accessible. For this example, we will create a WHITE
      business suit for Therese.
    
      The instructions also assume the installation of the latest Gimp:
    
                http://gimp-win.sourceforge.net/stable.html
    
      GIMP comes with a DDS plugin already installed that allows you to SAVE as
      DDS. If you use another image editing program, you will need to find a
      DDS plugin or a TGA to DDS stand alone conversion utility from the internet.
    
      A) Locate the original NPC's outfit:
    
         Normally the outfit skins are located under :
    
         <VTMB install dir>\Vampire\materials\models\character\npc
    
         Example :
    
         ...\Vampire\materials\models\character\npc\unique\santa_monica\therese
    
     B) Copy all outfit files to a working directory.
    
        I personally prefer a Desktop accessible directory such as:
    
        <Desktop>\work
    
     C) Use VPKTool to convert the TTZ files to TGA:
    
         - Run VPKTool
         - Click on the "Texture Tools" Tab
         - Browse to your working directory
         - Select a TTZ file.
         - Click on the "Convert TTZ to TGA" button.
         - Repeat C steps above for each TTZ file.
    
     E) Open the File up in Gimp (Or you image editing program of choice)
    
        In this specific example, we want to edit the files :
    
           businesssuit_body.tga
           businesssuit_skirt.tga
    
        For this example, we will do a simple color inversion. Find the
        "Free Select Tool" on the tool menu and outline the 3
        items in the image that are obviously fabric. If you mess up,
        dont worry. You can "ADD" to the selection by holding down
        shift. Or you can "REMOVE" from the selection by holding down
        CTRL.
    
        Once the 3 fabric areas are selected, from the Menu, select :
        Colors -> Invert
    
        When you save, specifiy "DDS" as the file format you wish to save in.
    
          COMPRESSION : DXT5
          [X] Generate mipmaps
    
        If this is not available (because your not using GIMP) You can save as
        TGA, but you will need to find a TGA to DDS conversion program from the
        internet.
    
        Open up the Skirt file and invert the entire image then save in the same
        fashion.
    
     E) Convert the DDS back into TTZ:
    
         - Run VPKTool
         - Click on the "Texture Tools" Tab
         - Browse to your working directory for your DDS files.
         - Check the Header Information:
    
           [X] Hint DXT5
    
         - Click the "convert DDS to TTZ" button.
    
         This will create a new TTZ file AND update the local TTH file. You
         must maintain both.
    
      F) Backup and Copy
    
         Backup the original files that you edited. (Typically simply rename)
         and then paste the new TTZ /TTH files into the materials directory.
    
         Now when you see Therese, she will have a WHITE suit on instead of
         a dark brown suit.
    
    ===============================================================================
    VIII. > > > > Cameras and Cut scenes
    ===============================================================================
    
        TODO (I didn’t do any camera controls or cut scene work with my mod)
    
    
    ===============================================================================
    IX. > > > > Custom Items
    ===============================================================================
    
    -------------------------------------------------------------------------------
    IX.1 Defining Custom items
    -------------------------------------------------------------------------------
    
      Items are defined in:
    
          /Vampire/vdata/items
    
      I am not going to go into a lot of detail about the item format. Most of the
      time you find an item similar to what you want to add and re-use its
      properties. You can point to any existing mdl file the game has to offer
      for how it looks when it is sitting on the ground.
    
      A) The Bad News:
    
          You can not add additional items to the game. What I mean by that is
          the item names were hard coded into the games executable. The directory
          above contains configuration data for all the items, however if you
          simply paste a new item into the directory it will not show up or be
          accessible within the game.
    
      B) The Good News:
    
          A lot of items were embedded into the game that are not used. How do you
          know what is used and what isn't? Not an easy question. Personally, I
          install cygwin. Then cd to cygdrive/c/Program.../Vampire and then type:
    
          grep -Hir <item_name> .
    
          It takes about 3 min to run and when it is finished it tells me if the
          item is used by the game and where. (It is important that you have
          installed whatever patches you plan on building upon and extracted all
          the meta data out of the map .bsp files before you run the grep).
    
          Don't pay attention to hits on the .vpk files. What you care about is if
          it is in the metadata of a BSP map file, a python script (.py) or dialog
          file (.dlg).
    
      C) A few examples:
    
          These items are unused by the original game:
    
              item_p_occult_lockpicking
              item_g_ring_serial_killer_1
    
          You can edit these items, there description, display model, etc...
          However, when embedding them within a map or accessing them from your
          scripting code, you will still have to refer to them by their original
          name.
    
      D) NOTES:
    
          There are a finite number of such items. Sometimes, adding a new item
          to the game means getting rid of something else. If you are building your
          mod on top of someone else's mod and you wish to add a special item, you
          must be certain that you do not use an item that one of your support mods
          depends on. Note the word "depend". If it is not necessary for the mods
          function, then you could remove the item from the mod so that you can use
          it.
    
          For example, WESP updated the 2 items above and embedded them into the
          game with the 5.6 Patch. However, they are not NEEDED by the game, so you
          could remove them if you needed them so that you could use them yourself.
          Wesp also freed up item_g_wireless_camera_2, item_g_wireless_camera_3 and
          item_g_wireless_camera_4 by changing the associated quest to only require
          1 stackable camera.
    
          The companion mod which builds on top of Wesp's work, uses
          item_g_wireless_camera_2 and item_g_wireless_camera_3. However, of those
          two items, only the first is NECESSARY for the mods functionality. So
          if you really needed the slots, you could use both of the item slots
          above as well as camera_3 and camera_4.
    
          These are just examples and some of the considerations you should be
          aware of.
    
    -------------------------------------------------------------------------------
    IX.2 Capturing Item usage events
    -------------------------------------------------------------------------------
    
      The VTMB engine does not provide a nice way to capture events. I will
      discuss 2 approaches here which are work arounds.
    
      A) Basics : Defining Aliases and Binds
    
          Throughout this guide I have focused on Python. I have mentioned console
          commands, but generally only when they are needed to accomplish something
          that can't be done from python.  Well this is one of those situations.
    
          The console supports the concept of aliases. An alias is like a tiny, one
          line program. It can execute console commands or python commands.
    
              ] alias foo "print 'This is a python command'"
              ] foo
              This is a python command
    
          The console also supports the concept of key bindings. A key can be bound
          to execute a string, or execute an alias:
    
              ] bind t " print 'You PRESSED T!'"
              ] bind f "foo"
    
          If you hide the console, then press t or f and then unhide the console,
          you will see messages printed.
    
          Finally, python supports the concept of KEYDOWN and KEYUP events through
          the use of "+" or "-" infront of an alias definition.
    
              ] alias +foo "print 'KEY DOWN!'"
              ] alias -foo "print 'KEY UP!'"
              ] bind f "+foo"
    
          +foo will execute when the f key goes down and -foo will execute when the
          f key comes up, even though you only bind f to +foo.
    
      B) Requiring users to setup special cfg values:
    
          So, one approach to capturing item usage events is to capture the
          attack event and then execute some code to see if you should do
          something special based on the weapon equipped.
    
            |--------------------------------------------------------------------|
            |Filename = [<vampire root>/Vampire/cfg/autoexec.cfg]                |
            |--------------------------------------------------------------------|
            | // Required by Mod                                                 |
            | alias u_i_1 "__main__.ScheduleTask(0.0,'OnPlayerAttackStart()')"   |
            | alias u_i_2 "__main__.ScheduleTask(0.0,'OnPlayerAttackEnd()')"     |
            | alias +m_attack "u_i_1;+attack"                                    |
            | alias -m_attack "u_i_2;-attack"                                    |
            |--------------------------------------------------------------------|
    
          Notice the code above uses ScheduleTask. This forks the event off so
          that it returns quickly and prevents any python errors/exceptions from
          breaking the users ability to attack.
    
            |--------------------------------------------------------------------|
            |Filename = [<vampire root>/Vampire/python/vamputils.py]             |
            |--------------------------------------------------------------------|
            | ...                                                                |
            | def OnPlayerAttackStart():                                         |
            |     if __main__.FindPlayer().HasWeaponEquipped("item_my_item")     |
            |         print "THEY ARE USING MY EDITED ITEM/WEAPON!\n"            |
            |         # MAKE PC UNSEEN but unable to move (example)              |
            |         __main__.ccmd.notarget=""                                  |
            |         __main__.ccmd.player_immobilize=""                         |
            |                                                                    |
            |                                                                    |
            | def OnPlayerAttackEnd():                                           |
            |     if __main__.FindPlayer().HasWeaponEquipped("item_my_item")     |
            |         print "THEY STOPPED USING MY ITEM/WEAPON!\n"               |
            |         __main__.ccmd.notarget=""                                  |
            |         __main__.ccmd.player_mobilize=""                           |
            |                                                                    |
            |--------------------------------------------------------------------|
    
          The last part is the tricky part. For all of this to work, the USER
          must manually hook up the +m_attack you defined within their config.cfg
          so that your script receives the OnPlayerAttackStart and
          OnPlayerAttackEnd notifications.
    
            |--------------------------------------------------------------------|
            |Filename = [<vampire root>/Vampire/cfg/config.cfg]                  |
            |--------------------------------------------------------------------|
            | ...                                                                |
            | bind "MWHEELUP" "invprev"                                          |
            | bind "MOUSE1" "+m_attack" // changed from bind "MOUSE1" "+attack"  |
            | bind "MOUSE2" "vdiscipline_last"                                   |
            | ...
            |--------------------------------------------------------------------|
    
          And this is the dilemma with this solution. It requires a user who is not
          afraid to get their hands dirty with a config.cfg file. Still it works
          and in some ways it is less intrusive since the user KNOWS what you are
          doing.
    
      C) AUTO Setup config values:
    
          Here, we use the same approach as A., however we do a little extra
          leg work so that the user doesn't have to do anything at all. For
          starters, we add a new line to your autoexec.cfg:
    
            |--------------------------------------------------------------------|
            |Filename = [<vampire root>/Vampire/cfg/autoexec.cfg]                |
            |--------------------------------------------------------------------|
            | // Required by Mod                                                 |
            | alias execonsole "exec console.cfg"                                |
            | alias u_i_1 "__main__.ScheduleTask(0.0,' OnPlayerAttackStart()')"  |
            | alias u_i_2 "__main__.ScheduleTask(0.0,' OnPlayerAttackEnd()')"    |
            | alias +m_attack "u_i_1;+attack"                                    |
            | alias -m_attack "u_i_2;-attack"                                    |
            |--------------------------------------------------------------------|
    
            The new execonsole alias allows us to write console commands to a file
            and execute them from python.
    
            The idea is, when the game loads, we fix the attack binding to point
            to our custom binding. Easier said than done. For one thing, this
            involves reading in the config.cfg file, parsing it for "+attack"
            and then sending a command to the game to rebind the associated
            key with "+m_attack". And we must not forget that the player can
            bind multiple keys to +attack (MOUSE1 and the letter "q" for example)
    
            |--------------------------------------------------------------------|
            |Filename = [<vampire root>/Vampire/python/vamputils.py]             |
            |--------------------------------------------------------------------|
            | ...                                                                |
            | def OnPlayerAttackStart():                                         |
            | ...                                                                |
            |                                                                    |
            | def OnPlayerAttackEnd():                                           |
            | ...                                                                |
            |                                                                    |
            | def FixAttackBinding():                                            |
            |   data = ''                                                        |
            |   fin = None                                                       |
            |   try:                                                             |
            |     fin = open('Vampire/cfg/config.cfg',"r")                       |
            |     line = fin.readline()                                          |
            |     while line:                                                    |
            |       s = line.rfind('"+attack"')                                  |
            |       if -1 != s:                                                  |
            |         r = s                                                      |
            |         s = line.find(' ')                                         |
            |         data='%sbind %s "+m_attack"\n' % (data,line[s:r].strip())  |
            |       line=fin.readline()                                          |
            |   finally:                                                         |
            |     if fin: fin.close()                                            |
            |                                                                    |
            |   if 0 != len(data):                                               |
            |     cfg=open('Vampire/cfg/console.cfg', 'w')                       |
            |     try: cfg.write(data)                                           |
            |     finally: cfg.close()                                           |
            |     __main__.ccmd.execonsole=""                                    |
            |                                                                    |
            | FixAttackBinding()                                                 |
            |--------------------------------------------------------------------|
    
            In this scenario, the binding is fixed when the game is started. If the
            user changes their config within the game, they would break the item
            until they restarted the game. However, telling a user they have to
            restart their game if they change the config isn't as complicated as
            walking them through editing their config.cfg file.
    
      D) Discovering what NPC is under the target hair:
    
          The game doesn't really support this, but here I talk about workarounds.
    
          There are two workarounds. The easy and the hard work around. The hard
          work around involves computing a range of values that represents a cone
          in the direction that the PC is facing and then grabbing the coordinates
          of all the NPCs on the map and seeing who is in that cone and who is
          closest. This actually isn't that hard if you treat the map as a 2D map,
          however when you bring the z axis into the equations (maybe pc is looking
          up at a balcony), the computations get more difficult. Other than the
          mathematical complexity, the other issue with this approach is that
          python is slow compared to C++.
    
          A much easier method is using a console command to change something about
          the npc under the crosshair and then examine all NPCs for the change.
          Once found, change the "something" back.
    
          The two console methods I was able to use with this method:
    
              npc_freeze
              npc_hearing_sensitivity #.#
    
          I mention 2, because if you are trying to make a freeze gun, you don't
          want to depend on NPCs who are frozen to identify who is under the target
          hair. On the other hand, freeze doesn't take parameters, so it is ideal
          as you can accomplish the grapple without having to write to the
          console.cfg file. Here is an example:
    
            |--------------------------------------------------------------------|
            |Filename = [<vampire root>/Vampire/python/vamputils.py]             |
            |--------------------------------------------------------------------|
            | ...                                                                |
            | # STUN GUN...                                                      |
            | def OnUsedMyWeapon(targetNPC):                                     |
            |     print "TARGET [%s] " % targetNPC.GetName()                     |
            |     targetNPC.Faint()                                              |
            |                                                                    |
            | def OnPlayerAttackStart():                                         |
            |     if __main__.FindPlayer().HasWeaponEquipped("item_my_item")     |
            |         __main__.grapple=None                                      |
            |         __main__.ccmd.npc_freeze=""                                |
            |         __main__.ScheduleTask(0.1,'OnGrappleNPC()')                |
            |                                                                    |
            | def OnPlayerAttackEnd():                                           |
            |     pass                                                           |
            |                                                                    |
            | def OnGrappleNPC(found=0):                                         |
            |     if found:                                                      |
            |         OnUsedMyWeapon(__main__.grapple)                           |
            |     else:                                                          |
            |         npcs = __main__.FindEntitiesByClass("npc_V*")              |
            |         for npc in npcs:                                           |
            |             try:                                                   |
            |                 if (npc.playbackrate==0.00):                       |
            |                     __main__.grapple=npc                           |
            |                     __main__.ccmd.npc_freeze=""                    |
            |                     __main__.ScheduleTask(0.1,'OnGrappleNPC(1)')   |
            |                     break;                                         |
            |             except:                                                |
            |                 pass                                               |
            |                                                                    |
            | def FixAttackBinding():                                            |
            |     ...                                                            |
            |--------------------------------------------------------------------|
    
          Using npc_hearing_sensitivity is basically the same, however you have to
          use the console function (See III.3) to send the initial command.
    
              npc_hearing_sensitivity 1.2
    
          If an npc is found, you can use npc.TweakParam("HEARING 1") to
          fix without having to issue another console command.
    
    ===============================================================================
    X. > > > > Miscellaneous
    ===============================================================================
    
      A) Death
    
          When an NPC is killed, a copy of their instance name is placed in a
          global array called __main__.G.morgue[]. The IsDead() function ultimately
          looks up the name in the array to decide if someone is dead.
    
      B) Reserved Entity Names
    
          "!player" <- refers to the (potentially unnamed) player.
    
          "!playerController" <- refers to the (potentially unnamed) player
                                 controller if one exists. (There are used by cut
                                 scenes)
    
          "!dialogpartner" <- When you begin a conversation with someone, this
                              refers to the person you are talking to. It only
                              remains valid while the conversation is active.
    
      C) Special Embedded Entity Targets
    
          Some embedded entities have a model reference instead of a targetname.
          Model reference takes the form : *<number> ex: "*8".
          This represents the 8th instance of the class type at runtime. So if you
          ran FindEntitiesByClass() on the class of the embedded entity, *8 would
          correspond to the array[8] instance returned by the function.
    
          This means when you embed new data into map files, you should always
          append changes to the bottom so that you don't risk messing up other
          index references.
    
    ===============================================================================
    XI. > > > > Legalities
    ===============================================================================
    
      VTMB offers no provisional rights to mod developers. This has different
      implications in different regions, however the bottom line is this:
    
          YOU CAN'T MAKE ANY MONEY FROM YOUR MOD (ie : you can't sell it)
    
      This should not come as a surprise since free work is generally convention
      within the modding communities.
    
      Also, DO NOT distribute vampire.exe with your mod. Doing so is blatantly
      illegal and would be construed as distributing a pirated or "cracked" version
      of the game.
    
    ===============================================================================
    XII. > > > > Common Scenarios and Examples
    ===============================================================================
    
    A) Discovering a location on the map directly in front of or behind you:
    ------------------------------------------------------------------------
    # USAGE : pc = FindPlayer()
    #         loc = TraceLine(pc.GetOrigin(),pc.GetAngles()[1],50)
    
    def TraceLine(pos, angle, dist):
        from math import pi as _pi
        from math import cos as _cos, sin as _sin
    
        # degToRad : r = d/(360/2pi)
        xoffset = dist * _cos((angle/(360/(2*_pi))))
        yoffset = dist * _sin((angle/(360/(2*_pi))))
    
        return (pos[0]+xoffset, pos[1]+yoffset, pos[2])
    
    
    B) Turn someone or something around 180 degrees. Calculates Facing.
    ------------------------------------------------------------------------
    ## Usage : angle = pc.GetAngles()[1]
    ##         facing=(0,RevAngle(angle),0)
    ##
    ## Param 1 = angle degrees as a decimal between 180 and -180
    
    def RevAngle(angle):
        return (abs(((angle+180)/360)-0.5)*360)-180
    
    C) Figure out if 2 objects are within a certain distance of each other in 3D.
    -----------------------------------------------------------------------------
    ## USAGE :  npc = FindEntityByname("VV")
    ##          near = npc.Near(FindPlayer().GetOrigin())
    ##
    ##  Param 1 = location (x,y,z)
    ##  Param 2 = radius [default 200]
    
    from __main__ import Character
    
    def _Near(self,loc,r=200):
    
        # Avoid square root function. very inefficient
        # if (Distance)^2 > (x2-x1)^2 + (y2-y1)^2 + (z2-z1)^2
    
        loc2=self.GetOrigin()
        xd=loc2[0]-loc[0]
        yd=loc2[1]-loc[1]
        zd=loc2[2]-loc[2]
        return (r*r) > (xd*xd) + (yd*yd) + (zd*zd)
    
    Character.Near=_Near
    
    D) Like Traceline, but you can offset the point by an angle. IE: 90 would be
       the point directly to your right. angle 0 would be the same as TraceLine().
    ----------------------------------------------------------------------------
    ## USAGE :  loc = FindPlayer().TraceCircle(50,90)
    ##
    ##  Param 1 = distance from entity
    ##  Param 2 = angle from entities current facing
    
    from __main__ import Character
    
    def _TraceCircle(self, radius=50, angleOffset=0):
        from math import pi as _pi
        from math import cos as _cos, sin as _sin
    
        pos   = self.GetOrigin()
        angle = self.GetAngles()[1] + angleOffset
    
        # degToRad : r = d/(360/2pi)
        xoffset = radius * _cos((angle/(360/(2*_pi))))
        yoffset = radius * _sin((angle/(360/(2*_pi))))
    
        return (pos[0]+xoffset, pos[1]+yoffset, pos[2])
    
    Character.TraceCircle=_TraceCircle
    
    
    E) Test if the PC is in stealth or not:
    ---------------------------------------
    ## USAGE :  inStealth = FindPlayer().IsStealth()
    
    from __main__ import Character
    
    def _IsStealth(self):
        squating = ((self.GetCenter()[2] - self.GetOrigin()[2]) == 18)
        return (self.active_obfuscate or squating)
    
    Character.IsStealth=_IsStealth
    
    F) Dynamically spawn an entity
    ---------------------------------------
    ## USAGE :  vv = SpawnEntity("MyVV")
    ##
    ## All but the first param is optional
    ##
    ## param 1 : entityName (string name of entity that you make it up)
    ## param 2 : entityType. VTMB internal classname. (def "npc_VVampire")
    ## param 3 : model. String specifying full internal model path.
    ## param 4 : distance in front of PC to create entity (def is 50)
    
    def SpawnEntity(entityName="", \
                    entityType="npc_VVampire", \
                    model="models/character/npc/unique/downtown/vv/vv.mdl", \
                    distance=50):
    
        pc = __main__.FindPlayer()
        position = pc.GetOrigin()
        angle = pc.GetAngles()[1]
    
        # calculate point in front of PC
        point = TraceLine(position,angle,distance)
    
        # reverse angle so npc is facing pc
        facing=(0,RevAngle(angle),0)
    
        ent = __main__.CreateEntityNoSpawn(entityType, point, facing )
        try: ent.SetRelationship("player D_NU 0")
        except: pass
        try: ent.SetModel(model)
        except: pass
        try: ent.SetName(entityName)
        except: pass
        __main__.CallEntitySpawn(ent)
        return ent
    
    G) Dynamically spawn an NPC
    ----------------------------
    ## USAGE :  vv = SpawnNPC("MyVV")
    ##
    ## All but the first param is optional
    ##
    ## param 1 : npcName (string name of NPC that you make it up)
    ## param 3 : model. String specifying full internal model path.
    ## param 4 : distance in front of PC to create entity (def is 50)
    
    def SpawnNPC(npcName="", \
                 model="models/character/npc/unique/downtown/vv/vv.mdl", \
                 distance=50):
    
        ent = SpawnEntity(npcName,"npc_VVampire", model, distance)
        ent.LookAtEntityEye("!player")
        return ent
    
    H) Dynamically spawn a physics Object
    -------------------------------------
    ## USAGE :  stool = SpawnPhysics ("MyStool")
    ##
    ## All but the first param is optional
    ##
    ## param 1 : objectName (string name of object that you make it up)
    ## param 3 : model. String specifying full internal model path.
    ## param 4 : distance in front of PC to create entity (def is 50)
    
    def SpawnPhysics(propName="", \
                     model="models/scenery/structural/society/stool.mdl", \
                     distance=50 ):
    
        return SpawnEntity(propName,"prop_physics", model, distance)
    
    
    I) Teleporting and Moving NPCs
    -------------------------------------
    
      1) Removing/Hiding
    
          All entities support the Kill() function, which removes the entity from
          the game world completely.
    
              ent.Kill()
    
          Alternatively, you can set an Entity to hidden:
    
              ent.ScriptHide()
    
          And then Unhide it when you want to:
    
              ent.ScriptUnhide().
    
          Hide/Unhide result in physical entities completely disappearing.
    
          Other methods include setting the model to NULL
    
              ent.SetModel("models/null.mdl")
    
          Changing a model to null allows an entity to continue to fire events
          Without being seen.
    
      2) Moving and Teleporting Entities
    
          If you want your entity to WALK somewhere, most entites have a
          WalkToNode() method. The easiest way to move an entity is to use the
          SetOrigin() method
    
              npc = FindEntityByName("Ugly Dude")
              npc.SetOrigin(FindPlayer().GetOrigin())
    
    ===============================================================================
                  < < < < < FREQUENTLY ASKED QUESTIONS > > > > >
    ===============================================================================
    
      Q:  Why aren't there any Frequently asked questions?
    
      A:  Because this is the first release of the Guide.
    
    ===============================================================================
                         < < < < < VTMB LINKS > > > > >
    ===============================================================================
    
    1) http://www.vampirebloodlines.com/
    
      The official site of VTMB.
    
    2) http://www.planetvampire.com
    
      If you want to talk about the game, the forums here can't be beat.
    
    
    3) http://www.patches-scrolls.de/vampire_bloodlines.php
    
      Wesp's Unofficial Patch Website:
    
    4)  http://www.strategyinformer.com/pc/mods/..."
    
      Strategy Informers VTMB Page.
    
    5)  http://www.fileplanet.com/94454/0/section/Vampire:...
    
      FilePlanet's VTMB Page
    
    6)  http://browse.files.filefront.com/Vampire...
    
      FileFront's VTMB Page
    
    7) http://www.gamebanshee.com/vampirebloodlines/
    
      Game Banshee's VTMB Page
    
    8) http://www.tessmage.com/
    
      Tess specializes in Skin's and even supports his own Unofficial Patch.
    
    9) http://vh.noirscape.org/files.php?cat=2
    
      Vampire Heaven (Dedicated to Vampires). Includes some VTMB stuff:
    
    10) http://paine.planetvampire.gamespy.com/?action=files
    
      What can I say? It is Pain's website dediicated to VTMB. Doesn't look like
      it has been updated for almost 3 years, but it has a few unique downloads.
    
    11) http://vampirebloodlines.ru/combat/files/
    
       A russian fan site. You can translate it using google's translation service.
       here
    
    12) http://corellon.clandlan.net/index.php?page=corellon/vtmb/index
    
      A spanish fan site. You can translate it using google's translation service.
      here
    
    13) http://www.vampire-network.net/
    
      A french fan site. You can translate it using google's translation service.
      here
    
    
    ===============================================================================
                          < < < < < APPENDICES > > > > >
    ===============================================================================
    
    -------------------------------------------------------------------------------
    A. > > > >  Entity Classes
    -------------------------------------------------------------------------------
    
      ai_script_conditions           logic_choreographed_scene
      aiscripted_schedule            logic_npc_condition
      aiscripted_sequence            logic_pythoncheck
      ambient_generic                logic_relay
      ambient_location               logic_squad_condition
      ambient_soundscheme            logic_timer
      camera_cinematic               logic_visibility_test
      camera_keyframe                math_counter
      camera_track                   move_rope
      env_beam                       mover_keyframe
    
      env_fade                       npc_VAndreiBlood
      env_floating_camera            npc_VAsianVampire
      env_particle                   npc_VBach
      env_particle_hud               npc_VBrujah
      env_physexplosion              npc_VCamera
      env_physimpact                 npc_VCameraSecurity
      env_shake                      npc_VChangBrosBlade
      env_shooter                    npc_VChangBrosClaw
      env_spark                      npc_VCop
      env_sprite                     npc_VDialogPedestrian
    
      env_steam                      npc_VGargoyle
      events_player                  npc_VGhoulCroucher
      events_world                   npc_VHengeyokai
      filter_activator_class         npc_VHuman
      filter_activator_feat          npc_VHumanCombatPatrol
      filter_activator_inventory     npc_VHumanCombatant
      filter_activator_mass          npc_VLasombra
      filter_activator_name          npc_VManBat
      filter_multi                   npc_VMingXiao
      func_areaportal                npc_VNewscaster
    
      func_areaportalwindow          npc_VPedestrian
      func_breakable                 npc_VProneDialog
      func_breakable_surf            npc_VRat
      func_brush                     npc_VSabbatGunman
      func_button                    npc_VSabbatLeader
      func_door                      npc_VScurrying
      func_door_rotating             npc_VSheriffMan
      func_dustmotes                 npc_VTaxiDriver
      func_elevator                  npc_VTzimisce
      func_illusionary               npc_VTzimisceHeadClaw
    
      func_keyframed_mover           npc_VTzimisceRunner
      func_lod                       npc_VVampire
      func_monitor                   npc_VVampireBoss
      func_movelinear                npc_VWerewolf
      func_particle                  npc_VYukie
      func_physbox                   npc_VZombie
      func_pushable                  npc_maker
      func_rotating                  npc_maker_fleshpile
      game_sign                      npc_maker_zombie
      game_text                      npc_payphone
    
      game_ui                        params_explosion
      hud_timer                      params_particle
      info_hint                      phys_animlink
      info_landmark                  phys_ballsocket
      info_node                      phys_constraint
      info_node_bach_run_1           phys_convert
      info_node_bach_run_2           phys_hinge
      info_node_bach_teleport_1      phys_thruster
      info_node_bach_teleport_2      point_camera
      info_node_bach_teleport_3      point_explosion
    
      info_node_bach_teleport_4      point_target
      info_node_chang_column         point_teleport
      info_node_chang_jumpbase       prop_button
      info_node_chang_ledge          prop_clockhand
      info_node_chang_teleport       prop_destructable
      info_node_climb                prop_doorknob
      info_node_cover_corner         prop_doorknob_electronic
      info_node_cover_low            prop_dynamic
      info_node_cover_med            prop_dynamic_ornament
      info_node_crosswalk            prop_hacking
    
      info_node_hint                 prop_haunted
      info_node_kick_over            prop_keypad
      info_node_link                 prop_largehull_ignore
      info_node_manbat_fly_to_point  prop_mover
      info_node_patrol_point         prop_padlock
      info_node_sabbat_arch          prop_physics
      info_node_sabbat_bottom        prop_physics_contested
      info_node_sabbat_dive          prop_radio
      info_node_sabbat_hide          prop_ragdoll
      info_node_sabbat_nojump        prop_sign
    
      info_node_sabbat_top           prop_slashable
      info_node_shoot_at             prop_switch
      info_node_tzimisce             scripted_sequence
      info_node_werewolf             security_camera
      info_node_werewolf_hint        sky_camera
      info_player_start              trigger_autosave
      info_target                    trigger_bomb_site
      info_teleport_destination      trigger_changelevel
      infodecal                      trigger_checkvolume
      inspection_brush               trigger_discipline_context
    
      inspection_node                trigger_electric_bugaloo
      intersting_place               trigger_environmental_audio
      intersting_place_conversation  trigger_hurt
      item_container                 trigger_impact
      item_container_animated        trigger_inventory_check
      item_container_lock            trigger_look
      keyframe_rope                  trigger_multiple
      light                          trigger_once
      light_dynamic                  trigger_player_activity_level
      light_environment              trigger_push
    
      light_spot                     trigger_small_hull
      logic_auto                     trigger_stealth_mod
      logic_case                     trigger_teleport
      logic_case_toggle              trigger_werewolf_zone
    
      Developer Notes:  How did I come up with this list?
      1) Used VPKTool to extract all maps (BSD) to txt files under meta directory.
      2) Installed CYGWin
      3) cd cygdrive/c/Program Files.../Vampire/maps/meta/
      4) cat `ls` | grep "classname" | sort | uniq > all.txt
    
    -------------------------------------------------------------------------------
    B. > > > >  Map Names
    -------------------------------------------------------------------------------
    
      1) Santa Monica                  4) Hollywood
           sm_pawnshop_1                    hw_609_1
           sm_apartment_1                   hw_ash_sewer_1
           sm_asylum_1                      hw_asphole_1
           sm_bailbonds_1                   hw_cemetery_1
           sm_basement_1                    hw_chinese_1
           sm_beachhouse_1                  hw_hub_1
           sm_diner_1                       hw_jewelry_1
           sm_embrace_1                     hw_luckystar_1
           sm_gallery_1                     hw_metalhead_1
           sm_gallery_1_particle_test       hw_netcafe_1
           sm_hub_1                         hw_redspot_1
           sm_hub_2                         hw_sewer_1
           sm_junkyard_1                    hw_sinbin_1
           sm_medical_1                     hw_tawni_1
           sm_oceanhouse_1                  hw_vesuvius_1
           sm_oceanhouse_2                  hw_warrens_1
           sm_pier_1                        hw_warrens_2
                                            hw_warrens_3
           sm_shreknet_1                    hw_warrens_4
           sm_tattoo                        hw_warrens_5
           sm_vamparena
           sm_warehouse_1              5) Chinatown
                                            ch_hub_1
      2) Los Angeles                        ch_cloud_1
           la_abandoned_building_1          ch_dragon_1
           la_bradbury_2                    ch_fulab_1
           la_bradbury_3                    ch_glaze_1
           la_chantry_1                     ch_lotus_1
           la_confession_1                  ch_ramen_1
           la_crackhouse_1                  ch_sewer_1
           la_dane_1                        ch_shrekhub
           la_empire_1                      ch_temple_1
           la_empire_2                      ch_temple_2
           la_empire_3                      ch_temple_3
           la_expipe_1                      ch_temple_4
           la_hospital_1                    ch_tsengs_1
           la_hub_1                         ch_zhaos_1
           la_malkavian_1
           la_malkavian_2              6) Special
           la_malkavian_3                   sp_boat
           la_malkavian_4                   sp_camwarehouse
           la_malkavian_5                   sp_epilogue
           la_museum_1                      sp_genesisdevice_1
           la_parkinggarage_1               sp_giovanni_1
           la_PlagueBearer_Sewer_1          sp_giovanni_2a
           la_sewer_1                       sp_giovanni_2b
           la_skyline_1                     sp_giovanni_3
           la_ventruetower_1                sp_giovanni_4
           la_ventrueTower_1b               sp_giovanni_5
           la_ventruetower_2                sp_lonewolf_1
           la_ventruetower_3                sp_masquerade_1
                                            sp_ninesintro
      3) E3                                 sp_observatory_1
           e3_chinese_1                     sp_observatory_2
           E3_Combat                        sp_smut
           E3_confession_1                  sp_soc_1
           E3_hub_1                         sp_soc_2
                                            sp_soc_3
                                            sp_soc_4
                                            sp_taxiride
                                            sp_theatre
                                            sp_tutorial_1
    
      Developer Notes:  How did I come up with this list?
      Answer) Vampire/vdata/system/mapnames_normalized.txt
      You can also see a list of most maps from console by
      Typing "maps". Not all of these maps are recognized
      (ie : nothing with sewer, smut, boat).
    
    -------------------------------------------------------------------------------
    C. > > > >  Item Name Summary
    -------------------------------------------------------------------------------
    
      item_a_body_armor                  item_k_hitman_ji_key
      item_a_hvy_cloth                   item_k_hitman_lu_key
      item_a_hvy_leather                 item_k_kiki_key
      item_a_lt_cloth                    item_k_leopold_int_key
      item_a_lt_leather                  item_k_lilly_key
      item_d_animalism                   item_k_lucky_star_murder_key
      item_d_dementation                 item_k_malcolm_office_key
      item_d_dominate                    item_k_malkavian_refrigerator_key
      item_d_holy_light                  item_k_murietta_key
      item_d_thaumaturgy                 item_k_museum_basement_key
      item_g_animaltrainingbook          item_k_museum_office_key
      item_g_astrolite                   item_k_museum_storage_key
      item_g_bach_journal                item_k_museum_storeroom_key
      item_g_badlucktalisman             item_k_netcafe_office_key
      item_g_bailbond_receipt            item_k_oceanhouse_basement_key
      item_g_bertrams_cd                 item_k_oceanhouse_sewer_key
      item_g_bloodpack                   item_k_oceanhouse_upstairs_key
      item_g_bluebloodpack               item_k_oh_front_key
      item_g_brotherhood_flyer           item_k_sarcophagus_key
      item_g_car_stereo                  item_k_shrekhub_four_key
      item_g_cash_box                    item_k_shrekhub_one_key
      item_g_chewinggum                  item_k_shrekhub_three_key
      item_g_computerbookhighgrade       item_k_skyline_haven_key
      item_g_computerbooklowgrade        item_k_tatoo_parlor_key
      item_g_driver_license_gamble       item_k_tawni_apartment_key
      item_g_drugs_drug_box              item_k_tutorial_chopshop_stairs_key
      item_g_drugs_morphine_bottle       item_m_money_clip
      item_g_drugs_perscription_bottle   item_m_money_envelope
      item_g_drugs_pill_bottle           item_m_wallet
      item_g_edane_print_report          item_p_gargoyle_talisman
      item_g_edane_report                item_p_occult_blood_buff
      item_g_eldervitaepack              item_p_occult_dexterity
      item_g_eyes                        item_p_occult_dodge
      item_g_gargoyle_book               item_p_occult_experience
      item_g_garys_cd                    item_p_occult_frenzy
      item_g_garys_film                  item_p_occult_hacking
      item_g_garys_photo                 item_p_occult_heal_rate
      item_g_garys_tape                  item_p_occult_lockpicking
      item_g_ghost_pendant               item_p_occult_obfuscate
      item_g_giovanni_invitation_maria   item_p_occult_passive_durations
      item_g_giovanni_invitation_victor  item_p_occult_presence
      item_g_guy_magazine                item_p_occult_regen
      item_g_hannahs_appt_book           item_p_occult_strength
      item_g_hatters_screenplay          item_p_occult_thaum_damage
      item_g_horrortape_1                item_p_research_hg_computers
      item_g_horrortape_2                item_p_research_hg_dodge
      item_g_idol_cat                    item_p_research_hg_firearms
      item_g_idol_crane                  item_p_research_hg_melee
      item_g_idol_dragon                 item_p_research_lg_computers
      item_g_idol_elephant               item_p_research_lg_dodge
      item_g_jumbles_flyer               item_p_research_lg_firearms
      item_g_junkyard_businesscard       item_p_research_lg_stealth
      item_g_keyring                     item_p_research_mg_brawl
      item_g_larry_briefcase             item_p_research_mg_finance
      item_g_lilly_diary                 item_p_research_mg_melee
      item_g_lilly_photo                 item_p_research_mg_security
      item_g_lilly_purse                 item_s_physicshand
      item_g_lillyonbeachphoto           item_w_avamp_blade
      item_g_linedpaper                  item_w_baseball_bat
      item_g_locket                      item_w_baton
      item_g_lockpick                    item_w_bush_hook
      item_g_mercurio_journal            item_w_chang_blade
      item_g_milligans_businesscard      item_w_chang_claw
      item_g_oh_diary                    item_w_chang_energy_ball
      item_g_pisha_book                  item_w_chang_ghost
      item_g_pisha_fetish                item_w_claws
      item_g_pulltoy                     item_w_claws_ghoul
      item_g_ring03                      item_w_claws_protean4
      item_g_ring_gold                   item_w_claws_protean5
      item_g_ring_serial_killer_1        item_w_colt_anaconda
      item_g_ring_serial_killer_2        item_w_crossbow
      item_g_ring_silver                 item_w_crossbow_flaming
      item_g_sewerbook_1                 item_w_deserteagle
      item_g_stake                       item_w_fireaxe
      item_g_vampyr_apocrypha            item_w_fists
      item_g_vv_photo                    item_w_flamethrower
      item_g_wallet                      item_w_gargoyle_fist
      item_g_warr_clipboard              item_w_glock_17c
      item_g_warr_ledger_1               item_w_grenade_frag
      item_g_warr_ledger_2               item_w_hengeyokai_fist
      item_g_warrens4_passkey            item_w_ithaca_m_37
      item_g_watch_fancy                 item_w_katana
      item_g_watch_normal                item_w_knife
      item_g_werewolf_bloodpack          item_w_mac_10
      item_g_wireless_camera_1           item_w_manbat_claw
      item_g_wireless_camera_2           item_w_mingxiao_melee
      item_g_wireless_camera_3           item_w_mingxiao_spit
      item_g_wireless_camera_4           item_w_mingxiao_tentacle
      item_i_written                     item_w_occultblade
      item_k_ash_cell_key                item_w_rem_m_700_bach
      item_k_carson_apartment_key        item_w_remington_m_700
      item_k_chinese_theatre_key         item_w_sabbatleader_attack
      item_k_clinic_cs_key               item_w_severed_arm
      item_k_clinic_maintenance_key      item_w_sheriff_sword
      item_k_clinic_stairs_key           item_w_sledgehammer
      item_k_edane_key                   item_w_steyr_aug
      item_k_empire_jezebel_key          item_w_supershotgun
      item_k_empire_mafia_key            item_w_thirtyeight
      item_k_fu_cell_key                 item_w_throwing_star
      item_k_fu_office_key               item_w_tire_iron
      item_k_gallery_noir_key            item_w_torch
      item_k_gimble_key                  item_w_tzimisce2_claw
      item_k_hannahs_safe_key            item_w_tzimisce2_head
                                         item_w_tzimisce3_claw
                                         item_w_tzimisce_melee
                                         item_w_unarmed
                                         item_w_uzi
                                         item_w_werewolf_attacks
                                         item_w_wolf_head
                                         item_w_zombie_fists
                                         weapon_physcannon
                                         weapon_physgun
                                         weapon_pistol
    
      Developer Notes:  How did I come up with this list?
      Answer) Vampire/vdata/system/items/
    
    -------------------------------------------------------------------------------
    D. > > > >  Game States (Thanks to wesp for this list)
    -------------------------------------------------------------------------------
    
    -3 Tutorial.
    -2 Tutorial done, transition to haven.
    -1 Entered haven.
    0 Entered Santa Monica.
    1 Convinced Trip to sell you guns.
    2 Showing Elysium tip for the first time (temporary).
    3 Showing combat tip for the first time (temporary).
    5 Spoke with Beckett at warehouse.
    10 Entered downtown.
    15 Elizabeth Dane completed.
    20 Met Bach at Grout's mansion.
    25 Returned from Grout's mansion.
    30 Spoke with Beckett at Museum.
    35 Returned to prince from Museum.
    40 Spoke with Andrei (added by wesp).
    45 Spoke with Gary.
    50 Mandarin started experiments.
    55 Rescued Barabus.
    60 Spoke with Chang brothers (added by wesp).
    65 Returned to prince from Giovanni mansion.
    70 Spoke with Johansen.
    75 Returned to prince from Leopold Society.
    80 Spoke with Ming-Xiao after Hallowbrook.
    85 Spoke with Prince about Ming-Xiao.
    90 Spoke with Jack after Griffith park.
    95 Spoke with end-game cabbie.
    100 Cabbie takes you to Chinatown (Kuei-Jin ending).
    105 Not used.
    110 Cabbie takes you to Prince (Prince ending).
    115 Cabbie takes you to Anarchs (Anarch ending).
    120 Cabbie takes you to Chantry (Camarilla ending).
    125 Cabbie takes you to Chinatown (Solo ending).
    
    -------------------------------------------------------------------------------
    E. > > > >  Common Models
    -------------------------------------------------------------------------------
    
    http://docs.google.com/Doc?id=dhgs89mq_3gtwn2chb
    
    -------------------------------------------------------------------------------
    F. > > > >  VCLAN Values (Patch 1.2)
    -------------------------------------------------------------------------------
    
    http://docs.google.com/Doc?id=dhgs89mq_4fgq4nrfg
    
    -------------------------------------------------------------------------------
    F.5.6 > > > >  VCLAN Values  (Patch 5.6)
    -------------------------------------------------------------------------------
    
    http://docs.google.com/Doc?id=dhgs89mq_5cxmzw4vg
    
    -------------------------------------------------------------------------------
    G. > > > >  Entity Details
    -------------------------------------------------------------------------------
    
    http://docs.google.com/Doc?id=dhgs89mq_6fmx3cxgt
    
    -------------------------------------------------------------------------------
    H. > > > >  Animations
    -------------------------------------------------------------------------------
    
    http://docs.google.com/Doc?id=dhgs89mq_7dbfdbwdh
    
    -------------------------------------------------------------------------------
    I. > > > >  Gestures
    -------------------------------------------------------------------------------
    
    http://docs.google.com/Doc?id=dhgs89mq_8fpssv86r
    
    -------------------------------------------------------------------------------
    J. > > > >  Console Variables and Commands
    -------------------------------------------------------------------------------
    
    http://docs.google.com/Doc?id=dhgs89mq_10cfs83dqp
    
    ===============================================================================
                    < < < < < Contributing Authors > > > > >
    ===============================================================================
    
    Initial Guide Creation:
      -  Dheu
    
    General Advice:
      -  Wesp
    
    ===============================================================================
                        < < < < < Final Words.... > > > > >
    ===============================================================================
    
      The material presented in this Guide is the result of six months of
      trial and error with VTMB as I built my own mod. It includes the basics, the
      lessons that I learned, the bugs that I discovered and the workarounds for
      those limitations that I once thought would be show stoppers.
    
      One thing that I learned over the months is that there is SO MUCH that I
      still down known about this game. It is by no means complete and I invite
      others to share their knowledge with the community at large. Feel free to
      email me if you have a contribution to make to this Guide. If it is small, I
      can add it myself or if it is larger, I an give you temporary write/update
      access to the document.
    
      My ultimate hope is that this Guide will encourage other developers to create
      new adventures or add new game enhancements.