_________________________________________
                   ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
                     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.