Neverwinter Citadel Project

Go Back   Neverwinter Citadel Project > Custom Content > Programming > Scripting and Functions

Reply
 
Thread Tools Display Modes
  #1  
Old 06-15-2009, 04:51 PM
pain pain is offline
Senior Member
 
Join Date: Dec 2007
Posts: 672
Default Area Modifiers - Maturing the Idea

Basically i've got water working the way i want for the most part and it's deployed on my PW and on Obisidian Shore. They seem to be helping to mature it by noting the various issues with it. So the basic concept of triggers defining water areas works even though it needs more work. Now i am working on implementing Wyverns OnBurn script, which allows me to cover both water and fire to some degree which makes me step back to focus on the bigger picture.

The basic concept so far is i have triggers used in the module itself as static definitions, which then define variables on the character ( ie "INWATER" = TRUE or FALSE ). Each is made possible with an onenter and onexit script, and since they are a trigger which can have up to i think it's about 125 points or so these can cover the Entire map or the can just define a puddle and be of any shape.

Supporting this i also define events for the entire area, this works the exact same way, and allows an entire area to be underwater.

This is suplemented by AOE's which also define things the same way. This allows spells to alter how things are working. This again is handled in a similar manner.

So i have a function that gets the variables in a certain spot as well. This allows someone outside of the water, to cast a spell on a target location which is water and have that wall of fire fizzle into a bunch of steam. It also can prevent stacking of the same AOE - perhaps even boosting the first AOE instead of making two expensive AOE's full of visual effects. Now if the caster is in water he has to contend with not being able to speak as he is holding his breath, but the spell target location also affects things.

So if a water elemental or a fire elemental are in an area defined as fire or water they are going to be a lot stronger or weaker, with custom effects, perhaps growing even. This is mainly being implemented by Wyvern, but my system i want to cater to this type of thinking.

So i am thinking i need a way to further allow multiple trigger types, a single variables on AOE's, Triggers and Areas which allows it to be defined as part of a given area type, just to make it so that function that checks for what is defined at a certain point in space does not have to check for 20 different variables -- instead of INWATER, FIRE, AIRBUBBLE, WILDMAGIC vars being checked it can just check ENVIROSTATE which has a 1 for WATER, 2 for fire, and a 4 for Airbubble ( or via bitwise math adding 1 and 2 and 4 allows it to be Airbubble AND underwater AND on fire at the same time ). And of course triggers can overlap at any time which can make things confusing but this will create a combined bit by adding anything it finds together ( if it finds a 1 and a 2, it will return a 3 which bitwise mathwise is a 1 and a 2 and i can check that in a single operation ).

Now then i have to ask all you folks out there, what should this entail besides just water. This includes all game effects, wild magic, dead magic, fire, water, brambles and quicksand of the wilderness. Basically it's important now to figure out the various things that are included in said system soas to make sure they have a cohesive spine and can work well together. Really the goal is to make the game mechanics for the environment work.

As a preliminary list i have the following.

Trigger/Area Terrain Markers

Environment States
  • SC_ENVIRO_NONE BIT0 - nothing going on
  • SC_ENVIRO_AIRPOCKET BIT1- works as a cutter for water, poison or the like
  • SC_ENVIRO_WATER BIT2- water environment ( fire does not work well, has complete water rules )
  • SC_ENVIRO_FIRE BIT3- area is in flames ( damaging, water or ice does not work well ) Triggers burning effect which is mitigated by how soaked a person is.
  • SC_ENVIRO_MAGMA BIT4- areas floor is very hot ( damaging )
  • SC_ENVIRO_COLD BIT5- area is very cold ( damaging )
  • SC_ENVIRO_ELECTRICAL BIT6- area is electrically active ( thunder and lightning damaging )
  • SC_ENVIRO_ACIDIC BIT7- area is acidic ( damaging )
  • SC_ENVIRO_NEGATIVE BIT8- area is negative ( damaging to living ) healing is weakened.
  • SC_ENVIRO_POSITIVE BIT9- area is positive ( damaging to dead ) healing is boosted and players heal.
  • SC_ENVIRO_GALE BIT10- ( gust of wind type effect ) tiny creatures are blown around.
  • SC_ENVIRO_LIGHTVEGETATION BIT11- ( cover - hiding in reeds for example )
  • SC_ENVIRO_VEGETATION BIT12- ( movement )
  • SC_ENVIRO_SLIPPERY BIT13- ( movement includes slipping - grease effect )
  • SC_ENVIRO_HARDTERRAIN BIT14- ( slow movement rocky, sand, mud )
  • SC_ENVIRO_BLINDING BIT15- ( sandstorm or icestorm )
  • SC_ENVIRO_DISEASE BIT16- ( contagious )
  • SC_ENVIRO_POISON BIT17- ( poison gas )
  • SC_ENVIRO_STENCH BIT18- ( ambience, just to tell it stinks like sulphur)
  • SC_ENVIRO_FLAMMABLE BIT19- ( combat, prone to fires )
  • SC_ENVIRO_BRIGHT BIT20- area is bright ( affect hiding )
  • SC_ENVIRO_DARK BIT21- area is dark ( affect hiding )
  • SC_ENVIRO_HOLY BIT22- ( boosts good magic, weakens evil )
  • SC_ENVIRO_PROFANE BIT23- ( boosts evil magic, weakens good )
  • SC_ENVIRO_LAW BIT24- ( boosts lawful magic, weakens chaos )
  • SC_ENVIRO_CHAOS BIT25- ( boosts chaos magic, weakens law )
  • SC_ENVIRO_DEADMAGIC BIT26- magic does not work
  • SC_ENVIRO_WILDMAGIC BIT27- magic works too well
  • SC_ENVIRO_TOWN BIT28- for server management
  • SC_ENVIRO_REST BIT29- for server management
  • SC_ENVIRO_NOREST BIT30- for server management
  • SC_ENVIRO_ANCHORED BIT31- no teleport blocker, blocks all by subschool having teleportation or calling.

Character States - these combine with the above and their purpose is to flag the character with a given state and then targeting scripts or a single heartbeat on that player. ( currently each effect spawns it's own heartbeat which is not a good idea in a larger system if multiple triggers are involved )

Note that these are applied along with the effects of a given state, so if water is left, these remain and are removed along with the water slowing effect. ( this allows me to only check for water code when they are in SC_ENVIRO_WATER or if they still have a state of WATER and thus remove the water effects. More indirect but it allows triggers to be more complicated. )
  • NONE
  • WATER - Character is to some degree in water-note an air bubble he is in water but not in water really.
  • SPLASHING - Character is on wet terrain
  • WADING - Character is partially in water
  • SUBMERGED - Character is holding breath underwater
  • DROWNING
  • PANICKED
  • ACIDIC
  • GALE
  • LTVEGETATION
  • VEGETATION
  • SLIPPERY
  • HARDTERRAIN
  • BLINDING
  • DISEASE
  • POISON
  • STENCH
  • FLAMMABLE
  • BRIGHT
  • DARK
  • HOLY
  • PROFANE
  • LAW
  • CHAOS
  • DEADMAGIC
  • WILDMAGIC
  • CONFUSED
  • ANCHORED

Variables stored on the character. Either bitmasks or integers ( DC's in some cases, and to show the amount of the effect on others. )
  • CSL_ENVIRO - bitmask ( see above )
  • CSL_CHARSTATE - bitmask ( see above )
  • CSL_BURNING - integer, 0 being not burning, 1 and above doubles as the DC
  • CSL_FLAMMABLE - indicates player is extra flammable
  • CSL_STOPDROPANDROLL - indirect means of indicating player is rolling on ground to put out fires
  • CSL_GUSTED - indirect means to note a gust of wind effect
  • CSL_SOAKED - adjusted from 1-20 and based on if in rain, water, lakes, wading, or from certain spells, decreases by 1 per round so it effective is how long you are soaked. Reduces non magical ( area ) fire damage by the given amount.
  • CSL_HOLDBREATH - 0 is not holding, above 0 is number of rounds that has been holding breath
  • CSL_LOSTBREATH - applies a reduction in how long they can hold their breath indirectly
  • CSL_AIRBUBBLES - applies an increase in how long they can hold their breath indirectly
  • CSL_WATERDC - DC for water saves ( goes up each round )
  • CSL_CURRENTHPS - the the heartbeat can detect damage and thus do concentration checks
  • CSL_WATERSURFACEHEIGHT - the height of the water area stored on the character
  • CSL_TERRAINSLOWINGFACTOR - the slowing caused by various terrain
  • CSL_WATERSLOWINGFACTOR - the slowing caused by water

Last edited by pain; 06-25-2009 at 05:42 PM.
Reply With Quote
  #2  
Old 06-15-2009, 08:35 PM
dunniteowl dunniteowl is offline
WIKI-Primate
 
Join Date: Jun 2006
Location: Bastrop, TX
Posts: 1,802
Send a message via Yahoo to dunniteowl
Default

Sheesh, and you said you didn't want a sim...

SNOW
SAND
ICE
MUD
SWAMP
ROCKY
SLIPPERY
(mossy, slick, oily, what have you)

Other character states that I can think of could be:
SOAKED (that would be wet, but not necessarily in water -- it could make spells like Fireball, Burning Hands, and other fire based spells less effective, while it could make, Ray of Frost, Ice Storm, Hail, Lightning Bolt, Shocking Grasp more effective.)

Pretty much any other should already be covered under most normal spell effects. If there are others, you know I'll shout 'em out.

regards,
dunniteowl
__________________
Simple does not mean easy. Easy does not mean without value. Value is not always measured with money. Money isn't class. Class is not superiority. Superiority is not being better than something or somebody. We are all somebody.
Reply With Quote
  #3  
Old 06-16-2009, 02:03 PM
pain pain is offline
Senior Member
 
Join Date: Dec 2007
Posts: 672
Default

I went and updated the list in first post.

I went and incorporated various effects for server management - town, rest and not rest zones.

Very simple consecration and desecration for the alignments.

Simplified terrain to game effects. Vegetation is split into two, one for hiding, one for movement. ( hides in bushes ).

Broadened out the damage types. Kept fire and magma, basically use both at once for hotter areas.

Note i am limited to 31 bits, so to add one, i have to remove one or consolidate. ( this actually helps keep things on topic and makes each one be the most useful )
Reply With Quote
  #4  
Old 06-17-2009, 03:35 PM
pain pain is offline
Senior Member
 
Join Date: Dec 2007
Posts: 672
Default

Ok i am thinking i need to do it in a manner as follows.

One: RegisterEntry
Two: RegisterExit

These set the state var on the character or NPC AND adds them to the global list of observed targets. So a water entery adds the water bit to the character and increments it's entry by one ( so STATE = 2 ( assuming bit 2 is water ), and INWATER =1 ). If they hit another trigger before they exit STATE stays at 2 and INWATER = 2. On exit the INWATER goes to 1, and the STATE stays at 2. Then they exit the other trigger and the STATE goes to 0 and the INWATER goes to 0.

Functions GetObjectState and GetTargetLocationState return the state var which can be checked via if ( GetObjectState(oPC) & 2 ) to see if water is involved.

Now the Register stores the character's reference on a central object. Each round this is iterated using the new iterator functions. Each is then checked to see if it's valid, if not it's deleted, if it's in transition which then pauses everything, or if it has a state var of some sort. ( this transistion is careful to allow a person to be in a water trigger, to leave said trigger, and if they arrive on the other side of the transition in another water trigger, they are treated as if they did not leave it )

This register is also used if a placeable catches on fire. Note that this code should be optimized to only deal with things in areas that players are currently using. If a placeable is burning in an area that the player is no longer in, it should be put out or paused ( or just do a lesser math and not bother with the look and feel of it ). When the object is no longer on fire, they are deregistered. This is all to increase ambience so it's of no use if no one is there to watch.

States that have per round effects ( slippery for example, or damage if they are in a fire zone, or it decrements the amount of air they have remaining ) will be applied as needed in the main iterator. An audit mode, summarizing the activity can be used by DM's, and a chat command to list all the objects currently being tracked. This should allow the DM's and Admins to manage the heartbeats. This likely will be done via other scripts so the core does not always have to be recompiled, and some way of easily extending this should be developed. Note that some things that don't run each round might also come into play.

This is of course very little to do with water, and includes fire, areas damaging a player due to extreme environments, lights and shadows affecting a players hiding ability. I just focus on water since i have it fully developed, but this should do all of the above states.

The goal is to lessen the need for heartbeats and to make these under control.

As an extra feature of said system, it might be able to have modes which can be changed as needed which increase or decrease the CPU usage, perhaps not doing placeable effects and just focusing on the PC's and their companions. Or doing the PCs every round and the other less important objects every 5th round.

Another thing to look at is the ability of invisibilty to return after combat, if they have the concealment still in effect the invisibility should return if they are not observed. They actually don't stop being invisible it's just the characters know they are there if they attack.

Now as another feature of this system, a registry of Cron entries, or periodic events to be run at a given time. These are checked on a per minute basis, some of which tied to the above. But basically it should allow storage of a list of entries such as run a script at dawn to wake up the town, run a script after 3 hours to restart the server, run a script to clean up any loot bags in empty areas, check if a player is idle and afk so they can be booted OR make things happen in areas where you want to enforce motion. ( i have a crossroads area where if you don't move in a minute the areas monster will come and port you to a random spot on the server mainly to prevent folks camping in wait for victims )
Reply With Quote
  #5  
Old 06-17-2009, 06:55 PM
dunniteowl dunniteowl is offline
WIKI-Primate
 
Join Date: Jun 2006
Location: Bastrop, TX
Posts: 1,802
Send a message via Yahoo to dunniteowl
Default

Good stuff, pain. On the invisibility. The spell (unless it changed when I wasn't looking) is negated on attack, is it not? Of course, a Ring of Invisibility should allow a person within 1 round of no attacks to simply become invisible and thus, increase the odds of slipping quietly away. Otherwise, wouldn't they have to recast?

Just asking.

dunniteowl
__________________
Simple does not mean easy. Easy does not mean without value. Value is not always measured with money. Money isn't class. Class is not superiority. Superiority is not being better than something or somebody. We are all somebody.
Reply With Quote
  #6  
Old 06-17-2009, 07:26 PM
pain pain is offline
Senior Member
 
Join Date: Dec 2007
Posts: 672
Default

Negated on improved or greater, but they leave you concealed because you are actually still invisible even if the mechanics of the game say you are not. Basically you are still invisible, but you are still fightable with 50% concealment which means folks are guessing as to where you truly are but they can only attack after you attack and reveal yourself.

Per the srd http://www.d20srd.org/srd/spells/inv...ityGreater.htm
This spell functions like invisibility, except that it doesn’t end if the subject attacks.

Now if you have greater invisibility and your first opponent is slain and the next opponent is in the next room, well you still are invisible. Therefore we have it so the invisibility can come back IF the player is still concealed.

Recasting is not a big deal if there are just a few, you have a ring, but it also works fine balance wise to have the convenience of it popping back on and we don't hand out rings - we are low magic so this really helps the casters deal with multiple groups of monsters without having to rebuff constantly.
Reply With Quote
  #7  
Old 06-19-2009, 04:01 AM
pain pain is offline
Senior Member
 
Join Date: Dec 2007
Posts: 672
Default

Basically an alpha version, concepts seem to work.

Code:
/////////////////////////////////////////////////////
///////////////// DESCRIPTION ///////////////////////
/////////////////////////////////////////////////////
// *********************************************
// **                 Fire                    **
// **  ( Thanks to Obsidian Shore - Wyvern )  **
// *********************************************

/* - FROM P&P DESCRIPTION -
Catching On Fire
Characters exposed to burning oil, bonfires, and noninstantaneous magic fires might find their
clothes, hair, or equipment on fire. Spells with an instantaneous duration don't normally set a
character on fire, since the heat and flame from these come and go in a flash. 

Characters at risk of catching fire are allowed a DC 15 Reflex save to avoid this fate. If a
character's clothes or hair catch fire, he takes 1d6 points of damage immediately. In each
subsequent round, the burning character must make another Reflex saving throw. Failure means
he takes another 1d6 points of damage that round. Success means that the fire has gone out.
(That is, once he succeeds on his saving throw, he’s no longer on fire.) 

A character on fire may automatically extinguish the flames by jumping into enough water to
douse himself. If no body of water is at hand, rolling on the ground or smothering the fire
with cloaks or the like permits the character another save with a +4 bonus. 

Those unlucky enough to have their clothes or equipment catch fire must make DC 15 Reflex
saves for each item. Flammable items that fail take the same amount of damage as the character. 
*/

// *********************************************
// **                 Water                   **
// **  ( Pain - Seed - Ideas by Dunniteowl )  **
// *********************************************
/*

Water slowing of 50%
Checks for models height, and drowns player only when head goes under.
Certain races and creatures and polymorphs are not affected by slowing or drowning ( water genasi, constructs, weasels can swim etc. )
Spells with a verbal component cannot be cast while holding breath unless player has metamagic to avoid issue.
Fire Spells require a DC check to cast
Acid based spells last one category of time shorter ( turns become rounds, hours become turns, rounds become seconds )
Area of effect spells have their durations shortened.
Invisibility does not work, and just provides concealment which persists with greater, and ends when they attack with regular.
Character can hold breath for twice their con score in rounds. Last 10 rounds count down to 0 after which they have to do fortitude saves to avoid damage.
Spells and damage can lower the rounds you can hold.
DC increases each round after breath holding stops and player gets a saving throw or they drown taking 10% of their hitpoints in damage.
When drowning player must save vs fear, note that once failed they basically are going to die.

*/




/////////////////////////////////////////////////////
//////////////// Includes ///////////////////////////
/////////////////////////////////////////////////////

#include "_SCUtility_Position"
#include "_SCUtility_Math"
#include "_SCUtility_Time"
#include "_SCUtility_Stealth"
#include "_SCUtility_MetaModifiers"
/////////////////////////////////////////////////////
///////////////// Constants /////////////////////////
/////////////////////////////////////////////////////

int CSL_ENVIRO_NONE			= BIT0;
int CSL_ENVIRO_AIRPOCKET	= BIT1; // * works as a cutter ( air inside a water area )
int CSL_ENVIRO_WATER		= BIT2; // * water environment ( fire does not work well )
int CSL_ENVIRO_FIRE			= BIT3; // * area is in flames ( damaging, water or ice does not work well )
int CSL_ENVIRO_MAGMA		= BIT4; // areas floor is very hot ( damaging )
int CSL_ENVIRO_COLD			= BIT5; // * area is very cold ( damaging )
int CSL_ENVIRO_ELECTRICAL	= BIT6; // area is electrically active ( thunder and lightning damaging )
int CSL_ENVIRO_ACIDIC 		= BIT7; // * area is acidic ( damaging )
int CSL_ENVIRO_NEGATIVE		= BIT8; // * area is pure negative ( damaging to living )
int CSL_ENVIRO_POSITIVE		= BIT9; // area is pure positive ( damaging to dead )
// THESE ARE CONDITIONS
int CSL_ENVIRO_GALE			= BIT10; // * ( GUST OF WIND TYPE EFFECT )
int CSL_ENVIRO_LTVEGETATION	= BIT11; // * ( COVER )
int CSL_ENVIRO_VEGETATION	= BIT12; // * ( MOVEMENT )
int CSL_ENVIRO_SLIPPERY		= BIT13; // * ( MOVEMENT )
int CSL_ENVIRO_HARDTERRAIN	= BIT14; // ( slow movement rocky, sand, mud )
int CSL_ENVIRO_BLINDING		= BIT15; // ( SANDSTORM OR ICESTORM )
int CSL_ENVIRO_DISEASE		= BIT16; // ( CONTAGIOUS )
int CSL_ENVIRO_POISON		= BIT17; // ( POISON GAS )
int CSL_ENVIRO_STENCH		= BIT18; // ( AMBIENCE, JUST TO TELL IT STINKS )
int CSL_ENVIRO_FLAMMABLE	= BIT19; // ( COMBAT, PRONE TO FIRES )
// light and dark
int CSL_ENVIRO_BRIGHT		= BIT20; // * AREA IS BRIGHT ( AFFECT HIDING )
int CSL_ENVIRO_DARK			= BIT21; // * AREA IS DARK ( AFFECT HIDING )
// Alignment
int CSL_ENVIRO_HOLY			= BIT22; // * ( BOOSTS GOOD MAGIC, WEAKENSEVIL )
int CSL_ENVIRO_PROFANE		= BIT23; // * ( BOOSTS EVIL MAGIC, WEAKENS GOOD )
int CSL_ENVIRO_LAW			= BIT24; // * ( BOOSTS LAWFUL MAGIC, WEAKENS CHAOS )
int CSL_ENVIRO_CHAOS		= BIT25; // * ( BOOSTS CHAOS MAGIC, WEAKENS LAW )
// Other effects
int CSL_ENVIRO_DEADMAGIC	= BIT26; // * MAGIC DOES NOT WORK
int CSL_ENVIRO_WILDMAGIC	= BIT27; // * MAGIC WORKS TOO WELL
int CSL_ENVIRO_TOWN			= BIT28; // * FOR SERVER MANAGEMENT
int CSL_ENVIRO_REST			= BIT29; // * FOR SERVER MANAGEMENT
int CSL_ENVIRO_NOREST		= BIT30; // * FOR SERVER MANAGEMENT
int CSL_ENVIRO_ANCHORED		= BIT31; // * NO TELEPORT BLOCKER



int CSL_CHARSTATE_NONE			= BIT0;
int CSL_CHARSTATE_WATER			= BIT2;

int CSL_CHARSTATE_SPLASHING = BIT4;
int CSL_CHARSTATE_WADING = BIT5;
int CSL_CHARSTATE_SUBMERGED = BIT6;

int CSL_CHARSTATE_DROWNING		= BIT7;
int CSL_CHARSTATE_PANICKED		= BIT8;
int CSL_CHARSTATE_ACIDIC		= BIT9;
int CSL_CHARSTATE_GALE			= BIT10; // * ( GUST OF WIND TYPE EFFECT )
int CSL_CHARSTATE_LTVEGETATION	= BIT11; // * ( COVER )
int CSL_CHARSTATE_VEGETATION	= BIT12; // * ( MOVEMENT )
int CSL_CHARSTATE_SLIPPERY		= BIT13; // * ( MOVEMENT )
int CSL_CHARSTATE_HARDTERRAIN	= BIT14; // ( slow movement rocky, sand, mud )
int CSL_CHARSTATE_BLINDING		= BIT15; // ( SANDSTORM OR ICESTORM )
int CSL_CHARSTATE_DISEASE		= BIT16; // ( CONTAGIOUS )
int CSL_CHARSTATE_POISON		= BIT17; // ( POISON GAS )
int CSL_CHARSTATE_STENCH		= BIT18; // ( AMBIENCE, JUST TO TELL IT STINKS )
int CSL_CHARSTATE_FLAMMABLE		= BIT19; // ( COMBAT, PRONE TO FIRES )
int CSL_CHARSTATE_BRIGHT		= BIT20; // * AREA IS BRIGHT ( AFFECT HIDING )
int CSL_CHARSTATE_DARK			= BIT21; // * AREA IS DARK ( AFFECT HIDING )
int CSL_CHARSTATE_HOLY			= BIT22; // * ( BOOSTS GOOD MAGIC, WEAKENSEVIL )
int CSL_CHARSTATE_PROFANE		= BIT23; // * ( BOOSTS EVIL MAGIC, WEAKENS GOOD )
int CSL_CHARSTATE_LAW			= BIT24; // * ( BOOSTS LAWFUL MAGIC, WEAKENS CHAOS )
int CSL_CHARSTATE_CHAOS			= BIT25; // * ( BOOSTS CHAOS MAGIC, WEAKENS LAW )
int CSL_CHARSTATE_DEADMAGIC		= BIT26;
int CSL_CHARSTATE_WILDMAGIC		= BIT27;
int CSL_CHARSTATE_CONFUSED		= BIT27;
int CSL_CHARSTATE_ANCHORED		= BIT31;

// Water States
const int CSL_WATERSTATE_NONE = 0;
const int CSL_WATERSTATE_SPLASHING = 1;
const int CSL_WATERSTATE_WADING = 2;
const int CSL_WATERSTATE_SUBMERGED = 3;


// SpellId's for effects
int CSL_ENVIRO_SPELLIDSTART	= 8000; // * Used to determine the start of the 31 spellid range, note it uses negative ints but i still want to keep it away from real spellids

const int SPELLENVIRO_WATERSLOWING = -10;
const int SPELLENVIRO_TERRAINSLOWING = -11;
const int SPELLENVIRO_BURNING = -12;

string CSL_ENVIRO_HEARTBEAT_SCRIPT = "TG_EnviroControl"; // script that handles the heartbeat, not implemented yet
/////////////////////////////////////////////////////
//////////////// Prototypes /////////////////////////
/////////////////////////////////////////////////////


void CSLEnviroEntry( object oPC, int iEnvironmentType, object oEnviroObject = OBJECT_SELF );
void CSLEnviroExit( object oPC, int iEnvironmentType, object oEnviroObject = OBJECT_SELF );
int CSLEnviroObjectIsWithin( object oPC, int iEnvironmentType );
int CSLEnviroObjectGetStatus( object oPC );
int CSLEnviroLocationIsWithin( location lTarget, int iEnvironmentType );
int CSLEnviroLocationGetStatus( location lTarget );
object CSLGetEnviroControl( object oThingInTargetArea = OBJECT_SELF );
int CSLGetExpirationRound( float fDuration );
float CSLGetRemainingDuration( int iExpirationRound );
void CSLEnviroControlHB();
void CSLEnviroObjectHeartbeat( object oPC = OBJECT_SELF );
int CSLEnviroSpellHookCasterCheck( int iCasterState, int iCharState = 0, object oCaster = OBJECT_SELF, int iSpellId = -1, int iDescriptor = -1, int iClass = 255, int iSpellLevel = -1, int iSpellSchool = -1, int iSpellSubSchool = -1, int iAttributes = -1 );
int CSLEnviroSpellHookTargetCheck( int iTargetState,  int iCharState = -1, object oCaster = OBJECT_SELF, int iSpellId = -1, int iDescriptor = -1, int iClass = 255, int iSpellLevel = -1, int iSpellSchool = -1, int iSpellSubSchool = -1, int iAttributes = -1 );
void CSLBurningStart( int iStartingDC = 1, object oPC = OBJECT_SELF );
void CSLEnviroSlowingWater( object oPC, int iRateDecrease = 50 );
void CSLEnviroSlowingTerrain( object oPC, int iRateDecrease = 50 );
int CSLEnviroHoldBreath( int iBreathHoldingRounds, int iPreviousHitpoints, object oPC = OBJECT_SELF );
int CSLToggleWaterStateBits( int iCharState, int iBitToSet = 0 );
int CSLEnviroCheckWaterState( object oPC = OBJECT_SELF, int iCharState = -1, int iEnviroState = -1, int iAreaState = -1 );
int CSLEnviroCheckDeadMagicState( object oPC = OBJECT_SELF, int iCharState = -1, int iEnviroState = -1, int iAreaState = -1 );

// older deprecating code
//int CSLCheckTriggerFlagsAtPosition( location lTarget, string sVarName );
//int getStateInWater( object oPC = OBJECT_SELF );

/////////////////////////////////////////////////////
//////////////// Implementation /////////////////////
/////////////////////////////////////////////////////

// VFXSC_PLACEHOLDER = 1800 a blank visual effect basically
// not sure if i even need both visual and the integer tracking the effect, will probably near the end pick the better option
// but it might just make it easier to spot if a character has any effects in the range instead of checking 31 different effects


// This is used by a 
void CSLEnviroEntry( object oPC, int iEnvironmentType, object oEnviroObject = OBJECT_SELF )
{
	if ( !GetIsObjectValid( oPC )  ) { return; }
	
	int iEnviroState = GetLocalInt( oPC, "CSL_ENVIRO" );
	int iSpellId = ( iEnvironmentType+CSL_ENVIRO_SPELLIDSTART );
	
	// CSLIncrementLocalInt( oPC, "INWATER", 1 );
	ApplyEffectToObject( DURATION_TYPE_PERMANENT, SetEffectSpellId( EffectVisualEffect( VFXSC_PLACEHOLDER ) , iSpellId ), oPC );
	
	iEnviroState = iEnviroState | iEnvironmentType; // bitwise add
	
	SetLocalInt( oPC, "CSL_ENVIRO", iEnviroState );
	
	// Need to register object to make sure it's being tracked here
	SetLocalObject( CSLGetEnviroControl(), ObjectToString(oPC), oPC );
	
}


void CSLEnviroExit( object oPC, int iEnvironmentType, object oEnviroObject = OBJECT_SELF )
{
	if ( !GetIsObjectValid( oPC )  ) { return; }
	
	int iEnviroState = GetLocalInt( oPC, "CSL_ENVIRO" );
	int iSpellId = ( iEnvironmentType+CSL_ENVIRO_SPELLIDSTART );
	
	// CSLDecrementLocalInt(oPC, "INWATER", 1);
	CSLRemoveEffectSpellIdSingle( SC_REMOVE_ONLYCREATOR, oEnviroObject, oPC, -iSpellId );
	
	if ( !GetHasSpellEffect( -iSpellId, oPC ) ) 
	{
		// remove the state var via bitwise math
		if ( iEnviroState & iEnvironmentType )
		{
			if (DEBUGGING >= 8) { SendMessageToPC( oPC, "Removing state var iEnvironmentType="+IntToString(iEnvironmentType)+" from iEnviroState="+IntToString(iEnviroState) ); }
			iEnviroState = iEnviroState - iEnvironmentType;
			SetLocalInt( oPC, "CSL_ENVIRO", iEnviroState );
		}
		
	}
	// if state is not in here, it will get automatically culled as needed
}


int CSLEnviroObjectIsWithin( object oPC, int iEnvironmentType )
{
	int iSpellId = ( iEnvironmentType+CSL_ENVIRO_SPELLIDSTART );
	if ( GetHasSpellEffect( -iSpellId, oPC ) ) 
	{
		return TRUE;
	}
	
	// yes duplicated, have to choose this or the previous logic to actually handle how this works
	int iEnviroState = GetLocalInt( oPC, "CSL_ENVIRO" );
	if ( iEnviroState & iEnvironmentType )
	{
		return TRUE;
	}
	return FALSE;
}

int CSLEnviroObjectGetStatus( object oPC )
{
	return GetLocalInt( oPC, "CSL_ENVIRO" );
}




// * Returns TRUE if the location has an object that matches the given en
int CSLEnviroLocationIsWithin( location lTarget, int iEnvironmentType )
{
	object oSubArea = GetFirstSubArea( GetAreaFromLocation(lTarget), GetPositionFromLocation( lTarget ) );
	int iEnviroState = 0;
	while( GetIsObjectValid( oSubArea ) )
	{
		if ( GetLocalInt(oSubArea, "CSL_ENVIRO" ) & iEnvironmentType ) { return TRUE; }
		oSubArea = GetNextSubArea( GetAreaFromLocation(lTarget) );
	}
	return FALSE;
}

// * Returns the Status for a given location, which can be looked at with bitwise math to see what conditions apply
int CSLEnviroLocationGetStatus( location lTarget )
{
	object oSubArea = GetFirstSubArea( GetAreaFromLocation(lTarget), GetPositionFromLocation( lTarget ) );
	int iEnviroState = 0;
	while( GetIsObjectValid( oSubArea ) )
	{
		iEnviroState = iEnviroState | GetLocalInt(oSubArea, "CSL_ENVIRO" );
		oSubArea = GetNextSubArea( GetAreaFromLocation(lTarget) );
	}
	return iEnviroState;
}

//GetFirstArea()
// Get the g control object (invisible placeable)(ipoint).
// Create one if missing. Battle control monitors the battlefield 
// and performs all neccessary administration using heartbeat script.
// Originally planned to use Area heartbeat to act as Battle control
// but SetEventHandler does not work on area
object CSLGetEnviroControl( object oThingInTargetArea = OBJECT_SELF )
{
	object oModule = GetModule();
	object oEC = GetLocalObject( oModule, "ENVIRO_CONTROL" );
	if( !GetIsObjectValid(oEC) )
	{
		//SendMessageToPC( GetFirstPC(), "Environment Control Created in "+GetName( oAR ) );
		// Battle control not exist, Create one
		oEC = CreateObject(OBJECT_TYPE_PLACEABLE, "plc_ipoint ", GetLocation( oThingInTargetArea ), FALSE, "plc_environmentcontrol"); 
		
		//SCCreatePlacable("plc_ipoint ", oThingInTargetArea, "plc_environmentcontrol");
		// Register New Battle Control
		SetPlotFlag(oEC, TRUE);
		SetEventHandler(oEC, SCRIPT_PLACEABLE_ON_HEARTBEAT, CSL_ENVIRO_HEARTBEAT_SCRIPT);
		SetLocalObject(oModule, "ENVIRO_CONTROL", oEC);
	}
	return oEC;
}

// * Gets the round a given effect is going to expire
// * Used to store an expiration time for later usage
// * Dependant on the current round number being constantly updated in this implementation
// * probably can revise so it uses the servers uptime instead
// * might be good to tag the session as well to remove the issue of effects cast on a previous session
int CSLGetExpirationRound( float fDuration )
{
	int iCurrentRound = GetLocalInt( GetModule(), "CSL_CURRENT_ROUND" );
	
	if ( fDuration <= 0.0f || iCurrentRound == 0 ) 
	{
		return 0;
	}
	
	return ( FloatToInt( fDuration/6 ) + iCurrentRound );
}


// * Gets the remaining duration based on the expiration round
// * Used to store an expiration time for later usage
// * Only works in rounds but it can be off by up to a round or so
float CSLGetRemainingDuration( int iExpirationRound )
{
	int iCurrentRound = GetLocalInt( GetModule(), "CSL_CURRENT_ROUND" );
	
	int iRemainingDuration = iExpirationRound - iCurrentRound;
	
	if ( iExpirationRound == 0 || iRemainingDuration <= 0 ) 
	{
		return 0.0f;
	}
	return RoundsToSeconds(iRemainingDuration);
}


// Battle Control Heart Beat
void CSLEnviroControlHB()
{
	object oEC = CSLGetEnviroControl();
	object oModule = GetModule();
	int iCurrentRound = GetLocalInt( oModule, "CSL_CURRENT_ROUND" )+1;
	
	
	
	// string get the current time in minutes
	// get the current beat number since module start
	// get the current
	
	
	/*
	// Loop thru the PCs
	object oPC;
	oPC = GetFirstPC();
	while ( GetIsObjectValid( oPC ) )
	{
		// Add in other heartbeat checks here, make sure everything here is very optimized as it gets run a lot
		// Need to look at how this gets implemented in the long run
		//SendMessageToPC( oPC, "Heart beating");
		
		
		
		checkHeartBeatElaborateParry( oPC );
		checkHeartBeatDeadlyDefense( oPC );
		checkHeartBeatTwoWeaponDefense( oPC );
		
		// This is probably obsolete
		// Note that this is only for the MP Invis fix, and if not used the entire heartbeat could be removed, and if used this variable check should be removed as well
		//if ( GetLocalInt( GetModule(), "SC_MPINVISFIX" ) == TRUE )
		//{
		//	// this checks for the invisibility status, and removes or adds it as needed
		//	SCHeartBeatInvisCheck( oPC );
		//}
		
		
		oPC = GetNextPC();
	}
	*/
	
	

	int count = GetVariableCount( oEC );
	
	if (DEBUGGING >= 8) { SendMessageToPC( GetFirstPC(), "CSLEnviroControlHB working on "+IntToString(count)+" objects" ); }
	int x;
	object oCurrent;
	string sCurrent;
	int iEnviroStatus = 0;

	for (x = count-1; x >= 0; x--) 
	{
		sCurrent = GetVariableName(oEC, x);
		oCurrent = GetVariableValueObject(oEC, x );
		
		
		if (DEBUGGING >= 8) { SendMessageToPC( GetFirstPC(), "iterating on "+sCurrent+" for objects #"+IntToString(count)+" "+GetName( oCurrent ) ); }
		if ( GetIsObjectValid( oCurrent ) )
		{
			if ( GetIsObjectValid(GetArea(oCurrent)) && !GetLocalInt(oCurrent, "TRANSITION") ) // make sure they are not in process of a transition - just wait until they are done
			{
				iEnviroStatus = GetLocalInt( oCurrent, "CSL_ENVIRO" );
				if ( iEnviroStatus == CSL_ENVIRO_NONE && !GetLocalInt( oCurrent, "CSL_BURNING" ))
				{
					if (DEBUGGING >= 8) { SendMessageToPC( GetFirstPC(), "Removing "+sCurrent ); }
					DeleteLocalObject( oEC, sCurrent ); // no status, we can stop tracking
				}
				else
				{
					// Do the drowning things, or whatever else here.
					// One branch for each state perhaps or all the logic in one place
					//ExecuteScript(SCR_EX_VD, oCurrent);
					//LaunchCoverFire(sArmy, ACS_BRR); //Barrage
					//LaunchCoverFire(sArmy, ACS_CTP); //Catapult
					// Drown effects
					// Lights
					CSLEnviroObjectHeartbeat( oCurrent );
				}
			}
		}
		else
		{
			DeleteLocalObject( oEC, sCurrent );
		}
	}
	
	
	SetLocalInt( oModule, "CSL_CURRENT_ROUND", iCurrentRound );

}


void CSLEnviroObjectHeartbeat( object oPC = OBJECT_SELF )
{
	if ( !GetIsObjectValid( oPC ) || !GetIsObjectValid( GetArea(oPC) ) || GetLocalInt( oPC, "TRANSITION" ) )
	{
		return;
	}
	
	int iPreviousHitpoints = GetLocalInt( oPC, "UW_CURRENTHPS"  );
	
	if (DEBUGGING >= 8) { SendMessageToPC( oPC, "CSLEnviroObjectHeartbeat" ); }
	
	int iAreaState = GetLocalInt( GetArea(oPC), "CSL_ENVIRO" );
	int iEnviroState = iAreaState | GetLocalInt( oPC, "CSL_ENVIRO" );
	int iCharState = GetLocalInt( oPC, "CSL_CHARSTATE" );
	
	
	int iBurning = GetLocalInt( oPC, "CSL_BURNING" );
	
	int iFlammable = GetLocalInt( oPC, "CSL_FLAMMABLE" );
	int iGusted = GetLocalInt( oPC, "CSL_GUSTED" );
	int iSoaked = GetLocalInt( oPC, "CSL_SOAKED" );
	
	
	// **********************************
	// **       WATER and DROWNING     **
	// **********************************
	iCharState = CSLEnviroCheckWaterState( oPC, iCharState, iEnviroState, iAreaState );
	int iBreathHoldingRounds = GetLocalInt( oPC, "CSL_HOLDBREATH" );
	if ( iCharState & CSL_WATERSTATE_SUBMERGED && GetIsOwnedByPlayer( oPC ) && !GetIsDead(oPC) && !CSLGetHasEffectType( oPC, EFFECT_TYPE_PETRIFY ) && CSLGetIsDrownable( oPC, TRUE ) )
	{		
		iBreathHoldingRounds = CSLEnviroHoldBreath( iBreathHoldingRounds, iPreviousHitpoints, oPC );
		SetLocalInt( oPC, "CSL_HOLDBREATH", iBreathHoldingRounds );
		iBurning = FALSE;
	}
	else if ( iBreathHoldingRounds > 0 )
	{
		SetLocalInt( oPC, "CSL_HOLDBREATH", 0 );
		DeleteLocalInt( oPC, "CSL_HOLDBREATH" );
		
		if ( !GetIsDead(oPC) )
		{
			SendMessageToPC(oPC, "You take in a big breath of air");
		}
		if ( iCharState & CSL_CHARSTATE_DROWNING )
		{
			iCharState = iCharState - CSL_CHARSTATE_DROWNING;
		}
		//SetLocalInt( oPC, "CSL_HOLDBREATH", 0 );
		//iBreathHoldingRounds = 0; // CSLGetMax( iBreathHoldingRounds-10, 0 );
		//SetLocalInt( oPC, "CSL_HOLDBREATH", 0 );
		SetLocalInt( oPC, "UW_CURRENTHPS", GetCurrentHitPoints(oPC) );
		SetLocalInt( oPC, "UW_DROWNING", FALSE );
		SetLocalInt( oPC, "UW_PANICKED", FALSE );
		SetLocalInt( oPC, "CSL_WATERDC", 10 );
	}
	
	// **********************************
	// **              WIND            **
	// **********************************
	if ( iEnviroState & CSL_ENVIRO_GALE )
	{
		if ( d2() == 1 )
		{
			iBurning = FALSE;
		}
		else
		{
			iBurning = iBurning+5;
		}
	
	}
	
	// **********************************
	// **            ACIDIC DAMAGE     **
	// **********************************
	if ( iCharState & CSL_CHARSTATE_SUBMERGED && iEnviroState & CSL_ENVIRO_ACIDIC ) // Acidic Gas of Some Sort
	{
	
	
	}
	
	// **********************************
	// **            COLD DAMAGE       **
	// **********************************
	if ( iEnviroState & CSL_ENVIRO_COLD  )
	{
	
	
	}
	
	// **********************************
	// **          ELECTRIC DAMAGE     **
	// **********************************
	if ( iEnviroState & CSL_ENVIRO_ELECTRICAL )
	{
	
	
	}
	
	// **********************************
	// **          NEGATIVE DAMAGE     **
	// **********************************
	if ( iEnviroState & CSL_ENVIRO_NEGATIVE )
	{
	
	
	}
	
	
	// **********************************
	// **          POSITIVE DAMAGE     **
	// **********************************
	if ( iEnviroState & CSL_ENVIRO_POSITIVE )
	{
	
	
	}
	
	
	if ( iEnviroState & CSL_ENVIRO_DEADMAGIC || iCharState & CSL_CHARSTATE_DEADMAGIC )
	{
		CSLEnviroCheckDeadMagicState( oPC, iCharState, iEnviroState, iAreaState );
	}
	
	// **********************************************
	// **  HIDING - Light Vegetation, Dark, Light  **
	// **********************************************
	if ( iEnviroState & CSL_ENVIRO_LTVEGETATION )
	{
	
	
	}
	
	if ( iEnviroState & CSL_ENVIRO_BRIGHT )
	{
	
	
	}
	
	if ( iEnviroState & CSL_ENVIRO_DARK )
	{
	
	
	}
	
	// **************************************************************
	// **  MOVEMENT - Heavy Vegetation, Hard Terrain and Slippery  **
	// **************************************************************
	if ( iEnviroState & CSL_ENVIRO_VEGETATION || iEnviroState & CSL_ENVIRO_HARDTERRAIN )
	{
	
	
	}
	
	if ( iEnviroState & CSL_ENVIRO_SLIPPERY && !CSLGetIsIncorporeal( oPC ) && !GetIsImmune( oPC, IMMUNITY_TYPE_KNOCKDOWN ) ) 
	{
		int iSaveDC = 15+iSoaked;
		if( !ReflexSave(oPC, iSaveDC, SAVING_THROW_TYPE_NONE) ) // HkSavingThrow(SAVING_THROW_REFLEX, oPC, iSaveDC, SAVING_THROW_TYPE_NONE, OBJECT_SELF, fDelay))
		{
			effect eSlippery = EffectVisualEffect(VFX_HIT_SPELL_ENCHANTMENT);
			eSlippery = EffectLinkEffects(eSlippery, EffectKnockdown() );
			eSlippery = SetEffectSpellId(eSlippery, -SPELL_GREASE);
			DelayCommand( CSLRandomBetweenFloat(0.0, 2.0), ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eSlippery, oPC, 4.0) );
		}
	}

	// ***********************************
	// **      POISON AND BLINDING      **
	// ***********************************
	if ( iEnviroState & CSL_ENVIRO_BLINDING && !GetIsImmune( oPC, IMMUNITY_TYPE_BLINDNESS ) ) // Blinding sand, snow or the like
	{
		int iSaveDC = 15+iGusted;
		//if(!HkSavingThrow(SAVING_THROW_REFLEX, oPC, iSaveDC, SAVING_THROW_TYPE_NONE, OBJECT_SELF, fDelay))
		if( !ReflexSave(oPC, iSaveDC, SAVING_THROW_TYPE_NONE) ) // HkSavingThrow(SAVING_THROW_REFLEX, oPC, iSaveDC, SAVING_THROW_TYPE_NONE, OBJECT_SELF, fDelay))
		{
			effect eBlind = EffectVisualEffect(VFX_DUR_SPELL_BLIND_DEAF);
			eBlind = EffectLinkEffects(eBlind, EffectBlindness() );
			eBlind = SetEffectSpellId(eBlind, -SPELL_BLINDNESS_AND_DEAFNESS);
			DelayCommand( CSLRandomBetweenFloat(0.0, 2.0), ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eBlind, oPC, 6.0) );
		}
	
	}

	if ( iCharState & ~CSL_CHARSTATE_SUBMERGED && iEnviroState & CSL_ENVIRO_POISON && !GetIsImmune( oPC, IMMUNITY_TYPE_POISON ) ) // Poison Gas of some sort
	{
	
	
	}
	
	// **********************************
	// **            FIRE DAMAGE       **
	// **********************************
	if ( iEnviroState & CSL_ENVIRO_FIRE || iEnviroState & CSL_ENVIRO_MAGMA )
	{
		iBurning = CSLGetMax( 1, iBurning );
	}
	
	// **********************************
	// **            BURNING           **
	// **********************************
	if ( iBurning )
	{
		iBurning = iBurning - iSoaked;
		iBurning = CSLGetMin( iBurning, 40 );
		
		if( !GetIsInCombat(oPC) )
		{
			// A character out of combat will be assumed to be actively putting out the fire: Stop Drop and Roll.
			// This is an indirect approach
			CSLEmoteDoStopDropAndRoll( oPC );
		}
		
		
		if ( GetLocalInt( oPC, "CSL_STOPDROPANDROLL" ) )
		{
			AssignCommand(oPC, SpeakString("*tries to put out fire*"));
			DeleteLocalInt( oPC, "CSL_STOPDROPANDROLL" );
		
			iBurning = iBurning - 4;			
		}
		
		
		if( iBurning < 5 || !ReflexSave( oPC, iBurning, SAVING_THROW_TYPE_FIRE))
		{
			iBurning = FALSE;
		}
	}

	if ( iBurning || iEnviroState & CSL_ENVIRO_FIRE )
	{
		int nDamage = d6(1+iGusted);
		effect eDam = EffectDamage(nDamage, DAMAGE_TYPE_FIRE);
		effect eVis2 = EffectVisualEffect(VFX_DUR_FIRE);
		string sVFXForFire = "sfx_fire";
		if ( iGusted || iBurning == 1 )
		{
			sVFXForFire = "fx_fire_lg";
			eDam = EffectLinkEffects(eDam, EffectVisualEffect(VFX_HIT_SPELL_FIRE));
		}
		
		if ( iBurning == 1 )
		{
			iBurning = 10;
		
		}
		else if ( iGusted )
		{
			iBurning = iBurning + iGusted;
		}
		else
		{
			iBurning--;
		}
		
		ApplyEffectToObject(DURATION_TYPE_INSTANT, eDam, oPC);
		ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eVis2, oPC, RoundsToSeconds(1));
		ApplyEffectToObject(DURATION_TYPE_TEMPORARY, EffectNWN2SpecialEffectFile( sVFXForFire ), oPC, 6.0);
	}
	
	SetLocalInt( oPC, "CSL_CHARSTATE", iCharState );
	SetLocalInt( oPC, "CSL_BURNING", iBurning );
	SetLocalInt( oPC, "UW_CURRENTHPS", GetCurrentHitPoints(oPC) );
}

// * Run in the spell hook on the caster
int CSLEnviroSpellHookCasterCheck( int iCasterState, int iCharState = 0, object oCaster = OBJECT_SELF, int iSpellId = -1, int iDescriptor = -1, int iClass = 255, int iSpellLevel = -1, int iSpellSchool = -1, int iSpellSubSchool = -1, int iAttributes = -1 )
{
	// **********************************
	// **        BREATH HOLDING        **
	// **********************************
	/*
	// this should already be done
	int iAreaState = GetLocalInt( GetArea(oPC), "CSL_ENVIRO" );
	int iEnviroState = iAreaState | GetLocalInt( oPC, "CSL_ENVIRO" );
	int iCharState = GetLocalInt( oPC, "CSL_CHARSTATE" );
	*/
	int iBreathHolding = GetLocalInt( oCaster, "CSL_HOLDBREATH");
	
	if ( iBreathHolding > 0) //( CSLGetIsDrownable( oCaster ) && !GetHasSpellEffect( SPELL_WATER_BREATHING, oCaster ) )
	{
		// check for if spell needs verbal component
		int iMetaMagic = CSLGetAutomaticMetamagic( GetMetaMagicFeat(), oCaster ); // { GetMetaMagicFeat();
		
		iMetaMagic = CSLGetAutomaticMetamagic( GetMetaMagicFeat() );
		
		
		iMetaMagic &= CSLReadIntModifier( oCaster, "Spell_MetaMagic" );
		// GetHasFeat( FEAT_EPIC_AUTOMATIC_SILENT_SPELL_1 )
	
		//SendMessageToPC(oCaster, "Metamagic = "+IntToString(iMetaMagic) );
		if (  !( iMetaMagic & METAMAGIC_SILENT ) && CSLGetIsVerbalComponentRequired( iSpellId ) )
		{
			SendMessageToPC(oCaster, "You cannot cast this spell when you cannot breathe");
			SetLocalInt( oCaster, "HKTEMP_Blocked", TRUE );
			return FALSE;
		}
	}
	
	if ( iCasterState & CSL_ENVIRO_WATER || iCharState & ( CSL_CHARSTATE_SPLASHING | CSL_CHARSTATE_WADING | CSL_CHARSTATE_SUBMERGED ) )
	{
		iCharState = CSLEnviroCheckWaterState( oCaster, iCharState, iCasterState );
	
	
	
	
	}
	
	
	return TRUE;

}

// * run on the target or the target location ( indirectly )
int CSLEnviroSpellHookTargetCheck( int iTargetState,  int iCharState = -1, object oCaster = OBJECT_SELF, int iSpellId = -1, int iDescriptor = -1, int iClass = 255, int iSpellLevel = -1, int iSpellSchool = -1, int iSpellSubSchool = -1, int iAttributes = -1 )
{
	int iDamageModType = CSLReadIntModifier( oCaster, "damagemodtype" );
	// **********************************
	// **             TOWN             **
	// **********************************
	if ( iTargetState & CSL_ENVIRO_TOWN && iAttributes != -1 && iAttributes & SCMETA_ATTRIBUTES_CANTCASTINTOWN )
	{
		SendMessageToPC(oCaster, "This spell is not permitted in town.");
		SetLocalInt( oCaster, "HKTEMP_Blocked", TRUE );
		return FALSE;
	}
	
	
	// **********************************
	// **          DEAD MAGIC          **
	// **********************************
	if ( iTargetState & CSL_ENVIRO_DEADMAGIC && iAttributes != -1 &&  iAttributes & SCMETA_ATTRIBUTES_MAGICAL )
	{
		SendMessageToPC(oCaster, "Magic is Deadened Where you targeted");
		SetLocalInt( oCaster, "HKTEMP_Blocked", TRUE );
		return FALSE;
	}
	
	// **********************************
	// **          WILD MAGIC          **
	// **********************************
	if ( iTargetState & CSL_ENVIRO_WILDMAGIC && iAttributes != -1 &&  iAttributes & SCMETA_ATTRIBUTES_MAGICAL )
	{
		// Just do random damage for now, need to vary i more
		SendMessageToPC(oCaster, "You sense your magic has gone awry somehow" );
		SetLocalInt( OBJECT_SELF, "HKTEMP_damagemodtype", CSLPickOneInt(DAMAGE_TYPE_COLD, DAMAGE_TYPE_FIRE, DAMAGE_TYPE_ACID, DAMAGE_TYPE_ELECTRICAL, DAMAGE_TYPE_SONIC, DAMAGE_TYPE_NEGATIVE, DAMAGE_TYPE_POSITIVE ) );
		if (DEBUGGING >= 8) { CSLDebug( "Hook: Damage Set to <color=red>"+CSLDamagetypeToString( GetLocalInt( OBJECT_SELF, "HKTEMP_damagemodtype"))+"</color>" ); }
	
	}
	
	// **********************************
	// **          ANCHORED            **
	// **********************************
	if ( iTargetState & CSL_ENVIRO_ANCHORED && iSpellSubSchool != -1 && iSpellSubSchool & ( SPELL_SUBSCHOOL_TELEPORTATION | SPELL_SUBSCHOOL_CALLING ) )
	{
		SendMessageToPC(oCaster, "The target area is anchored and your spell fizzles");
		SetLocalInt( oCaster, "HKTEMP_Blocked", TRUE );
		return FALSE;
	
	}
	
	
	// **********************************
	// **            HOLY              **
	// **********************************
	if ( iTargetState & CSL_ENVIRO_HOLY )
	{
		if ( iDescriptor & SCMETA_DESCRIPTOR_GOOD  )
		{
			SetLocalInt( oCaster, "HKTEMP_damagemodpercent", 150 );
			SetLocalInt( oCaster, "HKTEMP_sizemodpercent", 125 );
		}
		if ( iDescriptor & SCMETA_DESCRIPTOR_EVIL  )
		{
			SetLocalInt( oCaster, "HKTEMP_damagemodpercent", 75 );
			SetLocalInt( oCaster, "HKTEMP_sizemodpercent", 75 );
		}	
	}
	
	// **********************************
	// **           PROFANE            **
	// **********************************
	if ( iTargetState & CSL_ENVIRO_PROFANE )
	{
	
		if ( iDescriptor & SCMETA_DESCRIPTOR_EVIL  )
		{
			SetLocalInt( oCaster, "HKTEMP_damagemodpercent", 150 );
			SetLocalInt( oCaster, "HKTEMP_sizemodpercent", 125 );
		}
		if ( iDescriptor & SCMETA_DESCRIPTOR_GOOD  )
		{
			SetLocalInt( oCaster, "HKTEMP_damagemodpercent", 75 );
			SetLocalInt( oCaster, "HKTEMP_sizemodpercent", 75 );
		}
	}
	
	// **********************************
	// **             LAW              **
	// **********************************
	if ( iTargetState & CSL_ENVIRO_LAW )
	{
		if ( iDescriptor & SCMETA_DESCRIPTOR_LAW  )
		{
			SetLocalInt( oCaster, "HKTEMP_damagemodpercent", 150 );
			SetLocalInt( oCaster, "HKTEMP_sizemodpercent", 125 );
		}
		if ( iDescriptor & SCMETA_DESCRIPTOR_CHAOS  )
		{
			SetLocalInt( oCaster, "HKTEMP_damagemodpercent", 75 );
			SetLocalInt( oCaster, "HKTEMP_sizemodpercent", 75 );
		}
	
	}
	
	// **********************************
	// **          CHAOS              **
	// **********************************
	if ( iTargetState & CSL_ENVIRO_CHAOS )
	{
		if ( iDescriptor & SCMETA_DESCRIPTOR_CHAOS  )
		{
			SetLocalInt( oCaster, "HKTEMP_damagemodpercent", 150 );
			SetLocalInt( oCaster, "HKTEMP_sizemodpercent", 125 );
		}
		if ( iDescriptor & SCMETA_DESCRIPTOR_LAW  )
		{
			SetLocalInt( oCaster, "HKTEMP_damagemodpercent", 75 );
			SetLocalInt( oCaster, "HKTEMP_sizemodpercent", 75 );
		}
	
	}
	
	// **********************************
	// **          NEGATIVE            **
	// **********************************
	if ( iTargetState & CSL_ENVIRO_NEGATIVE )
	{
		// Reductions
		if ( iDescriptor & SCMETA_DESCRIPTOR_LIGHT )
		{
			SetLocalInt( oCaster, "HKTEMP_Spell_DurationCatAdj", -1 );
		}
		if ( iDescriptor & SCMETA_DESCRIPTOR_POSITIVE  )
		{
			SetLocalInt( oCaster, "HKTEMP_damagemodpercent", 75 );
			SetLocalInt( oCaster, "HKTEMP_sizemodpercent", 75 );
		}
		
		if ( iSpellSubSchool != -1 && iSpellSubSchool & SPELL_SUBSCHOOL_HEALING && !( iDamageModType & DAMAGE_TYPE_NEGATIVE ) )
		{
			SetLocalInt( oCaster, "HKTEMP_damagemodpercent", 75 );
			SetLocalInt( oCaster, "HKTEMP_sizemodpercent", 75 );
		}
		
		// Boosts
		if ( iDescriptor & SCMETA_DESCRIPTOR_DARKNESS )
		{
			SetLocalInt( oCaster, "HKTEMP_Spell_DurationCatAdj", 1 );
		}
		if ( iDescriptor & SCMETA_DESCRIPTOR_NEGATIVE || iDamageModType & DAMAGE_TYPE_NEGATIVE )
		{
			SetLocalInt( oCaster, "HKTEMP_damagemodpercent", 150 );
			SetLocalInt( oCaster, "HKTEMP_sizemodpercent", 125 );
		}
		
		if ( iSpellSubSchool != -1 && iSpellSubSchool & SPELL_SUBSCHOOL_SHADOW )
		{
			SetLocalInt( oCaster, "HKTEMP_damagemodpercent", 150 );
			SetLocalInt( oCaster, "HKTEMP_sizemodpercent", 125 );
		}
	}
	
	// **********************************
	// **           POSITIVE           **
	// **********************************
	if ( iTargetState & CSL_ENVIRO_POSITIVE )
	{
		
		// Boosts
		if ( iDescriptor & SCMETA_DESCRIPTOR_LIGHT )
		{
			SetLocalInt( oCaster, "HKTEMP_Spell_DurationCatAdj", 1 );
		}
		if ( iDescriptor & SCMETA_DESCRIPTOR_POSITIVE  )
		{
			SetLocalInt( oCaster, "HKTEMP_damagemodpercent", 150 );
			SetLocalInt( oCaster, "HKTEMP_sizemodpercent", 125 );
		}
		
		if ( iSpellSubSchool != -1 && iSpellSubSchool & SPELL_SUBSCHOOL_HEALING && !( iDamageModType & DAMAGE_TYPE_NEGATIVE ) )
		{
			SetLocalInt( oCaster, "HKTEMP_damagemodpercent", 150 );
			SetLocalInt( oCaster, "HKTEMP_sizemodpercent", 125 );
		}
		
		// Reductions
		if ( iDescriptor & SCMETA_DESCRIPTOR_DARKNESS )
		{
			SetLocalInt( oCaster, "HKTEMP_Spell_DurationCatAdj", -1 );
		}
		if ( iDescriptor & SCMETA_DESCRIPTOR_NEGATIVE || iDamageModType & DAMAGE_TYPE_NEGATIVE )
		{
			SetLocalInt( oCaster, "HKTEMP_damagemodpercent", 70 );
			SetLocalInt( oCaster, "HKTEMP_sizemodpercent", 75 );
		}
		
		if ( iSpellSubSchool != -1 && iSpellSubSchool & SPELL_SUBSCHOOL_SHADOW )
		{
			SetLocalInt( oCaster, "HKTEMP_damagemodpercent", 70 );
			SetLocalInt( oCaster, "HKTEMP_sizemodpercent", 75 );
		}
	
	}
	
	
	// **********************************
	// **            FIRE              **
	// **********************************
	if ( iTargetState & ( CSL_ENVIRO_FIRE | CSL_ENVIRO_MAGMA )  )
	{
		if ( iDamageModType == DAMAGE_TYPE_FIRE || ( iDamageModType == 0 && iDescriptor & SCMETA_DESCRIPTOR_FIRE )  )
		{
			SetLocalInt( oCaster, "HKTEMP_damagemodpercent", 150 );
			SetLocalInt( oCaster, "HKTEMP_sizemodpercent", 125 );
		}
		
		if ( iDamageModType == DAMAGE_TYPE_COLD || ( iDamageModType == 0 && iDescriptor & SCMETA_DESCRIPTOR_COLD )  )
		{
			SetLocalInt( oCaster, "HKTEMP_damagemodpercent", 70 );
			SetLocalInt( oCaster, "HKTEMP_sizemodpercent", 75 );
		}
	
	}
	
	// **********************************
	// **             COLD             **
	// **********************************
	if ( iTargetState & CSL_ENVIRO_COLD )
	{
		if ( iDamageModType == DAMAGE_TYPE_COLD || ( iDamageModType == 0 && iDescriptor & SCMETA_DESCRIPTOR_COLD )  )
		{
			SetLocalInt( oCaster, "HKTEMP_damagemodpercent", 150 );
			SetLocalInt( oCaster, "HKTEMP_sizemodpercent", 125 );
		}
		
		if ( iDamageModType == DAMAGE_TYPE_FIRE || ( iDamageModType == 0 && iDescriptor & SCMETA_DESCRIPTOR_FIRE )  )
		{
			SetLocalInt( oCaster, "HKTEMP_damagemodpercent", 70 );
			SetLocalInt( oCaster, "HKTEMP_sizemodpercent", 75 );
		}
	
	}
	
	
	// **********************************
	// **          UNDERWATER          **
	// **********************************
	if ( ( iCharState == -1 && iTargetState & CSL_ENVIRO_WATER ) || iCharState & ( CSL_CHARSTATE_SPLASHING | CSL_CHARSTATE_WADING | CSL_CHARSTATE_SUBMERGED ) )
	{
		
		if ( iDamageModType == DAMAGE_TYPE_FIRE || ( iDamageModType == 0 && iDescriptor & SCMETA_DESCRIPTOR_FIRE )  )
		{
			int iCastingClass = GetLastSpellCastClass();
			int iDC = 20 + iSpellLevel;
			int iSpellCraftRanks = GetSkillRank(SKILL_SPELLCRAFT, oCaster);
			int iINTMod = GetAbilityModifier(ABILITY_INTELLIGENCE, oCaster);
			int iSkillCheckRoll = d20()+iSpellCraftRanks+iINTMod;
			
			string sMessage = "Spellcraft check: Casting Fire spell underwater vs DC "+IntToString(iDC)+": Roll "+IntToString(iSkillCheckRoll);
	
			if(iSkillCheckRoll>=iDC)
			{
				sMessage = sMessage+" Successful.";
				if(GetIsPC(oCaster))
				{
					SendMessageToPC(oCaster, sMessage);
				}
				
			}
			else
			{
				sMessage = sMessage+" Failure.";
				SetLocalInt( oCaster, "HKTEMP_Blocked", TRUE );
				if( GetIsPC(oCaster) )
				{
					SendMessageToPC(oCaster, sMessage);
				}
			}
		}
		
		// Sonic spells are doubled in range
		if ( iDamageModType == DAMAGE_TYPE_SONIC || ( iDamageModType == 0 && iDescriptor & SCMETA_DESCRIPTOR_SONIC )  )
		{
			SetLocalInt( oCaster, "HKTEMP_damagemodpercent", 150 );
			SetLocalInt( oCaster, "HKTEMP_sizemodpercent", 200 );
		}
		
		
		if ( iDamageModType == DAMAGE_TYPE_ACID || ( iDamageModType == 0 && iDescriptor & SCMETA_DESCRIPTOR_ACID )  )
		{
			SetLocalInt( oCaster, "HKTEMP_Spell_DurationCatAdj", -1 );
		}
		
		if ( iDescriptor & SCMETA_DESCRIPTOR_GAS || iDescriptor & SCMETA_DESCRIPTOR_AIR )
		{
			SetLocalInt( oCaster, "HKTEMP_Spell_DurationCatAdj", -1 );
		}
		
		
		// Bambardment does half damage underwater
		if ( iSpellId == SPELL_BOMBARDMENT )
		{
			SetLocalInt( oCaster, "HKTEMP_damagemodpercent", 50 );
		}
		
		//Electrical spells are different in shape
		if ( iSpellId == SPELL_LIGHTNING_BOLT )
		{
			//void ActionCastSpellAtObject(int nSpell, object oTarget, int nMetaMagic=METAMAGIC_ANY, int bCheat=FALSE, int nDomainLevel=0, int nProjectilePathType=PROJECTILE_PATH_TYPE_DEFAULT, int bInstantSpell=FALSE);
			//void   ActionCastSpellAtLocation(int nSpell, location lTargetLocation, int nMetaMagic=METAMAGIC_ANY, int bCheat=FALSE, int nProjectilePathType=PROJECTILE_PATH_TYPE_DEFAULT, int bInstantSpell=FALSE, int nDomainLevel=0 );
	
			AssignCommand(oCaster, ActionCastSpellAtLocation(SPELL_SCINTILLATING_SPHERE, GetSpellTargetLocation(), METAMAGIC_ANY, TRUE, 0, PROJECTILE_PATH_TYPE_DEFAULT, TRUE));
			SetLocalInt( oCaster, "HKTEMP_Blocked", TRUE );
		}
		
		
	}
	
	// **********************************
	// **             GALE             **
	// **********************************
	if ( iTargetState & CSL_ENVIRO_GALE )
	{
		if ( iDescriptor & SCMETA_DESCRIPTOR_GAS )
		{
			SetLocalInt( oCaster, "HKTEMP_Spell_DurationCatAdj", -1 );
		}
		
		if ( iDescriptor & SCMETA_DESCRIPTOR_AIR )
		{
			SetLocalInt( oCaster, "HKTEMP_Spell_DurationCatAdj", 1 );
		}
	
	}
	
	
	return TRUE;
}


// * Initiates the burning event, just does the visual and the heartbeat will do the damage later
void CSLBurningStart( int iStartingDC = 1, object oPC = OBJECT_SELF )
{
	effect eBurning = EffectNWN2SpecialEffectFile( "fx_fire_lg" );
	eBurning = SetEffectSpellId(eBurning, SPELLENVIRO_BURNING );
	
	ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eBurning, oPC, 6.0);
	SetLocalInt( oPC, "CSL_BURNING", iStartingDC );
	SetLocalObject( CSLGetEnviroControl(), ObjectToString(oPC), oPC );
}


void CSLEnviroSlowingWater( object oPC, int iRateDecrease = 50 )
{
	if ( CSLGetIsSwimmer( oPC ) )
	{
		iRateDecrease = 0;
	}
	
	if ( iRateDecrease == 0 || iRateDecrease != GetLocalInt( oPC, "CSL_WATERSLOWINGFACTOR" ) )
	{
		CSLRemoveEffectSpellIdSingle(SC_REMOVE_ALLCREATORS, oPC, oPC, SPELLENVIRO_WATERSLOWING );
	}
	
	if ( iRateDecrease > 0 && !GetHasSpellEffect( SPELLENVIRO_WATERSLOWING, oPC ))
	{
		SendMessageToPC( oPC, "Applying Water Slowing of "+IntToString( iRateDecrease ) );
		effect eWaterSlowing = SupernaturalEffect(EffectMovementSpeedDecrease( iRateDecrease ));
		eWaterSlowing = SetEffectSpellId(eWaterSlowing, SPELLENVIRO_WATERSLOWING );
		ApplyEffectToObject(DURATION_TYPE_PERMANENT, eWaterSlowing, oPC);
	}
	SetLocalInt( oPC, "CSL_WATERSLOWINGFACTOR", iRateDecrease );
}



void CSLEnviroSlowingTerrain( object oPC, int iRateDecrease = 50 )
{
	
	if ( CSLGetIsSwimmer( oPC ) )
	{
		iRateDecrease = 0;
	}
	
	if ( iRateDecrease == 0 || iRateDecrease != GetLocalInt( oPC, "CSL_TERRAINSLOWINGFACTOR" ) )
	{
		CSLRemoveEffectSpellIdSingle(SC_REMOVE_ALLCREATORS, oPC, oPC, SPELLENVIRO_TERRAINSLOWING );
	}
	
	if ( iRateDecrease > 0 && !GetHasSpellEffect( SPELLENVIRO_TERRAINSLOWING, oPC ))
	{
		SendMessageToPC( oPC, "Applying Terrain Slowing of "+IntToString( iRateDecrease ) );
		effect eWaterSlowing = SupernaturalEffect(EffectMovementSpeedDecrease( iRateDecrease ));
		eWaterSlowing = SetEffectSpellId(eWaterSlowing, SPELLENVIRO_TERRAINSLOWING );
		ApplyEffectToObject(DURATION_TYPE_PERMANENT, eWaterSlowing, oPC);
	}
	SetLocalInt( oPC, "CSL_TERRAINSLOWINGFACTOR", iRateDecrease );
}



int CSLEnviroHoldBreath( int iBreathHoldingRounds, int iPreviousHitpoints, object oPC = OBJECT_SELF )
{
	int iRoundsCanHold = GetAbilityScore( oPC, ABILITY_CONSTITUTION, FALSE)*2;
	
	int iCurrentHitponts = GetCurrentHitPoints(oPC);
	int iMaxHitponts = GetMaxHitPoints(oPC);
	
	int iCharState = GetLocalInt( oPC, "CSL_CHARSTATE" );

	int iDC = GetLocalInt( oPC, "CSL_WATERDC" );
	int iAirBubbles = 0;
	
	if ( GetLocalInt( oPC, "CSL_LOSTBREATH" ) )
	{
		iBreathHoldingRounds = iBreathHoldingRounds+GetLocalInt( oPC, "CSL_LOSTBREATH" );
		//AssignCommand( oPC, ClearAllActions(TRUE) );
		FloatingTextStringOnCreature("*gulp*", oPC, TRUE);
		DeleteLocalInt( oPC, "CSL_LOSTBREATH" );
	}
	
	if ( iPreviousHitpoints != 0 && iCurrentHitponts < iPreviousHitpoints )
	{
		// we've lost hit points, this does not include hitpoints lost from drowning
		if( !GetIsSkillSuccessful(oPC, SKILL_CONCENTRATION, 10 + ( ( iPreviousHitpoints - iCurrentHitponts ) / iMaxHitponts )*10 ) )
		{
			//AssignCommand( oPC, ClearAllActions(TRUE) );
			iBreathHoldingRounds += ( ( iPreviousHitpoints - iCurrentHitponts ) / iMaxHitponts )*10;
			SendMessageToPC(oPC, "You could not concentrate well enough and lost some air!");
		}
	}
	
	//int bDrowning = GetLocalInt( oPC, "UW_DROWNING" );
	//int bPanicked = GetLocalInt( oPC, "UW_PANICKED" );
	
	//CSL_CHARSTATE_DROWNING
	//CSL_CHARSTATE_PANICKED
	
	 
	
	if ( iBreathHoldingRounds > iRoundsCanHold )
	{
		// now is getting hard to save, must save each round to avoid drowning
		
		
		if ( iCharState & CSL_CHARSTATE_DROWNING || !CSLAbilityCheck( oPC, ABILITY_CONSTITUTION, iDC, 0, TRUE, TRUE, TRUE  )  )
		{
			 if ( !iCharState & CSL_CHARSTATE_DROWNING)
			 {
				iDC = 20; // this is no longer used to the con check, but will be recycled for the morale check
			 }
			 iCharState = iCharState | CSL_CHARSTATE_DROWNING;
			 SendMessageToPC(oPC, "You cannot fight the urge to breath any longer! With a gasp, you inhale a large amount of water into your lungs!");
			 int iDamage = CSLGetMax( iMaxHitponts / 10, 1 );
			 ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectDamage(iDamage, DAMAGE_TYPE_BLUDGEONING, DAMAGE_POWER_NORMAL, TRUE), oPC);
			  
			 if ( GetHasSpellEffect(-SPELL_FEAR,oPC) || !WillSave(oPC, iDC, SAVING_THROW_TYPE_FEAR) )
			 {
				ApplyEffectToObject(DURATION_TYPE_TEMPORARY, SetEffectSpellId( CSLGetFearEffect(TRUE), -SPELL_FEAR), oPC, 9.0f);
			 }
			 
			 if ( iCurrentHitponts < (iMaxHitponts/2) ) // when hit points are reduced below half
			 {
				iDC++;
			 }
			 iDC++;
			 iBreathHoldingRounds++; // loses breath even faster now, but it's not going to be noticed really
			 FloatingTextStringOnCreature("*choke*", oPC, TRUE);
		}
		else
		{
			SendMessageToPC(oPC, "You struggle to hold your breath and are not sure how long you can continue!");
		}
		iDC++;
	
	}
	//else if ( iBreathHoldingRounds == iRoundsCanHold-5 ) 
	//{
	//	SendMessageToPC(oPC, "You have a short time, " + IntToString(iRoundsCanHold-iBreathHoldingRounds) + " rounds, before holding your breath will become very difficult.");
	//}
	else if ( iBreathHoldingRounds > 0 && ( iBreathHoldingRounds > iRoundsCanHold-10 || CSLGetIsDivisible( iRoundsCanHold-iBreathHoldingRounds,5 ) ) ) 
	{
		FloatingTextStringOnCreature("( "+IntToString( iRoundsCanHold-iBreathHoldingRounds )+" )", oPC, TRUE);
	}
	
	
	if ( iBreathHoldingRounds == 0 ) 
	{
		// You can hold your breath for a number of rounds equal to twice your constitution score.
		iBreathHoldingRounds = 1;
		iDC = 10; 
		SendMessageToPC(oPC, "You start holding your breath and have " + IntToString(iRoundsCanHold) + " rounds, before holding your breath will become very difficult.");
		
	}
	else
	{
		if ( !GetIsInCombat( oPC ) )
		{
			iAirBubbles = GetLocalInt( oPC, "CSL_AIRBUBBLES");
		}
		
		if ( iAirBubbles && iBreathHoldingRounds > 3 )
		{
			iBreathHoldingRounds -= iAirBubbles;
		}
		else
		{
			iBreathHoldingRounds++;
		}
	}
	SetLocalInt( oPC, "CSL_WATERDC", iDC );
	SetLocalInt( oPC, "CSL_CHARSTATE", iCharState );
	return iBreathHoldingRounds;
}

int CSLToggleWaterStateBits( int iCharState, int iBitToSet = 0 )
{
	
	iCharState &= ~CSL_CHARSTATE_SPLASHING;
	iCharState &= ~CSL_CHARSTATE_WADING;
	iCharState &= ~CSL_CHARSTATE_SUBMERGED;
	
	if ( iBitToSet == 0 )
	{
		return iCharState;
	}
	else
	{
		return iCharState | iBitToSet;
		//iCharState |= iBitToSet;
		//return iCharState;
	}
}



// run on triggers and on every round water is active on a character
int CSLEnviroCheckWaterState( object oPC = OBJECT_SELF, int iCharState = -1, int iEnviroState = -1, int iAreaState = -1 )
{
	// iCharState is not being used, mainly for consistency
	object oArea = GetArea(oPC);
	if ( iEnviroState == -1 )
	{
		int iAreaState = GetLocalInt( oArea, "CSL_ENVIRO" );
		int iEnviroState = iAreaState | GetLocalInt( oPC, "CSL_ENVIRO" );
	}
	
	if ( iCharState == -1 )
	{
		iCharState = GetLocalInt( oPC, "CSL_CHARSTATE" ); 
	}
	
	int iWaterState = GetLocalInt( oPC, "CSL_WATERSTATE" );
	
	
	//CSLEnviroObjectIsWithin( oPC, CSL_ENVIRO_WATER )
	//int iBreathholdingRounds = GetLocalInt( oPC, "CSL_BREATHHOLDING" );
	
	int iSoaked = GetLocalInt( oPC, "CSL_SOAKED" );
	
	// int iWaterState = GetLocalInt( oPC, "CSL_WATERSTATE" );
	
	// get the powerlevel 1-5 from the environment
	int iRainPower = GetWeather(oArea, WEATHER_TYPE_RAIN);
	int iSnowPower = GetWeather(oArea, WEATHER_TYPE_SNOW);
	int iThunderPower = GetWeather(oArea, WEATHER_TYPE_LIGHTNING);
	
	iSoaked = CSLGetMax( iSoaked, iRainPower );
	iSoaked = CSLGetMax( iSoaked, ( iSnowPower/2 ) );
	
	// Deal with water
	if ( iEnviroState & CSL_ENVIRO_WATER && !( iEnviroState & CSL_ENVIRO_AIRPOCKET ) )
	{
		float fWaterLevel = GetLocalFloat( oPC, "CSL_WATERSURFACEHEIGHT" ); // set by triggers as character moves around
		//int bInWaterDeep = GetLocalInt( GetArea(oPC), "INWATER"); // set by the area itself
		float fZPosition = CSLGetZPosition( oPC );
		
		if ( iAreaState & CSL_ENVIRO_WATER )
		{
			iWaterState = CSL_WATERSTATE_SUBMERGED;
			iCharState = CSLToggleWaterStateBits( iCharState, CSL_CHARSTATE_SUBMERGED );
			CSLEnviroSlowingWater( oPC, 50 );
			iSoaked = CSLGetMax( 21, iSoaked );
			if (DEBUGGING >= 8) { SendMessageToPC( oPC, "CSL_WATERSTATE_SUBMERGED DeepWater" ); }
		}
		else if ( fZPosition > fWaterLevel )
		{
			iWaterState = CSL_WATERSTATE_NONE;
			iCharState = CSLToggleWaterStateBits( iCharState, CSL_CHARSTATE_NONE );
			CSLEnviroSlowingWater( oPC, 0 );
			if (DEBUGGING >= 8) { SendMessageToPC( oPC, "CSL_WATERSTATE_NONE ZPosition="+CSLFormatFloat( fZPosition )+" > WaterLevel="+CSLFormatFloat( fWaterLevel ) ); }
		}
		else if ( CSLGetHasEffectType( oPC, EFFECT_TYPE_SLEEP ) )
		{
			iWaterState = CSL_WATERSTATE_SUBMERGED;
			iCharState = CSLToggleWaterStateBits( iCharState, CSL_CHARSTATE_SUBMERGED );
			CSLEnviroSlowingWater( oPC, 50 );
			iSoaked = CSLGetMax( 11, iSoaked );
			if (DEBUGGING >= 8) { SendMessageToPC( oPC, "CSL_WATERSTATE_SUBMERGED Sleeping" ); }
		}
		else if ( fZPosition+CSLGetCreatureHeight( oPC )*0.8994f < fWaterLevel )
		{
			iWaterState = CSL_WATERSTATE_SUBMERGED;
			iCharState = CSLToggleWaterStateBits( iCharState, CSL_CHARSTATE_SUBMERGED );
			CSLEnviroSlowingWater( oPC, 50 );
			iSoaked = CSLGetMax( 16, iSoaked );
			if (DEBUGGING >= 8) {SendMessageToPC( oPC, "CSL_WATERSTATE_SUBMERGED ZPosition="+CSLFormatFloat( fZPosition )+" Height="+CSLFormatFloat( CSLGetCreatureHeight( oPC ) )+" * 0.8994f="+CSLFormatFloat( fZPosition+( CSLGetCreatureHeight( oPC )*0.8994f ) )+" < WaterLevel="+CSLFormatFloat( fWaterLevel ) ); }
		}
		else if ( fZPosition+CSLGetCreatureHeight( oPC )*0.5f < fWaterLevel )
		{
			CSLEnviroSlowingWater( oPC, 25 );
			iWaterState = CSL_WATERSTATE_WADING;
			iCharState = CSLToggleWaterStateBits( iCharState, CSL_CHARSTATE_WADING );
			iSoaked = CSLGetMax( 4, iSoaked );
			if (DEBUGGING >= 8) {SendMessageToPC( oPC, "CSL_WATERSTATE_WADING ZPosition="+CSLFormatFloat( fZPosition )+" Height="+CSLFormatFloat( CSLGetCreatureHeight( oPC ) )+" * 0.5f="+CSLFormatFloat( fZPosition+( CSLGetCreatureHeight( oPC )*0.5f ) )+" < WaterLevel="+CSLFormatFloat( fWaterLevel ) ); }
		}
		else
		{
			CSLEnviroSlowingWater( oPC, 10 );
			iWaterState = CSL_WATERSTATE_SPLASHING;
			iCharState = CSLToggleWaterStateBits( iCharState, CSL_CHARSTATE_SPLASHING );
			iSoaked = CSLGetMax( 2, iSoaked );
			if (DEBUGGING >= 8) { SendMessageToPC( oPC, "CSL_WATERSTATE_SPLASHING ZPosition="+CSLFormatFloat( fZPosition )+" Height="+CSLFormatFloat( CSLGetCreatureHeight( oPC ) )+" * 0.5f="+CSLFormatFloat( fZPosition+( CSLGetCreatureHeight( oPC )*0.5f ) )+" < WaterLevel="+CSLFormatFloat( fWaterLevel ) ); }
		}
	}
	else if ( iCharState & ( CSL_CHARSTATE_SPLASHING | CSL_CHARSTATE_WADING | CSL_CHARSTATE_SUBMERGED ) )
	{
		CSLEnviroSlowingWater( oPC, 0 );
		iWaterState = CSL_WATERSTATE_NONE;
		iCharState = CSLToggleWaterStateBits( iCharState, CSL_CHARSTATE_NONE );
		if (DEBUGGING >= 8) { SendMessageToPC( oPC, "CSL_WATERSTATE_NONE Leaving Trigger" ); }
	}
	
	if ( CSLAdjustInvisibilityWhenInWater( iSoaked, oPC) )
	{
		SendMessageToPC( oPC, "While you are invisible, the water makes you quite visible");
	}
	
	if ( iSoaked > 0 )
	{
		SetLocalInt( oPC, "CSL_SOAKED", iSoaked-1 ); // being soaked can persist after one leaves a water area
	}
	
	SetLocalInt( oPC, "CSL_CHARSTATE", iCharState );
	SetLocalInt( oPC, "CSL_WATERSTATE", iWaterState );
	
	return iCharState;
}



// run on triggers and on every round water is active on a character
int CSLEnviroCheckDeadMagicState( object oPC = OBJECT_SELF, int iCharState = -1, int iEnviroState = -1, int iAreaState = -1 )
{
	object oArea = GetArea(oPC);
	if ( iEnviroState == -1 )
	{
		int iAreaState = GetLocalInt( oArea, "CSL_ENVIRO" );
		int iEnviroState = iAreaState | GetLocalInt( oPC, "CSL_ENVIRO" );
	}
	
	if ( iCharState == -1 )
	{
		iCharState = GetLocalInt( oPC, "CSL_CHARSTATE" );
	}
	
	// Deal with deadmagic
	if ( iEnviroState & CSL_ENVIRO_DEADMAGIC  )
	{
		SetLocalInt( oPC, "HKPERM_Blocked", TRUE );
		SetLocalInt( oPC, "DEAD_MAGIC", TRUE );
		if ( !iCharState & CSL_CHARSTATE_DEADMAGIC )
		{
			SetLocalInt( oPC, "CSL_CHARSTATE", iCharState | CSL_CHARSTATE_DEADMAGIC );
			if ( CSLRemoveAllMagicalEffects( oPC ) ) // only removes the first time
			{
				SendMessageToPC( oPC, "All of your magical effects suddenly extinguish.");
			}
		}
		
	}
	else 
	{
		SetLocalInt( oPC, "HKPERM_Blocked", FALSE );
		SetLocalInt( oPC, "DEAD_MAGIC", FALSE );
		if ( iCharState & CSL_CHARSTATE_DEADMAGIC )
		{
			iCharState = iCharState - CSL_CHARSTATE_DROWNING;
			SetLocalInt( oPC, "CSL_CHARSTATE", iCharState );
		}
	}
	return iCharState;
}

Last edited by pain; 06-25-2009 at 04:58 PM.
Reply With Quote
  #8  
Old 06-25-2009, 04:26 PM
pain pain is offline
Senior Member
 
Join Date: Dec 2007
Posts: 672
Default

Mechanics are worked out, on how to make this a workable system. Now for the questions on what "Should" i do with it. Quite a bit of this is outside the scope of my personal goals, but i think the richer i make it, the better.

You are tagged as being in a fire area for example, the system registers you, and it runs a script every round applying fire effects and it uses the same code to run a spell hook. So elemental damage of 1d6, except i have things interplay a bit more.

If you are in water you get a soaked value of 1-20, which is decremented by 1 each round until it goes to 0. That means the fire will have it's damage reduced.

It also can catch you on fire, so if you are in a fire area and leave it, you will keep making saves vs fire until it goes out. ( DC decreases each round )

Now if you are on fire and jump in a lake, you are soaked, which again puts out the fire.

This all seems great ( a lot based on what Wyvern is doing ) but i have a lot more to do than just fire.

Now i have gusts of wind, which do a 50/50 chance of putting the fire out or fanning the flames. They also reduce the soaked more ( drying you out so to speak ).

Now i also am looking at the spell hooks, if the target is in a fire area, the AOE size is reduced to 75% if its a cold spell, and the damage is adjusted by 75%, and if it's fire the AOE size increases to 125% and damage goes to 150%. Note that this is not really a choice, but just to indicate that things happen and to indicate a "boost" or a "nerf". Note that this applies if the target or target location is considered inside an area modifier.

So for another example of what i can do. The spell sub school is teleportation or calling, and the area modifier is Anchored which is the dimensional anchor effect. Teleportation does not work, nor do gate spells or bindings which are all of the above sub schools.

I have info for the descriptor, the subschool, the school, and i can do things for particular spells by id. And i know the area modifier and the character state modifier. These are the tools i can use for the logic without taxing the engine a lot. And i can combine effects to create an acid lake.

Now i need input on what folks would like to see for the per round effect, for interplay between effects ( waters effect on fire, perhaps water blocks poison so you can go under the poison. ) Really you can consider the fire descriptor to describe the 2nd edition plane of fire which does 1d6 damage per round, and the same for the other elements, but it also could be a fire pit or a wall of fire of some sort ( a dungeon could have a lake and a wall of fire, and you have to jump in the lake to get through the wall of fire while you are wet ).

I am of course researching this, but the above effects are so varied and diverse that i would appreciate any input. Some are just for ambience ( stench being sulfur smell indicating devils are near for example ) and the light and dark are to make hiding vary some. ( not sure how involved i can make those ).

I also need to figure out how to combine effects for extreme snow for example. The entire area could be COLD doing 1d6 damage a round in cold damage. The weather could be set to snow which would increase the soak on the players. The area could have 40 mph strong gales sending tiny creeature flying, and blinding due to the solidness of the white terrain. I probably have to have integers to cover the effects of cold weather gear that can protect from the cold damage. And it can vary which effects are in different spots, with ice at the bottom being slippery, and the snowy hills set to hard terrain and slowing movement. Slowing might be reduced with special gear, snow shoes, well this applies to water as well but i have no idea how to determine the rules. But basically the above should allow me to combine different things to create new effects assuming i have the correct visuals/map design so people know the slowing is from snow and the slipperyness is from ice. ( just though of gusts of wind where the player is on a grease or slippery spot, that should be even more fun as they go sailing away. )

I am updating the initial post with current modifiers, and the code block. There are a lot of ideas inside the code as well.

Last edited by pain; 06-25-2009 at 05:49 PM.
Reply With Quote
Reply

Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Forum Jump


All times are GMT -4. The time now is 08:18 AM.


Powered by vBulletin® Version 3.7.3
Copyright ©2000 - 2010, Jelsoft Enterprises Ltd.