#============================================================================== # # HERETIC'S SUPER EVENT SENSOR [XP] # Version 1.11 # Wednesday, Dec 5th, 2018 # #============================================================================== # # --- Version History --- # Version 1.0 : Saturday, November 23rd, 2013 # Version 1.01 : Saturday, October 18th, 2014 # - Fixed a Bug with Caterpillar where Touch Events would Trigger even # if the Caterpillar was paused. # Version 1.02 : Saturday, April 4th, 2015 # - Due to the creation of a Modular Passable Script which supports # a whole ton of features, another script called Looping Maps # needed to be made compatible # - Fixed a bug where Save Games had wrong pointer for $game_player # Bug caused loaded games to not recognize new instance of $game_player # as the Target for the Sensor. # - Thank you LittleDrago for the assistance in getting Looping Maps working # Version 1.021 : Friday, June 26th, 2015 # - Very minor change. Added attr_accessor :original_move_route for # compatability with F0's Pathfinder script. This script makes a few # manipulations to Move Routes using Pathfinders and I had forgotten # to add in the _accessor to this script since the _accessor had been # added by other scripts. This minor bugfix allows for just the Sensor # and a Pathfinder to be used without the need for other scripts that # simply added the one property. # Version 1.022 - Tuesday, April 12th, 2016 # - Helios pointed out I forgot to add attr_accessor :move_speed. Fixed # Version 1.1 - Monday, Nov 19th, 2018 # - WIP, adding implementation for Light Detection in conjunction with # my "Heretic's Dynamic Lights" script # - Feature added Per Request # * NEW Sensor_Config Options # > light=true # > listen_light=true # > on_light=true # > light_tol=123 # - Light Tolerance (light_tol=123) makes your Sensors LESS sensitive with # positive numbers, and MORE sensitive with negative numbers. The range # is -255 to 255 because Light Detection basically just checks the Alpha # value of the Shadowmap. # - The new listen_light and on_light are needed for Light Detection while # in either of those states, giving you higher levels of control. If you # chose to not use listen_light for example, a Player can hide in Darkness # and not be detected but CAN be heard. # - NOTE: Light Detection uses TWO PIXELS, one pixel at the very top and # the Bottom of your Target's Sprite. # Version 1.11 - Wednesday, Dec 5th, 2018 # - WIP, added even more features for Light Detection per request # - Added Options for Light and Dark Range # > light_range=5 # > dark_range=3 # - To use these features, first, make your sensor Light Sensitive or the # additional features WILL NOT WORK. Add a "light=true" comment to # your Sensor_Config. Then add light_range=5 or any number. This number # will OVERRIDE your standard range=4, so it is suggested to use a number # that is HIGHER than your standard range. The new "dark" feature works # similarly. Add dark=3 to set the range that your sensor can "see" when # the Target (typically Player) is shrouded in Darkness. You can use # both features together. The light/dark_ranges ONLY kick in when the # Dynamic Light System is casting shadows of some sort. So during "Day" # the Sensor will use range=4. # I STRONGLY RECOMMEND keeping your light_range value equal to or less # than your Default Range, or you will see the Sensor toggle on and off # and create strange behaviors you may not visually notice. # - Suggest setting a Sensor with these values: # Sensor_Config # range=4 listen_range=5 on_range=6 # light=true listen_light=true on_light=true # light_range=5 dark_range=3 # - NOTE - If a Sensor is made Light Sensitive and is NOT given either a # light_range or dark_range value, the Sensor WILL NOT SEE the Target # UNLESS the Target is Illuminated! # # # --- Basics --- # # Create a Sensor Event by putting Sensor_Config into a COMMENT on Page 1 # of an Event. It does NOT need to be at the top, but fairly close. # # On the NEXT LINE in the Comment where it says Sensor_Config, put in # "range=4" to "Sense" the Player from 4 tiles away. # # *** IMPORTANT *** # *** IMPORTANT *** # *** IMPORTANT *** # # SENSORS MUST HAVE A RANGE OR THEY WILL NOT WORK!!! # SENSORS MUST HAVE A RANGE OR THEY WILL NOT WORK!!! # SENSORS MUST HAVE A RANGE OR THEY WILL NOT WORK!!! # # When the Player is within that Range, Self Switch D will be turned to ON. # # Use this in combination with other Event Pages to make your Event behave # as you want it to. When the Player steps out of the Range of the Sensor # then Self Switch D (by default) is turned to OFF. # # Do NOT put Spaces with the = sign. # # This is GOOD # # Sensor_Config # range=4 # # # This will NOT WORK because it has Spaces! # # Sensor_Config # range = 4 # # This also will NOT WORK. Sensor_Config MUST be on its own line! # # Sensor_Config range=4 # # Options are NOT Case Sensitive. rANgE will work as well as Range or range. # # SENSORS MUST HAVE A RANGE OR THEY WILL NOT WORK!!! # # --- Features --- # # - Total Overkill of Options (yeah, I think that is kind of a feature) # - Highly Useful for Enemy AI # - Line of Sight to Target # - Different Types of Views (think Metal Gear) # - Obstruction of Line of Sight - WALL # - Can "See" Over Specified Tiles like Water for "Smarter Enemies" # - Triggered Settings can be Different than Non Triggered Settings # - Sensors can be set to Ignore Walls once triggered # - Uses Terrain Tags to Fix Problem Tiles like Water or Fences # - Should be compatible with Terrain Tag Scripts # - Can use Bush Tags to obstruct the View of Small Enemies # - Allows Terrain Tags to specify Trees that obstruct Flying Enemies View # - Sound - Movement makes Sound, and Movement can be Detected # - Sound Detection doesnt need additional scripts, just DONT MOVE! # - Compatible with Pathfinding Scripts # - Supports Suprise Attack Supported Battle Systems - XRXS Rewrite 1.03 # - Suprise Attack Script Calls can prevent Event from turning toward Player # - Can Target ANY Game_Character, not just Player # - Move Route Index Reset on Detrigger - Trust me, you want this. # - Every Event can have Different Settings # - The Constants in Config are used for Defaults, you can change Per Event # - Expandable "View" Definitions by Easy Aliasing # - Added Triggers for Enemy Contact with Heretic's Caterpillar # - Stores XY Coordinates of First Seen and First Heard for other uses. # - Stores XY Coordinates of Location of Sensor Event for returning to path # - Stealth abilities enabled by Game Switches (not Self Switches) # - Stealth Switches can also alter View and Range # - Documentation - Intended for better understanding of specific parts # - Demo - Extremely complex script is given Demos for each feature # # # Think METAL GEAR! Metal Gear is a game that is based on Stealth over # always fighting. What this script is intended to allow you to do is # based on Events as Enemies. Enemies may not always be able to see # you. If they can't See you, it makes sense that they wont try to # attack you. Some enemies might be able to Hear you moving around. # Others may be Flying Enemies that can't see you through what you # specify as a Tree. You can set up Smaller Enemies that cant see # through Grass very well to have their view obstructed by the Bush Flag. # # # # # --- Basics: View --- # # Super Event Sensor is called "Super" becase it has a LOT of Options! Just # so you know, they can go in ANY order! # # The FIRST Option you need to learn about is VIEW # # VIEW is a Shape inside of a Sensor's Range. It is designed to simulate # different types of Field of View. For example, as Humans, we have what # is called Peripheral View. Setting up a View is EASY. It is just a # number between 0 and 9. # # View=0 means a Sensor can see EVERYTHING in its Range. # View=5 means a Sensor can see only partly within its Range. # View=9 means a Sensor is BLIND, except for the Tile it is standing on. # # Example: # # Sensor_Config # range=4 # view=4 # # This is easy to remember if you wear Glasses! The higher the Number, the # worse that Sensor can "see". # # View is relative to an Event's Direction. # # This is best explained in the Demo with the Visual Effects. # # Basically a Sensor's View gets progressively "worse" with a Higher Number. # # Keep in mind that VIEW is a SHAPE inside of the Range. With a View of 1 # the Sensor will not be able to see in a Sraight Line behind it. A View of 2 # increases that area behind it a bit. A View of 4 is what I call Peripheral # View, and is a normal Field of View for Humans. A view of 6 is Metal Gear # where a Sensor can only see right in front of it. A View of 9 is Blind but # a View of 8 is what I call "Laser View" where a Sensor can ONLY see in a # straight line in front of it. # # Many of the settings for your Sensor that you'll soon learn about come # from the Script Configuration settings, which gives each Sensor a # set of Default values. Range is Required as it is the only one # that does NOT use a Script Config Default Value. # # # # # --- Basics: WALL --- # # The next option you need to learn is WALL. Sensors can be prevented from # "seeing" through "walls" (non passable tiles) by adding the word "WALL" to # the Sensor_Config. # # Sensor_Config # range=4 # view=4 # wall # # Walls are determined by either being a Non Passable Tile, or by Terrain Tags # # What this means is that a Sensor Event will not be able to "see" you through # any Tile that is Unpassable for that direction, even if you are within # that Sensor Event's Range. If a WALL obstructs a Sensor's Line of Sight, it # will NOT turn Self Switch D to ON. # # Terrain Tags are used to help a Sensor Event "know" what tiles it should # be able to "see" through. Like WATER. Water is Not Passable, but you can # see over Water in Real Life so it should be given a Terrain Tag so it does # NOT obstruct a Sensor's Views. # # # # # --- Basics: 'ON' --- # # Sensors have MORE THAN ONE STATE. The first State that you need to learn # about is the 'ON' State. This means an Event has "seen" you. What it does # is it turns Self Switch D to ON. # # Now, while in the 'ON' State, a Sensor can be given a Different both a # different Range and different View! These options are called 'on_range # and 'on_view'. They are intended so that Sensor Events can behave in a # more realistic way. For example, once an enemy with a limited View has # seen you, it will now be 'Alerted' to your presence and thus pays more # attention to its surroundings. # # Sensor_Config # range=4 on_range=8 # view=4 on_view=0 # # 'On_View' by default is set to 0 because of the way the Approach command # works. It sometimes causes an Event to turn away from the Player, which # would put the Player outside of its Field of View, and thus, disable the # Sensor's 'ON' State. # # ONLY use on_view if you use a Move Type other than Approach. # # # # # # --- Basics: Smart Enemies: WALL and ON_WALL --- # # In real life, if you witness someone commit a crime that runs out of # your Field of View, you will still know exactly where they went. # # This is where ON_WALL comes into play. # # Now, because you may want BOTH Smart and Stupid Enemies, you need to tell # the Sensor which one they will be. Thus 'ON_WALL' requires a Setting. # # on_wall=true -> Enemy is STUPID and is confused when you run behind a Wall. # on_wall=false -> Enemy is SMART and can still "see" you. Walls are ignored. # # Example: Smart Enemy # # Sensor_Config # range=4 on_range=8 # view=5 on_view=0 # wall on_wall=false # # Default for WALL is set up in this Scripts Configuration. Its probably # easiest to just use On_Wall=True/False for each Sensor Event so they # all work the way you want. # # # # # # # --- Bush and Tree --- # # Bush and Tree Options are also available for both Small and Flying Events. # # For example, a MOUSE would have its Line of Sight to you obstructed by # any Tall Grass, or BUSH. A Flying Enemy may not be able to see you # through a Tree. Bush just makes any tile with a Bush Flag (turns your # feet green) a wall. Tree needs a Terrain Tag to be set in the Database. # # When these are the TOP TILES in your MAP, Bush and Tree will behave just # like WALL. Thus, Small Enemies like Snakes or Rats can not see through # Bush Tiles and Flying Enemies (Harpees) can not see through Trees. # # Example: Small Enemy # # Sensor_Config # range=4 on_range=6 # view=4 on_view=0 # wall on_wall=true # bush # # Example: Flying Enemy (Note, there is no WALL option here) # # Sensor_Config # range=4 on_range=6 # view=4 on_view=0 # # TREE # # Bush and Tree Options also have corresponding 'ON' Settings. This allows # you to have both 'Smart Small Enemies' and 'Smart Flying Enemies'. It # works just like WALL. on_tree=false or on_bush=true Bush is best used # also with the WALL setting, where as a Flying Enemy can see OVER a wall # so that option is typically best excluded. Thus, for a Flying Enemy, the # ONLY type of Tile that can block its Line of Sight is a Tree. # # Use of these Options will most likely be Uncommon. # # # # # --- Listen and Sound --- # # The next major concept to learn is the LISTEN setting. # # Sound is a difficult concept to convey to a Player, so heavy and complex # eventing will most likely be needed. Listen allows Sensor Events to # Listen for Player Movement. The concept is that the Faster you move # the more Sound you make. # # Thus, LISTEN does require an Option, which defines what Move Speed it # is "listening" for. If you move and your Move Speed is equal to or # greater than the Speed for which the Sensor is "Listening", it will be # "triggered", or change its State to 'ON'. Thus, if you move SLOWLY by # a Listening Sensor, it won't detect your presence. But if you SPRINT by # that Listening Sensor, it WILL detect your presence, and thus enable # Self Switch D (by default). # # Example: # # Sensor_Config # range=5 # view=6 # wall on_wall=false # listen=4 # # Unlike View, the LISTEN option will "listen" for any Movement within # that Sensor's Range. This means that for a Listening Enemy, to sneak # up, you have to MOVE SLOWLY. # # Note: The Listen Option will allow you to put in Decimals. # Thus, "listen=4.25" will work as expected. # # # # --- Advanced: Switch and Listen_Switch --- # # This is really where this script starts getting tricky. It has more to do # with Event Pages than anything else. # # Every Event can have a ton of Pages. Each Event Page needs to have a # Page Condition. Thats why Self Switches were used, as they only affect # that particular Event. # # Now, by default, the Listen_Switch is also set to Self Switch D. Thus # a Sensor will turn on Self Switch D when it either "sees" or "hears" you. # # For even more intelligent Enemies, more Event Pages are needed. This will # allow you to have a totally separate Page be triggered for when a Sensor # "sees" you or it "hears" you. # # Example: # # Sensor_Config # range=5 on_range=8 # view=6 on_view=0 # wall on_wall=false # listen=4 # listen_switch=C # # # *** IMPORTANT *** # # LISTEN_SWITCH Option IS CASE SENSITIVE!!! # # Always use CAPS when dealing with Self Switches! I did NOT do # anything to check the Case for reasons I'll explain later. # # What will happen with the Sensor_Config above is that when a Sensor Event # has "seen" you, it will enable Self Switch D, but if it "hears" you, it # will enable only Self Switch C, not D. Thus, your Game Event will have # a Total of THREE Pages! When the Listen Switch is turned on, that Page # can be set to load with a proper Page Condition of Self Switch C. # # Page 1 - No Page Conditions # Page 2 - Self Switch C Page Condition # Page 3 - Self Switch D Page Condition # # Now, if a Sensor "sees" you after it "hears" you, "see" will OVERRIDE # the "hear" Event Page. # # This leads me to explain your next State. The Basic States are OFF and ON. # # Now, you have OFF, LISTEN, and ON. This allows you to Event a totally # different NPC behavior depending on State the Sensor is in. # # # # # # --- Advanced: Listen State --- # # This isn't as hard as it sounds! # # The LISTEN STATE can affect the Range and View Options! # # It is easier to explain it this way. Look at it from the HUMAN perspective! # You hear something, you are slightly alerted so you'll pay a bit more # attention to your surroundings. But, just because you heard a noise does # not mean that you are ready to get in a fist fight. That is what the # Listen State is supposed to emulate. # # Example: # # Sensor_Config # range=5 listen_range=6 on_range=10 # view=7 listen_view=6 on_view=0 # wall on_wall=false # listen=4 listen_switch=C # # Because LISTEN is a STATE, there is NO on_listen Option. # # This also means that everything else can be affected by the Listen State. # # As a result, you have WALL, BUSH, and TREE Listen Options. # # Example: (Just using Wall for a VERY Alert Guard) # # Sensor_Config # range=5 listen_range=6 on_range=10 # view=7 listen_view=4 on_view=0 # wall listen_wall=true on_wall=false # listen=4 # listen_switch=C # # The example uses the OFF State settings on the Left Column, LISTEN State # settings in the Center column, and ON State settings in the Right Column. # # This example will show you EVERY Sensor_Config setting you can put in # so far, again organized like above. # # Example: (shows ALL options so far, not ALL Sensor Options period however) # # Sensor_Config # range=5 listen_range=6 on_range=10 # view=7 listen_view=6 on_view=0 # wall listen_wall=true on_wall=false # bush listen_bush=true on_bush=false # tree listen_tree=true on_tree=false # listen=4 # listen_switch=C # # # # # --- Advanced: More Self Switches --- # # I recommend that you grab a copy of Game Guy's "More Self Switches" script. # # His script will allow you to add MORE PAGE CONDITIONS and Self Switches! # # Since I've already burned through HALF of your available Self Switches, we # are probably going to need some more if we are to properly Event out an # Enemy. If you don't have a copy of his script, you'll most likely find # that you are Out of Switches and need more! # # Next thing is that just like defining the Self Switch that "Listen" uses # with listen_switch=C, you can also define which Self Switch that the 'On' # state uses as well. That Option is just called "switch". It needs you # to tell it which Self Switch to change for its 'On' State. # # Example: (simplified for focus) # # Sensor_Config # range=4 on_range=6 # switch=A listen_switch=C # # In the above example, I just specified it to use Self Switch A instead of # the Default of D. However, with the "More Self Switches" script, you # could also do this: # # Example: (using More Self Switches script) # # Sensor_Config # range=4 on_range=6 # listen=4 # switch=Attack listen_switch=Listening # # # # *** IMPORTANT *** # # Switch and Listen_Switch Options are CASE SENSITIVE!!! # # These are DIFFERENT!!! # switch=C # switch=c # # When handling the Built In Self Switches, they MUST be Capitalized! # # When using "More Self Switches", the Values MUST MATCH CASE!!! # # These are DIFFERENT and will NOT trigger properly!!! # # switch=attack # switch=ATTAck # # The words "switch" and "listen_switch" are NOT case sensitive, but the # VALUES are case sensitive! # # # # # # # # # --- Advanced: More Self Switches as a PAGE CONDITION --- # # To use "More Self Switches" as a Page Condition, is super easy. Just put # in a Comment box on the FIRST LINE: # # switch:switch_name # # The above MUST be in a COMMENT, not a Script, and MUST be the VERY FIRST # thing on that Event Page for it to work as a Page Condition! # # Example: # # Sensor_Config # range=4 # listen=4 # switch=A # listen_switch=Listening # # Event Page 1 - No Conditions # Event Page 2 - switch:Listening (in a COMMENT at TOP of Event Page) # Event Page 3 - Self Switch A # # Because "switch:switch_name" MUST be at the Top of the Event Page, you # can put your Sensor_Config BELOW it and it will still work just fine. # # # # # # --- Very Advanced: Stealth --- # # This is where OVERKILL starts. And I didn't just aim for a little bit # of OVERKILL, this is pretty much Genocide! # # STEALTH Options are based on GAME SWITCHES. Notice that I did NOT say # Self Switches, but Game Switches. Most of this will not use any # Sensor_Config Options for your Sensor Events. # # In the SCRIPT CONFIG, you'll see three sets of options: # # INVIS_SWITCHES = Array # QUIET_SWITCHES = Array # STEALTH_SWITCHES = Array # # When these Game Switches are ON, then a Sensor will NOT be able to "sense" # you! This allows you to create Potions that use a Common Event to enable # one of those Game Switches. Normally I give those Common Events a WAIT # command so it is a Temporary Effect. Otherwise, it pretty much borks # your whole game as long as that Game Switch is turned ON. # # For INVIS_SWITCHES, if one of those Game Switches is ON, then a Sensor # will NOT be able to "see" you but CAN still "hear" you! For QUIET_SWITCHES # a Sensor will NOT be able to "hear" you, but CAN still "see" you! # # The STEALTH_SWITCHES will prevent a Sensor from either "seeing" or "hearing" # you at all! # # You may not want some Sensors to respond to those Stealth Switches. Thus # you can fully DISABLE a Sensor from responding to those Switches by just # adding "stealth=false" to the Sensor_Config. # # Sensor_Config # range=5 listen_range=6 on_range=10 # view=7 listen_view=4 on_view=0 # wall listen_wall=true on_wall=false # listen=4 listen_switch=C # STEALTH=false # # # Setting "stealth=false" in your Sensor's Sensor_Config will prevent ALL # the Stealth Switches from affecting your Sensor. This includes all the # Invis, Quiet, and Stealth. # # I use the term "Stealth" as both a Generalized Term that includes all # three categories of Stealth, and as a Specific Form of Stealth. # So Invis, Quiet, and Stealth are summarized when I say "Stealth". # If I am referring to a specific Stealth Setting, that setting will # cause both Invisibility and Inaudible (Quiet). This may cause a bit # of confusion in the way I try to explain how Stealth all works. # # # # # --- Very Advanced: Stealth and Auto State Switches --- # # I wrote another Script that enables Game Switches to be turned on with # Equipment and Armor. The script is called "Auto State Switches". I # used it to create Rings of Invisibility, Rings of Silence, and Rings of # Stealth. Basically, each Armor enables an Auto State. The script checks # the Party for any of those States, then changes specific Game Switches # according to what State they have. # # In the Collection Demo, I set up the Auto State Switch ID's to correspond # to the same Game Switches that are used for Invis, Quiet, and Stealth. # # Once the ID's are configured in Both Scripts, when the Player equips # a bit of Stealth Equipment, the Sensor can no longer "Sense" the Player. # # # # # --- Stealth States --- # # If a Player equips some sort of Stealth Gear, Sensors are completely # unable to detect the Player. This UNBALANCES GAMEPLAY. Thus, you # get even more Options to deal with. # # You can specify how much a Type of Stealth will affect a Sensor. You # are Limited to Three Categories, Range, View, and Listen. Just be glad # I did not add more. # # Stealth is broken down into Three Categories: Invis, Quiet, and Stealth. # The three Categories are now States. # # for the Invis Category, you can set a Range and View. # # Example: # # Sensor_Config # invis_range=3 range=4 listen_range=5 on_range=6 # invis_view=7 view=6 listen_view=5 on_view=0 # # Since Invisibility does NOT have any effect on being Heard, no Listen # option is created. # # For the Quiet Category, you can ONLY set a LISTEN level. Remember that # Listen is based on Move Speed. The Option is called "quiet_listen" and # the Value is the Move Speed when a Quiet Switch is turned on. # # Example: # # Sensor_Config # range=4 listen_range=5 on_range=6 # listen=4 # quiet_listen=5 # # The "Stealth" Category is both Invisible and Quiet together. That means # it is More Powerful than the other two States and any Stealth settings # will OVERRIDE either Invis or Quiet. This allows you to use them # together. Stealth Category allows access to Range, View, and Listen, so # it gets THREE Options. # # Example: # # Sensor_Config # stealth_range=3 invis_range=4 range=5 listen_range=5 on_range=8 # stealth_view=7 invis_view=6 view=5 listen_view=4 on_view=0 # stealth_listen=6 quiet_listen=5 listen=4 # # # Stealth States are ONLY used in the OFF State. Once a Sensor has "heard" # you, it uses the Listen State settings. In the above example, that is # the Fourth column. # # This is intended as a means for you to Balance your Gameplay without # giving the Player an Unfair advantage. # # You may also note that by this time, you've completely filled up your # entire COMMENT box with Sensor Config options! No worries, just put in # another Sensor_Config COMMENT. Each Comment MUST have Sensor_Config as # the FIRST LINE of the COMMENT. But you can use as many COMMENTS as # you need. Like I said, it is called "Super" because it has a LOT of # Options. By now, if you intend on using Stealth at all, you're gonna # need almost every single one of those Options! # # Congratulations! That was probably the hardest thing to understand # about the entire script! But there is still a LOT more to learn # on how it works. But the rest of it is EASY by comparison to # what you should know by now. Lets get on to what you'll use # it for. # # # # --- Suprise Attack --- # # I modified my revision of the XRXS Battle System Version 1.03 to allow # for Suprise Attacks. If a Player is able to sneak up on an Enemy in # the Game Map, the Battle System will give everyone in the Party a # free turn to just pound on the Enemy in Battle. It works by filling a # Progress Meter to the Max and setting the Enemy Progress (called CP in # that Script) to 0 so the Party can go immediately without having to # wait for their Progress Bars to fill. Likewise, if the Enemy gets # the drop on a Player, it will Punish the Player by allowing the Enemy # to get some Free Whacks at the Party. # # Suprise Attack requires a Script Call! # # Unsuprisingly the Suprise Attack script is called suprise_attack(). # # This should be applied to EVERY Event Page as the FIRST COMMAND to run. # You'll need to run "suprise_attack()" before you use a Move Route, or # anything else. # # What it does is it saves the Event's direction. Normally, when you # trigger an Event, that Event will turn toward the Player. When you # call "suprise_attack()", it will save the Event's original direction # and "fix" the direction so the event is facing away from the Player. # # Now I ran into some issues where I needed to change pages to trigger a # Battle properly. Thus, I also set up suprise_attack() to be called # on each page so every time an Event changes pages, suprise_attack() # will need to be called so it can fix the Event's directions. # # Suprise Attacks happen when you touch an Event from the BACK only. # # For an Enemy to get a Suprise Attack on the Player, the Event needs to # touch the Player from the Player's Back. So if a Player runs away from # an Event on the Map, they can just as easily get Suprise Attacked. # # Most of this is actually easier than it sounds. In the Collection Demo # I set up a bunch of Sensor Enemies that you can just Copy and Paste. It # is probably easier than trying to explain suprise attack, as it's quite # simple. Just call it as the first Event Command (ones that aren't comments) # for each Page of the Event. # # --- Training Your Players for Suprise Attack --- # # Suprise Attack uses a value in $game_temp. You can use a script to force # the Game Temp variable. # # $game_temp.suprise_attack = nil or false - No suprise attack # $game_temp.suprise_attack = 1 - Player got a Suprise Attack on Enemy # $game_temp.suprise_attack = 2 - Enemy got a Suprise Attack on Player # # Suprise Attack will need a compatible Battle System. Each Battle System's # author will need to make their own changes so Suprise Attack does something # in its Battle. Right now, at the time of release of this script, Suprise # Attack is ONLY compatible with Heretic's Revision of the XRXS Battle System. # # # # --- Respawning Enemies --- # # Respawning Enemies is a Pain! Until now! When an Enemy Event dies, it is # usually not in the same place you put it in the Editor. When an Enemy on # the Map needs to be Respawned, it needs to go back to its original location. # # Just run an Event Script -> respawn() to put that Event back to its # original location on the Map! This command just repositions the # event to its @start_x and @start_y coordinates with the initial # direction as well, but does not do anything else but put the Event # back to its Starting Coordinates with its Starting Direction. # # Run this after waiting for a period of time for an Enemy to respawn while # the player is still on the map. # # VERY USEFUL # # # # # --- Interpreter --- # # There is another Sensor_Config option called "Inter" which is short for # the word Interpreter. Basically what it does is it allows changing # Self Switches while the Interpreter is running. # # By default, Sensors are not allowed to change Self Switches while the # interpreter is running, like when a Cut Scene is taking place. But # if you want it to be able to change, add the word "inter" to your # Sensor_Config. This also uses Script Settings for you to change the # defaults for ALL Sensors. # # Example: # # Sensor_Config # range=4 # inter # # --- Lock --- # # Lock works much like the Interpreter command. It prevents a Sensor from # kicking up to the next State until that Event is not running its list # of Event Commands. It is best left as is, but if you really feel like # changing it, just add the word "lock" to your Sensor_Config. # # # # # # --- Pathfinding Auto Clearing Move Routes --- # # Pathfinding causes some problems when the Events change Pages. I had to # do some extra stuff to make Pathfinding routes for both Blizzard's and # ForeverZer0's Pathfinders to be cleared when changing Event Pages. # # You may need to clear these manually from within an Event. Typically after # the end of a Battle. For pathfinders to clear properly, you'll need to # put in your "clear pathfind" command, then WAIT one frame before you make # that event do anything else. I put in an Option so you can make a # pathfind clear automatically. The option is called "clear_pathfinds" # which you can use to override the Default settings or just change the # Script Settings. Right now, it only clears Blizz's and F0's pathfinders. # # --- Pathfinding for Triggers --- # # When a Sensor is Triggered, it stores the Coordinates of the Trigger and # its own Location. This allows you to Pathfind an Event to where a Sound # was generated at without going directly after the Player. It does this # for both the See and Listen State Triggers. The values are stored # in the following variables: # # @sensor.hear_loc[x,y] # @sensor.my_hear_loc[x,y] # @sensor.see_loc[x,y] # @sensor.my_see_loc[x,y] # # This allows you to Pathfind the Event both to the location of the Trigger # and Back to where it was at before it was triggered. Those coordinates # once there will stay there until the next time the Event is triggered, then # they are accurately updated. # # I also included an Option for requiring an Event to finish its Move Commands # before allowing it to turn its Listen Switch off. That allows Investigation # of Sounds to complete if the Player moves out of Range. The default setting # should work just fine, but if you want to turn it off for some reason, you # can manually change the Self Switch to Off. If you do this however, set # @sensor.hear_loc[2] = false so the next time it triggers, it will store # the correct coordinates. There is an Option in the Sensor that I did not # create a Sensor_Config option for called @listen_move_finish if you wish # to change it. @sensor.listen_move_finish = false # # --- Methods for Triggers --- # # I added in a bunch of Move Commands for dealing with Listen Triggers. These # should be run in a Move Route -> Script. They are NOT smart so they will # cause an Event to get stuck on Non Passable tiles, so they need to be # called at the right time to be useful. # # turn_toward_sound - Turns toward a Recorded Sound Location # turn_away_from_sound - Turns away from a Recorded Sound Location # move_toward_sound - Moves toward the Source of a Recorded Sound # move_away_from_sound - Moves Away from the Source of a Recorded Sound # turn_toward_sensor_target - Turns toward the Target, not stored Sound Loc # turn_away_from_sensor_target - Turns away from Target, not stored Sound Loc # move_toward_sensor_target - Just like Move Toward Player (ANY Target) # move_away_from_sensor_target - Run Away!!! # # --- Sensor Target --- # # By default, the Player is the Target for a Sensor. Right now, Sensors can # only have ONE target, which is the Player. You can give a Sensor a Target # other than the Player if you wish. This option is called "target". The # Target needs to be an Event ID. # # Example: # # Sensor_Config # range=4 # target=156 # # This will make the Sensor only Sense for Event ID #156 instead of the Player. # # I aliased Approach so that a Sensor Event will Approach its Target instead # of the Player. You can have it both ways though. You can add the word # approach in the Sensor_Config so that a Sensor Event will still approach # the Player but it Senses another target. # # NOTE: I intend on changing this script later so that a Sensor can have # multiple Targets. Its intended for compatability with my Caterpillar. # # --- Requiremets --- # # Oddly enough, this is seldom documented. Most Scripts, including this one # are very dependant on command_355 Fix. What happens is if you have a # script call that returns False, Event Commands fail to continue processing. # Change is Very Easy. Script Interpreter 7 at Line 277, change or comment # out where it says "return false" so that Event Commands can continue # to run. Otherwise your eventing just stops right there. # # I put Comments in my Event Scripts. Any sort of # Comment in an Event # script will cause this to happen without changing the Interpreter as # mentioned above. # # --- Recommended Additional Scripts --- # # It is NOT REQUIRED, but STRONGLY RECOMMENDED that you grab yourself # More Self Switches by Game_Guy / Gameus. Big reason is that the # limits of ABCD are far too constraining. His script WILL allow # you to have More Event Pages that are CONDITIONAL, even if the # Self Switch Page Condition Box is not checked. Refer to his # script for more information. # # I also recommend a Pathfinding Script. I am in the process of trying to # get the two big ones that I know of, ForeverZer0s Pathfind and Blizz's # Lagless Pathfinder to work in a way that allows Pathfind Calls to be # used as Move Routes. I have updated ForeverZero0's Pathfind Script to # allow for using Pathfinds as Move Routes. # # # # # --- Sensor_Config Options and Values --- # # These are all Options you can put in your Sensor_Config # # # range=N - REQUIRED - N is the Range in Map Tiles. IE range=4 # on_range=N - Range when Self Switch ON. Default is same as Normal Range. # switch=D - Which Self Switch to be changed (Default 'D' in Config) # view=N - Type of Field of View. N currently a number between 0-6 # on_view=N - Type of Field of View when Self Switch is Enabled. Default 0 # wall - If Walls (Non Passable Tiles) can obstruct Field of View # on_wall=t/f - MUST PUT TRUE OR FALSE for B - Walls with Self Switch On # tree - If Terrain Tag Tiles Obstructs FoV - Flying Enemies # on_tree=t/f - If Trees obstruct view when Self Switch is On - Config # bush - If Bush Tag Obstructs FoV - Small Ground Enemies # on_bush=t/f - If Bush Obstructs view when Self Switch is On - Config # listen=L - Allows Sound from Movement based Trigger # light=t/f - Version 1.1, Targets can HIDE in Shadows # listen_light=t/f - Heightened Awareness of Sensor # on_light=t/f - When true, Target can no longer hide in darkness # inter - Change or Dont Change while Interpreter is running - Config # lock - Same as Inter, dont change when this event is running # listen_switch=C - Self Switch to change with Listen Trigger # listen_range=N - When Listen Switch is on, Range is THIS # listen_wall=true/false - When Listen Switch is on, Wall is THIS # listen_bush=true/false - When Listen Switch is on, Bush is THIS # listen_tree=true/false - When Listen Switch is on, Tree is THIS # stealth - Sensor responds to Stealth Game Switches # invis_view - Sensor Field of View when Game Switch Stealth is ON # invis_range - Sensor Distance can See or Hear when Stealth Switch ON # quiet_listen - Sensor Movement Speed Detection Level when Stealthed ON # stealth_view - Overrides invis_view, Stronger, Same as Invis View # stealth_range - Overrides invis_range, Stronger, Same as Invis Range # stealth_listen - Overrides quiet_listen, Stronger, Same as Quiet Listen # suprise_no_turn - Keeps Enemy NPC's turned away on Trigger when true # target=Event_ID - Sets an Event to be the Sensor's Target over Player # approach - Approach the Player instead of Target # # NOTE: I did not set up any simple means for altering an Events # Terrain Tags as well as many of the other properties. They are all # wide open except for ID because that has to remain the ID of the # Event on the Game Map. Target is accessible through target = # which does error checking to ensure new target is a Game_Character. # # To change ANY OTHER properies each Sensor Events instance of the class, # just access the @sensor object directly. # @sensor.target = Game_Character # @sensor.view = Some Number # @sensor.my_see_loc = [] # # # # # --- Limitations ---- # # Line of Sight is NOT obstructed by Events. Its just too expensive to check. # I may change that later, but it will require an additional script for # optimizing checking specific events over ALL events. And honestly, I dont # want to force anyone who wants to use this script to have to install all # sorts of dependancies. This script should work by itself with no problems. # # Restoring Move Route Index only checks the Page that was active prior # to changing the Self Switch to On. So it only stores ONE Move Route Index # Move Route Index MUST be the same as the Move Route that the Event had # at the time of Page Change. So changing a Move Route will only work # if the Move Route is the same as it was before. If you Force a Move Route # then, when the Page is reloaded, if the Move Route is different, it will # not reset the Index for a totally different Move Route. Useful for # Autonomous Move Routes. # # # # --- DYNAMIC LIGHTS --- # # New in Version 1.1 # # I added some new code to make this script fully compatible with my Dynamic # Lighting Script. The Dynamic Lighting Script already has some features to # allow checking if a Character is currently being Illuminated or not by # making a Script Call with in_light?(id, tolerance). To see if the Player # is In Light, you can do this one of two ways, either $game_player.in_light?() # or by adding 0 for the Target to check: "in_light?(0)" from an Event Script. # The script in_light?(id, tolerance) will return TRUE if character is lit, and # false if they are covered in more than 50% Shadow. Due to the fact that # most Game Shadows need to be partially translucent for humans to see through # them, an Additional Argument is used to make adjustments to the sensitivity. # This Option is called TOLERANCE. So, if you have relatively faint shadows, # you can INCREASE the TOLERANCE to DECREASE the SENSITIVITY of the Light # Detection code. For example, "in_light?(0, 50)" will check to see if the # Player is illuminated, but makes the Sensitivity LESS SENSITIVE. The # way it works is by determining the Average Alpha Value of the Shadow # at that location. Alpha has a range of 0 to 255, so Tolerance maxes out # at 255. That gives you an idea of how much to adjust your Tolerance by. # You can also use Negative Numbers to INCREASE sensitivity, but during my # testing, it is already quite sensitive. # # To add LIGHT SENSITIVE SENSORS, I've added FOUR new options in Version 1.1, # so also do not forget that you can have MORE THAN ONE SENSOR_CONFIG comment # # light=true - Add this to your Sensor_Config to hide Target in Shadow # listen_light=true - Addding this causes Targets to be detected when Unlit # on_light=true - Add this so once Target is detected, they can not hide # light_tol=123 - Add this to adjust Light Sensitivity # # The Sensor_Config as displayed above will allow the Player to walk right # in front of a Sensor and NOT BE DETECTED unless the Player is Illuminated. # The additional settings of Listen Light and On Light allow you to define # behaviors for your Sensor once those states have been Triggered. For example # once a Sensor has its Listen State triggered, so a Guard is Listening, when # the option "listen_light=true" is set, then Shadows will no longer hide the # Player from the Sensor. Basically it emulates a state of increased awareness # of the Guard / Enemy / Sensor / whatever. The option of "on_light=true" is # used to prevent the Player from running and hiding in Darkness. This could # be quite useful if you have a Guard that is Patrolling, and has a flashlight # or Light Source attaced to the Sensor. Due to the random nature of chasing # the player, the Guard may turn away from the Player, which would cause the # Player to again be in darkness, thus, Untrigger the Sensor. By adding the # on_light=true Option, once the Sensor is Triggered, it will remain triggered # as long as the player remains within the on_range / range parameter. So # once the Player has been detected, the Player will remain visible to the # sensor as so long as the Player is in range, regardless of being lit up # or hiding in shadow. This encourages your players to stay hidden during # any sort of stealth type gameplay, and gives you control over the behavior # of the Sensors. # # DEBUG - Heretic's Dynamic Lighting Script also has a DEBUG feature. During # Debug Gameplay (running from the Editor) you can press the F5 key to toggle # the Light Effects ON and OFF. This WILL AFFECT SENSOR BEHAVIOR. When the # Light Effects are temporarily disabled, the Shadowmap is NOT RENDERED, which # will make Shadow Stealth IMPOSSIBLE DURING DEBUG. # # New in Version 1.11 # # - Added the ability to set a Range when Dynamic Lighting is active. # # light_range=5 # dark_range=3 # # When at NIGHT or INDOORS with Dynamic Ligthing ON, you can use the light and # dark range options to customize Sensor Sensitivity. When there is no light # effects going on, the default range will be used. Also, if your Sensor # is Light Sensitive, it WILL BE BLIND unless the Target is Illuminated, or # is assigned a light_range or dark_range # # The concept of Darkness is useful for Stealth. So it isnt just for making # the game look purdy. The Dark Range option is basically intended to make # a sensor still be able to see its Target, but with less visibility. Thus # if you have your Sensor_Config set with a range=4 for example, a decent # dark_range would be a little less than your default range. range=4 and # just add "dark_range=3" so the Sensor can still "see" its target, just not # quite as well. # # The Light Range feature works the exact opposite. It is intended to INCREASE # how far a Sensor can see its Target. Imagine a pitch black night. Someone # shines a flashlight from miles away. It really stands out like a sore thumb. # The Light Range feature is intended to simulate this. It is also intended to # have a HIGHER value than your Default Range, but NOT HIGHER than on_range. # # Sensor_Config: range=4 dark_range=3 light_range=5 on_range=5 # # NOTE: Light and Dark Range features ONLY kick in at NIGHT or INSIDE. Refer # to Heretic's Dynamic Lighting Script for details. The Dynamic Lighting # script uses TWO Swtiches. The first is the "Day Switch". If the time is # currently "Day", then the Switch is set to ON. Dynamic Light script does # this AUTOMATICALLY. The other switch is MANUAL. This is called the INDOOR # switch. The INDOOR switch is intended to be turned on when your map is # an Indoor Map. This allows Dynamic Lighting to be different depending on # the type of Enviornment your map is used for. During the day when you are # outside, you wont really see any shadows. But inside you will still see # shadows. With proper setup, you can have Light come in dynamically thru # windows, and react to Lightning during Storms, but will be not used as # sources of light at night while inside. It allows for VERY dynamic maps # and environments. # # You may also note that your Sensor can also have their own Light Sources. # This may be useful for creating a visual indication that a Guard is actively # searching for the Player, but has not yet found them. # # NOTE - You MUST use Heretic's Dynamic Lighting script for this to also work. # # Heretic's Dynamic Lighting is OPTIONAL and DOES NOT NEED to be installed # for Super Event Sensor to work properly. Documentation for all Dynamic # Light code is contained within Heretic's Dynamic Lighting script. # # --- Legal --- # # By using this script, you are agreeing to the legal conditions. This # Script is distributed Without Warranty. I and any Contributing # Authors can not be held liable for any damages caused by the use # of this script. # # You may use, alter and build upon and distribute this script provided # that you credit me and any other Authors that contribute to this work. # # You may use this script in Commercial Games without compensation provided # that I am credited in some way shape or form. I dont expect to have my # name in the Opening Credits, but Closing Credits is expected if used. Any # other contributing Authors must also be credited. # # Contributing Authors are not allowed to alter the Legal Terms of this script. # # You may NOT sell this version or any alterations of this script. # # You may NOT claim work that was not done by you as your own. # # This script may only be used in the prescribed manner: RPG Games. # It is recommended that this script not be printed out with the intent # of using the printed script as toilet paper. # # # # --- Configuration --- # # The Configuration options are the Default Values for Sensor Events. Each # Sensor can be set to have a DIFFERENT value than the Default Config settings. # That is what Sensor_Config is mostly used for. Sensor_Config is a COMMENT # on Page 1 of an Event. It does NOT have to be at the very top. Every # Sensor Option except those dealing with Arrays can be changed with the # Sensor_Config. All other Options can be accessed by accessing the # @sensor object for that event. module Sensor_Config # To enable a Sensor Event, put Sensor_Config into a Comment on Page 1 # of an Event. The Comment does NOT have to be at the top. It reads # up to the number of SENSOR_READ_LINES to grab the Sensor_Config # and get any of that Event's Sensor Settings. # # You CAN have MULTIPLE Sensor_Configs, but each MUST have Sensor_Config # at the top of each Comment, and BOTH have to START within the # of lines. So if you need to read MORE lines to grab your Sensor_Config # then increase this number. The default should be more than plenty. SENSOR_READ_LINES = 10 # Number of Lines to read for Each event on Page 1 # These are the Defaults for when an Event is initialized. They can # be changed PER EVENT by accessing the Sensor Value for that event # $game_map.events[@event_id].sensor.switch or .view Etc # Use Sensor_Config Options to alter from Default when Map is Loaded # or use @sensor.value to change during gameplay. #--- Settings for VIEW and LISTEN --- SENSOR_SELF_SWITCH = 'D' # Self Switch to use when "See" Triggered LISTEN_SELF_SWITCH = 'D' # Self Switch to use when Listen Triggers LISTEN_MOVE_FINISH = true# Finish Custom Move Route before Disabling TREE_TERRAIN_TAG = 2 # Terrain Tags for Flying Enemies that Obstruct View WALL_TERRAIN_TAG = 3 # Terrain Tags that WILL Obstruct View - Some Fences NOT_WALL_TERRAIN_TAG = 4 # Terrain Tags that do NOT Obstruct View - Water ON_WALL_DEFAULT = false # Default for Event Setup, override with Config ON_BUSH_DEFAULT = false # Default for On Bush setting if not overridden ON_TREE_DEFAULT = false # Default for On Tree setting if not overridden # NOTE: Listen settings use Standard See Switch OFF Settings #--- Movement Corrections --- SAVE_MOVE_INDEX = true # Save and Restore Move Route Index on Page Change SUPRISE_NO_TURN = true # Prevent Event Turning Toward on Trigger w/scpt call SENSOR_APPROACH = true # Approach Target over Player (Default is Player) CLEAR_PATHFINDS = true # Clear Pathfinding Scripts Paths on Refresh #--- Interpreter ---- INTERPRETER = false # Change Switch while Interpreter Running (Default) # This can be altered with a Sensor_Config of "inter" to set to opposite val LOCK = false # Change Switch while this Event Running #--- Item Customizations --- # NOTE: Arrays Used to allow for Multiple Switches for Multiple Equipment # and Potions and Spells all simultaneously. SENSOR_STEALTH = true # Enable for Stealth Switches or Game System INVIS_SWITCHES = [134, 140] # Enabled Game Switch prevents VIEW TRIGGERS QUIET_SWITCHES = [135, 141] # Enabled Game Switch prevents LISTEN TRIGGERS STEALTH_SWITCHES = [136,142] # Enabled Game Switch prevents BOTH TRIGGERS # I use ITEMS and COMMON EVENTS to handle these Game Switches # NOTE: These are Game Switches, not SELF SWITCHES # NOTE: Script checks $game_system.sensor_stealth. The Setting here is # used as the Default. Change on the fly in $game_system #-------------------------------------------------------------------------- # * Returns the Version Number of this Script #-------------------------------------------------------------------------- $super_event_sensor = 1.1 def version # Returns current version number return 1.1 end end #============================================================================== # ** Event Sensor #------------------------------------------------------------------------------ # This class is contained within Events and is created by using # a Comment on the First Page of an Event with the string "Sensor_Config" # on the First Comment Line by itself. Sensor Options go on the Lines # that are BELOW keyword "Sensor_Config". # # This is a FULL LIST of EVERYTHING you can edit for every Sensor! # # Sensor_Config # range=4 #============================================================================== class Event_Sensor #-------------------------------------------------------------------------- # * Public Instance Variables - Event_Sensor #-------------------------------------------------------------------------- attr_accessor :enabled # If Sensor is allowed to be Updated attr_accessor :switch # Self Switch ABCD or More Self Switches Script attr_accessor :listen_switch # Self Switch for when Listen Triggers attr_accessor :inter # Allow Switch to Change during Interpreter attr_accessor :lock # Prevents Switch Change while "Locked" or Run attr_accessor :range # Distance Character can "See" attr_accessor :listen_range # Distance can "See" when Listen Switch is ON attr_accessor :listen_move_finish # Dont Disable Switch until Move is Done attr_accessor :on_range # Distance Character "Sees" when Self Switch On attr_accessor :view # Type of Field of Vision attr_accessor :listen_view # Type of View when Listen Switch is ON attr_accessor :on_view # Type of Field of Vision when Self Switch On attr_accessor :light # If Target needs to be Illuminated attr_accessor :listen_light # If heard, needs Illumination for Detection attr_accessor :on_light # Once detected, if Darkness hides attr_accessor :light_tol # Light Tolerance (Increase for Less Sensitive) attr_accessor :light_range # Range when Target is Illuminated attr_accessor :dark_range # Range when Target is NOT Illuminated attr_accessor :wall # Boolean - True / False - Walls obstruct View attr_accessor :listen_wall # Walls obstruct View while Listen Switch ON attr_accessor :on_wall # Bool - Walls Obstruct View with Self Switch attr_accessor :wall_tags # Terrain Tags that Obstruct View attr_accessor :not_wall_tags # Obstructs Movement but Viewable Terrain Tags attr_accessor :tree # For Flying Enemies - Default is False attr_accessor :tree_tags # Terrain Tag for View Obstructing tiles attr_accessor :listen_tree # If Trees obstruct View while Listen Switch ON attr_accessor :on_tree # When Self Switch On if Trees obstruct View attr_accessor :bush # Boolean - True if Bush Obstructs View attr_accessor :listen_bush # If Bushes obstruct View with Listen Switch ON attr_accessor :on_bush # When Self Switch On if Bushes obstruct View attr_accessor :listen # Target's Move Speed at or above attr_accessor :see_loc # [X,Y] Array of where See was Triggered attr_accessor :hear_loc # [X,Y] Array of where Hear was Triggered attr_accessor :my_see_loc # This Events XY where it was at when Triggered attr_accessor :my_hear_loc # This Events XY where it was at when Triggered attr_accessor :stealth # If Sensor Responds to Game Stealth Switches attr_accessor :invis_switches # Switches for THIS EVENT to prevent VIEW attr_accessor :quiet_switches # Switches for THIS EVENT to prevent LISTEN attr_accessor :stealth_switches # Switches for THIS EVENT prevents EITHER attr_accessor :invis_range # Range when Invis'd (Only when OFF) attr_accessor :invis_view # View when Invis'd (Only when OFF) attr_accessor :quiet_listen # Listen Move Speed for Stealth (Quiet) attr_accessor :stealth_range # Range when Off and Stealth Switch is ON attr_accessor :stealth_view # Type of Field of View when Stealthed attr_accessor :stealth_listen # Listen Move Speed when Stealthed attr_accessor :start_x # For a Respawning Enemy - X attr_accessor :start_y # For a Respawning Enemy - Y attr_accessor :start_d # For a Respawning Enemy - Direction attr_accessor :clear_pathfinds # Clears Pathfinds on Refreshes attr_accessor :suprise_no_turn# Reset Direction when Suprised - Config attr_accessor :approach # Approach Player instead of Target attr_accessor :save_move_index# Option for Restoring Move Route Index attr_accessor :last_page_data # For Changing Event Pages # Check if the Sensor Config is already Included (prevents errors) unless included_modules.include? (Sensor_Config) # Make the Constants inside the Module Config available for shorter code include Sensor_Config end #-------------------------------------------------------------------------- # * Object Initialization for Sensor Object - Event_Sensor # event : @event # direction : @event.direction prior to Event Page checks # cfg : Comment with "Sensor_Config" on First Line of Comment #-------------------------------------------------------------------------- def initialize(event, direction, cfg) # Game Map Event ID of Parent Event @id = event.id # If the Event is currently "Sensing" - True by Default @enabled = true # Distance the Character can "see" the Target (from range=5) @range = (cfg.gsub(/ range=([\d]+)/i){$1}) ? $1.to_i : 0 # Capture Number for Target Setting - target=0 Player or target=23 Event cfg.gsub(/target=([\d]+)/i){$1} # If Target= is a Number (Only Numbers Captured) if $1 and $1.to_i.is_a?(Numeric) and $1.to_i > -1 # Assign that Number to Target @target = $1.to_i # NOTE: Error Checking for Event Existence does not occur here, done later else # Capture Non Numbers for Error Reporting cfg.gsub(/target=([\D]+)/i){$1} # If target=letters, explain there is a Problem if $DEBUG and not $1.nil? # Explain to only use Numbers in Sensor_Config for target=N print "Warning: Problem in your Sensor_Config for\n\n", "Event ID: ", @id, "\n", "Event X: ", event.x, "\n", "Event Y: ", event.y, "\n\n\n", "When you set a Target in your Sensor_Config\n", "the Target needs to be a Number!\n\n", "Example:\n\n", "Sensor_Config\nrange=3\ntarget=12\n\n", "Player used as this Sensor Event's Target" end # Default is a Game Player @target = $game_player end # Capture "inter" (Interpreter) for this Characters Interpreter Setting @inter = (cfg !~ / inter/) ? INTERPRETER : !INTERPRETER # Capture "lock" (Interpreter) for this Characters Lock Setting @lock = (cfg !~ / lock/) ? LOCK : !LOCK # "Switch" for Self Switch to Change - Alphanumeric Underscore ONLY @switch=(cfg !~ / switch=([\w]+)/i) ? SENSOR_SELF_SWITCH : $1.to_s # "Listen Switch" for Self Switch to Change - Alphanumeric Underscore ONLY @listen_switch=(cfg !~/listen_switch=([\w]+)/i)?LISTEN_SELF_SWITCH: $1.to_s # Capture Number for Listen Setting - listen=MOVE_SPEED - listen=4 cfg.gsub(/ listen=([\d\.]+)/i){$1} # Listen - Noise Level (well, movement speed) to Enable Self Switch @listen = ($1.nil?) ? nil : $1.to_f # Capture Number for Listen Range Setting - cfg.gsub(/listen_range=([\d]+)/i){$1} # Listen Range - Distance Character "sees" Target when Listen Switch is ON @listen_range = (@listen and not $1.nil?) ? $1.to_i : @range # Capture Number for On Range Setting - on_range=5 cfg.gsub(/on_range=([\d]+)/i){$1} # On Range - Distance the Character "sees" Target when Self Switch is ON # Instead of defaulting to just Range, when Listen is Enabled, if # the Listen Range is greater than the Default Range, the Listen Range # is preferred over the smaller range. It saves from having to always # put in too many naming parameters. @on_range = (not $1.nil?) ? $1.to_i : (@listen and @listen_range>@range) ? @listen_range : @range # If CFG has view=Number in the Text, use Capture Group $1 to get the value view = (cfg.gsub(/ view=([\d]+)/i){$1}) ? $1 : nil # Type of Field of View based on Event CFG Setting if view # Branch by Integer - Written out so People can Read and Understand case view.to_i when 0 # Event can see everywhere within Distance (Insects) @view = 0 when 1 # Can see everything EXCEPT in straight line behind @view = 1 when 2 # Can see 75% around them and Behind except for small Angle @view = 2 when 3 # Can see 62% around them and some Behind except for big Angle @view = 3 when 4 # Can view Peripherally (Sides and Front - Humans and Animals) @view = 4 when 5 # Can view Peripherally but Blind on Sides (wearing a Helmet) @view = 5 when 6 # Event can see only In Front of them (Metal Gear Solid) @view = 6 when 7 # Can see almost only stright in front of them @view = 7 when 8 # Can see only in a Straight Line in front of them @view = 8 when 9 # BLIND @view = 9 else # View is set but not 0-9 Check Alias, Default to 0 - Full Visibility @view = alias_sensor_view(cfg) # Use this for adding more Views # It is a Method I used so you could Alias it if you wanted to # add more Views from the Config without having to redefine # this method end else # View not specified in CFG so Default to 0 - Full Visibility @view = 0 end # Capture Number for Listen View Setting - on_view=5 cfg.gsub(/listen_view=([\d]+)/i){$1} # Listen View - Type of Field of View when Listen Switch is On @listen_view = (@listen and not $1.nil?) ? $1.to_i : @view # When True, Listen will not disable Self Switch until Move is Complete @listen_move_finish = LISTEN_MOVE_FINISH # Capture Number for On View Setting - on_view=3 cfg.gsub(/on_view=([\d]+)/i){$1} # On View - Type of Field of View when Self Switch is On @on_view = ($1.nil?) ? 0 : $1.to_i # Capture for this Characters Wall Setting cfg.gsub(/( wall)/i){$1} # If Walls Obstruct the View - Enable by putting "wall" in the Event CFG @wall = ($1.nil?) ? false : true # Capture Number for Listen Wall Setting - listen_wall=true/false cfg.gsub(/listen_wall=(true|false)/i){$1} # Listen Wall - If Can See through Walls when Listen Switch is On @listen_wall = ($1.nil?) ? @wall : ($1.downcase == "true") ? true : false # Capture Number for On Wall Setting - on_wall=true/false cfg.gsub(/on_wall=(true|false)/i){$1} # On Wall - If Can See through Walls when See Self Switch is On @on_wall=($1.nil?)?ON_WALL_DEFAULT: ($1.downcase=="true")? true : false # Wall Terrain Tag to Block View when Passable as a Tile - Fences @wall_tags = [Sensor_Config::WALL_TERRAIN_TAG] # Not Wall Terrain Tag to Ignore when Unpassable as a Tile - Water @not_wall_tags = [Sensor_Config::NOT_WALL_TERRAIN_TAG] # Tree - Use for Flying Enemies - Put in Event CFG cfg.gsub(/( tree)/i){@tree = true} # Tree Terrain Tags @tree_tags = [Sensor_Config::TREE_TERRAIN_TAG] # Capture Number for Listen Tree Setting - cfg.gsub(/listen_tree=(true|false)/i){$1} # LIsten Tree - If Can See through Trees when Listen Self Switch is On @listen_tree = ($1.nil?) ? @tree : ($1.downcase == "true") ? true : false # Capture Number for On Tree Setting - cfg.gsub(/on_tree=(true|false)/i){$1} # On Tree - If Can See through Trees when Self Switch is On @on_tree = ($1.nil?) ? Sensor_Config::ON_TREE_DEFAULT : ($1.downcase == "true") ? true : false # Bush - Use for Obstructing View in any Tile with a Bush Flag - cfg.gsub(/( bush)/i){@bush = true} # Capture Number for Listen Bush Setting - listen_bush=true / false cfg.gsub(/listen_bush=(true|false)/i){$1} # Listen Bush - If Can See through Bush when Listen Switch is On @listen_bush = ($1.nil?) ? @bush : ($1.downcase == "true") ? true : false # Capture Number for On Bush Setting - cfg.gsub(/on_bush=(true|false)/i){$1} # On Bush - If Can See through Bush when Self Switch is On @on_bush = ($1.nil?) ? ON_BUSH_DEFAULT: ($1.downcase=="true")? true:false # If Sensor responds to the Stealth Game Switches cfg.gsub(/stealth=(true|false)/i){$1} # Capture String for "light=true/false" cfg.gsub(/ light=(true|false)/i){$1} # If Sensor is Light Reactive (Target must be Illuminated) @light = ($1.nil?) ? @light : ($1.downcase == "true") ? true : false # Capture String for "listen_light=true/false" cfg.gsub(/listen_light=(true|false)/i){$1} # If Sensor is Listen Light Reactive (Target must be Illuminated) @listen_light = ($1.nil?) ? @listen_light : ($1.downcase == "true") ? true : false # Capture String for "on_light=true/false" cfg.gsub(/on_light=(true|false)/i){$1} # If Switch to Remain ON after Switch is Triggered @on_light = ($1.nil?) ? @on_light : ($1.downcase == "true") ? true : false # Capture value for Light Tolerance (higher number = less sensitive) cfg.gsub(/light_tol=([\d.-]+)/i){@light_tol = $1.to_f} # Clamp Value for Light Tolerance @light_tol = (@light_tol) ? [[-255,@light_tol].max, 255].min : nil # Capture and set Value for Light Range cfg.gsub(/light_range=([\d]+)/i){@light_range = $1.to_i} # Capture and set Value for Dark Range cfg.gsub(/dark_range=([\d]+)/i){@dark_range = $1.to_i} # If Heretic's Dynamic Light Script is not available if not defined?(Game_Light) or not defined?(Cache_Light) # Clear Light Properties @light = nil @listen_light = nil @on_light = nil @light_tol = nil @light_range = nil @dark_range = nil @light_in_sw = nil @light_day_sw = nil # Specific Constant for Heretic's Dynamic Lighting elsif defined?(Light_Config) # If Switch for Indoor Lighting is Defined in Light_Config if defined?(Light_Config::Switch_Indoor) and Light_Config::Switch_Indoor # Game Switch ID that is set to ON when Indoor Lighting is used @light_in_sw = Light_Config::Switch_Indoor end # If Switch for Daytime is Defined in Light_Config if defined?(Light_Config::Day_Switch) and Light_Config::Day_Switch # Game Switch ID that is set to ON when Lighting is Daytime @light_day_sw = Light_Config::Day_Switch end end # See and Hear Loc saves TARGET XY Position when Triggered @see_loc = [] @hear_loc = [] # Saves XY Location of THIS Event, not the Target when Target Triggers # Useful for Pathfinding back to last location @my_see_loc = [] @my_hear_loc = [] # - Default to CONFIG els use the Value in stealth=true/false @stealth=($1.nil?)?SENSOR_STEALTH : ($1.to_s.downcase=="true")? true:false # Invisible Switches - From Config @invis_switches = INVIS_SWITCHES # Quite Switches - From Config @quiet_switches = QUIET_SWITCHES # Stealth (Both Invis and Quiet) - From Config @stealth_switches = STEALTH_SWITCHES #--- Invisibility (Its a Game Switch, not Self Switch, see Config) --- # Distance the Character can "see" when Target is Invis (Switch is On) cfg.gsub(/invis_range=([\d]+)/i){@invis_range = $1.to_i} # Distance the Character can "see" when Target is Stealthed cfg.gsub(/invis_view=([\d]+)/i){@invis_view = $1.to_i} #--- Quiet (Its a Game Switch, not Self Switch, see Config) --- # NOTE: Quiet only affects the Movement Speed, Range not available here # Listen for Movement Speed when Stealth is used cfg.gsub(/quiet_listen=([\d\.]+)/i){@quiet_listen = $1.to_f} #--- Stealth (Another Game Switch - Overrides Invis and Quiet if used) --- # Distance the Character can "see" when Target is Stealthed cfg.gsub(/stealth_range=([\d]+)/i){@stealth_range = $1.to_i} # Type of Field of View when Stealthed cfg.gsub(/stealth_view=([\d]+)/i){@stealth_view = $1.to_i} # Listen for Movement Speed when Stealth is used cfg.gsub(/stealth_listen=([\d\.]+)/i){@stealth_listen = $1.to_f} # Start X and Start Y used for Enemy Respawning @start_x = event.x @start_y = event.y @start_d = direction # Clear Pathfinds on Refresh @clear_pathfinds = Sensor_Config::CLEAR_PATHFINDS # Default from Config - Save and Restore Move Route Index on Page Change # Set to False per Event with @sensor.save_move_index = false # When set to False, when Page changes, Move Route Index not restored @save_move_index = Sensor_Config::SAVE_MOVE_INDEX # Approach Target instead of Player # NOTE: putting the tag 'approach' uses Opposite Value of Script Config @approach = (cfg !~ /approach/) ? SENSOR_APPROACH : !SENSOR_APPROACH # Enable or Disable Event Turn Towards Player on Trigger @suprise_no_turn = Sensor_Config::SUPRISE_NO_TURN # Page just prior to changing Self Switch 0 - Index, 1 - Page, 2 - Move Rt @last_page_data = [] end #-------------------------------------------------------------------------- # * Alias Sensor View - Event_Sensor # cfg : Sensor Config (already passed into arg for you) # # This is intended to be Aliased if you want to add any more visibility # settings in for some script that you write. You can alias the init # method or this method if you just want to alter the view without # needing to do more work and logic handling just to add a number. # # Just tell Players to add your new View in the Sensor_Config by setting # the view to what ever numbers your Alias handles. view=14 #-------------------------------------------------------------------------- def alias_sensor_view(cfg) # Returns the Default return 0 end #-------------------------------------------------------------------------- # * Target? - Event_Sensor # - Value only if Target exists (not Nil as target can be set to Nil) #-------------------------------------------------------------------------- def target? # Return Target if it exists return @target if @target end #-------------------------------------------------------------------------- # * Target= Setter Method for this Event's Target - Event_Sensor # target : new target (Game_Character) #-------------------------------------------------------------------------- def target=(target) # Check for Types, need a Game Player or Game Event if target.is_a?(Game_Character) # Set the New Target to the Argument @target = target # If Target is set to a Number elsif target.is_a?(Numeric) # Set Target to the Number @target = target # If Target is set to Nil (used to Disable Sensor, same as @enabled=false) elsif target.nil? # Set Target to Nil for Disabling @target = nil # The Argument passed isnt a Game_Character so explain what happened else if $DEBUG # Display Error and explain why their script call didnt work print "Warning: Error in your Script Call using Super Event Sensor", "\n\nYou tried to change Event ",@id,"'s Target by using\n", "'@sensor.target=", target, "'\n\n", "Target needs to be a Player, Event, or a Number.\n\n", "To set the Player as the Sensor's Target:\n", "target=$game_player\nor\ntarget=0\n\n", "To set an Event as the Sensor's Target:\n", "target=$game_map.events[@event_id]\nor\ntarget=event_id\n", "(target=27)", "To set the Target using a Number:\n", "target=0 for the Player or\n", "target=1 to use an Event ID" end end # Clear See and Hear Locations @see_loc = [] @hear_loc = [] @my_see_loc = [] @my_hear_loc = [] end #-------------------------------------------------------------------------- # * Get Target - Event_Sensor # - Returns the Target as Game_Character or provides # an Error Message #-------------------------------------------------------------------------- def get_target # Return Target if Target is a Game Character return @target if @target.is_a?(Game_Character) # If Alternate Target if @target.is_a?(Numeric) # Return Player return $game_player if @target == 0 # Get Game Event as Target target = $game_map.events[@target] # Check for Non Existent Events if $DEBUG and target.nil? # Explain the Problem print "Warning: Problem with a Sensor Event: \n", "Sensor Event ID: ", @id, "\n", "Sensor Event X: ", $game_map.events[@id].x, "\n", "Sensor Event y: ", $game_map.events[@id].y, "\n\n", "The Sensor Event's Target Event ID: ", @target, "\n", "That Event doesn't exist on this Map.\n\n", "Game will now Crash.\n\n", "(Crash to avoid Error Spam)" # Crash is allowed here otherwise Mappers will get heavily spammed # with this Error Message repeatedly and it can be difficult # to close. end # Return the Target when it exists return target end end #-------------------------------------------------------------------------- # * Target In Light - Event_Sensor # - Determines if Sensor Target is Illuminated # - Should only evaluate if Heretic's Dynamic Lighting is available #-------------------------------------------------------------------------- def target_in_light? # Get the Sensor's Target target = get_target # Set Value of Tolerance to 0 if not set tolerance = @light_tol ? @light_tol : 0 # Return Light Range if Target is In Light return true if target.in_light?(target.id, tolerance) end #-------------------------------------------------------------------------- # * Use Light Range - Event_Sensor # - Determines if Sensor is Light Sensitive # - Should only return true if Heretic's Dynamic Lighting is available #-------------------------------------------------------------------------- def use_light_range? if (@light or @listen_light or @on_light) and ($game_switches[@light_in_sw] or not $game_switches[@light_day_sw]) # Use Light Range return true end end #-------------------------------------------------------------------------- # * Get Range - Event_Sensor # - Range is dependant on quite a few things. This command checks # the Conditions of Self Switches and any Game Stealth Switches # to return the proper value depending on the state of the # switches that are checked. #-------------------------------------------------------------------------- def get_range # Self Switch switch_key = [$game_map.map_id, @id, @switch] # Get current Switch Value switch = $game_self_switches[switch_key] # Self Switch for Listen listen_key = [$game_map.map_id, @id, @listen_switch] # Get Current Switch Value for Listen listen_switch = $game_self_switches[listen_key] # If Sensor is Light Sensitive (1 = lit, 2 = in darkness, nil for no lights) lit = !use_light_range? ? nil : target_in_light? ? 1 : 2 # If System Setting and the Sensor responds to Stealth Switches if $game_system.sensor_stealth and @stealth # Invis Default invis = false # If this Sensor has a Specified Invis Range if @invis_range # Check each of the Sensor's Invis Game Switches (Reverse for Priority) for invis_switch_id in @invis_switches.reverse # If the Game Switch for Invis is ON if $game_switches[invis_switch_id] # Set Invis for this Frame Update to True invis = true # Exit Loop break end end end # Stealth Default (Stealth is both Invis + Quiet) stealth = false # If this Sensor has a Specified Stealth Range if @stealth_range # Check each of Sensor's Stealth Game Switches (Reversee for Priority) for stealth_switch_id in @stealth_switches.reverse # If the Game Switch for Stealth is ON if $game_switches[stealth_switch_id] # Set Stealth for this Frame Update to True stealth = true # Exit Loop break end end end # This Sensor does not respond to Stealth Switches else # Dont use Stealth to Calculate invis = false stealth = false end # Get Range depending on state of See and Listen Self Switches range = (switch) ? @on_range : # If Listen Range if set (Listen Switch is ON) (listen_switch and @listen) ? @listen_range : # Elsif Self Switches are OFF, check for Stealth Ranges (stealth and @stealth_range) ? @stealth_range : # Elsif Self Switches Off, Stealth not set, check Invis Ranges (invis and @invis_range) ? @invis_range : # Elsif Conditions to use Light Range (@light_range and lit == 1) ? @light_range : # Elsif Conditions to use Dark Range (@dark_range and lit == 2) ? @dark_range : # Else is to just use the Default Range set with range=n @range # Return the Value of Range return range end #-------------------------------------------------------------------------- # * Update - Sensor Detection - Event_Sensor #-------------------------------------------------------------------------- def update # Don't update if Sensor is Disabled or Starting to trigger return if not @enabled or $game_map.events[@id].starting or not @target # If Event Setting prohibits Switch Change while Interpreter running # Since Sensors can be allowed to change Self Switch States while # the Interpreter is running, this method also checks for lock? to # see if that specific event is executing. Turning ON switches is # allowed, but turning OFF switches requires that Event is not "locked" # or executing its list of Commands. It is possible to force a Self # Switch to Off in an Event Command. If you do that, it is recommended # that you also set @hear_loc[2] to false so a new sound can be # recorded. You may also have to clear any Pathfinds manually if # your game uses them. Override Lock check by putting "lock" in your # Sensor_Config. Trust me, just leave it off # If Sensor is not allowed to change state while Interpreter is Running # (Not Allowed, or @inter = false, is the Default # Allow a Sensor to change states with Sensor_Config inter=true) if not @inter # If ANY Event is executing its Commands return if $game_system.map_interpreter.running? end # Self Switch switch_key = [$game_map.map_id, @id, @switch] # Get current Switch Value switch = $game_self_switches[switch_key] # Self Switch for Listen listen_key = [$game_map.map_id, @id, @listen_switch] # Get Current Switch Value for Listen listen_switch = $game_self_switches[listen_key] # Calculate Range on current state of Switches and Self Switches range = get_range # If we have a Sensor Distance greater than 0 and ... if range > 0 # Whether or not the Target is in Sensor Range depending on Self Switch in_sensor_range = in_sensor_range?(range) # Temp Variables if in_sensor_range can_see = can_see? can_hear = can_hear?(range) else can_see = false can_hear = false end # Used for Updating Last Seen Coordinates if in_sensor_range and can_see # Method used to allow Aliasing - Stores LAST Seen Location set_see_location elsif in_sensor_range and can_hear # Method use to allow Aliasing - Only stores FIRST Heard location set_hear_location end # Parent Event parent = $game_map.events[@id] # If See and Listen Switches use the Same Switch, just "See" if not @listen or @switch == @listen_switch # If In Sensor Range doesnt match the Self Switch if in_sensor_range and not switch and (can_see or can_hear) # Change the Self Switch for Event $game_self_switches[switch_key] = true # Stores (or updates if allowed) Map X, Y where first Seen set_see_location # Stores Sensor Events Loc when Triggered only Once until Reset set_my_see_location # Store Move Route Index, Event Page, and Move Route store_page_data # Check for Clearing Pathfinds check_pathfinders # Refresh the Event so Pages can Change $game_map.events[@id].refresh # If Page is the Same, then no Change if $game_map.events[@id].page == @last_page_data[1] # Just clear the stored data @last_page_data = [] end # Refresh the Graphics $game_map.need_refresh = true # Only disable if out of Sensor Range or Vision blocked by Wall elsif switch and (not in_sensor_range or not can_see) and (not parent.lock? or @lock) # Change the Self Switch $game_self_switches[switch_key] = false # Clears the Stored Seen Map X, Y Coordinates @see_loc[2] = false # Clears this Events XY Position where it was at when Triggered @my_see_loc[2] = false # Check for Clearing Pathfinds check_pathfinders # Refresh the Event so the Pages change and can fix Move Route $game_map.events[@id].refresh # Cheaper than Refreshing whole Map # Restoring the Move Route Index is handled in Refresh Alias end # Else means See and Listen Switches are Different so handle accordingly else # If Listen Switch is ON regardless if In Range or not, wait until # Event has finished moving a Non Repeating Move Route, then # turn off the Switch. Other conditions for Listen disable handled # differently. if listen_switch and not can_hear and not switch and not can_see and @listen_move_finish and parent.move_type == 3 and (not parent.lock? or @lock) and not parent.move_route_forcing and not parent.move_route.repeat and parent.move_route.list[parent.move_route_index].code == 0 # Change the Listen Self Switch $game_self_switches[listen_key] = false # Clears the Stored Seen and Heard Map X, Y Coordinates of Target @see_loc[2] = false @hear_loc[2] = false # Check for Clearing Pathfinds check_pathfinders # Refresh the Event so the Pages change and can fix Move Route # Restoring the Move Route Index is handled in Refresh $game_map.events[@id].refresh # if In Sensor Range elsif in_sensor_range # If can Hear but Cant See if can_hear and not can_see and not switch and not listen_switch # Change the Self Switch for Event $game_self_switches[listen_key] = true # Stores (or updates if allowed) Map X, Y where first Heard set_hear_location # Stores Sensor Events Loc when Triggered only Once until Reset set_my_hear_location # Store Move Route Index, Event Page, and Move Route store_page_data # Check for Clearing Pathfinds check_pathfinders # Refresh the Event so the Pages change and can fix move Routes $game_map.events[@id].refresh # If Page is the Same, then no Change if $game_map.events[@id].page == @last_page_data[1] # Just clear the stored data because Page didnt change # and Engine already handles restoring Move Routes @last_page_data = [] end # In Sensor Range and Can See and See Switch is Off elsif can_see and not switch # Change the Self Switch for Event $game_self_switches[switch_key] = true # Stores (or updates if allowed) Map X, Y where first Seen set_see_location # Stores Sensor Events Loc when Triggered only Once until Reset set_my_see_location # Check for Clearing Pathfinds check_pathfinders # If Page IS a Listen Page if listen_switch # Turn OFF the Listen Switch $game_self_switches[listen_key] = false # Hear Location to Writable @hear_loc[2] = false # Refresh here after Both Switches are Changed, updates Event Page $game_map.events[@id].refresh # NOTE: Data Not Stored because it was already stored by Hear # If Page is NOT a Listen Page else # Store Move Route Index, Event Page, and Move Route store_page_data # Refresh the Event so Pages can Change $game_map.events[@id].refresh # If Page is the Same, then no Change if $game_map.events[@id].page == @last_page_data[1] # Just clear the stored data because Page didnt change # and Engine already handles restoring Move Routes @last_page_data = [] end end # Refresh to change Pages - Cheaper than Refreshing Map $game_map.events[@id].refresh # Elsif In Sensor Range and Can't See but Switch is On elsif not can_see and switch and not listen_switch and (not parent.lock? or @lock) # Change the See Self Switch $game_self_switches[switch_key] = false # If Can Hear and Listen Switch is Off if can_hear # Change the Listen Self Switch $game_self_switches[listen_key] = true end # Check for Clearing Pathfinds check_pathfinders # Refresh to change Pages - Cheaper than Refreshing Map $game_map.events[@id].refresh end # Only disable if out of Sensor Range or Vision blocked by Wall elsif not in_sensor_range and (switch or listen_switch) and (not parent.lock? or @lock) # If Listen has been Triggered and Option if listen_switch and not switch and @listen_move_finish # If Non Repeating Custom Move Route not End of List (code 0) if parent.move_type == 3 and not parent.move_route.repeat and parent.move_route.list[parent.move_route_index].code != 0 # Prevent Disabling Switch return end end # Change the See Self Switch $game_self_switches[switch_key] = false # Change the Listen Self Switch $game_self_switches[listen_key] = false # Clears the Stored Seen and Heard Map X, Y Coordinates of Target @see_loc[2] = false @hear_loc[2] = false # Check for Clearing Pathfinds check_pathfinders # Refresh the Event so the Pages change and can fix Move Route # Restoring the Move Route Index is handled in Refresh $game_map.events[@id].refresh end end # If Sensor Range is 0 and Switch is On elsif (switch or listen_switch) and (not parent.lock? or @lock) # Turn the See Self Switch Off $game_self_switches[switch_key] = false $game_self_switches[listen_key] = false if @listen # Clears the Stored Seen and Heard Map X, Y Coordinates of Target @see_loc[2] = false @hear_loc[2] = false # Clears Stored Coordinates of THIS Events XY Loc when Target Triggered # Allows for Pathfinding back to original Triggered location @my_see_loc[2] = false @my_hear_loc[2] = false # Check for Clearing Pathfinds check_pathfinders # Refresh the Event so the Pages change - Cheaper than Refreshing Map $game_map.events[@id].refresh end # Reset See and Hear Locations to Writable if Switches are Off @see_loc[2] = false if not switch @hear_loc[2] = false if not listen_switch end #-------------------------------------------------------------------------- # * Check Pathfinders for Clearing - Event_Sensor # - May need to be Aliased #-------------------------------------------------------------------------- def check_pathfinders # If Sensor Option is to Clear Pathfinders if @clear_pathfinds # if Blizz Pathfinder if $lagless_path_finder and $lagless_path_finder >= 1.21 # Clear Blizz Pathfinding $game_map.events[@id].clear_path_target end # If ForeverZer0 Pathfinder with Heretic Additions if Interpreter::method_defined?(:clear_pathfind) # Clear the Parents Pathfinds $game_system.map_interpreter.clear_pathfind(@id) end end end #-------------------------------------------------------------------------- # * Store Page Data - Event_Sensor # - Alias this Method for other Condition Handling #-------------------------------------------------------------------------- def store_page_data # If this Sensors Setting is set to NOT restore Pages Move Route Index return if not @save_move_index or @last_page_data != [] # Self from Parent Object event = $game_map.events[@id] # Check for Non Existent Move Routes (Caused by other Scripts return if event.move_route.nil? # Stores Event Data in Array 0 - Move Route Index, 1 - Page, 2 Move Route @last_page_data[0] = event.move_route_index @last_page_data[1] = event.page @last_page_data[2] = event.move_route end #-------------------------------------------------------------------------- # * In Sensor Range? - Event_Sensor # # - Determines the Range of a Sensor based on Sensor Setting and any # Self Switches that are Enabled that affect Range. # ** Range, Listen Range, and On Range ** #-------------------------------------------------------------------------- def in_sensor_range?(range = nil) # Get Calculated Range if not set in Arg range = get_range if range.nil? # Target target = get_target # Parent Event Values x = $game_map.events[@id].x y = $game_map.events[@id].y # Total Distance from Event to Player return ((x - target.x).abs + (y - target.y).abs <= range) # --- CAUSE OF CRASH --- # # If your game Crashed and you ended up at this line due to the # game Crashing, you most likely tried to change the Sensor Event's Target # to an Event, and that Event doesn't exist. Its a problem with your # Sensor_Config where target=123 and Event ID 123 doesnt exist. Change # the Sensor's Sensor_Config to an Event ID that does exist to fix the # problem. Nothing wrong with the code, this was intentional so your # game functions as you expect it to. end # If Heretic's Loop Maps is installed if Game_Map.method_defined?(:map_loop_passable?) #-------------------------------------------------------------------------- # * In Sensor Range? - Event_Sensor # # - Determines the Range of a Sensor based on Sensor Setting and any # Self Switches that are Enabled that affect Range. # ** Range, Listen Range, and On Range ** # # Replacement Definition used with Heretic's Loop Maps #-------------------------------------------------------------------------- def in_sensor_range?(range = nil) # Get Calculated Range if not set in Arg range = get_range if range.nil? # Target target = get_target # Distance between Self and Target x_dist = $game_map.events[@id].sensor_distance_between_x(target.x) y_dist = $game_map.events[@id].sensor_distance_between_y(target.y) # Total Distance from Event to Player return (x_dist.abs + y_dist.abs <= range) # --- CAUSE OF CRASH --- # # If your game Crashed and you ended up at this line due to the # game Crashing, you most likely tried to change the Sensor Event's Target # to an Event, and that Event doesn't exist. Its a problem with your # Sensor_Config where target=123 and Event ID 123 doesnt exist. Change # the Sensor's Sensor_Config to an Event ID that does exist to fix the # problem. Nothing wrong with the code, this was intentional so your # game functions as you expect it to. end end #-------------------------------------------------------------------------- # * Set See Location - Event_Sensor # # - Created as a Method to allow you to Alias this with other Scripts to # allow adjusting the Logic of When and Where to store See Coords # Other Scripts may allow an Enemy Movement Pattern to move to the # Location stored in this Variable. # # - Always stores Seen Location Data when can be seen #-------------------------------------------------------------------------- def set_see_location # Store the Location of Target where initially observed @see_loc = [get_target.x, get_target.y, true] end #-------------------------------------------------------------------------- # * Set My See Location - Event_Sensor # # - This will record the Location the Sensor Event was at when it # was Triggered by the Target. This is useful for getting a # Sensor Event back to where it was expected to be when on # a Predefined Move Route Path. If the Sensor Event just moves # randomly about, this is useless, but it is still stored. # # - Between See and Hear, only ONE set of coordiantes is stored #-------------------------------------------------------------------------- def set_my_see_location if @my_see_loc[2] != true and @my_hear_loc[2] != true # Store the Sensor Events Map XY Coordinates when Trigered @my_see_loc = [$game_map.events[@id].x, $game_map.events[@id].y, true] end end #-------------------------------------------------------------------------- # * Set Hear Location (When an Enemy has heard a Movement) - Event_Sensor # # - Created as a Method to allow you to Alias this with other Scripts to # allow adjusting the Logic of When and Where to store See Coords # Other Scripts may allow an Enemy Movement Pattern to move to the # Location stored in this Variable. # # - See overrides Hear. When Not Seen, it will store only the First Heard # location but does NOT Update #-------------------------------------------------------------------------- def set_hear_location # If Location has NOT been Stored if @see_loc[2] != true and @hear_loc[2] != true # Store the Location of Target where initially observed @hear_loc = [get_target.x, get_target.y, true] end end #-------------------------------------------------------------------------- # * Set My Hear Location - Event_Sensor # # - This will record the Location the Sensor Event was at when it # was Triggered by the Target. This is useful for getting a # Sensor Event back to where it was expected to be when on # a Predefined Move Route Path. If the Sensor Event just moves # randomly about, this is useless, but it is still stored. # # - Between See and Hear, only ONE set of coordiantes is stored #-------------------------------------------------------------------------- def set_my_hear_location if @my_see_loc[2] != true and @my_hear_loc[2] != true # Store the Sensor Events Map XY Coordinates when Trigered @my_hear_loc = [$game_map.events[@id].x, $game_map.events[@id].y, true] end end # If Heretic's Loop Maps is installed if Game_Map.method_defined?(:map_loop_passable?) #------------------------------------------------------------------------ # * Set See Location - Event_Sensor # - Loop Maps Replacement Definition #------------------------------------------------------------------------ def set_see_location # Get the Target t = get_target # Check for Map Loops tx = ($game_map.loop_horizontal?) ? t.x % $game_map.width : t.x ty = ($game_map.loop_vertical?) ? t.y % $game_map.height : t.y # Store the Location of Target where initially observed @see_loc = [tx, ty, true] end #------------------------------------------------------------------------ # * Set My See Location - Event_Sensor # - Loop Maps Replacement Definition #------------------------------------------------------------------------ def set_my_see_location if @my_see_loc[2] != true and @my_hear_loc[2] != true # Sensor Event from Map e = $game_map.events[@id] # Check for Map Loops x = ($game_map.loop_horizontal?) ? e.x % $game_map.width : e.x y = ($game_map.loop_vertical?) ? e.y % $game_map.height : e.y # Store the Sensor Events Map XY Coordinates when Trigered @my_see_loc = [x, y, true] end end #------------------------------------------------------------------------ # * Set Hear Location (When an Enemy has heard a Movement) - Event_Sensor # - Loop Maps Replacement Definition #------------------------------------------------------------------------ def set_hear_location # If Location has NOT been Stored if @see_loc[2] != true and @hear_loc[2] != true # Get the Target t = get_target # Check for Map Loops tx = ($game_map.loop_horizontal?) ? t.x % $game_map.width : t.x ty = ($game_map.loop_vertical?) ? t.y % $game_map.height : t.y # Store the Location of Target where initially observed @hear_loc = [tx, ty, true] end end #------------------------------------------------------------------------ # * Set My Hear Location - Event_Sensor # - Loop Maps Replacement Definition #------------------------------------------------------------------------ def set_my_hear_location if @my_see_loc[2] != true and @my_hear_loc[2] != true # Sensor Event from Map e = $game_map.events[@id] # Check for Map Loops x = ($game_map.loop_horizontal?) ? e.x % $game_map.width : e.x y = ($game_map.loop_vertical?) ? e.y % $game_map.height : e.y # Store the Sensor Events Map XY Coordinates when Trigered @my_hear_loc = [x, y, true] end end end # End Loop Maps Definitions #-------------------------------------------------------------------------- # * Get Listen - Event_Sensor # - Listen is dependant on quite a few things. This command checks # the Conditions of Self Switches and any Game Stealth Switches # to return the proper value depending on the state of the # switches that are checked. #-------------------------------------------------------------------------- def get_listen # If System Setting and the Sensor responds to Stealth Switches if $game_system.sensor_stealth and @stealth # Quiet (Stealthed Target) Default quiet = false # If this Sensor has a specified Quiet (Stealth) Listen Level if @quiet_listen # Check each of the Sensor's Quiet Game Switches (Reverse for Priority) for quiet_switch_id in @quiet_switches.reverse # If the Game Switch for Invis is ON if $game_switches[quiet_switch_id] # Set Quiet for this Frame Update to True quiet = true # Exit Loop break end end end # Stealth Default (Stealth is both Invis + Quiet) stealth = false # If this Sensor has a Specified Stealth Range if @stealth_listen # Check each of Sensor's Stealth Game Switches (Reversee for Priority) for stealth_switch_id in @stealth_switches.reverse # If the Game Switch for Stealth is ON if $game_switches[stealth_switch_id] # Set Stealth for this Frame Update to True stealth = true # Exit Loop break end end end # This Sensor does not respond to Stealth Switches else # Dont use Stealth to Calculate quiet = false stealth = false end # Get Listen Level - If Target Stealthed and stealth Listen specified listen = (stealth and @stealth_listen) ? @stealth_listen : # Elsif Target is Quiet (Stealth) and Quiet Specified (quiet and @quiet_listen) ? @quiet_listen : # Else use the normal Listen Level @listen # Return the Value of Listen return listen end #-------------------------------------------------------------------------- # * Can Hear - Event_Sensor # range : Sensor's Range depending on Switches # # - Hearing is based on making Noise while you Move. The faster a # character Moves, the more Noise they will create. This does # not have limitations the way Can See does, like Walls. #-------------------------------------------------------------------------- def can_hear?(range = nil) # Cant Hear if not Enabled return false if not @listen # If Stealth is Enabled and not Override if $game_system.sensor_stealth and @stealth and not @quiet_listen and not @stealth_listen # Merge Arrays switch_ids = @quiet_switches + @stealth_switches # Check each Switch ID for Value for switch_id in switch_ids # If Game Switches (not a Self Switch) Quiet or Stealth Switch is ON if $game_switches[switch_id] == true # Quiet so can NOT Hear return false end end end # Get the Target as a Game Character target = get_target # If Target Move Speed is at or above the Listen Level if target.move_speed >= get_listen # Sensor Distance from Target is within Sensing Distance if in_sensor_range?(range) # If the Target is Moving (Calculated in case Redefined) if target.real_x != target.x * 128 or target.real_y != target.y * 128 # The Target can be Heard return true end end end # Target was not heard - Default return false end #-------------------------------------------------------------------------- # * Get View - Event_Sensor # - View is dependant on quite a few things. This command checks # the Conditions of Self Switches and any Game Stealth Switches # to return the proper value depending on the state of the # switches that are checked. #-------------------------------------------------------------------------- def get_view # Self Switch switch_key = [$game_map.map_id, @id, @switch] # Get current Switch Value switch = $game_self_switches[switch_key] # Self Switch for Listen listen_key = [$game_map.map_id, @id, @listen_switch] # Get Current Switch Value for Listen listen_switch = $game_self_switches[listen_key] # If System Setting and the Sensor responds to Stealth Switches if $game_system.sensor_stealth and @stealth # Invis Default invis = false # If this Sensor has a Specified Invis Range if @invis_view # Check each of the Sensor's Invis Game Switches (Reverse for Priority) for invis_switch_id in @invis_switches.reverse # If the Game Switch for Invis is ON if $game_switches[invis_switch_id] # Set Invis for this Frame Update to True invis = true # Exit Loop break end end end # Stealth Default (Stealth is both Invis + Quiet) stealth = false # If this Sensor has a Specified Stealth Range if @stealth_view # Check each of Sensor's Stealth Game Switches (Reversee for Priority) for stealth_switch_id in @stealth_switches.reverse # If the Game Switch for Stealth is ON if $game_switches[stealth_switch_id] # Set Stealth for this Frame Update to True stealth = true # Exit Loop break end end end # This Sensor does not respond to Stealth Switches else # Dont use Stealth to Calculate invis = false stealth = false end # Get View depending on state of See and Listen Self Switches view = (switch) ? @on_view : # If Listen Range if set (Listen Switch is ON) (listen_switch and @listen) ? @listen_view : # Elsif Self Switches are OFF, check for Stealth Views (stealth and @stealth_view) ? @stealth_view : # Elsif Self Switches Off, Stealth not set, check Invis Ranges (invis and @invis_view) ? @invis_view : @view # Else is to just use the Default View set with view=n # which if not set, just defaults to 0 which is View All # Return the Value of View return view end #-------------------------------------------------------------------------- # * Get X - Event_Sensor # - Returns X Coordinates of Event #-------------------------------------------------------------------------- def get_x(char) return char.x end #-------------------------------------------------------------------------- # * Get Y - Event_Sensor # - Returns Y Coordinates of Event #-------------------------------------------------------------------------- def get_y(char) return char.y end #-------------------------------------------------------------------------- # * Get Wall D (Direction) # - Determines which Direction the Sensor Target is facing # dx : Distance X # dy : Distance Y #-------------------------------------------------------------------------- def get_wall_d(dx, dy) # If coordinates are equal if dx == 0 and dy == 0 d = 0 # If horizontal distance is longer elsif dx.abs > dy.abs # Direction to Target dx > 0 ? d = 4 : d = 6 # If vertical distance is longer else # Direction to Target dy > 0 ? d = 8 : d = 2 end # Return value return d end # If Heretic's Loop Maps is installed if Game_Map.method_defined?(:map_loop_passable?) #------------------------------------------------------------------------ # * Get X - Event_Sensor # - Returns X Coordinates of Event #------------------------------------------------------------------------ def get_x(char) $game_map.loop_horizontal? ? char.x % $game_map.width : char.x end #------------------------------------------------------------------------ # * Get Y - Event_Sensor # - Returns Y Coordinates of Event #------------------------------------------------------------------------ def get_y(char) $game_map.loop_vertical? ? char.y % $game_map.height : char.y end #------------------------------------------------------------------------ # * Get Wall D (Direction) # - This Alias adjusts values for Looping Maps # dx : Distance X # dy : Distance Y #------------------------------------------------------------------------ alias loop_map_get_wall_d get_wall_d unless $@ def get_wall_d(dx, dy) if $game_map.loop_horizontal? && dx.abs > $game_map.width / 2 dx += (dx < 0) ? $game_map.width : -$game_map.width end if $game_map.loop_vertical? && dy.abs > $game_map.height / 2 dy += (dy < 0) ? $game_map.height : -$game_map.height end # Call Original with any adjusted values return loop_map_get_wall_d(dx,dy) end end # End Looping Maps Definitions #-------------------------------------------------------------------------- # * Decide if a Wall exists between self and Target # target : Game_Character (Player or Event) # # - Walls will impede the ability to view the Target. Walls are considered # any Tile that is NOT Passable, unless the Tile has a Terrain Tag # that self will ignore with the Not a Wall array. # # - Not Passable means Not Passable in all 4 Directions. Thus, certain # types of Graphics that allow some movement are treated as Passable. # # - Things like Water are not passable, but are not technically able to # impede the ability to be viewed. So Water is given a Terrain Tag # so that this Character will not treat Water as a Wall. Short bushes # or other types of graphics may be considered to also impede movement # but are Not considered to be Walls. This will include things like # Fences or Low Bushes. # # - tree - Flying Enemies can have their View obstructed when tree # is added to the Event CFG, and if the Terrain Tag for Tree # is set on your Tileset in the Database. # # - listen_tree=true/false - Setting for when Listen Switch is Active # # - on_tree=true/false - Flying Enemy can see through Tree when Self # Switch is On. If On Tree == True, can NOT see through Trees # when Self Switch is ON, if On Tree == False, CAN see through trees # when Self Switch is ON. # # - bush - Mice and Small Enemies can have their View obstructed when # the string is added to the CFG. Game Logic allows for both # and to obstruct the View, but it wont make any sense # in Real Life applications. # # - listen_bush - Setting for when Listen Switch is Active # # - on_bush=true/false - Same as On Tree - Uses Config for a Default # when Sensor_Config is not set. # # - on_wall=true - for Animals, on_wall=false for Humanoids # When a NPC sees you and approaches, Animals may be confused by the # target going behind a rock, but more intelligent enemies will # continue to pursue you. #-------------------------------------------------------------------------- def wall?(target) # Get current Switch Value switch = $game_self_switches[[$game_map.map_id, @id, @switch]] # Get Listen Switch Value listen_switch = $game_self_switches[[$game_map.map_id, @id, @listen_switch]] # Use Switch Value to determine if Wall obstructs Line of Sight wall = (switch) ? @on_wall : (listen_switch and @listen) ? @listen_wall : @wall # Use Switch Value to determine if Tree obstructs Line of Sight tree = (switch) ? @on_tree : (listen_switch and @listen) ? @listen_tree : @tree # Use Switch Value to determine if Tree obstructs Line of Sight bush = (switch) ? @on_bush : (listen_switch and @listen) ? @listen_bush : @bush # Default - See through Everything return false unless wall or tree or bush # Get Parent Event as E e = $game_map.events[@id] # Get the Target as a Game Character target = get_target # Get difference in Sensor and Target sx = $game_map.events[@id].sensor_distance_between_x(target.x) sy = $game_map.events[@id].sensor_distance_between_y(target.y) # Get the Shortest Direction to Target d = get_wall_d(sx, sy) # TX, TY true if Sensor X, Y match Target X, Y # -1 if Sensor less than Target tx = (get_x(e) == get_x(target)) ? true : sx > 0 ? -1 : 1 ty = (get_y(e) == get_y(target)) ? true : sy > 0 ? -1 : 1 # Map Coordinates to check map = $game_map.events[@id].map_xy_between(target) # For each Map Coordinate between self and Target for i in 0...map.size # Map XY Coordinates from map xy = map[i] # Get the Map Terrain Tag terrain_tag = $game_map.terrain_tag(xy[0],xy[1]) # True if View is Obstructed by Bush (Mice and Small Animals) return true if bush and $game_map.bush?(xy[0],xy[1]) # If this Characters View can be obstructed by a Tree (Flying Enemies) return true if tree and @tree_tags.include?(terrain_tag) # If a Terrain Tag is marked specifically as a Wall for this Character return true if wall and @wall_tags.include?(terrain_tag) # If the Map Tiles (NOT Events) are Not Passable if wall and not $game_map.sensor_viewable?(xy[0],xy[1],i,d,tx,ty,e,@id) # There is a Wall between self and Target unless Terrain Tag excludes return true unless @not_wall_tags.include?(terrain_tag) end end # No Walls Detected between Sensor (self) and Target return false end #-------------------------------------------------------------------------- # * Target Lit? - Event_Sensor # target : Game_Character (Player or Event) # NOTE - Requires Heretic's Dynamic Light Script (and DLL) # - Fature added in Version 1.1 # - When Dynamic Lighting is enabled and Sensor is Light Sensitive # it allows Targets (Player) to hide in Darkness # - Returns True if Lighting is Disabled or Not Available for proper # detection of Targets #-------------------------------------------------------------------------- def target_lit?(target, switch, listen_switch) # True if No Dynamic Lights are Implemented return true if not @light and not @listen_light and not @on_light # Illuminated if Dynamic Lights are Off return true if not $game_system.dynamic_lights # If Sensor is Light Sensitive if @light # Illuminated if Switch and cant sneak back into hiding in darkness return true if @on_light and switch # TRUE if Listening and Target NOT Illuminated return true if @listen_light and listen_switch # Set Value of Tolerance to 0 if not set tolerance = @light_tol ? @light_tol : 0 # Return the State of Target Illumination (ID, Tolerance) return target.in_light?(target.id, tolerance) end end #-------------------------------------------------------------------------- # * Can See? - Decide if Sensor can see Target # target : Game_Character (Player or Event) # # - Can See determined by how far self can see # - Can See is restricted by view (View is a Shape) # - Can See is Obstructed by Walls not flagged as Non Walls - Water #-------------------------------------------------------------------------- def can_see? # If Stealth is Enabled and not Override for Invis / Stealth # NOTE: Override adjusts the View allowing Stealth, done elsewhere if $game_system.sensor_stealth and @stealth and not @invis_view and not @stealth_view # Merge Arrays switch_ids = @invis_switches + @stealth_switches # Check each Switch ID for Value for switch_id in switch_ids # If Game Switches (not a Self Switch) Invis Switch is ON if $game_switches[switch_id] == true # Invis so can NOT See return false end end end # Get the Target as a Game Character target = get_target # Self Switch Key switch_key = [$game_map.map_id, @id, @switch] # Get current Switch Value switch = $game_self_switches[switch_key] # Listen Switch Key listen_key = [$game_map.map_id, @id, @switch] # Get current Switch Value listen_switch = $game_self_switches[listen_key] # Use Switch Value to determine if Wall obstructs view wall = (switch) ? @on_wall : (listen_switch and @listen)?@listen_wall:@wall # Use Switch Value to determine if Tree obstructs view tree = (switch) ? @on_tree : (listen_switch and @listen)?@listen_tree:@tree # Use Switch Value to determine if Bush obstructs view bush = (switch) ? @on_bush : (listen_switch and @listen)?@listen_bush:@bush # View determined by Self Switch and Stealth Switches view = get_view # Determine Illumination Target State (true when no lights) lit = (target_lit?(target, switch, listen_switch) or @light_range or @dark_range) # Can see if Full View or Target is right on top of Event return true if (view == 0 and not wall and not tree and not bush and lit) # If Self Switch is ON and no Visual Obstructions if (switch and !@on_wall and !@on_bush and !@on_tree and @on_view == 0) or (listen_switch and !@listen_wall and !@listen_bush and !@listen_tree and @listen_view == 0 and lit) # This Character can see its Target return true else # False if Lighting available and Target not Illuminated (other script) return false if not lit # Parent Event Variables x = get_x($game_map.events[@id]) y = get_y($game_map.events[@id]) d = $game_map.events[@id].direction # Distance between Self and Target x_dist = $game_map.events[@id].sensor_distance_between_x(target.x) y_dist = $game_map.events[@id].sensor_distance_between_y(target.y) # If standing at same Location as Target and No Walls obstructing View if x_dist == 0 and y_dist == 0 and not wall?(target) # Enable Switch return true end # If Can cee Everything and View is obstructed by Wall if view == 0 # Can See unless View of Target obstructed by Wall (inc Trees & Bushes) return true unless wall?(target) # 95% Can see everywhere except directly behind in a Straight Line elsif view == 1 if ((d != 2 and y_dist > 0) or (d != 8 and y_dist < 0) or (d != 6 and x_dist > 0) or (d != 4 and x_dist < 0)) # Can See unless View of Target obstructed by Wall return true unless wall?(target) end # 75% of Full View (Cant view small angle Behind) elsif view == 2 if (d == 2 and not (y_dist > 0 and y_dist.abs > x_dist.abs)) or (d == 8 and not (y_dist < 0 and y_dist.abs > x_dist.abs)) or (d == 4 and not (x_dist < 0 and x_dist.abs > y_dist.abs)) or (d == 6 and not (x_dist > 0 and x_dist.abs > y_dist.abs)) # Can See unless View of Target obstructed by Wall return true unless wall?(target) end # 62% of Full View (Cant view most of Behind) elsif view == 3 if (d == 2 and not (y_dist > 0 and y_dist.abs >= x_dist.abs)) or (d == 8 and not (y_dist < 0 and y_dist.abs >= x_dist.abs)) or (d == 4 and not (x_dist < 0 and x_dist.abs >= y_dist.abs)) or (d == 6 and not (x_dist > 0 and x_dist.abs >= y_dist.abs)) # Can See unless View of Target obstructed by Wall return true unless wall?(target) end # Peripheral (Can see Sides but not Behind at ALL) elsif view == 4 if (d == 8 and y_dist >= 0) or (d == 2 and y_dist <= 0) or (d == 4 and x_dist >= 0) or (d == 6 and x_dist <= 0) # Can See unless View of Target obstructed by Wall return true unless wall?(target) end # Peripheral - Everything in Front but Blind to Sides (wearing a Helmet) elsif view == 5 if (d == 2 and y_dist < 0) or (d == 4 and x_dist > 0) or (d == 6 and x_dist < 0) or (d == 8 and y_dist > 0) # Can See unless View of Target obstructed by Wall return true unless wall?(target) end # Only Sees Straight Ahead (Metal Gear Style) elsif view == 6 if (d == 8 and y_dist > 0 and y_dist.abs >= x_dist.abs) or (d == 2 and y_dist < 0 and y_dist.abs >= x_dist.abs) or (d == 4 and x_dist > 0 and x_dist.abs >= y_dist.abs) or (d == 6 and x_dist < 0 and x_dist.abs >= y_dist.abs) # Can See unless View of Target obstructed by Wall return true unless wall?(target) end # Only Sees Straight Ahead (More Limited than even Metal Gear Style) elsif view == 7 if (d == 8 and y_dist > 0 and y_dist.abs > x_dist.abs) or (d == 2 and y_dist < 0 and y_dist.abs > x_dist.abs) or (d == 4 and x_dist > 0 and x_dist.abs > y_dist.abs) or (d == 6 and x_dist < 0 and x_dist.abs > y_dist.abs) # Can See unless View of Target obstructed by Wall return true unless wall?(target) end # Only STRAIGHT LINE in front (Tripwire, Laser or Robot) elsif view == 8 if (d == 8 and y_dist > 0 and x == get_x(target)) or (d == 2 and y_dist < 0 and x == get_x(target)) or (d == 4 and x_dist > 0 and y == get_y(target)) or (d == 6 and x_dist < 0 and y == get_y(target)) # Can See unless View of Target obstructed by Wall return true unless wall?(target) end # BLIND - Use or this setting to temporarily stun or disable elsif view == 9 # The Blind can NOT See return false else # Use this if you need to Modify this script return alias_can_see?(target, switch_key, switch, x_diff, y_diff, view) end end # Default - Needed for Interpreter return false end #-------------------------------------------------------------------------- # * Alias of Can See # # - Create an Alias of this method if you want to have more conditions # that are not provided. I've already passed all the relevant # variables as arguments for you to use so you shouldnt have # to do any extra work to do any condition checking. # # If you want to add a View to a Sensor, there is a method in there # that you can alias as well. Aliasing the method in the Event_Sensor # class will help you have the View assigned to the Event when the # Event is created on the map. That will allow you to not have to # use any Autorun, Parallel, or Common Events to set up your Events # the way you want. In short, it saves you a ton of work. #-------------------------------------------------------------------------- def alias_can_see?(target, switch_key, switch, x_diff, y_diff, view) # Return False as a Default return false end end #============================================================================== # ** Game_System #============================================================================== class Game_System #-------------------------------------------------------------------------- # * Public Instance Variables #-------------------------------------------------------------------------- attr_accessor :sensor_stealth # Enable to allow Switches to Stealth Player #-------------------------------------------------------------------------- # * Game System Initialization #-------------------------------------------------------------------------- alias super_event_sensor_initialize initialize def initialize # Call Original or Other Aliases super_event_sensor_initialize # If Stealth by specified Game Switches is Enabled @sensor_stealth = Sensor_Config::SENSOR_STEALTH end end #============================================================================== # ** Game_Character #============================================================================== class Game_Character #-------------------------------------------------------------------------- # * Public Instance Variables #-------------------------------------------------------------------------- attr_accessor :move_speed # Movement Speed Exponent attr_accessor :move_type # Fixed, Random, Approach, Custom attr_accessor :move_route # Object - Characters Move Route attr_accessor :move_route_index # Int - Move Route Index attr_accessor :direction # Character Facing Direction attr_accessor :prelock_direction # Direction before Event Trigger attr_accessor :pre_suprise_direction # Direction facing before Suprised attr_accessor :original_move_route # Pathfinder Compatability #-------------------------------------------------------------------------- # * Sensor Distance Between X #-------------------------------------------------------------------------- def sensor_distance_between_x(x) @x - x end #-------------------------------------------------------------------------- # * Sensor Real Distance Between X #-------------------------------------------------------------------------- def sensor_real_distance_between_x(real_x) @real_x - real_x end #-------------------------------------------------------------------------- # * Sensor Distance Between Y #-------------------------------------------------------------------------- def sensor_distance_between_y(y) @y - y end #-------------------------------------------------------------------------- # * Sensor Real Distance Between Y #-------------------------------------------------------------------------- def sensor_real_distance_between_y(real_y) @real_y - real_y end # If Heretic's Loop Maps is installed if Game_Map.method_defined?(:map_loop_passable?) #------------------------------------------------------------------------ # * Sensor Distance Between X # - Loop Maps Replacement Definition #------------------------------------------------------------------------ def sensor_distance_between_x(x) xd = @x - x if $game_map.loop_horizontal? && xd.abs > $game_map.width / 2 xd += (xd < 0) ? $game_map.width : -$game_map.width end return xd end #------------------------------------------------------------------------ # * Sensor Real Distance Between X # - Loop Maps Replacement Definition #------------------------------------------------------------------------ def sensor_real_distance_between_x(real_x) xd = @real_x - real_x if $game_map.loop_horizontal? && xd.abs / 128 > $game_map.width / 2 xd += (xd < 0) ? $game_map.width * 128 : -$game_map.width * 128 end return xd end #------------------------------------------------------------------------ # * Sensor Distance Between Y # - Loop Maps Replacement Definition #------------------------------------------------------------------------ def sensor_distance_between_y(y) yd = @y - y if $game_map.loop_vertical? && yd.abs > $game_map.height / 2 yd += (yd < 0) ? $game_map.height : -$game_map.height end return yd end #------------------------------------------------------------------------ # * Sensor Real Distance Between Y # - Loop Maps Replacement Definition #------------------------------------------------------------------------ def sensor_real_distance_between_y(real_y) yd = @real_y - real_y if $game_map.loop_vertical? && yd.abs / 128 > $game_map.height / 2 yd += (yd < 0) ? $game_map.height * 128 : -$game_map.height * 128 end return yd end end #-------------------------------------------------------------------------- # * Map XY Between - Game_Character # target : game_character (player or event) # # This will return an Array of Map XY Coordinates between a Target # Character 1 is at Map Coordinates x=5 and y=7 # Character 2 is at Map Coordinates x=2 and y=7 # Array Returned will be all Map Coordinates at and between # return [[2,7],[3,7],[4,7],[5,7]] # X and Y are checked for 0 to prevent Division by Zero errors #-------------------------------------------------------------------------- def map_xy_between(target) # Distance between this Character (Sensor Event) and Target x = sensor_real_distance_between_x(target.real_x) * 1.0 y = sensor_real_distance_between_y(target.real_y) * 1.0 # Holds Map X and Y coordinates: [[1,2],[2,3],[3,4]] map=[] # Self Switch Key switch_key = [$game_map.map_id, @id, @sensor.switch] # Range can Vary if the Self Switch is On range = $game_self_switches[switch_key] ? @sensor.on_range : @sensor.range # If this Character and Target have Same X if x == 0 # Start at Tile Self is on offset = 0 # While the Distance does include each Map Tile on and between in Limit while y.abs >= offset.abs and range >= ((offset/128).round).abs # Add Map Coordinate to Result map << [@x, ((@real_y + offset)/128).round] # Increment for each Map Tile offset += (y < 0) ? 128 : -128 end # If this Character and Target have Same Y elsif y == 0 # Start at Tile Self is on offset = 0 # While the Distance does include each Map Tile on and between while x.abs >= offset.abs and range >= ((offset/128).round).abs # Add Map Coordinate to Result to Return map << [((@real_x + offset)/128).round, @y] # Increment for each Map Tile offset += (x < 0) ? 128 : -128 end # X and Y differ. We need Trig to determine each Map X Y Coordinate else # Use Larger Distance to calculate in Increments of 128 if x.abs > y.abs # Start at Tile Self is on x_offset = 0 # Tangent tan = x/y # Hypotenuse hypot = Math.hypot(128, 128 / tan) # Add Map Coords for each Map Tile On and Between Character and Target while x_offset.abs <= x.abs and range * 128 >= hypot.abs # Determine Length of Side using Trig y_offset = x_offset / tan # Generate Map Coordinates based on Real Positions map_x = (@real_x/128).round + (x_offset/128).round map_y = (@real_y/128).round + (y_offset/128).round # Add Array to Map of Stored Coordinates betwen Target map << [map_x, map_y] # Increments of 128 for One Tile for each Real Map Coordinate x_offset = (x < 0) ? x_offset + 128 : x_offset - 128 # New Hypotenuse for Limiting Total Tiles hypot = Math.hypot(x_offset, y_offset) end # Y distance is Larger than X Distance. Increment by 128. else # Start at Tile Self is on y_offset = 0 # Tangent tan = y/x # Hypotenuse hypot = Math.hypot(128, 128 / tan) # Add Map Coords for each Map Tile On and Between Character and Target while y_offset.abs <= y.abs and range*128 >= hypot.abs # Determine Length of Side using Trig x_offset = y_offset / tan # Generate Map Coordinates based on Real Positions map_x = (@real_x/128).round + (x_offset/128).round map_y = (@real_y/128).round + (y_offset/128).round # Add Array to Map of Stored Coordinates betwen Target map << [map_x, map_y] # Increments of 128 for Real Map Coordinates y_offset = (y < 0) ? y_offset + 128 : y_offset - 128 # New Hypotenuse for Limiting Total Tiles hypot = Math.hypot(x_offset, y_offset) end end end # Return any collected Map Coordinates between Event and Target return map end # If Heretic's Loop Maps is installed if Game_Map.method_defined?(:map_loop_passable?) #------------------------------------------------------------------------ # * Map XY Between - Game_Character # - Corrects Sensor Coordinates on Looping Maps #------------------------------------------------------------------------ alias loop_map_map_xy_between map_xy_between unless$@ def map_xy_between(target) # Call Original result = loop_map_map_xy_between(target) # If Map Loops if result and $game_map.loop? h = $game_map.loop_horizontal? v = $game_map.loop_vertical? # Correct the Coordinates for i in 0...result.size # Remainder the Results result[i][0] %= $game_map.width if h result[i][1] %= $game_map.height if v end end # Return Modified Results return result end end # End Loop Map Optional Definition #-------------------------------------------------------------------------- # * Move Type : Approach - Redefinition to allow Move Toward Target #-------------------------------------------------------------------------- def move_type_toward_player # Get difference in player coordinates sx = @x - $game_player.x sy = @y - $game_player.y # Get absolute value of difference abs_sx = sx > 0 ? sx : -sx abs_sy = sy > 0 ? sy : -sy # If separated by 20 or more tiles matching up horizontally and vertically if sx + sy >= 20 # Random move_random return end # Branch by random numbers 0-5 case rand(6) when 0..3 # Approach player # If Sensor and OPTION Enabled and Sensor Target isnt Default of Player if @sensor and @sensor.enabled and @sensor.approach and @sensor.get_target.is_a?(Game_Event) # Move Toward Sensor Target Event instead of Default Player move_toward_sensor_target else # Default move_toward_player end when 4 # random move_random when 5 # 1 step forward move_forward end end #-------------------------------------------------------------------------- # * Turn Toward Sound - Sensors ONLY!!! # - Sound used as Intermediary to enable See Triggers # - Hey, what was that Noise? Turn to look at it! #-------------------------------------------------------------------------- def turn_toward_sound # Invalid if not Sensor or Sound Loc return if not @sensor or not @sensor.enabled or @sensor.hear_loc[0] == nil # Get difference in sound coordinates sx = @x - @sensor.hear_loc[0] sy = @y - @sensor.hear_loc[1] # If coordinates are equal if sx == 0 and sy == 0 return end # If horizontal distance is longer if sx.abs > sy.abs # Turn to the right or left towards player sx > 0 ? turn_left : turn_right # If vertical distance is longer else # Turn up or down towards player sy > 0 ? turn_up : turn_down end end #-------------------------------------------------------------------------- # * Turn Away from Sound # - Useful for Children that become Frightened by a Sound # - Hey, what was that Noise? Mommy save me! #-------------------------------------------------------------------------- def turn_away_from_sound # Invalid if not Sensor or Sound Loc return if not @sensor or not @sensor.enabled or @sensor.hear_loc[0] == nil # Get difference in sound coordinates sx = @x - @sensor.hear_loc[0] sy = @y - @sensor.hear_loc[1] # If coordinates are equal if sx == 0 and sy == 0 return end # If horizontal distance is longer if sx.abs > sy.abs # Turn to the right or left away from player sx > 0 ? turn_right : turn_left # If vertical distance is longer else # Turn up or down away from player sy > 0 ? turn_down : turn_up end end #-------------------------------------------------------------------------- # * Move toward Sound # - Useful for "Dumb" Investigations #-------------------------------------------------------------------------- def move_toward_sound # Invalid if not Sensor return if not @sensor or not @sensor.enabled or @sensor.hear_loc[0] == nil # Get difference in Location Coordinates sx = @x - @sensor.hear_loc[0] sy = @y - @sensor.hear_loc[1] # If coordinates are equal if sx == 0 and sy == 0 return end # Get absolute value of difference abs_sx = sx.abs abs_sy = sy.abs # If horizontal and vertical distances are equal if abs_sx == abs_sy # Increase one of them randomly by 1 rand(2) == 0 ? abs_sx += 1 : abs_sy += 1 end # If horizontal distance is longer if abs_sx > abs_sy # Move towards player, prioritize left and right directions sx > 0 ? move_left : move_right if not moving? and sy != 0 sy > 0 ? move_up : move_down end # If vertical distance is longer else # Move towards player, prioritize up and down directions sy > 0 ? move_up : move_down if not moving? and sx != 0 sx > 0 ? move_left : move_right end end end #-------------------------------------------------------------------------- # * Move Away From Sound # - Frightened by a Sound #-------------------------------------------------------------------------- def move_away_from_sound # Invalid if not Sensor return if not @sensor or not @sensor.enabled or @sensor.hear_loc[0] == nil # Get difference in Location Coordinates sx = @x - @sensor.hear_loc[0] sy = @y - @sensor.hear_loc[1] # If coordinates are equal if sx == 0 and sy == 0 return end # Get absolute value of difference abs_sx = sx.abs abs_sy = sy.abs # If horizontal and vertical distances are equal if abs_sx == abs_sy # Increase one of them randomly by 1 rand(2) == 0 ? abs_sx += 1 : abs_sy += 1 end # If horizontal distance is longer if abs_sx > abs_sy # Move away from player, prioritize left and right directions sx > 0 ? move_right : move_left if not moving? and sy != 0 sy > 0 ? move_down : move_up end # If vertical distance is longer else # Move away from player, prioritize up and down directions sy > 0 ? move_down : move_up if not moving? and sx != 0 sx > 0 ? move_right : move_left end end end #-------------------------------------------------------------------------- # * Turn Toward Sensor Target # - Manually Call this for proper Turn Animations when Target != Player #-------------------------------------------------------------------------- def turn_toward_sensor_target # Invalid if not Sensor return if not @sensor or not @sensor.enabled or not @sensor.target? # Get Sensors Target as a Game Character target = @sensor.get_target # Get difference in sound coordinates sx = @x - target.x sy = @y - target.y # If coordinates are equal if sx == 0 and sy == 0 return end # If horizontal distance is longer if sx.abs > sy.abs # Turn to the right or left towards player sx > 0 ? turn_left : turn_right # If vertical distance is longer else # Turn up or down towards player sy > 0 ? turn_up : turn_down end end #-------------------------------------------------------------------------- # * Turn Away from Sensor Target # - Useful for Children that become Frightened by a Sound # - Hey, what was that Noise? Mommy save me! #-------------------------------------------------------------------------- def turn_away_from_sensor_target # Invalid if not Sensor return if not @sensor or not @sensor.enabled or not @sensor.target? # Get Sensors Target as a Game Character target = @sensor.get_target # Get difference in sound coordinates sx = @x - target.x sy = @y - target.y # If coordinates are equal if sx == 0 and sy == 0 return end # If horizontal distance is longer if sx.abs > sy.abs # Turn to the right or left away from player sx > 0 ? turn_right : turn_left # If vertical distance is longer else # Turn up or down away from player sy > 0 ? turn_down : turn_up end end #-------------------------------------------------------------------------- # * Move toward Sensor Target # - This causes Approach for Sensors to Move Toward their Target # if the Target is an Event instead of the Deafult of Player. #-------------------------------------------------------------------------- def move_toward_sensor_target # Invalid if not Sensor return if not @sensor or not @sensor.enabled or not @sensor.target? # Get Sensor Target as a Game Character target = @sensor.get_target # If Self is Target return if $game_map.events[@id] == target.id # Get difference in player coordinates sx = @x - target.x sy = @y - target.y # If coordinates are equal if sx == 0 and sy == 0 return end # Get absolute value of difference abs_sx = sx.abs abs_sy = sy.abs # If horizontal and vertical distances are equal if abs_sx == abs_sy # Increase one of them randomly by 1 rand(2) == 0 ? abs_sx += 1 : abs_sy += 1 end # If horizontal distance is longer if abs_sx > abs_sy # Move towards player, prioritize left and right directions sx > 0 ? move_left : move_right if not moving? and sy != 0 sy > 0 ? move_up : move_down end # If vertical distance is longer else # Move towards player, prioritize up and down directions sy > 0 ? move_up : move_down if not moving? and sx != 0 sx > 0 ? move_left : move_right end end end #-------------------------------------------------------------------------- # * Move Away From Sensor Target # - This can be used to allow a Running Away type of Behavior #-------------------------------------------------------------------------- def move_away_from_sensor_target # Invalid if not Sensor return if not @sensor or not @sensor.enabled or not @sensor.target? # Get Sensor Target as a Game Character target = @sensor.get_target # If Self is Target return if $game_map.events[@id] == target.id # Get difference in player coordinates sx = @x - target.x sy = @y - target.y # If coordinates are equal if sx == 0 and sy == 0 return end # Get absolute value of difference abs_sx = sx.abs abs_sy = sy.abs # If horizontal and vertical distances are equal if abs_sx == abs_sy # Increase one of them randomly by 1 rand(2) == 0 ? abs_sx += 1 : abs_sy += 1 end # If horizontal distance is longer if abs_sx > abs_sy # Move away from player, prioritize left and right directions sx > 0 ? move_right : move_left if not moving? and sy != 0 sy > 0 ? move_down : move_up end # If vertical distance is longer else # Move away from player, prioritize up and down directions sy > 0 ? move_down : move_up if not moving? and sx != 0 sx > 0 ? move_right : move_left end end end #------------------------------------------------------------------------ # * Unlock - Alias - Resets Event Turning Toward Player on Trigger # # - Resets ONLY if a Suprise Attack Function has been called #------------------------------------------------------------------------ alias suprise_attack_unlock unlock def unlock if @id != 0 and not $game_temp.suprise_attack # Reset the Suprise Direction @pre_suprise_direction = 0 end # Call Original or Other Aliases suprise_attack_unlock end end #============================================================================== # ** Game_Map #------------------------------------------------------------------------------ # This class handles the map. It includes scrolling and passable determining # functions. Refer to "$game_map" for the instance of this class. #============================================================================== class Game_Map #-------------------------------------------------------------------------- # * Determine if Tile is Viewable # x : x-coordinate # y : y-coordinate # i : Iteration Counter # max : Maximum Number of Iterations # d : Parent Sensor's Direction # id : Used during development to only have one sensor display results # # - Pretty much a Duplicate of passable? without Events. # - I pulled out Event Checking because it is done every frame and causes # very bad framerates, even with Optimizations. I may change this # at a later time, but it will require an additional script to speed # up Event Iterations. Yay, more stuff to write. # # ---- Crash Course in BITWISE Operations (Binary) ---- # # Every computer Byte has 8 Bits. 0000 0000 Bits can be 1 or 0 only. # # For Passability Bits, 4 of the 8 Bits are used. 0000 1111 # # The Four Bits represent Four Directions. Each one of the four Bits # represents one of four Directions. # # 0000 0001 = 1 in Decimal - Down Obstacle Bit is set # 0000 0010 = 2 in Decimal - Left Obstacle Bit is set # 0000 0100 = 4 in Decimal - Right Obstacle Bit is set # 0000 1000 = 8 in Decimal - Up Obstacle Bit is set # # Do you see how each Byte has only one On bit for each direction? # # 0000 1111 = 15 Obstacle Bit set in All Directions, or 0x0f which is 15 # or 1 + 2 + 4 + 8 = 15 in Decimal. # # bit variable is Direction changed to a single binary bit # # Change direction (0,2,4,6,8,10) to obstacle bit (0,1,2,4,8,0) # bit = (1 << (d / 2 - 1)) & 0x0f # # 0000 0100 = Character Right, 4 in Decimal after conversion # although Right 6 on Numeric Keypad before conversion # It needs to be converted so the new direction has only # one ON bit per direction. # # BITWISE operator & compares two bytes and returns matching bits # compare both bytes, return bits that are on in both. If a bit # is not ON in both bytes, 0 is used for that bit. # # 1111 0100 & 0000 1111 = 0000 0100 or obstacle bit set so not passable #-------------------------------------------------------------------------- def sensor_viewable?(x, y, int, d, tx, ty, sensor, id) # If coordinates given are outside of the map unless valid?(x, y) # Not Viewable unless on the Map return false end # If Sensor and Target on same Tile return true if tx == true and ty == true # Four Bits Needed: bit, o_bit, a_bit, and a_o_bit # One Bit for each direction checked # Change direction (0,2,4,6,8,10) to obstacle bit (0,1,2,4,8,0) bit = (1 << (d / 2 - 1)) & 0x0f # Get Opposite Direction o_d = d == 2 ? 8 : d == 4 ? 6 : d == 6 ? 4 : d == 8 ? 2 : 0 # Change Opposite Direction (0,2,4,6,8,10) to obstacle bit (0,1,2,4,8,0) o_bit = (1 << (o_d / 2 - 1)) & 0x0f # Get Alternate Direction if tx == true or ty == true a_d = 0 else # Alternate Direction (Up or Down flip to Left or Right) a_d = (d == 2 or d == 8) ? (tx == 1 ? 6 : 4) : (d == 4 or d == 6) ? (ty == 1 ? 2 : 8) : 0 end # Change Alternate Direction (0,2,4,6,8,10) to obstacle bit (0,1,2,4,8,0) a_bit = (1 << (a_d / 2 - 1)) & 0x0f # Alternate Opposite Direction a_o_d = a_d == 2 ? 8 : a_d == 4 ? 6 : a_d == 6 ? 4 : a_d == 8 ? 2 : 0 # Change Alternate Opposite Dir (0,2,4,6,8,10) to obstacle bit (0,1,2,4,8,0) a_o_bit = (1 << (a_o_d / 2 - 1)) & 0x0f # Loop searches in order from top of layer for i in [2, 1, 0] # Get tile ID tile_id = data[x, y, i] # Tile ID acquistion failure if tile_id == nil # Not Viewable return false # If obstacle bit is set in all directions - bits: ^v<> elsif @passages[tile_id] & 0x0f == 0x0f # Not Viewable return false # In between Tiles Diagonal - Target is Up 1, Right 4: bits >^, <^, < < < < elsif (tx == true or ty == true) and ((int == 0 and @passages[tile_id] & bit != 0) or (int != 0 and @passages[tile_id] & o_bit != 0)) # Not Viewable when any of the bits checked match an obstacle bit return false # If priorities other than that are 0 elsif @priorities[tile_id] == 0 # Viewable return true end end # Viewable return true end end class Game_Event < Game_Character #-------------------------------------------------------------------------- # * Public Instance Variables #-------------------------------------------------------------------------- attr_accessor :page # Needed for Page Changes to Event attr_accessor :sensor # ALL Sensor Related Settings (Object) attr_accessor :trigger # Action, Player Event Touch, Auto, etc #-------------------------------------------------------------------------- # * Initialize Sensor Event #-------------------------------------------------------------------------- alias super_event_sensor_initialize initialize def initialize(map_id, event) # Call Original or Other Aliases super_event_sensor_initialize(map_id, event) # Creates Sensor Events and Reads Configuration Options check_event_sensor_comments(event) # Used for making Events not turn toward you when Triggered @pre_suprise_direction = 0 end #-------------------------------------------------------------------------- # * Check Sensor Event Comments # event : Game_Event # # - Create a Sensor Event by making a COMMENT on Event Page #1 and # putting "Sensor_Config" in on the FIRST LINE, then any # Sensor Options on any Comment Line BELOW Sensor_Config # - Sensors DO NOT WORK without giving a Range, which is NOT assigned # a Default Value. # # Sensor_Config # range=4 view=4 # # * Do NOT put ANYTHING ELSE on the line that says "Sensor_Config" # # - This will NOT work: # Sensor_Config range=4 # # * It doesnt work because range=4 MUST be on a Line BELOW "Sensor_Config" # # - Do NOT put SPACES Config Options # * range=4 - Ok! # * range = 4 - BAD! # # - NOT LIMITED TO ONE COMMENT #-------------------------------------------------------------------------- def check_event_sensor_comments(event) # Sensor Config String sensor_cfg = '' # Check the LEFT Page for Activation regardless if Page is Active or not page = @event.pages[0] # Increment i = 0 # Check Comments for Sensor Config while i <= Sensor_Config::SENSOR_READ_LINES # Each Command from the Page List of Event Commands command = page.list[i] # If an Event Command exists if command # Command Code code = command.code # Command Parameters from Multi Line Comment ONLY p = (code == 408) ? command.parameters[0] : '' # if Command is the First Line of a Comment (command.code 108) # and Regex Sensor_CFG in the Comment on the First Line # /A is Regex - Start of string, the " *" allows for accidental spaces if command.parameters[0] =~ /\ASensor_Config *$/ read_cfg = true elsif read_cfg and code != 408 read_cfg = false end # If Read Sensor Config and Multi Line Comment (command.code 408) if read_cfg and code == 408 # Get Current Sensor Config Options from this Comment Line # NOTE: Do NOT remove either set of Space Characters # Causes "on_view=" to be read as the "view=" setting w/regex sensor_cfg += ' ' + p + ' ' # While still reading Multi Line Comments # NOTE: As long as the Multi Line Comment starts before Line 10 # then ALL of the Sensor_Config Options will be read in that comment while read_cfg # If Next Command is a Multi Line Comment if page.list[i + 1] and page.list[i + 1].code == 408 # Advance the Index i += 1 # Next Sensor_Config parameters p = page.list[i].parameters[0] # Add the Line to the the Sensor Config Options from each Line # NOTE: Do NOT remove either set of Space Characters sensor_cfg += ' ' + p + ' ' else # Set read_cfg to false to exit this loop read_cfg = false end end end end # Increment i += 1 end # If Sensor Config is not Empty String, Initialize the Sensor Settings if sensor_cfg != '' # Create the Sensor Object in the Event @sensor = Event_Sensor.new(@event, @direction, sensor_cfg) end end #-------------------------------------------------------------------------- # * Event Update - Aliased #-------------------------------------------------------------------------- alias sensor_original_event_update update def update # Update Sensor Object for Target Detection @sensor.update if @sensor and @sensor.enabled # Call Original or Other Aliases sensor_original_event_update end #-------------------------------------------------------------------------- # * Sensor Can See? # # - Returns NIL if there is no Sensor Object. Only Sensors can See. # - Returns FALSE if the Event is "Unable to see" the Target # - Two different returns to allow for additional error checking # nil and false, use === to typecheck #-------------------------------------------------------------------------- def sensor_can_see? # If not Sensor return nil if not @sensor # Check the Sensor Object for Visibility return @sensor.can_see? end #-------------------------------------------------------------------------- # * Sensor Can Hear? # # - Returns NIL if there is no Sensor Object. Only Sensors can Hear. # - Returns FALSE if the Event is "Unable to hear" the Target # - Two different returns to allow for additional error checking # - Always FALSE if Target is not Moving, may need a Parallel to monitor. #-------------------------------------------------------------------------- def sensor_can_hear? # If not Sensor return nil if not @sensor or @erased or not @sensor.enabled # Get the Sensor's Range range = @sensor.get_range # Check the Sensor Object for Sound Detection (Movement + Target Speed) return @sensor.can_hear?(range) end #-------------------------------------------------------------------------- # * Sensor Heard? # # - hear_loc[2] remains True while Target is still in Sensor Range. It # is set to False when Target exists the Sensor Range and Listen Switch # is set to False to allow for New Sound Location Recording. This can # also be useful in this call to check if a Sensor has an Active # Recorded Sound that is preventing New Sounds from being recorded. # It works DIFFERENT than sensor_can_hear? which will only return true # at the moment a Sensor "Hears" a Target and is only for One Frame. #-------------------------------------------------------------------------- def sensor_heard? # If Sound is Triggered and still in Sensor Range - True, otherwise nil return @sensor.hear_loc[2] end #-------------------------------------------------------------------------- # * Refresh - Alias - Restores Move Route Index on Refresh #-------------------------------------------------------------------------- alias sensor_page_reset_move_route_index_refresh refresh def refresh # Call Original or Other Aliases sensor_page_reset_move_route_index_refresh # If Sensor Event and it is Enabled if @sensor and @sensor.enabled # If option to Save / Restore Move Route Index enabled for This Event if @sensor.save_move_index # If Page is Saved and Page changing to matches the Saved Page # AND the Move Route matches the Saved Move Route at time of Switch On if @sensor.last_page_data != [] and # Not an Empty Array @sensor.last_page_data[1] == @page and # Same Page @sensor.last_page_data[2] == @move_route # Same Move Route # Then we can Reset the Move Route Index @move_route_index = @sensor.last_page_data[0] # Clear Saved Page Data - Prevents Movement Bugs @sensor.last_page_data = [] # NOTE: This is very useful. But only works if the Event has NOT # been moved off of its Autonomous Movement Path. You can also # use a Pathfinding Script to return to the next Target. # Both have Limitations. end end end end #-------------------------------------------------------------------------- # * Only create Aliases Once - Fixes an F12 Bug in case F12 Fix not used # # - Since not everyone knows about this, I try very hard to thoroughly # test my scripts and do extra work to prevent any potential problems # that others using my scripts may cause. If I did not do this, it # would require additional Scripts, which just decreases compatability. # I believe scripts should work as intended and expected at release # to the best of a scripters ability. This being no exception, I make # it work by fixing as many issues as I can. #-------------------------------------------------------------------------- unless self.method_defined?('suprise_attack_lock') #------------------------------------------------------------------------ # * Lock - Alias - Fixes Event Turning Toward Player on Trigger # # - ONLY Storess SENSOR Events enabled with Option per Event. # - This just stores an Events Direction at the Time of Trigger. # - Retains Value across Event Pages # - Clears Value when Event is done #------------------------------------------------------------------------ alias suprise_attack_lock lock def lock # If a Pre Suprise Direction has not been stored yet if @sensor and @sensor.enabled and @sensor.suprise_no_turn and @pre_suprise_direction == 0 # Store the Prelock Direction as the Pre Suprise Direction @pre_suprise_direction = @direction end # Call Original or Other Aliases suprise_attack_lock end end #============================================================================ # Conditional Definition for Heretic's Caterpillar - Compatability #============================================================================ if defined?(Game_Caterpillar) #-------------------------------------------------------------------------- # * Match Coordinates? - Game_Event # - Returns True on Matching Coordinates # - Defined by multiple scripts, this is a Backup if method does not exist #-------------------------------------------------------------------------- unless self.method_defined?(:mp_match_coordinates?) def mp_match_coordinates?(x, y, tx, ty) x == tx && y == ty end end #-------------------------------------------------------------------------- # * Touch Event Starting Determinant - Game_Event # # This allows a Monster Event to trigger by touching a Cat Actor # or a Cat Follower, not just the Player. #-------------------------------------------------------------------------- alias event_trigger_touch_cat_actors check_event_trigger_touch def check_event_trigger_touch(x, y) # Run Original or Other Aliases event_trigger_touch_cat_actors(x, y) # If a Touch Event and Caterpillar is Active and not Paused if @trigger == 2 and $game_switches[Game_Caterpillar::CATERPILLAR_ACTIVE_SWITCH] and not $game_switches[Game_Caterpillar::CATERPILLAR_PAUSE_SWITCH] # Check each Cat Actor if it is has been Touched by this Touch Event for actor in $game_system.caterpillar.actors # If Matching Coordinates if mp_match_coordinates?(x, y, actor.x, actor.y) # If starting determinant other than jumping is front event if not jumping? and not over_trigger? and not @starting # Begin executing List of Event Commands start end end end end end end end #============================================================================== # ** Interpreter #============================================================================== class Interpreter #-------------------------------------------------------------------------- # * Suprise Attack from Game Map from an Event # # NOTE: This should ONLY be called when Battle is GUARANTEED. # # NOTE: ONLY Works with Sensor Events to prevent Conflicts. # # I don't reset the Variable. That is handled in the Battle System. # # That also means that if you dont use the Battle System, do NOT # call this command as it will screw up the Direction any of your # Events are facing. # # This should ONLY be used with my Rewrite of the XRXS BATTLE SYSTEM # or Any other Battle Systems that support Suprise Attacks. Any # other Battle Systems that are altered to support this script # should reset the variable $game_temp.suprise_attack to NIL. # # As each Battle System may work a bit differently, it is up to their # Authors as how to best implement a Suprise Attack as to what kinds # of effects it will have in Battle. # # In my Rewrite of the XRXS Battle System 1.03, it is intended to Max Out # the Suprisers CP which provides "Free Attacks" on the Losers. Other # Battle Systems may handle this differently. Refer to the docmentation # of those Battle Systems as to what the effects of a Suprise Attack # will be. #-------------------------------------------------------------------------- def suprise_attack # Only on the Game Map return unless $scene.is_a?(Scene_Map) # Event e = $game_map.events[@event_id] # Only on the Game Map for Sensors return unless e.sensor and e.sensor.enabled # Enemy 'T'arget (Player by Default unless specified otherwise) t = e.sensor.get_target # If Player already Suprised the Enemy if $game_temp.suprise_attack == 1 # Reset the Enemy Direction so it is facing Away from Player e.direction = e.pre_suprise_direction end # This is triggered multiple times to create One Battle - Retain Values return unless not $game_temp.suprise_attack # If Player Suprised the Enemy by attacking from the Back if (t.y < e.y and e.prelock_direction == 2 and t.direction == 2) or (t.y > e.y and e.prelock_direction == 8 and t.direction == 8) or (t.x < e.x and e.prelock_direction == 6 and t.direction == 6) or (t.x > e.x and e.prelock_direction == 4 and t.direction == 4) # Suprise Attack Possible - Agility checked in Battle Setup $game_temp.suprise_attack = 1 e.direction = e.pre_suprise_direction # If Enemy Suprised the Player by attacking from the Back elsif (t.y > e.y and e.prelock_direction == 2 and t.direction == 2) or (t.y < e.y and e.prelock_direction == 8 and t.direction == 8) or (t.x > e.x and e.prelock_direction == 6 and t.direction == 6) or (t.x < e.x and e.prelock_direction == 4 and t.direction == 4) # Suprise Attack Possible - Agility checked in Battle Setup $game_temp.suprise_attack = 2 end end #-------------------------------------------------------------------------- # * Sensor Can See? # # - Used for Script Calls and Conditional Event Branching # - Returns NIL if not a Sensor and False if unable to see #-------------------------------------------------------------------------- def sensor_can_see? # Returns the Value of can_see? from the Event return $game_map.events[@event_id].sensor_can_see? end #-------------------------------------------------------------------------- # * Sensor Can Hear? # # - Used for Script Calls and Conditional Event Branching # - Returns NIL if not a Sensor and False if unable to hear # - Best used as a Parallel because this only returns true for # one Frame. #-------------------------------------------------------------------------- def sensor_can_hear? # Returns the Value of can_hear? from the Event return $game_map.events[@event_id].sensor_can_hear? end #-------------------------------------------------------------------------- # * Sensor Heard? # # - Used for Script Calls and Conditional Event Branching # - Returns TRUE if Sensor has Sound Coordinates recorded # - Returns FALSE or NIL if Sensor is "Listening" for new Sound # coordinates to be Recorded. Coordinates are stored in hear_loc[0,1] #-------------------------------------------------------------------------- def sensor_heard? # If Sound is Triggered and still in Sensor Range - True, otherwise nil return $game_map.events[@event_id].sensor.hear_loc[2] end #-------------------------------------------------------------------------- # * Sensor Target = New Target # # - Used for changing an Event's Target # - Usage: sensor_target(New Target) # ** New Target MUST be an Event or Player Object # # - This will ONLY assign a New Target for THIS event. You can change # the Target for Another Event by accessing that Event's Sensor Object # directly. $game_map.events[event_id].sensor.target = New Target # # - The alternative way will still provide some Error Checking for # the New Targets Class Types to prevent Fatal Errors. # # Since the Script Box has Line Wrap problems, you'll probably need # to use a variable to get everything to fit without Line Wrapping. # # Script in Event Commands: # # s = $game_map.events[event_id].sensor # target = $game_map.events[other_event_id] # s.target = target #-------------------------------------------------------------------------- def sensor_target(new_target) # If not a Sensor Event if not $game_map.events[@event_id].sensor and $DEBUG # Explain why the script failed print "Warning: sensor_target is unable to change Targets because\n", "the Event, ", @event_id, " is NOT a Sensor Event!\n", "To make a Sensor Event, you MUST make a COMMENT", "Sensor_Config into that Comment on Page 1.\n", "Sensor_Config does NOT have to be at the Top of the list." else # Changes Target for Event with Error Checking in target=(target) def $game_map.events[@event_id].sensor.target = new_target end end #-------------------------------------------------------------------------- # * Respawn # # - This will place a Sensor Event back to its Initial Location # that you placed the Event at in the Map Editor. # # - You can change its Location by accessing @sensor.start_x and y # from a Script Call. # # I tried to set this up so all you have to do is COPY and PASTE # Events without too much editing to any of them. That way you # dont have to come in and specify a Location for each event # every time you want a new enemy. For direction and Move Routes, # it may speed things along to BUILD YOUR OWN TEMPLATES. # # I hope this speeds things up for you. #-------------------------------------------------------------------------- def respawn_enemy event = $game_map.events[@event_id] if event.sensor and event.sensor.enabled # Reset to Starting Coordinates event.moveto(event.sensor.start_x, event.sensor.start_y) event.direction = event.sensor.start_d # Clear other Stored Variables relevant to One Instance of Page Changes event.sensor.last_page_data = [] event.sensor.see_loc = [] event.sensor.hear_loc = [] # Clears Stored Coordinates of THIS Events XY Loc when Triggered event.sensor.my_see_loc = [] event.sensor.my_hear_loc = [] end # Needed for Interpreter return true end end #============================================================================== # ** Scene_Load #============================================================================== class Scene_Load < Scene_File #-------------------------------------------------------------------------- # * Read Save Data - Scene_Load # file : file object for reading (opened) # - Reassigns New Instance of Player on Game Load #-------------------------------------------------------------------------- alias super_event_sensor_read_save_data read_save_data unless $@ def read_save_data(file) # Call Original or other Aliases super_event_sensor_read_save_data(file) # Update all Sensor Target Characters for event in $game_map.events.values # If Event is a Sensor if event.sensor != nil # If Player if event.sensor.target?.is_a?(Game_Player) # Assign the New Instance of Player as Target event.sensor.target = $game_player end end end end end #------------------------------------------------------------------------- # Class Scene Battle #------------------------------------------------------------------------- class Scene_Battle #-------------------------------------------------------------------------- # * Battle Ends # result : results (0:win 1:lose 2:escape) #-------------------------------------------------------------------------- alias super_event_sensor_suprise_attack_battle_end battle_end def battle_end(result) # Call Original or Other Aliases super_event_sensor_suprise_attack_battle_end(result) # Set Suprise Attack to Nil if not handled by Battle System $game_temp.suprise_attack = nil end end #============================================================================ # ** Game_Temp #============================================================================ class Game_Temp #------------------------------------------------------------------------ # * Public Instance Variables #------------------------------------------------------------------------ attr_accessor :suprise_attack # When Triggered from Game Map #------------------------------------------------------------------------ # * Object Initialization #------------------------------------------------------------------------ alias super_event_sensor_suprise_attack_initialize initialize def initialize # Call Original Method or Other Aliases - Is that name long enough? super_event_sensor_suprise_attack_initialize # New Property for Suprise Attack if not already set. @suprise_attack |= nil end end