#============================================================================== # # HERETIC'S DYNAMIC LIGHTS [XP] # Version - 1.02 # Tuesday, December 25th, 2018 # #============================================================================== # # ----- Special Thanks ----- # # I need to give Special Thanks to Blizzard and KK20 on Chaos-Project.com for # helping me make a DLL file to rotate the Lights! Also, Xilef on HBgames.org # for also helping get rid of debug dependancies! Thanks everyone! # # ----- Features ----- # # - Very Fast Light and Shadow System # - Fully Dynamic Lights (sorry, no realtime shadows) # - Detect if a Character is being Illuminated with "in_light?(id)" # - Built In Day and Night System # - Pre Formatted Time in a Game Variable to display with \v[n] - 3:52 PM # - Separate Indoor and Outdoor Settings (with a Game Switch) # - Easy to configure Event Lights with Comments # - Lights can be customized to Flicker either Size or Opacity # - Smooth Transitions between Day and Night # - Events and Lights can have different Opacity during Day and Night # - Built In Game Clock or Sun / Moon Graphic (or both) # - Highly Customizable, Length of Day, Color of Day, Night and Indoors, etc. # - Lights can be Rotated # - Shadows can prevent Lights for rooms and tops of buildings for logic # - Spotlights point at characters and follow their movements # - Pendulum Lights swing at the end of Pendulums # - Standalone Script, should work fine with the SDK and Non SDK # - Tested with XP Ace on Chaos-Project.com and it seems to work # - Gluten, Peanut, and Lactose Free # # # ===== CHANGE LOG ===== # # Version 1.01 - Friday, December 8th, 2017 # - Unreleased, minor bugfix for Screen Shake Alignment with Lights # # Version 1.02 - Tuesday, December 25th, 2018 # - Fixed a minor bug with Hue Rotation not working as a standalone script. # - Added Comments detailing new compatability features with Super Event Sensor # > Super Event Sensor update to Version 1.11 with Light Sensitivity Features # > Sensors are now hindered by darkness # > See Super Event Sensor Version 1.11 for more information on usage # # # ----- COMMENT CONFIGURATION OPTIONS (QUICK REFERENCE) ----- # # Apply each of the following Comments to an Event for corresponding Effects: # # NOTE: Use a \light[File.png] Comment before any other Light related Comments. # # NOTE: The Comments used for configuration need to be in the Top 20 Lines of # the List of Event Commands on each Page. If needed, you can change # how many Event Commands are scanned in Light_Config. Anything below # a \light[] Comment can be in ANY order. # # The * Character means Optional Parameter (not required) # The ** Character means Requires Rotate, Zoom, and Pendulums script # # # \light[folder/file.png] # Creates a Light and uses File as Source # \light_match_direction # Rotates Light to Direction of Character # \light_up[File1.png] # Light Image when Character faces Up # \light_down[File2.png] # Light Image when Character faces Down # \light_left[File3.png] # Light Image when Character faces Left # \light_right[File4.png] # Light Image when Character faces Right # \light_opacity[0 to 255] # Opacity of Light Image # \light_hue[0 to 360] # Hue of Light Image when Color is used # \light_hue_speed[N] # Speed by which to Rotate the Hue # \light_zoom_xy[2.0, 2.0] # Size XY Multiplier (Defaults are 1.0) # \light_width[N] # Specify Width in Pixels, precise control # \light_height[N] # Specify Height in Pixels, precise control # \light_offset_xy[-16, 32] # Position Lightsource from Character # \light_flicker_opacity[L, H, D] # Opacity [Low, High, and Duration] # \light_flicker_size[S, B, D] # Zoom Size X&Y [Small, Big, and Duration] # \light_flicker_size_x[S, B, D] # Zoom X Size [Small, Big, and Duration] # \light_flicker_size_y[S, B, D] # Zoom Y Size [Small, Big, and Duration] # \light_disabled # Disables Light on Creation # \light_no_jump # Lights do not track with Jumps or Floats # \light_shadow[N*] # nil, 0, 1, or 2 - Shadow Type* -see notes # \light_shadow_match_opacity # Auto Matches Shadow Opacity to Shadowmap # \light_after_shadow # Renders Light after Shadows are drawn # \light_cutout[Alt Char File*] # Makes Character "Fullbright"* -see notes # \light_daynight_opacity[D, N, D] # Light Opacity [Day, Night, Duration* ] # \light_event_opacity[D, N, D] # Event Opacity [Day, Night, Duration* ] # \light_angle[45] # Specify Angle of Rotation (needs DLL) # \light_match_angle # Match Angle of Rotated Character** # \light_spotlight[Offset, Max] # Make Spotlight [Vertical Adj, Max Zoom]** # \light_pendulum[Y Offset*] # Tracks a Pendulum on X Position* # \light_pendulum_y # Tracks a Pendulum on Y Position # \light_pendulum_opacity # Fades Opacity to 0 as Rotated to 90 Deg # \light_pendulum_zoom[Zoom] # Increases Zoom as Rotated to 90 Degrees # \light_lightning_opacity[N] # Opacity of Light during Lightning Flash # \lightning_event_opacity[N] # Opacity of Event during Lightning Flash # # *** These Comments require Rotate, Zoom, Pendulum script *** # # \pendulum # Character becomes a Pendulum** # \pendulum_length[Length] # Used for Physics Speed of Oscillation** # \rotate_target[character id] # Point Event at Character** # \rotate_angle[Angle] # Use to make Pendulums Swing** # \rotate_center[N] # 0 for Top, 1 for Middle, 2 for Bottom** # \rotate_target_dist[Max_Dist] # Max Dist of 6 Tiles from Spotlight** # \rotate_target_min[Angle] # Minimum Angle of Character Rotation** # \rotate_target_max[Angle] # Maximum Angle of Character Rotation** # # # There are a LOT of Comment Options you can use. Any Comments that start # with "\light_" is specific to this script. Comments that do not start # with "\light_" are features provided by Rotate, Zoom, and Pendulum script # except for \lightning_event_opacity[N] because that feature does not require # that an Event is a Light to use. # # ----- IMPORTANT ----- # # Do NOT use Variables as Page Conditions! # # If you check the Formatted Time Variable, your game will CRASH! Don't do it! # # If you check the Game Clock (in seconds), your Event Page will not change # as you expect it to. This is due to both Performance and Functionality # issues caused by checking the Game Clock Variable. Don't do it! # # "Heretic's Unlimited Event Page Conditions" is strongly recommended to be # used with this Script as it overcomes the limitation of not checking Game # Variables related to Time, and is available on Chaos-Project.com # # To use Time as a Page Condition, which allows you to use multiple lines: # # - @>Comment: Condition: time = $game_variables[75] # : time > 25200 and time < 61200 # # The example above, when used with Unlimited Page Conditions, will cause # that Event's Page to become Active when Time is between those two values. # # # ----- Installation ------ # # Place below the SDK if you use it, or below Scene Debug and above Main. # # Place below Heretic's Looping Maps if used. # # Place below Heretic's Vehicles if used. # # Place below Heretic's Caterpillar 2.0+ if used. # # STRONGLY Recommended to use with Heretic's Unlimited Event Page Conditions. # # Recommended to use with Heretic's Rotate, Zoom, and Pendulum script. # # When used with the scripts mentioned above, place this script BELOW. It will # run when placed above it, but the order of processing is important and may # cause some visual glitches. # # This script requires TransformBitmap.dll for any Light Rotations. It should # be in the same folder as your Game.exe files. # # COPY the SunMoon.png from the Pictures folder to your game, or change it in # the Light_Config to your own custom graphic. # # This script puts all of the Graphics to use as Light Sources in its own # folder. Place all Light Graphics in the Graphics/Lights folder. You may # have to create this folder. You can specify Subfolders in the Lights folder # if you have a lot of Lights and need better organization. # # # ----- Instructions ----- # # Heretic's Dynamic Lights is easy to use! To turn an Event into a Light, just # add a comment that says \light[] and that's all there is to it! # # - Comment: \light[] - Creates a Light Source # # Next, set the Time to Night by setting Game Variable 75 to 0 for Midnight # and you now have a Shadowmap that your Light can cut through! # # Of course, there are a ton of other options! Lights use Image Files. All # of the Image Files reside in their own Folder: Graphics/Lights and are best # made from an Image Type that supports an Alpha Channel like .png graphics. # # NOTE: Light Graphics without an Alpha Channel will not display as expected. # # # *** IMPORTANT *** # # Comment Configurations for Lights do NOT need to be on the very first line. # They do need to be within the first 20 Lines. Many scripts use Comments to # do special things, and this allows for a high degree of compatability when # used with those other scripts. # # You should always put your \light[file] Comments above ALL other Comments # that set ANY other Options for your Light. Each Light Option needs to use # its own Comment, so you can't do Multi Line Configs. # # Example: Comment: \some_config_for_another_script # Comment: \light[Light1.png] # Comment: \light_option_1[param1, param2, param3] # Comment: \light_option_2[param1, param2, param3] # # # You can specify an Image File to use for your Light Source between the two # brace characters. If no Image File is specified, then Default_Light_Image # is used. You can also specify subfolders as well. # # - Comment: \light[LightSoft.png] - Specify a File for Light Source # - Comment: \light[Map05/LightWindow.png] - Specify a Folder and a File # # # Events use Pages and you can have DIFFERENT LIGHTS on DIFFERENT PAGES! This # allows you to "Turn On" and "Turn Off" Lights! This can be easily achieved # by using Set Self Switch A to on and having a Page with Self Switch A as a # Page Condition. # # You can match a single Light to the Direction of a Character! Simply add a # a Comment to your Light. # # - Comment: \light_match_direction # # The above Comment doesn't require [] characters because it uses the Graphic # that is assigned in the \light[File.png] Comment. The default Direction is # down, and is used as a Reference Point. Thus, with a Graphic where the # Light points Down, when that Character (Player or Event) turns to the Right # the script will handle rotating that Graphic for you. # # To enable this on the Player, you need to run a Script: # # - Script: l = $game_player.light # l.match_direction = true # # # Using the \light_match_direction Option may not look appropriate for certain # types of Graphics. Thus, you may need to set different Graphics for each # Direction a Character can face. # # - Comment: \light_up[File1.png] # - Comment: \light_down[File2.png] # - Comment: \light_left[File3.png] # - Comment: \light_right[File4.png] # # These Comments are useful when your Light Image does not rotate properly. To # set these options on the Player, you need to run Scripts: # # - Script: l = $game_player.light # l.up = "File1.png" # l.down = "File2.png" # l.left = "File3.png" # l.right = "File4.png" # # The File Names have no restrictions so you can name them what ever you want # and even put them in Subfolders if you feel the need. # # - Comment: \light_up[My Folder/Lantern_Up.png] # - Script: l = $game_map.events[41] # l.down = "My Folder/Lantern_Down.png" # # # You can specify the Opacity of your Light Source! It expects a Number within # the range of 0 to 255 and allows for Floats such as 112.5 for very precise # control (needed for Transitions) of Opacity. # # - Comment: \light_opacity[128] # # NOTE: This does not affect the Opacity of the Event Graphic. # # # # You can set the Hue of a Light with some Color in the Image File. # # - Comment: \light_hue[180] # # NOTE: Hue uses Degrees so it needs to be a number between 0 and 360 Degrees. # # You can also automatically rotate the Hue of a Light with a Comment: # # - Comment: \light_hue_speed[Number] # # # # You can Zoom an Image also in order to make a small light appear much bigger # or smaller in order to suit your mapping needs. The Comment needs both the # X and Y Zoom to be specified. This allows very precise control of the Size # of your Light Image without making you create separate Images for each Light # Source in your game. # # - Comment: \light_zoom_xy[X Multiplier, Y Multiplier] # - Comment: \light_zoom_xy[2.0, 2.0] # # The example above will double the Size of the Image of your Light Source. # # # # You can precisely position where your Light Source will render in relation to # your Event by using an Offset XY. # # - Comment: \light_offset_xy[X Offset, Y Offset] # - Comment: \light_offset_xy[-16, 32] # # The example above will set the Center of your Light Source to 16 Pixels to # the Left of the Bottom Center of your Event and 32 Pixels Down from the # Bottom Center of the Event. This is needed because you don't have very # precise control when positioning an Event when they move around. # # # # You can precisely control the Width and Height of a Light Source with # the following comments: # # - Comment: \light_width[Width in Pixels] # - Comment: \light_width[256] # - Comment: \light_height[Height in Pixels] # - Comment: \light_height[256] # # The above examples show you how to make a light source exactly 256 x 256. It # is very useful if you have a light you need to have very precise control over # the size without trying to figure out Zoom Multipliers. It is most useful # when you have a Shadow with \light_shadow[2] explained later. # # # # You can "Flicker" both the Opacity and the Size of a Light Source. Both look # very similar in game, but both were included for reasons that will be stated # later in this documentation. When Opacity is set to 0, the Light will not # be visible. When the Light is set to 255, the Light is fully visible and # displays depending on the Alpha Channel of the Image and Shadowmap. # # - Comment: \light_flicker_opacity[Min, Max, Duration] # - Comment: \light_flicker_opacity[192, 255, 20] # # The example above will gradually transition back and forth between an Opacity # of 192 and 255 over 20 frames, giving you a smooth transition between the two # different Opacities that you specify. # # # # You can make your Light Source "Flicker" in Size to create more believable # illumination provided by logical Light Sources such as Flames or Candles. # # - Comment: \light_flicker_size[Min, Max, Duration] # - Comment: \light_flicker_size[1.0, 2.0, 20] # # The example above will go back and forth between a Zoom of 1.0 to 2.0 taking # the specified value of 20 Frames for each Transition. This affects both the # X and Y Sizes at the same time. # # # You can "Flicker" the X and Y Size completely seperate of each other in order # to create a "Dancing Light" where the shape of your Light Source is not quite # a constant or perfectly symetrical shape. # # - Comment: \light_flicker_size_x[Min, Max, Duration] # - Comment: \light_flicker_size_y[Min, Max, Duration] # - Comment: \light_flicker_size_x[1.0, 1.25, 8] # - Comment: \light_flicker_size_y[1.0, 1.25, 7] # # The examples above can be used at the same time! This is intentionally done # so that your X and Y Size do not always line up perfectly. Here, both Lights # will go back and forth between a Size of 1.0 which is the default size for # all graphics, and slightly larger 1.25, and the X Size will transition over # 8 frames and the Y Size will transition over 7 frames. This will cause both # of the Sizes to start off at the same size, then only be the same size every # 56 Frames (8 * 7) which creats a nice "Flicker" effect for logical Light # Sources such as Campfires or Candles. # # When a Light is created, the default Zoom values will be used, then will # transition to your first values. Since you can use Pages and have different # Light effects on each page, you may want to set the initial Zoom of the Light # by using a \light_zoom_xy[1.25, 1.25] Comment. This will prevent Lights that # "grow" when they are turned on. # # # # When used with the Day / Night System, you can have different Opacities for # both the Light Source and Event Graphics. This allows you to create nice # looking Windows in Houses and Buildings. The smooth transition between Day # and Night will also smoothly transition your Light Sources as well. # # - Comment: \light_daynight_opacity[Day Opacity, Night Opacity, Duration* ] # # NOTE: Duration is Optional and can be left blank. When specified, the Value # used will override how long a Day / Night Transition takes. This will # let you make Candles appear quickly but not instantaneously. # # - Comment: \light_daynight_opacity[255, 0] # # The above example is very useful for making a Window a logical Light Source. # During the Day, the Opacity of the Light Image will be at full visibility # at 255 and will not be visible at Night, with a very smooth Transition as # Day fades to Night and Night fades to Day. # # - Comment: \light_daynight_opacity[0, 255, 20] # # The above example would be useful for a Candle that can be seen at Night but # not during the Day. It also uses the Duration Option as the 3rd Parameter to # turn on automatically at Night, but taking 20 Frames for a Transition from # Day to Night to appear, and Night to Day to disappear. # # NOTE: All Light Sources with the above Comments will be affected regardless # if your Game Switch is set to Indoor or Outdoor. # # Lights that Flicker in Opacity can NOT use the \light_daynight_opacity[] as # it causes a conflict in determining the Opacity of the Light. This is why # you can flicker both Opacity and Size as they both look so similar that you # may not be able to tell the difference. When \light_daynight_opacity[] is # used, if you want a Flicker effect, use Flicker Size instead. # # # # The Opacity of Events can also be set to automatically change with Day and # Night Transitions. This is useful for displaying the Graphic of an Event # that you want to be visible at a certain time and not visible at another. # # - Comment: \light_event_opacity[Day Opacity, Night Opacity, Duration* ] # - Comment: \light_event_opacity[255, 0] # # The above example might be useful if you wanted to change the color of your # Windows during the day and just use Tileset Graphics at Night. The 3rd # Parameter to specify the Duration is not set in this example so the Opacity # of the Event Graphic (not the Light Source in this case) so the Illuminated # Window will fade in as a Night to Day Transition occurs (Dawn) and will fade # out as a Day to Night Transition occurs (Dusk) to create a convincing and # properly illuminated enviornment in your game. # # - Comment: \light_event_opacity[0, 192, 20] # # The example above here could be useful as a Campfire, where the Flames of the # fire are only visible at night. This would be best used by also only showing # the Light Source at Night as well. The Comment will cause the Graphic of the # Event to appear over 20 Frames instead of a full Dawn or Dusk Transition that # could take much longer. # # # You can ROTATE your Light Sources also! KK20 and Blizzard on chaos-project # helped me out, so special thanks to them! To Rotate a Light Source, you # need to set the Angle of the Light: # # - $game_map.events[12].light.angle = 45 # # NOTE: This requires that TransformBitmap.dll is available, but does not # require that my Rotate, Zoom, and Pendulum script is available. # # You can use a Parallel Event to control the angles, or if you want something # a bit fancier, you can use a Comment so the Light matches the Angle of the # character (Player or Event) as they turn! # # - Comment: \light_match_angle # # NOTE: This feature requires both TransformBitmap.dll and Rotate, Zoom, and # Pendulum script are available. # # The example above works when you combine this script with Heretic's Rotate, # Zoom, and Pendulums script. That script allows you to Rotate Events and # even have them Point at the Player as they walk by! It is a full script # and has many features not covered by the documentation here. # # # You can also use Rotation to match the Direction of a Character! # # - Comment: \light_match_direction # # This will cause your Light to turn toward the direction your character is # set to face. When creating Light Graphics, keep in mind to make them point # DOWN in your Image Editor and the script will handle the rest. # # # # ===== TIME ===== # # The game world only gets dark at night. You can control the Flow of Time by # turning Game Switch 74 (Default) to ON. As time progresses, the Shadow that # covers your screen will fade out until only Lights with Color are visible. # # To control the current Time, use Game Variable 75, which is used to remember # the number of Seconds since Midnight. Thus, if you want to set the Time to # something like 7AM, you need to multiply 7 by the number of seconds in an # hour. So 7AM is 7 * 3600 = 25200 as the value you would set to the variable # # You can also control how fast Time will flow by setting Minutes Per Day in # either the Light_Config section or with a Script Call. # # - Script: $game_system.minutes_per_day = 10 # # The above example will make it so a full Game Day takes 10 Minutes in the # real world. You need to know this so that you understand how the value # of the data in Game Variable 75 is processed by the script. # # ===== ADVANCING TIME ===== # # This script also remembers how many times that Day / Night cycles have taken # place. This is stored in Total Days. To check the Total Days since the # start of the game, run a Script: # # - Script: $game_system.total_days # # NOTE: You can NOT change the value of Total Days, only read it. # # Total Days may be of use to you if you wish to label your Days. I did not # build in any form of Calendar System into this script, however, you may be # able to use Total Days to at least determine the Day of the Week, such as # Monday, Tuesday, etc. To do this, just get the Remainder of Total Days # divided by 7. Use the % Operator to get a Remainder. So lets say that you # want 0 to be Sunday. Get the Total Days, then something like "day = td % 7" # which will give you a number between 0 and 6 for 7 total days including 0. # # If you have any intent of using Total Days in your Game, you also need to # know that when you change the Time by setting Game Variable 75, you may # also increase the Total Days. Since Time can not flow Backward, if the # current time is greater than the time you set, then Total Days is increased # by 1. That means if the time is currently 6PM and you set the time to 5PM # by changing Game Variable 75 (which is done by setting a value of 17 * 3600 # or 61200 for 5PM) then Total Days is increased by 1. # # I've also tried to take into accomodation that Resting at an Inn is expected # to take time. It would be normal to set the Time to something like 7AM for # your game while resting at an Inn. In the event that there is insufficient # time for the party to rest, then Total Days are also increased by 1. If you # go to bed at 4AM (like me), and try to wake up at 7AM (not like me), then you # will feel exhausted during the day. Since Inns are expected to fully recover # the Party, Total Days are advanced by 1 if they try to go to bed at 4AM and # expect to wake up at 5AM. # # This is an Option in the Light_Config section of this script, under the title # of Min_Inn_Time, which is in Hours. I set the default value to 6 Hours for # a minimum time to fully rest, and be functional throughout the day. You can # set this to 0 if you have cases where you allow the Player to wait and do # not want to increase Total Days. It may be equally useful to only change # the value of Minimum Inn Time to a number above 0 when giving your players # a chance to save at an Inn, if time is adjusted at Inn type events. Total # Days will still be increased by 1 at Midnight regardless of the value you # set Minimum Inn Time to. # # The Light_Config sets the Default Values for New Games. Once you start a # game, then values are stored in $game_system, using the same name. If you # change a value in Light_Config and load a saved game, then the default value # that is set in Light_Config won't be used because the value is already set # in $game_system. # # To change the value of Min Inn Time after a New Game has been started, you # just need to access the same name (in Lower Case) in $game_system with # a script call: # # - Script: $game_system.min_inn_time = 8 # * Set the Minimum Time to Rest at an Inn to 8 Hours, or increases Total Days # # Recap, change Light_Config for New Games and change $game_system to change # during gameplay. # # ===== INDOOR / OUTDOOR LIGHTING ===== # # This script uses two different Colors for your Shadowmap. The Outdoor Color # is used when the Indoor Switch is set to OFF and Indoor Color is used when # the Switch is set to ON. By default, the ID of this Game Switch is 75. # # Color is not the only difference between Indoor and Outdoor Lighting. The # Outdoor Color will fade between the Outdoor Day Color to Outdoor Night Color # as day and night cycles occur. The Indoor Color will remain the same as day # and night transitions take place. This is why you were given so many Options # as you will need logical Light Sources such as windows, doors, fire places, # glowing orbs, etc to illuminate interior enviornments, and why you can also # give your Players a Flaming Torch or Flashlight. # # With certain Comments, your Lights can react to Lightning during Storms and # day and night cycles. Lightning and Daynight Lights will respond the same # whether your Indoor Switch is ON or OFF. # # When you move the Player between Indoor and Outdoor Maps, change the Switch # immediately before transferring the Player. You can use Fade Effects if # you desire. If you transition the Player between two Indoor Rooms and need # to change the Color of the Indoor Shadow, set the Color before transferring # the Player. Again, you can use Fade Effects when transferring the Player. # # To change the Indoor Shadow Color, you need to run a script: # # - Script: s = $game_system # : color = Color.new(64, 64, 64, 128) # : set_indoor_color(color) # # The Color command is Case Sensitive and needs to use a Capital "C" as the # first letter. The values are Red Green Blue and Alpha. Alpha is used to # determine how transparent the Shadowmap is. Although it is an Optional # parameter when creating a Color, get used to using it as it is practially a # required parameter when working with shadows. Not all shadows should be # displayed as a solid Pitch Black. When Alpha is 255, which is Default when # you create a Color and do not specify the Alpha value, you create a solid # color that will obscure everything from the players field of view. When you # set the value of your Alpha parameter to 0, then the Color is not visible at # all. This is what I used for the Shadow Day Color. I set the colors of the # day and night shadows to be different so they could fade to a blueish color # at night and more orangish during the day to allow for a a transition that # doesnt involve simply changing the Alpha value, thus, you get warm looking # dawns and sunsets that cast a semi orange glow instead of a blue hue. # # - Script: set_indoor_color(color) # - Script: set_day_color(color) # - Script: set_night_color(color) # # # # # ===== LIGHT DETECTION ===== # # You can also check if a Character is being Illuminated by any Light Sources! # # NOTE: This works ONLY at Night or Indoors! # # To check if a Character is being Illuminated by any Lights, then you will # need to run a Script! # # - Script: in_light?(arg*, tolerance*) # * arg : ID of Character (use 0 for Player or 1+ for Events) or a Character # * Tolerance : Number (0 to 255) - Causes less sensitivity, try 30 as base # # You can call "in_light?()" from either a "Script..." from the List of Event # Commands, a Conditional Branch -> Script, or a Move Route -> Script. # # The Optional Argument of "arg" can be left blank, in which case, the Event # running the script will check itself. Arg can use either Event IDs or # even Game_Characters. You can use either 0 or $game_player to check if # the Player character is being illuminated. # # Examples: # # - Conditional Branch: Script: in_light?($game_player) # * Returns true if Player is being illuminated # # - Conditional Branch: Script: in_light?(12) # * Returns true if Event 12 is being Illuminated # # - Move Route: Script: start if in_light?($game_player) # * Script will start to execute if Player is Illuminated # # An additional argument of Tolerance is also provided in case you need the # Light Detection to be a bit less sensitive. Tolerance should be a number # between 0 and 255. Dont be surprised if you set it to 255 and never see # your script call return true, so use moderation. A good starting point # is probably around 25 or 30, but it also depends on the Colors you set # up for your game. # # Example: # - Conditional Branch: Script in_light?($game_player, 20) # * Returns True if Player is In Light, with a bit less sensitivity # # NOTE: If you use the second argument of Tolerance, you MUST set an Arg! # In the event you want the Event to check itself, either put in the # Event's ID as your first parameter, or use nil so it will again default # to checking itself. # # Example: # - Conditional Branch: Script in_light?(nil, 10) # * Returns True if this Event is In Light, with a bit less sensitivity # # # There are a few things you should know about how this Light Detection call # works. # # You will get nil as the result if the Target (specified in arg* is not on the # Screen. You'll also get a nil return in Battle or if you have Dynamic Lights # turned off. # # This script is quite simple. The script checks TWO PIXELS at the center of # the Character's Sprite. If the Color of that Pixel is less than or equal to # the Color of the Shadowmap at Night, or at any time when the Indoor Switch is # set to ON, then the character is NOT Illuminated, in which case, a FALSE type # is returned. # # You may wish to check this by also using a Type Check with the === Operator. # # - Move Route: Script: result = start if in_light?(0) === false # * This checks if the Player is detected as "not in light" and not failed # # Detecting if the Player is Illuminated, even in its simplicity, can add an # element of Stealth to your Gameplay! # # ===== LIGHT DETECTION IN SUPER EVENT SENSOR ===== # # I updated my Super Event Sensor to Version 1.11 for compatability with the # Light Detection feature available in this script. # # * NEW Sensor_Config Options # > light=true # > listen_light=true # > on_light=true # > light_tol=123 # > light_range=4 # > dark_range=2 # # Additional documentation for Light and Dark is available in the Super Event # Sensor Script. Basically, to make a Sensor Light Sensitive, add a Light # parameter in the Sensor_Config with "light=true" (no quotes). This will make # a Sensor unable to detect the Target (typically Player) AT ALL UNLESS the # Target is illuminated! However, this adds a bit too much ability to sneak # about and removes all challenge to players. If you don't want the Target # to be completely invisible to your Sensors, add a "light_range=5" or a # "dark_range=2" to the Sensor_Config. Light Range ONLY triggers and is used # in place of the default "range=4", and is NOT USED when a Sensor is in either # a Listen State or an ON State. # # Sensors that are intended to simulate Intelligent Enemies have 3 States, a # default "Range", followed by a "Listen Range" used when they have heard # a target BUT unable to make Line of Sight (such as hiding behind objects or # other impassable terrain), and finally an "ON" State, which is used when # the Enemy has SEEN the Target. Think of it like this. Default Guard is just # doing his rounds not paying much attention. The Guard hears something, so it # pays more attention to what is going on around it, thus, the Sensor is more # sensitive and the Target can be seen at a greater distance, and a final ON # State where they are looking farther away than the Default or Listen State. # # When incorporating Light Sensitivity into your gameplay mechanics, both of # the Light and Dark Ranges ONLY affect the Default State, but DO NOT affect # either the Listen or ON States. Thus, think of it like this. Guard is doing # a standard patrol at night. Range is usually 4, meaning the Sensor will be # triggered at 4 tiles away. Light Range will OVERRIDE the Range=4, thus, the # Range will be 5 with "light_range=5" because Light makes things show up a bit # easir. The Dark Range works the exact opposite. Darkness HIDES characters # but does NOT completely obsure them. Thus, the DARK RANGE should be LESS # than the Default Range. When the Target is in Darkness, then the Dark Range # will be used instead of the Default Range. This allows you to create Sensors # that are Less Sensitive when the Target is shrouded in darkness, such as in # a Cave or At Night. # # Listen Light is a bit of a confusing term. This refers to the Listen State # of the Sensor. When listen_light=true, the Sensor will remain in its Listen # State even if the Target is not illuminated, so they will retain their # "heightened awareness". The same thing applies for "on_light=true". Once # a Sensor has been triggered, it no longer matters if the Target is being # illuminated or not, the Sensor will remain active until the Target leaves # the Range, defeats the Enemy, or the Sensor meets any other scriptable sets # of conditions that you can set them up to do, such as investigate the last # tile the Sensor had Line of Sight with the Target, then return to their # previously assigned patrol route. # # The "light_tol=123" was incorporated to allow you to increase Tolerance of # how sensitive Light Detection should be. This is necessary because the # script was set up based on what you see. If you see a Black Shadow, it will # not trigger Light Detection, however, that may be too much darkness for the # visual aesthetics of your game. Since you can make your Shadowmap partly # obsure the visuals, it makes Light Detection too sensitive, thus, you can # turn DOWN the sensitivity so you can balance your gameplay according to your # game mechanics. Either Pitch Black Shadows, or a slight hint of shadows # are visual styles that would be limited by functionality of the script if I # did not incorporate this feature. # # Light Tolerance for example, would be set with "light_tol=30" to reduce the # Sensor's Light Sensitivity by 30. Light Sensitivity is based on Alpha Values # of the Shadowmap. Alphas range from 0 to 255, so your break point would be # right about 127 Alpha. Anything ABOVE 127 would be considered to be "IN # DARKNESS" and anything lower than that would be considered to be "IN LIGHT". # If you set the Alpha of your Shadowmap to be only 50, then the Target will # always be considered to be "IN LIGHT", thus, you would need to decrease the # sensitivity of Light Detection. In that case you would ADD at least 77 to # the "light_tol=77" of the Sensor_Config to allow any hiding in shadow. Since # 77 + 50 = 127, you can understand why this feature is necessaro. You would # probably need to increase it a bit more than that in order to balance your # gameplay according to your needs. The ONLY time you probably would not need # to customize Light Sensitivity at all would be if the Alpha of yoru Shadows # were set to 255, which hides ALL of your visuals, which is definitely not # appropriate for all games. # # # # # ===== SHADOWS ===== # # You can also create a Shadow. Shadows are basically normal lights with a few # options available to you. Most importantly, they render after all the Lights # have been drawn. This allows you to fill in areas that you may not want any # Lights to illuminate. # # To make a Light a Shadow, simply add a Comment: # # - Comment: \light_shadow[N*] # N - Optional Parameter, but DOES require the [] characters # 0 - Default, Normal Light # 1 - Ignores Contents of Image and fills with Shadowmap Color # 2 - Cuts a Hole based on Light Graphic and fills with Image Alpha # # Examples: # - Comment: \light[ImageFile.png] # - Comment: \light_shadow[] # - Comment: \light_shadow[0] # - Comment: \light_shadow[1] # - Comment: \light_shadow[2] # # NOTE: PNG files are practically required here because you can edit the Alpha # Channel in a PNG file, while you can not in .bmp or .jpg files # # Shadows are useful for Rooms or Tops of Buildings where all character's light # source would not be able to reach. # # Shadows are rendered after Lights. This gives you a bit more control over # what your map looks like, especially when adding Color to your Lights. When # the Optional Parameter of 1 or 2 are used, the contents of the Image you use # for your lightsource are replaced with the Shadowmap Color so ONLY the Alpha # Channel is used in drawing a Shadow. With Option 1, your Shadow will appear # as Darker than the rest of the map, depending on the contents of your Alpha # Channel. When Option 2 is used, again, the RGB contents of the image are # ignored and a HOLE is cut out matching the size of the image. This is useful # for making entire rooms that may be close to Light Sources that "bleed" thru # the walls and prevents illuminating another room. After the HOLE is cut out # of the Shadowmap, then the contents based on the Alpha Channel are drawn into # the HOLE. Thus, when using Option 2, it is usually a good idea to use an # Image that uses Maximum Alpha along the edges or fill the entire contents of # the Image with a solid color. You can Overlap multiple Shadow Sources with # no odd graphic contents showing up as long as your image has a solid max # Alpha Channel. # # The Optional Parameters of 1 and 2 use the DLL file, so you may need to keep # the dimensions of your Image Source relatively small for performance on low # end machines that your players may be using. # # Lights render in their Event ID Order. Thus, Player renders first, followed # by Event001, then Event002. Normal Lights are rendered first, then Shadows # and lastly, Cutouts. This means if you cleverly arrange your Event Lights # you can use Lights with higher Event IDs than a Shadow and a \light_shadow[] # comment, you can have Light Sources even when the Player's Lights do not # appear to do anything. # # NOTE: I have not put anything in to create Realtime Shadows. Sorry. # # # # # ===== AFTER SHADOWS ===== # # Although Shadows are useful for making it appear that the Player's Light can # not shine through walls, there may be instances where you still want those # rooms to be illuminated. # # Lights are rendered in a specific order: Normal Lights (default), Shadows, # then After Lights, then Shadow Cutouts. In each set, Lights are rendered # in the order of their respective Event IDs. Thus, the Player is normally # rendered first as the Player has an Event ID of 0, followed by Event #1 # then Event #2 in each set. Thus, Normal Lights are rendered first, so you # may end up with a sequence of Event IDs, 5, 6, 10. Then Shadows are drawn # which can have Event IDs that do not match the first set, resulting in # something like Event IDs 7, 8 being drawn after Normal Lights are drawn. # Next, After Shadow Lights are rendered. You can have Events 2, 3, and 9 # drawn after both Lights, then Shadows are drawn. # # The order of processing here will dramatically affect how your lights look # in your map. # # To make a Light render after Shadow Lights are drawn, just add a Comment # below your \light[] Comment: # # - Comment: \light_after_shadow # # This will be useful to you because these lights will be drawn after any # Normal Lights and Light Shadows are drawn, which will help you make it appear # as if the Player's Light does not shine through a wall while still # illuminating the Room or tops of buildings. The comment above gives you a # huge level of control over the order in which Lights are rendered without # having to fight with Event ID sequences. # # While Mapping, you may wish to keep your Logical Light Sources that have # an \light_after_shadow Comment either small enough or far away enough that # they do not appear to "Bleed Over" into neighboring rooms. # # NOTE: Remember that all Lights are PER PAGE, so you can use different Pages # in the event you have a Light that you need to both Shine and Not # Shine through Shadowed Lights. # # # # # ===== SHADOW CUTOUTS ===== # # Cutouts allow you make a character render as "Fullbright". This prevents any # shadows or light coloring from covering this character. These characters can # still be covered by fog, and other priority graphic layers. With proper # placement of these types of Lights on your Map, your Map can still look # very good and convincing. # # Cutouts are HIGHLY useful for illuminating logical light sources in your map # that would normally not be covered by the Shadowmap. To make a Cutout, just # add a Comment to your Light: # # - Comment: \light_cutout[] # # By default, the character's graphic is used as the Spritesheet. However, you # may wish to have only certain parts of your character cut through the Shadow. # Thus, the Optional Parameter will accept a Character File Name, as shown # in this example: # # - Comment: \light_cutout[001-Fighter01.png] # # NOTE: RGB values of the Image you specify here are IGNORED and ONLY the # Alpha Channel of the Image is used for your Cutout. # # Subfolders ARE allowed for organization within the Character directory! # # - Comment: \light_cutout[Cutouts/001-Fighter01.png] # # NOTE: When specifying a File, they need to be in your Graphics/Characters # folder, or Graphics/Characters/MyFolder if you specify a Subfolder. # # By specifying a File, you can have a Gradient Light Cutout, which is very # useful for Lamp Posts so the non illuminating parts of the Lamp can still # be covered by Shadow, but the Light Source part of your graphic is fully # illuminated an is unaffected by ANY shadows or other Lights at all. This # feature could also be highly useful for drawing a players eyes to a specific # object, character, door, or point of interest on your map. # # You can use Tileset Graphics or Character Graphics for Cutouts! Sorry, but # I did not set anything up so you could use Autotiles, but since you can # use the Optional Parameter, you can make something that suits your needs # for Autotiles as well. When Character Graphics are used, the Direction # and Pattern (for Stepping) are also identically matched. This does create # a bit of a compatability issue with other scripts which allow for more # frames of animation for characters. Due to the low probability that anyone # would ever encounter such an issue, I have not resolved this at this time but # may do so upon reqest. # # Shadow Cutouts are NOT affected by Zoom or Opacity. The intent here was # to allow you to create a Cutout only for the Character based on the contents # of the Alpha Channel of your Graphic Source from your Optional Parameter. # Another one of my scripts allows you to resize the Sprites. Cutouts will # work perfectly when a Character Sprite is scaled. If you set the opacity of # the Light to 0, cutouts will still cut out a hole in the shadowmap so the # Sprite of the character stands out against the shadowmap. # # Lets say you have two different logical Light Source graphics. One is just # a fire that does not show wood or any thing else. The other Light Source # is a Street Lamp. You could use just the character graphic for the fire # while the Street Lamp would most likely look better when another File is # specified. The Override File from the Optional Parameter would be edited # and maintain the same dimensions as the original, but replace any part of # the Street Lamp Cutout where it is not the logical Light Source with a # fully transparent color, such as the Post, while the Glass of the Street # Lamp would use a Full Alpha color. Any Color is acceptable since it is # ignored and ONLY the Alpha Channel is used when determining Cutouts. # # Yet another one of my scripts allows you to use Pendulums and Rotate the # Sprites of Characters. Shadow Cutouts dont work perfectly with Pendulums # or Rotated Characters unfortuantely. This is because there is no way to # access a Rotated Graphic from within RPG Maker. Had the code to rotate # images been available to users, the effect would have been perfect. There # was really no choice for me in this matter. # # NOTE: Cutout Lights look best when no characters will cover these Light # Sources as that will ruin any Suspension of Disbelief in Shadows. # # # # # ===== SPOTLIGHTS ===== # # The Rotate, Zoom, and Pendulums script is needed for pointing a graphic at # a character. To point a Spotlight at a Character, just add a Comment # to the Event that reads: # # - Comment: \rotate_target[character id] # # The Character ID is the Event ID, or simply use 0 for the Player! # # You can use this script to create "Spotlights", or Lights that focus on a # Character and resize dynamically so that the Character is always in the # Spotlight. To do this, use a Comment: # # - Comment: \light_spotlight[Offset, Max_Zoom] # # NOTE: For this feature to work, you must have Heretic's Rotate, Zoom, and # Pendulums script! # # The "offset" is because the center of a Spotlight is not at the edge of your # light Graphic. If the center of your images Spotlight is 40 pixels from the # edge of your graphic, then use 40 as your Offset. # # The Max Zoom parameter will keep your Graphic from becoming too large, which # can cause SEVERE performance issues and looking "bitty" at very large scales. # # I recommend setting the Max_Zoom parameter to 4.0 or 5.0 at most. # # Spotlights will be positioned so they rotate at the Top Center of your image. # # This helps a lot with performance as Spotlights because half of the Graphic # would normally be wasted, but is still calculated which causes performance # drops on larger scales. Smaller graphics will improve performance but look # worse when scaled to large sizes. Large graphics will look better but at # the price of extremely noticable lag. It is up to you to decide a proper # balance between performance and quality for your game. # # My Rotate, Zoom, and Pendulum Script has a feature for a Max Distance to # rotate. This is done with a Comment: # # - Comment: \rotate_target_dist[N] # - Comment: \rotate_target_dist[4] (4 tiles or 128 pixels away) # # N is the distance in Tiles. Each tile in RPG Maker is 32 pixels. Thus # a value of 4 will give you 128. That means that the Spotlight will point # at the character until they are 128 pixels away. # # Since there are several Comments that are needed and another script required # take a look at this example to get a Spotlight working! # # - Comment: \rotate_target[0] # Points at the Player (1+ is Events) # - Comment: \rotate_target_dist[6] # Max Dist of 6 Tiles from Spotlight # - Comment: \light[PointLight.png] # Creates a Light and sets the Graphic # - Comment: \light_match_angle # Light Angle matches Event Angle # - Comment: \light_spotlight[32, 5.0] # Spotlight - [Offset, Max Zoom] # # My Rotate, Zoom, and Pendulum Script has a lot of features. One feature that # relates to Spotlights is the ability to limit how far a Character can turn. # The features are called \rotate_target_min[N] and \rotate_target_max[N] # This script is set up so that if those two features are used (they should be # used together) this script will use those so Spotlights stop tracking the # movement of a Character if they are outside of the range! # # -Comment: \rotate_target_min[Angle] # -Comment: \rotate_target_max[Angle] # # The above examples will prevent a Spotlight from pointing at a Character if # the Angle of the Light that will point at the assigned character is out of # range. Refer to the Rotate, Zoom, and Pendulums documentation for more # information. In the script, if these two Comments are used, then Spotlights # will not adjust Size or Angle when @angle_fix is true. # # # # ===== PENDULUM LIGHTS ====== # # This script will also allow you to put a Light on the bottom of a Pendulum! # # To do this, first, create a Pendulum with a Comment like this: # # - Comment: \pendulum # # NOTE: Requires Heretic's Rotate, Zoom, and Pendulums Script. # See that script for more things you can do with Pendulums # # Next, we need to create a Light. # # - Comment: \light[ForwardLight.png] # # Then to make the Light follow the Pendulum, add this Comment: # # - Comment: \light_pendulum[Vertical Offset*] # # The Offset is Optional and is used to position the Light in relation to # the bottom of the Pendulum Graphic. You can put in Negative Numbers if # you want your Light closer to the top of the screen or Positive Numbers # to position lower than the graphic. # # Pendulum Lights do NOT rotate by default. You can set the Light to use # the same Angle as its Event by adding a Comment: # # - Comment: \light_match_angle # # This will cause any rotations of the Character to propogate to the Light. # # Example of all the Comments: # # - Comment: \pendulum # Event becomes a Pendulum # - Comment: \rotate_angle[-60] # Highest angle of swinging for Pendulum # - Comment: \light[Lantern.png] # Makes a Light and assigns the Graphic # - Comment: \light_pendulum[32] # 32 is the Offset, you can also use -32 too # - Comment: \light_match_angle # Matches Angle of Event Rotation to Light # # NOTE: The above does NOT change the Y position. # # In order to make a Pseudo-3d enviornment look correct with Pendulums, there # are times you want a Light to look like it is against a Wall, and other times # it looks like it is over Flat Ground. When a Pendulum is swaying over flat # ground, it won't move up or down. However, this does not look correct when # a Pendulum Light is shining against a vertical wall. Thus, there is another # option to match the Y Coordinate of the Pendulum also. # # - Comment: \light_pendulum_y # # When the above Comment is created on an Event, your Pendulum Light will look # correct when the Light is shining on a Vertical Wall. # # Lights in the really real world also have a Linear Falloff. This means that # the closer a Light is to a solid object, the brighter it appears. When used # with Pendulums, the closer a Light is to the ground, the higher its Opacity. # This option will allow your Light to fade its Opacity to 0 as it approaches # either 90 Degrees or -90 Degrees, and is at its full opacity when the Light # is closest to the ground, which is considered at 0 Degrees, or "bottom of # a swing". # # - Comment: \light_pendulum_opacity # # Another illusion that is simulated by this script is the Zoom of a Pendulum # Light. The closer a Light is to the ground, the "smaller" but "brighter" it # appears. Thus when rendering Pendulum Lights, an Option is provided to set # how much bigger a light will appear when it is farthest to the ground. It is # not recommended to use when rendering Lights against vertical surfaces such # as Walls. This can be used with Zoom XY together as it is a Multiplier. To # set up Pendulum Zoom, add another Comment: # # - Comment: \light_pendulum_zoom[Zoom] # # The recommended value for the Zoom is quite small, such as only 0.5 or .75 # as higher values appear disproportionate. Lights that have already been # given a Zoom XY value may need higher numbers for the effect to be noticable. # # Since there are numerous options for Pendulum Lights, I will simplify this # for you. There are two ways that Pendulums will be used. Over flat surfaces # like flat ground, or against vertical surfaces, such as Building or Cave # Walls. Vertical or Horizontal. Thats it. Here are two examples for all # configurations needed for both types. # # * WALL PENDULUM - Light shines against a Vertical Wall # # - Comment: \pendulum # Event becomes a Pendulum # - Comment: \rotate_angle[-60] # Highest angle of swinging of Pendulum # - Comment: \light[Lantern.png] # Makes a Light and assigns the Graphic # - Comment: \light_pendulum[-32] # -32 is Vertical Offset, Negative on Walls # - Comment: \light_pendulum_y # Adjusts Y coordinate to Pendulum Position # - Comment: \light_match_angle # Matches Angle of Event Rotation to Light # # NOTE: Options NOT in use: \light_pendulum_opacity, \light_pendulum_zoom[0.75] # # # * GROUND PENDULUM - Light shines against the Horizontal Flat Ground # # - Comment: \pendulum # Event becomes a Pendulum # - Comment: \rotate_angle[-60] # Highest angle of swinging of Pendulum # - Comment: \light[Lantern.png] # Makes a Light and assigns the Graphic # - Comment: \light_pendulum[32] # 32 is the Vertical Offset, Pos value # - Comment: \light_pendulum_opacity # Fade Opacity as 90 Degrees is reached # - Comment: \light_pendulum_zoom[0.75] # Zooms to value closer to 90 Degrees # # NOTE: Options NOT in use: \light_pendulum_y, \light_match_angle # # # # ===== LIGHTNING ===== # # You can also simulate Hollywood style Lightning Flashes using this script! # # NOTE: Lightning typically looks best at night with a dark shadow to make it # the most noticable. # # To manually cause Lightning to Flash, it is a simple Script call: # # - Script: $game_system.flash_lightning(color*, duration*) # # Both Color and Duration are Optional. The Default Color is set in the # Light_Config section of this script, and Duration defaults to 60 frames # when no value is specified. A typical Lightning Flash would look like # the example below: # # - Script: c = Color.new(192, 192, 224) # $game_system.flash_lightning(c, 40) # # The above Script Call will cause the Shadow to immediately snap to the color # that you specify, followed by a pause for a Hollywood style Lightning Flash # then snap back to that color, and fade back to the current Shadow Color over # the number of frames specified, which is 40 frames in this example. # # NOTE: Lightning ONLY affects the Shadowmap when the Indoor Switch is set # to OFF. Lights and Events can still be set to react to Lightning. # # # # # To make a Lightsource react to Lightning, just add a Comment: # # - Comment: \light_lightning_opacity[N] # # Lights with this Comment will set their Opacity to N during a Lightning Flash # and fade out as a Lightning fades out. Hollywood style Lightning Flash is # also used on Lightsources as well. If the Opacity of a Light is higher than # the Color Opacity of the Lightning, then only the higher Opacity is used so # you dont get Lightning Flashes that "flash darkness", because that would just # look very silly. Basically, indoor Dark Windows will flash a Light during a # Lightning Flash. # # NOTE: Lightning Lights can be Indoor or Outdoor. They are not affected by # the Indoor Switch. Just like Christmas Lights, they are recommended # for Indoor or Outdoor use ONLY, as opposed to using them underwater? # Maybe in space? Underground? Inside a can of Pork and Beans? # # # # You can also make Events react to Lightning Flashes as well. # # \lightning_event_opacity[N] # # The above Comment will temporarily change the Opacity of the Event to N when # a Lightning Flash occurs, then transition to a Non Lightning Opacity (what # ever it is supposed to be without Lightning) over the period of time that # the Lightning Flash takes to occur. This is most useful for Indoor Windows # or other Indoor Events you want the Opacity to change during a Lightning # Flash. # # NOTE: Lightning Event Opacity CAN be used on Non Lightsource Events, or # Events that do NOT have a \light[] Comment. # # # # ************************** # ***** AUTO STORMS ****** # ************************** # # Storms can sometimes be difficult to set up by using Parallel Events for the # control of your Storms. You can flip a Game Switch to ON to let this Script # handle the Storms for you automatically. By Default, the Game Switch ID # is set to 71. When you set this Game Switch to ON, Lightning and Thunder # will play automatically for you. You can set how long you want the delays # between the Lightning and Thunder in the Light_Config section of this script. # # In real life, there is normally a delay between a Lightning Flash and the # following Thunderclap. I set this script up to put in a random time for # the delay. The longer the Delay between Lightning and Thunder, the lower # the Pitch of the Thunderclap becomes. This randomness allows for a bit more # of a realistic experience for the Players. # # I also set the Volume of the Thunderclap to be 20dB lower when Inside. # # You can call a Lightning Flash manually by a Script Call with the following: # # - Script: $game_system.flash_lightning(color*, duration*) # # Color is simply the Shadowmap Color during the Flash. # Duration is how many frames to return to the current color. # # *** Example: # - Script: $game_system.flash_lightning(Color.new(224,224,240), 60) # # The above example will flash a bright blue light taking 60 frames to fade. # # NOTE: Auto Storms only look good at night because flashes are not visible # during the day, depending on the color you set for day color. # # # # ********************************************** # ***** GAME SWITCHES AND GAME VARIABLES ***** # ********************************************** # # There are numerous Game Switches and Game Variables that have been set up to # allow you to easily interact with the Day Night System. These Game Switches # and Game Variables can be customized in your Light_Config section of the # script. I STRONGLY recommend labelling each of these Switches and Variables # so you can understand what they do. # # The first switch that you will want to know about is to allow Time to flow. # # - Enable Time: Game Switch 74 (default) to ON. # # # # The next important Switch is the Indoor / Outdoor Switch. This script uses # different Colors for the shadows inside and outside. To use Indoor Colors # simply flip this Switch. # # - Indoor Shadow Color: Game Switch 75 (default) to ON. # # # # Some Switches are NOT intended for you to turn them ON or OFF. I call these # types of Switches AUTO Switches, or labeled as (Auto). The state of this # Switch is controlled internally by the Script. It is intended for you to # use for Checking the current state of Day or Night, but is NOT intended for # you to change, as it will be immediately changed again by the Script. # # You may need to Check whether or not it is currently Day or Night. To Check # wheter it is currently Day or Night, simply Check the value of the Day_Switch # # - Day Switch (AUTO): Game Switch 72 (default) - ON when Day, Off when Night # # This allows you to have Events that behave differently during Day or Night # such as Locking Doors, Closing Shops, and even Lights that disappear during # the day but can be seen at night! # # # # I created a CLOCK that you can display or hide with another Switch. Display # the CLOCK by setting the Display Clock Switch to ON and hide it by setting # it to OFF. # # - Display CLOCK: Game Switch 73 (default) - ON to display, OFF to hide # # The CLOCK has two ways to display the current time. An actual "Clock" that # shows Time Values such as 3:12 PM or 10:51 AM, or a Sun / Moon Icon that will # rotate, where the Sun will be rotated to the top during day and the Moon # will rotate to the top during night. Personally, I do not like the way that # either of them look. If you want to modify or create your own Clock, feel # free to do so. # # NOTE: I did NOT do anything with the Menu System to display the current Game # Time due to the availability of Custom Menu Systems. You may need to # request a modification to those scripts to display Current Time from # the respective Authors. # # You can display either the Clock, Sun / Moon, or Both with Scripts: # # @>Script: s = $game_system # s.show_formatted_time = true / false # # @>Script: s = $game_system # s.show_sunmoon_time = true / false # # # # *** TIME VARIABLES *** # # # This script uses TWO Variables for remembering Time. The first Variable # uses Seconds to calculate everything that has to do with Day / Night. This # variable can be changed so you can control exactly what time it is in # Seconds. For example, if you want to set the current Game Time to 8:00 AM, # you will need to convert that into Seconds before assigning the value to # this variable. Multiply Hours * Seconds in an hour. Thus, 8:00 AM will # become 28800. That is the value you want to use to set the Time. # # - Clock Variable (in Seconds): Game Variable 75 (default) # # This variable can be used by Event Conditions to check for Page Conditions or # for use with Conditional Branches. # # # # # The 2nd variable displays Formatted Time. Since your Players will probably # be very confused if you tell them the time is currently 61200 (5PM), you # will need to be able to show them a Formatted Time, such as 4:20 PM. This # is most useful with the "Show Text..." command. To display Formatted Time # in a Text Window, it is the same as displaying the Value of any Variable. # # Place this Text in a "Show Text..." window: \v[74] # # Example to display Formatted Time in Text: # @>Text: The time is currently \v[74]. # # The output of the above example will display like this: # # "The time is currently 1:15 PM." # # I handled all of the Text Formatting for you. Currently, I have no intention # of an option to dislay the Formatted Text in Military Time. If Scripters # want to make modifications to their Menu Systems to display Formatted Time # in their Menus, they simply need to read the value of $game_variables[74] # # There is one MAJOR issue with Formatted Time and that is that Conditional # Branches can NOT use this Variable for Conditions. # # *** IMPORTANT *** # # The use of Conditional Branches or Page Conditions that check Formatted Time # will cause your game to CRASH. # # If you need to check Formatted Time, you need to use SCRIPTS ONLY. Under the # Event Commands -> Conditional Branch (page 4) -> Scripts. It would read # something like this: # # Conditional Branch: Script: $game_variables[74] == "5:15 PM" # # # # *** PAGE CONDITIONS *** # # As stated above, you can use the Day Switch (Auto) as a Page Condtion, but # you can NOT use either of the TIME Variables in your Game Variables! The # reason for this is first because this would cause a String to Integer # comparison. Basically, ask yourself if "Chicken is greater than 39". It # does not make any sense. # # Also, you can NOT use Game Variable[75] (by default) as a Page Condition # for your Events! The reason for this variable being uncheckable by a # page condition is both performance and functionality. When an Event changes # a Page, all of its Move Routes and List of Event Commands get reset. Thus # you can NOT use the Variable Condition as a Page Condition with predictable # results. # # Luckily for you, this script was written by Captain Overkill himself. I did # write yet another Script that you CAN use to check a Variable Condition. I # actually wrote this other Script specifically to take care of the problems # caused by not being able to use Variable Page Conditions. I called that # script "Heretic's Unlimited Event Page Conditions". Basically what it does # for you is allows you to put literally ANY Script Calls in as a Page # Condition! # # If you have "Heretic's Page Script Conditions" script and wanted to use it # to check the Time Variable (75 by default, the one that measures seconds) # you could do something like this: # # @>Comment: Condition: t = $game_variables[75] # : t > 31000 and t < 65000 # # The above Condition would make that Event's Page Active ONLY when the # condition you put in evaluates to true. Thus, if the time is less than # the example of 31000, then the Page will not be displayed so you won't # see it in game. # # I put together a few other methods that you can use specifically for you # to combine with "Heretic's Page Script Conditions". # # @>Comment: Condition: dawn? # @>Comment: Condition: day? # @>Comment: Condition: dusk? # @>Comment: Condition: night? # @>Comment: Condition: lightning? # # NOTE: You MUST have "Heretic's Page Script Conditions" for the above # features to work as they are NOT part of this script. # # Day evaluates to true only during the Day, so Dawn and Dusk are excluded # Dawn and Dusk are exactly that, only while the Shadowmap is changing color. # Night also excludes Dawn and Dusk so it only evaluates to true when the # sun has fully set. Lightning evaluates to true when there is a Lightning # Flash occuring. # # # # # *** DAWN AND DUSK *** # # Dawn and Dusk occur automatically when Time is Enabled (Game Switch 74), but # you may not want Dawn and Dusk to be controlled by the Script. You can set # this to suit your needs by changing Manual_Dawn_Dusk in Light_Config to # true. You will need to use Events to control Dawn and Dusk with Script Calls # when Manual_Dawn_Dusk is used. # # @>Script: $game_system.call_dawn(*args) # @>Script: $game_system.call_dusk(*args) # # Once Dawn or Dusk is called, the Color of the Outdoor Shadowmap will smoothly # transition between the two Colors, even if Enable Time Switch is OFF. # # # # I put in another option to Pause Time when Interpreter is running. When this # is set to true ($game_system.pause_time_interpreter = true / false), when # the Player interacts with an Event, the Clock will STOP! # # NOTE: This Option does NOT affect Parallel Events. # # # # *** SHADOWMAP COLORS *** # # This script uses 3 colors: # - Day Color # - Night Color # - Indoor Color # # During Day / Night cycles, the Color of the Shadowmap transitions smoothly # between the values you assign to Day Color and Night Color. The default of # the Color for Day uses the Opacity parameter so the Shadowmap entirely fades # out during the day. The Default Night Color has some Opacity, but is not # set to a Pitch Black color. # # color = Color.new(Red, Green, Blue, Opacity) # # Become familiar with the above command, if you are not already. Also pay # attention to the Capitalization. The first letter of Color has to be a # Capital Letter when using Color.new, where "color =" does not need to use # a Capital Letter. # # The Indoor Color does not have any Automatic Transitions during Dawn or Dusk. # The use of Event Lights will still occur during Dawn Dusk Transitions even # when the Indoor Switch is set to ON. This is what enables you to change # the way the interior of a building looks during Day or Night. # # You can set these Colors with Script Calls: # # @>Script: set_day_color(Color.new(255, 255, 255, 0)) # @>Script: set_night_color(Color.new(64, 32, 32, 192)) # @>Script: set_indoor_color(Color.new(128, 96, 96, 160)) # # You can also Transition a Shadowmap Color with Script Calls: # # @>Script: $game_system.shadowmap_color_to(new_color, duration) # @>Script: $game_system.shadowmap_indoor_color_to(new_color, duration) # # NOTE: Using shadowmap_color_to may mess with your Lighting if you are using # the scripts Day / Night Cycles, so pay attention to the Time! # # The examples here dont fit in the Script box so some local variables are # most likely needed. An actual script call would look like this: # # @>Script: color = Color.new(255, 128, 128, 64) # s = $game_system # s.shadowmap_color_to(color, 20) # # The above example would transition the Outdoor Color (Indoor Switch is OFF) # to the color specified. For Indoor Colors, simly use # # @>Script: color = Color.new(255, 128, 128, 64) # s = $game_system # s.shadowmap_indoor_color_to(color, 20) # # # # ******************************************* # ***** SCRIPT CALLS (QUICK REFERENCE) ***** # ******************************************* # # You most likely wont need but one or two of these very often, if ever, but # here they are if you want to do something you can't do with Switches or # by setting or checking Variables. # # $game_system.dynamic_lights = true / false # $game_system.dawn? - Returns true if dawn is occuring # $game_system.day? - Returns true if fully day, excludes dawn / dusk # $game_system.dusk? - Returns true if dusk is occuring # $game_system.night? - Returns true if fully night, excludes dawn / dusk # $game_system.lightning? - Returns true if a Lightning Flash or is next frame # $game_system.shadowmap_color = Color # $game_system.shadowmap_indoor_color = Color # $game_system.shadowmap_color_to(Color, Duration) # $game_system.shadowmap_indoor_color_to(Color, Duration) # $game_system.shadowmap_day_color = Color # $game_system.shadowmap_night_color = Color # $game_system.shadowmap_indoor_color = Color # $game_system.shadowmap_lightning_color = Color # $game_system.flash_lightning(Color*, Duration*) # Causes Lightning Flash # $game_system.lightning_first_frames = 2 # Duration of First Flash # $game_system.lightning_double_frames = 3 # Pause Between Flashes # $game_system.shadowmap_lightning_duration # Countdown Timer # $game_system.shadowmap_lightning_period # Total Period of Time of Lightning # $game_system.shadowmap_opacity = 0 to 255 # $game_system.minutes_per_day = 5 # Five Minutes in Real Time is one Game Day # $game_system.total_days # Total number of Days since Game Start # $game_system.min_inn_time # Min Time to Rest at Inn or Advance Days # $game_system.pause_time_interpreter = true / false # $game_system.pause_time_in_battle = true / false # $game_system.call_dawn # $game_system.call_dusk # $game_system.show_formatted_time = true / false # $game_system.show_sunmoon_time = true / false # $game_system.weather_over_shadow = true / false # $game_system.fog_over_shadow = true / false # # --- Character Script Calls --- # # Either use $game_player or $game_map.events[event_id] shown as "char" # # char.set_flicker_size(Min Zoom, Max Zoom, Duration) # char.set_flicker_size_x(Min Zoom, Max Zoom, Duration) # char.set_flicker_size_y(Min Zoom, Max Zoom, Duration) # char.clear_flicker_size # char.zoom_light_to(New Zoom, Duration) # char.opacity_light_to(New Opacity, Duration) # char.set_flicker_opacity(New Opacity, Duration) # char.clear_flicker_opacity # # --- Access the Light Object --- # # The Player and Events with \light[] Comments on each Page are given a Light # Object which has more script calls. Pretty much anything in the Light Object # that uses an :attr_accessor or has a def method_name can be used. # # char.light.property = new_property # char.light.property_array = [new_property, new_property_2] # char.light.method # char.light.method(arg1, arg2) # # These are the Properties you can change # # char.light.enabled = true / false # char.light.image_file = "File.png" # char.light.up = "File.png" # char.light.down = "File.png" # char.light.left = "File.png" # char.light.right = "File.png" # char.light.zoom_xy = [Zoom_X, Zoom_Y] # char.lighyt.offset_xy [Offset_X, Offset_Y] # char.light.angle = 0 to 360.0 # char.light.spotlight = [Offset, Max_Y_Zoom] # char.light.target_percent = (Internally used for Spotlights) # char.light.flicker_size = [Min, Max, Duration] # char.light.opacity = 0 to 255 # char.light.lightning_opacity = nil - 0 to 255 becomes Lightning Sensitive # char.light.daynight_opacity = [Day Opacity, Night Opacity, Duration*] # char.light.match_direction = true / false # char.light.match_angle = true / false # char.light.pendulum = Pendulum Y Offset # char.light.pendulum_y = true / false # char.light.pendulum_opacity = true / false # char.light.pendulum_zoom = float (0.5 or 1.25) # # The more important properties and methods of the Light Object has been given # methods in the Character Class as a shortcut for Script Calls except for # changing the truth value of Enabled. # # $game_player.light.enabled = true / false # # All of the rest of the properties are controlled entirely by Comment Configs. # # # ******************************** # ***** MISC INFORMATION ****** # ******************************** # # Most of the work you will end up doing while using my Dynamic Lights script # has nothing to do with Lights themselves, but rather, setting up your Maps # and Events to change according to time of day. You will need to think very # carefully about how you are going to do things in your game so that you do # not end up biting off more than you can chew. # # The reason is that Players have certain Expectations of things to happen in # the Day and a totally different set of Expectations at Night. Your biggest # job as a Creator of ANY game is to make sure these Expectations are met. # # Players will have an expectation that NPC (Non Playable Characters) will act # like normal people do in real life, as best as can be simulated. That means # that people will go to bed, wake up, go outside, do daily routines, then # go back to bed at some point in time. # # This can overload how much work you can do as a creator, so you need to think # carefully while in the planning stages of your game as to how you are going # to meet the Expectations of your Players and target audience, and achieve # these goals in a reasonable manner. This is one of the reasons that I built # the features into my scripts that I did. My scripts are intended to increase # your ability to provide new features and exceed expectations without causing # you to experience a Work Overload. # # My recommendations are that if you have a Town, then you should PAUSE TIME # when you go into town. This will SAVE YOU WORK. When a Player goes in to # a Town, they expect that there will be some people on the street during the # Day and they are in bed with their doors locked at night. Although my script # can handle the transition of changing the colors, what can not be handled as # easily is making the Townsfolk go inside. That is a lot of extra work that # can be easily avoided by PAUSING TIME when you go into a Town. # # This can also cause Frustrations for your Players, and that is something that # needs to be avoided at all costs. Their frustration will come from having to # leave town and wait in a dangerous area until they are able to progress the # story or whatever goals you have set out for them, such as upgrading weapons, # getting a new character, or what ever your game goals are. They do NOT want # to be forced into waiting. # # The logical solution to the problem of wating causes frustration is to allow # them to REST at INNS. This is a logical way of passing time very quickly and # allowing them to start achieving any logical daytime goals they are aware of. # When you allow a Player to enter a Town, you may want to use some Conditional # Branches to check the current time, and use Event Pages to make the Townsfolk # go inside at Night. Leave your INN unlocked and available to the Player at # all times so that they can advance time as needed. They rest and recover and # time will advance so basically it is two birds with one stone. # # The real killer here would be to allow time to flow, then changing the Event # Pages right in front of the Player so the Townsfolk literally disappear right # before their eyes. That would be a total Violation of Player Expecations and # end up costing you your reputation and that of your game. You really need to # put some serious thought about how to achieve your goals in a reasonable way # that can be achieved. # # This is also only a suggestion. If you have the skills, time, and manpower # that you could achieve animating all of your townsfolk going in doors when # night falls, great, go for it! Im sure Players will be very impressed by # high quality workmanship that you put forth, but for those who simply do not # have the available time or resources to take on that level of work, it may # be better to simply PAUSE TIME when your player enters a town and use Event # Pages to make it appear as if the Townsfolk are inside and in bed. # # --- Version History --- # # - Tuesday, August 16th, 2016 # * Initial Release # - Friday, Dec 8th, 2017 # * Fixed minor Bug with Screen Shake causing Shadowmap to become # Unaligned from the Game Screen # # *********************************** # ******* LIGHT_CONFIG ********* # *********************************** # # This section controls the Configuration of this Script. Most of the Values # that are used are only used as Default Values for Game System except for # the Width and Height, and Switch / Variable IDs, which can not be changed # during Gameplay. The switches and variables themselves can be changed during # gameplay but the IDs for these switches and variables can not be changed # during gameplay. # # These should be pretty well explained so there is no need to repeat #============================================================================== # ** Light_Config # - Set the Default Values for the Script here. # - You can safely edit these to suit your needs #============================================================================== class Light_Config # Start Game with Dynamic Lights enabled or disabled (doesnt reset with Load) Dynamic_Lights_Default = true # Screen Width and Height - Should allow compatability with other scripts Width = 640 Height = 480 # Image of Default Lightsource in Graphics/Lights Folder if Comment: \light[] Default_Light_Image = "Light.png" # Debug Mode enables Toggling Lights On and Off with F5 Key Debug = true #=== Game Switches ==== # Game Switch to change to On when Daytime (Auto Switch dont change Manually) Day_Switch = 72 # Game Switch to Display Clock on Map Display_Clock = 73 # Game Switch to Enable Time Switch_Time = 74 # Switch Number to use for Indoor / Outdoor Lighting Switch_Indoor = 75 # Game_Switch used for Automatic Lightning Flashes during Storm Storm_Switch_ID = 71 #=== Game Variables === # Game Variable to display Formatted Time: (4:35PM) Formatted_Time_Variable = 74 # Game Variable to remember the Clock (in Seconds) Clock_Variable = 75 # Matches the Tone of the Clock (both SunMoon and Formatted) to Game Screen Clock_Match_Tone = true #=== Light Options === # Initial Game Time (28800 is 8:00 AM in Seconds) Start_Time = 28800 # Number of Minutes for one 24 Hour cycle Minutes_Per_Day = 10 # Minimum Time needed to fully Rest at an Inn (in Hours) Min_Inn_Time = 6 # The time (in number of Seconds) that Dawn occurs (05:00 AM in Seconds) Dawn_Time = 18000 # The time (in number of Seconds) that Dusk occurs (05:00 PM in Seconds) Dusk_Time = 61200 # Dawn Dusk Duration (in Hours of Game Time) Dawn_Dusk_Duration = 2 # Manual Dusk Dawn - Use call_dawn and call_dusk for Day Night Transition Manual_Dawn_Dusk = false # Image to use in Clock for Sun / Moon (in Pictures Folder, for Clock) Sun_Moon_Image = "SunMoon.png" # Start Color - Used ONLY at very start of Game to intialize values Start_Color = Color.new(32, 32, 96, 128) # Default Colors for Day, Night and In - Color.new(red, green, blue, opacity) Shadow_Day_Color = Color.new(255, 210, 0, 0) # Red, Full Opacity Shadow_Night_Color = Color.new(32, 32, 96, 160) # Blue'ish, Some Opacity Shadow_Indoor_Color = Color.new(64, 48, 48, 160) # Slightly Red, Low Opacity # Default Color of Shadowmap during a Lightning Flash Shadow_Lightning_Color = Color.new(180, 180, 240, 255) # Soft Blue Gray # Opacity of Shadow at Night (usually best to set Opacity in the Colors) Shadow_Opacity = 255 # Default Fog above Shadowmap, change in $game_system.fog_over_shadow Fog_Over_Shadow = false # Default Weather above Shadowmap, change in $game_system.weather_over_shadow Weather_Over_Shadow = false # Max Time between Lightning Flashes in Frames (Min and Max Values) Lightning_Delay = [100, 400] # Number of Frames for First Lighting Flash at Full Color / Opacity Lightning_First_Frames = 2 # Pause Duration in Frames between the Double Lighting Flash Lightning_Double_Frames = 2 # Default Duration of Lightning Flash in Frames (20 frames per second) Lightning_Default_Duration = 60 # Sound File to play during Lightning (in Audio/SE folder) Thunder_Sound = '061-Thunderclap01' # Delay in Frames between Flash and Thunderclap (Min and Max) Thunder_Delay = [10, 120] # Volume of Thunderclap - First Number is Outside, Second Number is Inside Thunder_Volume = [100, 80] # Lower the Pitch of the Thunder Inside Thunder_Dampen_Pitch = true # Default Option to not increase time when Interpreter is running Pause_Time_Interpreter = true # Pauses Time when In Battle Pause_Time_In_Battle = true # Transform Interpolate makes Stretched or Rotated Lights look less Bitty Transform_Interpolate = true # Number of Event Commands to read for Light Event Configuration Comment_Limit = 20 end #============================================================================== # ** DO NOT EDIT BELOW THIS LINE UNLESS YOU REALLY KNOW WHAT YOU ARE DOING ** #============================================================================== #============================================================================== # ** RPG - Module # - Main RPG::Module #============================================================================== module RPG #============================================================================ # ** RPG - Module # - Main RPG::Cache Module #============================================================================ module Cache #------------------------------------------------------------------------ # * Light - RPG::Cache # - Returns Cached Image of Light Files # filename : Name of File on System - Light.png # hue : Color Rotation in 360 degrees #------------------------------------------------------------------------ def self.light(filename, hue) # Loads File or Cached Image of graphics stored in Lights folder self.load_bitmap("Graphics/Lights/", filename, hue) end #------------------------------------------------------------------------ # * Shadow - RPG::Cache # - Returns Cached Image of Light Files # filename : Name of File on System - Light.png #------------------------------------------------------------------------ def self.shadow(filename) # Loads File or Cached Image of graphics stored in Lights folder self.load_bitmap("Graphics/Lights/", filename, :shadow) end end #============================================================================ # ** Weather - Class # - RPG::Weather # - Allows the individual Weather Sprites to have their Z-Index set in # other methods so Weather can appear over the Shadowmap #============================================================================ class Weather #------------------------------------------------------------------------ # * Public Instance Variables - RPG::Weather #------------------------------------------------------------------------ attr_accessor :sprites # Sprites used for Rain or Snow end end #============================================================================== # ** Float - Class # - Code by KK20 on Chaos-Project.com #============================================================================== class Float < Numeric #-------------------------------------------------------------------------- # * Approx - Float < Numeric #-------------------------------------------------------------------------- def approx target = self.round return target if (self + 0.00001 >= target && self - 0.00001 <= target) return self end end #============================================================================== # ** Cache_Light - Class # - Used as a Cache so the slower DLL is only called when needed #============================================================================== class Cache_Light #------------------------------------------------------------------------ # * Public Instance Variables - Cutout_Light #------------------------------------------------------------------------ attr_reader :bitmap # Saves an Image processed by the DLL attr_accessor :name # Name of Character or Tilemap attr_accessor :tile_id # Tile ID attr_accessor :angle # Angle in Degrees of this Images Rotation attr_accessor :color # Color of stored Image attr_accessor :hue # Hue of stored Image #-------------------------------------------------------------------------- # * Initialize - Cache_Light # - Creates the Object # bitmap : Bitmap Image to save #-------------------------------------------------------------------------- def initialize(bitmap) @bitmap = bitmap.clone @name = nil @tile_id = 0 @angle = nil @color = nil end #-------------------------------------------------------------------------- # * Dispose - Cache_Light # - Frees the Bitmap from Memory #-------------------------------------------------------------------------- def dispose @bitmap.dispose end end #============================================================================== # ** Spriteset_Map - Class #============================================================================== class Spriteset_Map #-------------------------------------------------------------------------- # * TransformBitmap - Win32API -> Spriteset_Map # - DLL File used for RGSS Bitmap Rotation # src_object : Object ID of RPG::Bitmap (bmp.object_id) # matrix : Transformation Matrix packed pointer # - DLL Arguments: # long sourceObject : Pointer to Ruby Bitmap Object of Source Bitmap ID # long destObject : Pointer to Ruby Bitmap Object of Dest Bitmap ID # float *matrix : pointer to Packed Transformation Matrix # int interpolate : Use Linear Interpolation for cleaner Bitmaps #-------------------------------------------------------------------------- TransformBitmap = Win32API.new('TransformBitmap', # File Name for DLL 'TransformBitmap', # Function Name in DLL 'llpl', # Argument Object Types 'i') # Return Object Type #-------------------------------------------------------------------------- # * Fill_Color_DLL - Win32API -> Spriteset_Map # - Fills Bitmap RGB values with current Shadowmap Color # - Does not change value of Alpha Channel # long sourceObject : Pointer to Ruby Bitmap Object of Source Bitmap ID # float *color : Pointer to Packed RGB Color Value Array [R,G,B] #-------------------------------------------------------------------------- Fill_Color_DLL = Win32API.new('TransformBitmap','fill_color', 'lp','i') #-------------------------------------------------------------------------- # * Public Instance Variables - Spriteset_Map #-------------------------------------------------------------------------- attr_accessor :shadowmap # Holds Shadows for Lights to cut through #-------------------------------------------------------------------------- # * Initialize - Spriteset_Map # - Alias of Main Spriteset Creation Method to add Lighting stuff #-------------------------------------------------------------------------- alias light_spritemap_initialize initialize unless $@ def initialize # Call Original or other Aliases light_spritemap_initialize # Shorthand for Game System s = $game_system # Width and Height of Screen width, height = Light_Config::Width, Light_Config::Height # Create Shadow Sprite Object @shadowmap = Sprite.new(@viewport1) # Bitmap Object for Sprite Shadow @shadowmap.bitmap = Bitmap.new(width, height) # Blend Type to Subtract to allow Holes from Stretch BLT @shadowmap.blend_type = 2 # Set Opacity to 0 if Lights are not Enabled, or use Config Color @shadowmap.opacity = (s.dynamic_lights) ? s.get_shadowmap_opacity : 0 # Layers Shadows with Fogs according to Config @shadowmap.z = s.fog_over_shadow ? @fog.z - 10 : @fog.z + 10 # Determine whether Shadowmap is over Weather Sprites for weather_sprite in @weather.sprites # Determine if Weather Sprite is Over or Under the Shadowmap weather_sprite.z = ($game_system.weather_over_shadow) ? 3100 : 1000 end # Fill Bitmap with Shadowmap Color @shadowmap.bitmap.fill_rect(0, 0, width, height, s.get_shadowmap_color) # Hash to hold Rotated Lights @rotated_lights = {} # Hash to hold Shadowed Lights @shadowed_lights = {} # Hash to hold Cutout Lights @cutout_lights = {} # Debugging Option with the F5 Key @last_light_debug_change = Graphics.frame_count # Scan all Events for Light Properties scan_lights # Fill the Shadowmap with Light Sources fill_shadowmap end #-------------------------------------------------------------------------- # * Update Shadowmap Z - Spriteset_Map # - Allows Fog over Shadow or Shadow over Fog #-------------------------------------------------------------------------- def update_shadowmap_z # If value of Remembered Fog over Shadow Value has changed if @last_fog_over_shadow != $game_system.fog_over_shadow # Change the Z Value for the Shadowmap @shadowmap.z = $game_system.fog_over_shadow ? @fog.z - 10 : @fog.z + 10 end # Remember state of property @last_fog_over_shadow = $game_system.fog_over_shadow end #-------------------------------------------------------------------------- # * Update Weather Z - Spriteset_Map # - Allows Shadows over Weather or Weather over Shadows (Rain, Snow, etc) #-------------------------------------------------------------------------- def update_weather_z # If value of Remembered Weather over Shadow Value has changed if @last_weather_over_shadow != $game_system.weather_over_shadow # Iterate Weather Sprites for weather_sprite in @weather.sprites # Determine if Weather Sprite is Over or Under the Shadowmap weather_sprite.z = ($game_system.weather_over_shadow) ? 3100 : 1000 end end # Remember state of property @last_weather_over_shadow = $game_system.weather_over_shadow end #-------------------------------------------------------------------------- # * Sort Lights - Spriteset_Map # - Sorts Lights into Groups for Rendering Order # event : Game_Event #-------------------------------------------------------------------------- def sort_light(event) # If Event is a Light if event.light # If Light has an After Shadow Flag and is not a Shadow Light if event.light.after_shadow and not event.light.shadow # Add Light Event to After Shadow Hash @after_shadow_chars[event.id] = event # If Light has a Shadow Flag elsif event.light.shadow # Add Light Event to Shadow Hash @shadow_chars[event.id] = event # Normal Light else # Add Light Events as Light Source @light_chars[event.id] = event end # If Light has a Cutout Flag if event.light.cutout or event.light.cutout_name != nil # Add Light Event to the Cutout Hash @cutout_chars[event.id] = event end end end #-------------------------------------------------------------------------- # * Scan Lights - Spriteset_Map # - Rechecks Events for Light Property to use as Lightsource in Shadows # - Called after a Map Refresh #-------------------------------------------------------------------------- def scan_lights # Create or Empty Characters with Lights Hash @light_chars = {} # Create or Empty Characters with Lights with a Shadow Flag @shadow_chars = {} # Create or Empty Characters with Lights with a After Shadow Flag @after_shadow_chars = {} # Create or Empty Characters with Cutout Light Flags @cutout_chars = {} # Add Player as a Light Source @light_chars[0] = $game_player # Get Light Sources from Map Events for event in $game_map.events.values # Sort Lights into Groups for Render Order sort_light(event) end # Clear the Scan Flag from the Game Map $game_map.scan_lights = false end #-------------------------------------------------------------------------- # * Light Rotate XY Opacity - Spriteset_Map # - Adjusts X, Y and Opacity values for Rotated Lights and Pendulums # char : Game_Character # x : X Coordinate for Light Position on Screen # y : Y Coordinate for Light Position on Screen # ow : Original Width # oh : Original Height # opacity : Opacity of Light (0 to 255) #-------------------------------------------------------------------------- def light_rotate_xy_opacity(char, x, y, ow, oh, opacity) # Zoom Level and Opacity of the Light for proper Rect in Stretch BLT zoom_x, zoom_y = char.light.zoom_xy[0], char.light.zoom_xy[1] # If Light has a Target Character, it is a Spotlight (ignores Arrays) if char.rotate_target.is_a?(Numeric) and char.light.spotlight[0] # Clamp Angle to 360 Degrees, * -1 for Counter Clockwise Rotation angle = char.light.angle %= 360 * -1 # Shorthand for Target Percent Value to adjust Zoom tp = (char.light.target_percent) ? char.light.target_percent : 1 # Convert Degrees to Radians radian = Math::PI * angle / 180 # Adjust the XY Coordinates to the Top Edge Center x += (ow * Math.sin(radian)).round / 2 * zoom_x * tp y += (oh * Math.cos(radian)).round / 2 * zoom_y * tp # Else If Light is a Pendulum elsif char.light.pendulum and char.pendulum # If Character is a Tile if char.tile_id != 0 # Set Pendulum Graphic Height for positioning ph = 16 * char.sprite_zoom_y # If Character Name is not Quotes elsif char.character_name != "" # Get the Character Graphic cg = RPG::Cache.character(char.character_name, char.character_hue) # Determine Pendulum Height from Graphic for positioning ph = cg.height / 4 * char.sprite_zoom_y end # Clamp Angle to 360 Degrees, * -1 for Counter Clockwise Rotation angle = char.angle %= 360 * -1 # Convert Degrees to Radians radian = Math::PI * angle / 180 # Adjust the XY Coordinates to the Top Edge Center x += ph * Math.sin(radian) y += ph * Math.cos(radian)- ph if char.light.pendulum_y # Adjust Y Coordinate for Pendulum Offset if it is set y += char.light.pendulum if char.light.pendulum.is_a?(Numeric) # If a Flag is set for Pendulum Opacity if char.light.pendulum_opacity # Pendulum fades out Light Opacity up to 90 Degree Angle opacity = opacity * Math.cos(radian) end end # Return Modified Values as Array return [x, y, opacity] end #-------------------------------------------------------------------------- # * Render Lightsource - Spriteset_Map # - Renders Image file into the Shadowmap # - This is where the "Magic" of this script happens! It isn't as difficult # as it sounds. If you are reading this, then youre definitely interested # in how this works. Thus, I will explain it here. First thing is you # need a Shadowmap. I used a Shadowmap that just covered the screen, but # you can set up a Shadowmap to cover whole maps also. Covering just the # screen was done for efficiency. Next, the Blend Type of the Shadowmap # sprite is set to 2 for Subtract. That also means your colors are going # to be Inverted. White becomes Black. Fill the Shadowmap with White and # that is going to produce a Black Shadowmap. After that, it is just a # matter of taking other Bitmaps and using stretch_blt to draw them into # the Shadowmap Bitmap. If you don't redraw the Shadowmap each Frame like # I do in this script and cover the whole map, you can use a "Fog of War" # in your game, but be warned as the Shadowmaps will not be saved when # you save a game or transfer maps, so extra code needs to be written to # do things like that. Also, covering an entire map will cause issues # with other scripts like Looping Maps because Lights will only cut out # the "Light Holes" on one side of the Shadowmap. This script works just # fine with Looping Maps because I only cover the Screen, not the whole # map. Other things to remember are that Colors are Inverted when you set # your Shadowmap Sprite to have a Blend Type of 2. As a result, the Image # you use as your Light Source will use a Solid Black to "cut out" a hole # in your Shadowmap. Any color in your Light Source Bitmap that is not a # fully Black color will leave some of the Shadowmap visible so it will # still be partly visible. So the more Color you add in your Light Source # the more of the Shadowmap is still visible, making your Colored Light a # bit darker than you expect. Much of the code here is used to determine # position adjustments for the features of the Script, but basic concept # is to take a Lightsource and use a Block Transfer (blt) or to Stretch # Block Transfer (stretch_blt, see RPG Maker Help File) to cut out the # Light from the Shadowmap. # char : Game_Character - Player or Events #-------------------------------------------------------------------------- def render_lightsource(char, cutout = nil) # If Light is Enabled for this Character if char.light.enabled and not char.transparent # If Light needs a Cutout of Character or Tile from Graphic if char.light.cutout and cutout # Create or Load a Bitmap that will Cutout the Character from Shadowmap light_bmp = shadow_cutout(char) # Quit if no Bitmap return if not light_bmp # If Light is a Shadow elsif char.light.shadow # Image of Character's Light Source (from Cache, not rotated) light_bmp = RPG::Cache.shadow(char.light.image_file) # If Shadow Color needs to be set to Shadowmap Color if char.light.shadow > 0 # Shorthand for current Shadowmap Color color = $game_system.get_shadowmap_color # Ignore Contents of Image and fill Bitmap with Shadowmap Color DLL light_bmp = fill_shadow(light_bmp, char, color) # Return if DLL was unsuccessful in processing return if not light_bmp # Set Opacity of the Image to Shadowmap Color Alpha opacity = $game_system.get_shadowmap_color.alpha end # Standard Light else # Image of Character's Light Source (from RPG::Cache, not rotated) light_bmp = RPG::Cache.light(char.light.image_file, char.light.hue) end # Remember Original Width and Original Height ow, oh = light_bmp.width, light_bmp.height # Use Overrides for Width and Height if Specifc Values used ow = char.light.width if char.light.width oh = char.light.height if char.light.height # Zoom Level and Opacity of the Light for proper Rect in Stretch BLT zoom_x, zoom_y = char.light.zoom_xy[0], char.light.zoom_xy[1] opacity = char.light.opacity # If Pendulum Light Zoom is in use if char.light.pendulum_zoom and char.pendulum # Clamp Angle to 360 Degrees, * -1 for Counter Clockwise Rotation angle = char.angle %= 360 * -1 # Convert Degrees to Radians radian = Math::PI * angle / 180 # Determine Zoom Multiplier - 0 Degrees, nothing added 90, Full Zoom pendulum_zoom = (Math.sin(radian) * char.light.pendulum_zoom).abs # Add Zoom Value to existing Zoom X and Y equally on both zoom_x += pendulum_zoom zoom_y += pendulum_zoom end # Adjust for Zoom w, h = ow * zoom_x, oh * zoom_y # Character's Screen X and Y so Light displays at Character scr_x, scr_y = char.screen_x, char.light_screen_y # Center the Light Image to the Position of the Character x, y = scr_x - (w / 2), scr_y - (h / 2) # Adjust the X and Y position of Light for any Position Offsets x += char.light.offset_xy[0] y += char.light.offset_xy[1] # If Light is Rotated, or Light is a Spotlight (target_percent) if char.light.angle != 0 or char.light.target_percent # Use DLL to Rotate Bitmap (return nil for performance when not needed) light_bmp = transformBitmapDLL(light_bmp, char, scr_x, scr_y, cutout) # Skip if no Bitmap is returned return if not light_bmp.is_a?(Bitmap) # Recalculate Width and Height since dimensions have changed w, h = light_bmp.width * zoom_x, light_bmp.height * zoom_y # Override Width and Height if specified w = char.light.width if char.light.width h = char.light.height if char.light.height # Center the Light Image to the Position of the Character x, y = scr_x - (w / 2), scr_y - (h / 2) # Readjust the X and Y position of Light for any Position Offsets x += char.light.offset_xy[0] y += char.light.offset_xy[1] end # Adjust X, Y, and Opacity for Rotations x, y, opacity = light_rotate_xy_opacity(char, x, y, ow, oh, opacity) # If Character is a Cutout and Cutout is called (doesn't work perfect) if char.light.cutout and cutout # Recalculate X and Y positions and Width and Height x, y, w, h = cutout_make_xywh(light_bmp, char, scr_x, scr_y) # Force Opacity to Max for Cutouts opacity = 255 end # Adjust X and Y Coordinates if needed to display on the Screen x = correct_light_screen_x(x, w) y = correct_light_screen_y(y, h) # Adjust Light X Position for Screen Shake x = adjust_light_screen_shake_x(x) y = adjust_light_screen_shake_y(y) # Config Options Shorthand for Screen Width and Screen Height sw, sh = Light_Config::Width, Light_Config::Height # Create Rectangle with Positions and adjusted size values rect = Rect.new(x, y, w, h) # Determine Bottom and Right Positions for Offscreen Checks bx, by = rect.x + rect.width, rect.y + rect.height # If Rectangle won't draw on the Shadowmap if bx < 0 or by < 0 or rect.x > sw or rect.y > sh # Free Memory of Temporary Bitmap light_bmp.dispose if cutout and char.tile_id == 0 # Skip drawing this Lightsource on the Shadowmap return end # If Light is a Shadow if char.light.shadow # If Flag is set to Match Opacity to Shadowmap Color if char.light.shadow_match_opacity # Set Opacity of the Image to Shadowmap Color Alpha opacity = $game_system.get_shadowmap_color.alpha end # If Light is a Shadow requires a Cutout to match the Shadowmap if char.light.shadow == 2 # Force Opacity of the Image to Shadowmap Color Alpha opacity = $game_system.get_shadowmap_color.alpha # Cut out a Hole in the Shadowmap to match Shadowmap Color @shadowmap.bitmap.fill_rect(rect, Color.new(0,0,0,0)) end end # Draw Image into the Shadowmap Sprite to create a Light Hole in Shadow @shadowmap.bitmap.stretch_blt(rect, light_bmp, light_bmp.rect, opacity) # Free Memory of Temporary Bitmap light_bmp.dispose if cutout and char.tile_id == 0 end end #-------------------------------------------------------------------------- # * Adjust Light Screen Shake X - Spriteset_Map # - Using Screen Shake, the Shadowmap and Lights do not line up properly # with the displayed screen. This method adjusts the X Position of # the Light so that it lines up properly with any Shaking that may # be occuring # - Returns X value adjusted for Screen Shake Position # x : X Coordinate of top left corner of Light Bitmap #-------------------------------------------------------------------------- def adjust_light_screen_shake_x(x) # Adjusts Light Position for Inverse Value of Screen Shaking return x - $game_screen.shake end #-------------------------------------------------------------------------- # * Adjust Light Screen Shake Y - Spriteset_Map # - This code is currently unused for modifying the value of Y due # to Screen Shake command only affecting the X Screen Coordinate # - Intended as a Placeholder for other scripts which may need to adjust # the value of Y for "Vertical Shaking" # y : Y Coordinate of top left corner of Light Bitmap #-------------------------------------------------------------------------- def adjust_light_screen_shake_y(y) # No Modifications to Y, Intended for Aliases to make changes return y end #-------------------------------------------------------------------------- # * Adjust Shadowmap Screen Shake X - Spriteset_Map # - Using Screen Shake, the Shadowmap and Lights do not line up properly # with the displayed screen. This method adjusts the X Position of # the Shadowmap so that it lines up properly with any Shaking that may # be occuring. # - Nothing to return #-------------------------------------------------------------------------- def adjust_shadowmap_screen_shake_x # Adjusts Light Position for Inverse Value of Screen Shaking @shadowmap.x = $game_screen.shake end #-------------------------------------------------------------------------- # * Correct Light Screen X - Spriteset_Map # - Method for any Screen X Adjustments if needed, can be aliased # x : Current X Coordinate to display Light on Shadowmap # width : Calculated Value of the Width of the Shadowmap Image #-------------------------------------------------------------------------- def correct_light_screen_x(x, width) # Return Unmodified Value (see alternate definition for other adjustments) return x end #-------------------------------------------------------------------------- # * Correct Light Screen Y - Spriteset_Map # - Method for any Screen Y Adjustments if needed, can be aliased # y : Current Y Coordinate to display Light on Shadowmap # height : Calculated Value of the Height of the Shadowmap Image #-------------------------------------------------------------------------- def correct_light_screen_y(y, height) # Return Unmodified Value (see alternate definition for other adjustments) return y end #-------------------------------------------------------------------------- # If Heretic's Loop Maps is installed #-------------------------------------------------------------------------- if Game_Map.method_defined?(:map_loop_passable?) #------------------------------------------------------------------------ # * Correct Light Screen X - Loop Map Version - Spriteset_Map # - Adjusts X Coordinate for Looping Maps when needed # - Lights at Bottom Edge of Light may need this if Light is out of range # x : Current X Coordinate to display Light on Shadowmap # width : Calculated Value of the Width of the Shadowmap Image #------------------------------------------------------------------------ def correct_light_screen_x(x, width) # If Map Loops Vertical if $game_map.loop_horizontal? # Shorthand for Map Width in Pixels mw = $game_map.width * 32 # Subtract Height of Map if Light Y exceeds Display Values x -= mw if x + width >= mw end # Return Value with any adjustments return x end #------------------------------------------------------------------------ # * Correct Light Screen Y - Loop Map Version - Spriteset_Map # - Adjusts Y Coordinate for Looping Maps when needed # - Lights at Bottom Edge of Light may need this if Light is out of range # y : Current Y Coordinate to display Light on Shadowmap # height : Calculated Value of the Height of the Shadowmap Image #------------------------------------------------------------------------ def correct_light_screen_y(y, height) # If Map Loops Vertical if $game_map.loop_vertical? # Shorthand for Map Height in Pixels mh = $game_map.height * 32 # Subtract Height of Map if Light Y exceeds Display Values y -= mh if y + height >= mh end # Return Value with any adjustments return y end end #-------------------------------------------------------------------------- # * Fill Shadowmap - Spriteset_Map # - Updates the Shadowmap Sprite that covers the screen # - Renders Lights, Shadows, After Shadow Lights, then Cutouts in that order #-------------------------------------------------------------------------- def fill_shadowmap # Scan all Events for Light Properties if Map was Refreshed scan_lights if $game_map.scan_lights # Determine whether the Shadowmap Sprite is Visible @shadowmap.visible = $game_system.dynamic_lights # No further processing if System Option is Disabled return unless $game_system.dynamic_lights # Clear the Bitmap for Memory @shadowmap.bitmap.clear # Correct Position for Screen Shake adjust_shadowmap_screen_shake_x # Rectangle for Shadowmap to fill the Screen shadow_rect = Rect.new(0, 0, Light_Config::Width, Light_Config::Height) # Set Opacity to match Game System @shadowmap.opacity = $game_system.get_shadowmap_opacity # Fill Bitmap with Shadowmap Color @shadowmap.bitmap.fill_rect(shadow_rect, $game_system.get_shadowmap_color) # Iterate each Lightsource after Sorting for k in @light_chars.keys.sort # Get the Character from the Hash character = @light_chars[k] # If Character Light Object does not exist if character.light.nil? # Remove Character from Hash @light_chars.delete(character.id) # Skip to next Light next end # Render the Image File of the Lightsource into the Shadowmap render_lightsource(character) end # Iterate each Shadowsource after Sorting for k in @shadow_chars.keys.sort # Get the Character from the Hash character = @shadow_chars[k] # If Character Light Object does not exist if character.light.nil? # Remove Character from Hash @shadow_chars.delete(character.id) # Skip to next Shadow next end # Render the Image File of the Shadowsource into the Shadowmap render_lightsource(character) end # Iterate each Lightsource to render After Shadow after Sorting for k in @after_shadow_chars.keys.sort # Get the Character from the Hash character = @after_shadow_chars[k] # If Character Light Object does not exist if character.light.nil? # Remove Character from Hash @after_shadow_chars.delete(character.id) # Skip to next Shadow next end # Render the Image File of the Shadowsource into the Shadowmap render_lightsource(character) end # Iterate each Cutout after Sorting for k in @cutout_chars.keys.sort # Get the Character from the Hash character = @cutout_chars[k] # If Character Light Object does not exist if character.light.nil? # Remove Character from Hash @cutout_chars.delete(character.id) # Skip to next Shadow next end # Render the Image File of the Shadowsource into the Shadowmap render_lightsource(character, true) end end #-------------------------------------------------------------------------- # * Update Shadowmap Debug - Spriteset_Map # - Toggles Lights and Blend mode when in Debug and F5 Key is pressed #-------------------------------------------------------------------------- def update_shadowmap_debug # If Debug is Enabled (Automatically Disabled if not $DEBUG) if $game_system.light_debug # If the F5 Key is being pressed and Delay if Input.press?(Input::F5) and Graphics.frame_count - 10 > @last_light_debug_change # If Dynamic Lights are Enabled if $game_system.dynamic_lights # If Blend Type is 2 (Subtract, normal for a Shadowmap) if @shadowmap.blend_type == 2 # Change the Blend Type to Normal @shadowmap.blend_type = 0 else # Disable Dynamic Lights $game_system.dynamic_lights = false # Set the Blend Type of the Shadowmap to Subtract @shadowmap.blend_type = 2 end else # Turn Dynamic Light Effects ON $game_system.dynamic_lights = true end # Remember last time F5 was pressed (prevents "sticky keys") @last_light_debug_change = Graphics.frame_count end end end #-------------------------------------------------------------------------- # * Shadowmap Update - Spriteset_Map # - Updates the Shadowmap Sprite that covers the screen #-------------------------------------------------------------------------- def shadowmap_update # Update Debug Options update_shadowmap_debug # Determine if Fog is over Shadow or not update_shadowmap_z if @shadowmap # Determine if Weather is over Shadow or not update_weather_z # Fill the Shadowmap with Light Sources allow visibility fill_shadowmap if @shadowmap end #-------------------------------------------------------------------------- # * Update - Spriteset_Map # - Main Update Method #-------------------------------------------------------------------------- alias light_spritemap_update update unless $@ def update # Call Original or other Aliases light_spritemap_update # Update the Shadowmap shadowmap_update end #-------------------------------------------------------------------------- # * Dispose - Spriteset_Map #-------------------------------------------------------------------------- alias light_spritemap_dispose dispose unless $@ def dispose # Call Original or other Aliases light_spritemap_dispose # Dispose of Shadowmap @shadowmap.dispose # Dispose of any Rotated Bitmaps (used as a Cache for performance) @rotated_lights.each {|l| l[1].dispose} # Dispose of any Shadowed Bitmaps (used as a Cache for performance) @shadowed_lights.each {|l| l[1].dispose} # Dispose of any Cutout Bitmaps (used as a Cache for performance) @cutout_lights.each {|l| l[1].dispose} end #-------------------------------------------------------------------------- # * Light Make Rotate Size - Spriteset_Map # - Determines a new Bounding Box of a rotated Light based on Angle # - Formulas by KK20 # bitmap : RPG::Bitmap from RPG::Cache # sin : Trig value # cos : Trig value # w : Bitmap Width # h : Bitmap Height #-------------------------------------------------------------------------- def light_make_rotate_size(w, h, sin, cos) # Use Positive Values sin, cos = sin.abs, cos.abs # Determine Size of new Bitmap using Max for Rectangles new_width = [(w * cos + h * sin).to_i, w].max new_height = [(w * sin + h * cos).to_i, h].max # Return New Values return [new_width, new_height] end #-------------------------------------------------------------------------- # * Resize Light Bmp - Spriteset_Map # - Uses Trig to determine a Rectangle that will contain the Rotated Image # - Creates a new Bitmap and uses Stretch Block Transfer to resize the # source image prior to rotation # light_bmp : RPG::Bitmap used as Lightsource # character : Game_Character - Player or Event # cutout : Light has a Cutout Flag (BOOL) #-------------------------------------------------------------------------- def resize_light_bmp(light_bmp, character, cutout) # Determine Width and Height w = light_bmp.width h = light_bmp.height * character.light.target_percent # Create a New Bitmap to hold the strecthed image bmp = Bitmap.new(w, h.round) # Stretch the Old Light Bitmap to the New Container bmp.stretch_blt(Rect.new(0,0, w, h.round), light_bmp, light_bmp.rect) # Free Source Memory (only dispose of bmps that arent from Cache) light_bmp.dispose if cutout and character.tile_id == 0 # Return the New Image return bmp end #-------------------------------------------------------------------------- # * Cache Light Cutout - Spriteset_Map # - Stores a Cached version of Image that was manipulated # bitmap : Bitmap # character : Game_Character #-------------------------------------------------------------------------- def cache_light_cutout(bitmap, character) # If Bitmap is not a Bitmap or it is Disposed if not bitmap.is_a?(Bitmap) or bitmap.disposed? # Quit processing return end # Checked for Cached Images if @cutout_lights[character.id] # Dispose of the Cached Image @cutout_lights[character.id].dispose end # If Character is a Map Tile Graphic if character.tile_id >= 384 # Use Tileset Name as Name name = $game_map.tileset_name # Tileset ID in use by Character tile_id = character.tile_id # If Event is using a specified Cutout Image Name elsif character.light.cutout_name and character.light.cutout_name != "" # Use File Name as Name (including Paths for MyFolder/MyCutout.png) name = character.light.cutout_name # Set Tileset ID to 0 for specified Cutout tile_id = 0 # If Character Graphic is not an empty string elsif character.character_name != "" # Use Character Name as Name name = character.character_name # Set Tileset ID to nil for Character tile_id = 0 else # Quit processing return end # If any Cutout Lights are already Cached if @cutout_lights[character.id] # Dispose of them before storing the new Light @cutout_lights[character.id].dispose end # Create a Cache Object to store the modified version of the Image @cutout_lights[character.id] = Cache_Light.new(bitmap) @cutout_lights[character.id].name = name @cutout_lights[character.id].tile_id = tile_id end #-------------------------------------------------------------------------- # * Shadow Cutout - Spriteset_Map # - Uses Character or Tileset Graphic and Cuts out a hole in the Shadowmap # - Characters use DLL to fill RGB Channels with Black for a Cutout Color # and appears Transparent in game # - Tiles and Characters are Cached # - Character Graphics cache the full spritesheet, then creates a Bitmap # that uses BLT to transfer the appropriate Animation Frame to the Bitmap # then returns the Temporary Bitmap, which is later disposed in rendering # - Tiles create a 32x32 Bitmap that is cached instead of trying to recache # the entire tileset map, which can sometimes be massive, 50 megs+, so # a smaller 32x32 cached tile is faster to manage # - Avoids calling the DLL file when ever possible for performance # char : Game_Character (Player or Events) #-------------------------------------------------------------------------- def shadow_cutout(char) # Return nothing if there is no Light, Bitmap, not Enabled or no Shadowmap return nil if not char.light return nil if not char.light.enabled return nil if not char.light.cutout return nil if not @shadowmap # Use Cutout File Name if specified or just use the Character Name if char.light.cutout_name and char.light.cutout_name != "" name = char.light.cutout_name elsif char.character_name != "" name = char.character_name elsif char.tile_id >= 384 name = $game_map.tileset_name else # Empty Character, has no Graphic to modify return nil end # Tile ID of Character tile_id = (char.tile_id and char.tile_id >= 384 ) ? char.tile_id : 0 # Checked for Cached Images if @cutout_lights[char.id] and @cutout_lights[char.id].name == name and @cutout_lights[char.id].tile_id == tile_id and not @cutout_lights[char.id].bitmap.disposed? # Return the Cached Version of the Bitmap if it is a Tile (not dispsed) return @cutout_lights[char.id].bitmap if char.tile_id >= 384 # Get the Cached Bitmap which has already been modified by the DLL src = @cutout_lights[char.id].bitmap # Width and Height of Character w, h = src.width / 4, src.height / 4 # Rectangle for Block Transfer rect = Rect.new(char.pattern * w, (char.direction - 2) / 2 * h, w, h) # Create a New Bitmap for Block Transfer (disposed in render_lightsource) bmp = Bitmap.new(w, h) # Use Block Transfer for the Graphic to use bmp.blt(0, 0, src, rect) # Return bmp with Block Transferred Frame for Animation return bmp end # If a Cutout Character is specified if char.light.cutout and char.light.cutout_name != "" # Load the Bitmap from the RPG::Cache for Characters src = RPG::Cache.character(char.light.cutout_name, 0).clone # Use DLL to fill Clone with Black to cut a Hole and ignore Alpha result = Fill_Color_DLL.call(src.object_id, [0,0,0].pack('F*')) # If DLL is successful if result == 0 # Width and Height of Character w, h = src.width / 4, src.height / 4 # Rectangle for Block Transfer rect = Rect.new(char.pattern * w, (char.direction - 2) / 2 * h, w, h) # Create a New Bitmap for DLL bmp = Bitmap.new(w, h) # Use Block Transfer for the Graphic to use bmp.blt(0, 0, src, rect) # Cache the Full Spritesheet for Performance (Clones src Graphic) cache_light_cutout(src, char) # Free Memory src.dispose # Return the modified Temp Bitmap, disposed by render_lightsource return bmp else # Free Memory src.dispose if src.is_a?(Bitmap) and not src.disposed? end # If Character is using a Tileset Graphic elsif char.tile_id >= 384 # Use Map Tileset as Graphic Source src = @tilemap.tileset # XY Position of Tile from the Tilemap x = (char.tile_id - 384) % 8 * 32 y = (char.tile_id - 384) / 8 * 32 # Rectangle for Block Transfer rect = Rect.new(x, y, 32, 32) # Create a New Bitmap for DLL bmp = Bitmap.new(32, 32) # Use Block Transfer for the Graphic to use bmp.blt(0, 0, src, rect) # Use DLL to fill this Bitmap with Black to cut a Hole and ignore Alpha result = Fill_Color_DLL.call(bmp.object_id, [0,0,0].pack('F*')) # If DLL is successful if result == 0 # Cache the Tile for Performance (Clones the Temporary Bitmap) cache_light_cutout(bmp, char) # Free Memory and Dispose of Temporary Bitmap bmp.dispose # Return the modified Bitmap from Cached Object (not disposed) return @cutout_lights[char.id].bitmap else # Free Memory bmp.dispose if bmp.is_a?(Bitmap) and not bmp.disposed? end # If Character is using a Character Graphic elsif char.character_name != "" # Clone the original Bitmap from the RPG::Cache so Cache is Unaltered src = RPG::Cache.character(char.character_name, char.character_hue).clone # Use DLL to fill this Bitmap with Black to cut a Hole and ignore Alpha result = Fill_Color_DLL.call(src.object_id, [0,0,0].pack('F*')) # If DLL is successful if result == 0 # Width and Height of Character w, h = src.width / 4, src.height / 4 # Rectangle for Block Transfer rect = Rect.new(char.pattern * w, (char.direction - 2) / 2 * h, w, h) # Create a New Bitmap to hold Character Sprite (smaller than Source) bmp = Bitmap.new(w, h) # Use Block Transfer for the Graphic to use bmp.blt(0, 0, src, rect) # Cache the Full Spritesheet for Performance (Clones src Graphic) cache_light_cutout(src, char) # Free Memory src.dispose # Return the modified Temp Bitmap, disposed in render_lightsource return bmp else # Free Memory src.dispose if src.is_a?(Bitmap) and not src.disposed? end end end #-------------------------------------------------------------------------- # * Cutout Make XYWH - Spriteset_Map # - Recalculates and returns X and Y Positions and new Width and Height # NOTE: Use of \sprite_zoom_x / y with different values on Rotated Lights # do not line up, but have been left in because they are close when # the angle of rotation is a multiple of 90 degrees. Rotated Cutouts # are not recommended. # light_bmp : RPG::Bitmap from Cache # char : Game Character # scr_x : Screen X (prevents recalculating) # scr_y : Screen Y (prevents recalculating) #-------------------------------------------------------------------------- def cutout_make_xywh(light_bmp, char, scr_x, scr_y) # Force Width and Height of Cutout to match Character Sprite w, h = light_bmp.width, light_bmp.height # Multiply any Sprite Zooms if used to mach Character Size w *= char.sprite_zoom_x if char.sprite_zoom_x h *= char.sprite_zoom_y if char.sprite_zoom_y # Adjust X and Y to match Character for Cutout Lights x = scr_x - (w / 2) y = scr_y - h # If Character is a Pendulum if char.light.pendulum and char.pendulum # If Character is a Tile if char.tile_id != 0 # Set Pendulum Graphic Height for positioning ph = 16 * char.sprite_zoom_y # If Character Name is not Quotes elsif char.character_name != "" # Get the Character Graphic cg = RPG::Cache.character(char.character_name, char.character_hue) # Determine Pendulum Height from Graphic for positioning ph = cg.height / 4 * char.sprite_zoom_y end # Close approximation of Position x += (ph / 2) * Math.sin(radian) # If Character is Rotated elsif char.light.angle != 0 # Convert Degrees to Radians, Negative 1 makes Counter Clockwise radian = Math::PI * (char.light.angle %= 360 * -1) / 180 # If Character Graphic with Spritesheets if char.character_name != "" # Get Cached Character Graphic from RPG::Cache ccg = RPG::Cache.character(char.character_name, char.character_hue) # Character Graphic Width and Height sprite_w, sprite_h = ccg.width / 4.0, ccg.height / 4.0 else # Use Standard Tile Size as Width and Height sprite_w, sprite_h = 32, 32 end # Remember Original Width and Height before any Multipliers sprite_ow, sprite_oh = sprite_w, sprite_h # If Zooms are Not Equal (NOT WORKING) if char.sprite_zoom_x != char.sprite_zoom_y # Shorthand for Sprite Zooms szoom_x, szoom_y = char.sprite_zoom_x, char.sprite_zoom_y # Determine Difference in Zoom Levels zd = [szoom_x, szoom_y].max - [szoom_x, szoom_y].min # If X Zoom is more than Y Zoom if szoom_x > szoom_y # Recalculate Width and Height using Trig w = light_bmp.width * (zd * Math.cos(radian).abs + szoom_y) h = light_bmp.height * (szoom_x - (zd * Math.sin(radian).abs)) # Then Y Zoom is greater than X Zoom else # Recalculate Width and Height using Trig w = light_bmp.width * (zd * Math.sin(radian).abs + szoom_x) h = light_bmp.height * (szoom_y - (zd * Math.sin(radian).abs)) end # Recalculate X and Y Coordinates due to change in Width and Height x, y = scr_x - (w / 2), scr_y - h else # Apply any Sprite Zooms if applicable sprite_w *= char.sprite_zoom_x if char.sprite_zoom_x sprite_h *= char.sprite_zoom_y if char.sprite_zoom_y end # Adjust Y Position for difference in Offset y += (h - sprite_h) / 2.0 # Check Center of Rotation (these dont work perfect due to DLL) if char.rotate_center == 1 # Center of Sprite # Adjust Position for Zoom Differences y += (sprite_h - sprite_oh) / 2.0 if char.sprite_zoom_y elsif char.rotate_center == 0 # Top of Sprite # Adjust X Position for Rotational Offsets using Trig x += sprite_w / 2.0 * Math.sin(radian) # Adjust Y Position to Top of Sprite y += sprite_h / 2.0 * Math.cos(radian) # Adjust for difference in position y -= sprite_h / 2.0 # Adjust for Difference in Zoom Position y += sprite_h - sprite_oh if char.sprite_zoom_y elsif char.rotate_center == 2 # Bottom of Sprite # Adjust X Position for Rotational Offsets using Trig x -= sprite_w / 2.0 * Math.sin(radian) # Adjust Y Position to Bottom of Sprite y -= sprite_h / 2.0 * Math.cos(radian) # Adjust for difference in position y += sprite_h / 2.0 end end # Return calculated values as Array return [x, y, w, h] end #-------------------------------------------------------------------------- # * TransformBitmapDLL - Spriteset_Map # - Uses DLL to rotate a cached RGSS Bitmap Image for performance # - Returns a new Bitmap, or nil if certain conditions not met # - Code assistance by KK20 and Blizzard, who basically wrote the DLL # NOTE: Images in RPG::Cache are NEVER rotated # light_bmp : RPG::Bitmap to be rotated # character : Game_Character (Player or Events) # scr_x / y : Screen X and Y of Char, passed as args for performance # cutout : If Light is using a Cutout Flag (needs disposal) #-------------------------------------------------------------------------- def transformBitmapDLL(light_bmp, character, scr_x, scr_y, cutout) # Return nothing if there is no Light, Bitmap, not Enabled or no Shadowmap return nil if not character.light return nil if not light_bmp return nil if not character.light.enabled return nil if not @shadowmap # Clamp Angle to max of 360 degrees, -1 to rotate counter-clockwise angle = character.light.angle % 360.0 * -1 # Convert Angle to Radian Value rads = Math::PI * angle / 180 # Determine Trig values for Sin and Cos sin = Math.sin(rads).approx.to_f cos = Math.cos(rads).approx.to_f # Target Percent tp = character.light.target_percent # Source Bitmap Width src_w = light_bmp.width # If Light is using a Target Percent src_h = (tp) ? (tp * light_bmp.height).round : light_bmp.height # Determine new Width and Height of the new Bitmap new_w, new_h = light_make_rotate_size(src_w, src_h, sin, cos) # If a Bitmap has already been Rotated and Angle has not been changed if @rotated_lights[character.id] and @rotated_lights[character.id].name == character.light.image_file and @rotated_lights[character.id].angle == angle and @rotated_lights[character.id].hue == character.light.hue # If the saved Image has not been Disposed and size matches if not @rotated_lights[character.id].bitmap.disposed? and @rotated_lights[character.id].bitmap.width == new_w and @rotated_lights[character.id].bitmap.height == new_h # Free Memory of Temporary Bitmap that need Disposal light_bmp.dispose if cutout and character.tile_id == 0 # Use the Stored Image instead of calling the DLL again return @rotated_lights[character.id].bitmap end end # If Light is using a Target Percent (for Spotlights) if character.light.target_percent # Resize the Light Image Bitmap light_bmp = resize_light_bmp(light_bmp, character, cutout) end # Containing Matrix for DLL, 4 Corners, Top Left is 0, calculate others matrix = [cos, -sin, 0.0, sin, cos, 0.0, 0.0, 0.0, 1.0] # Create a new RGSS Bitmap to use as a Blank Canvas to draw rotated Light lightCanvas = Bitmap.new(new_w, new_h) # Change BOOL to Int for DLL (DLL file expects an Int, not a Bool interpolate = ($game_system.transform_interpolate) ? 1 : 0 # Use the DLL to rotate the Canvas result = TransformBitmap.call(light_bmp.object_id, lightCanvas.object_id, matrix.pack('F*'), interpolate) # Free Memory if needed of Temporary Bitmap light_bmp.dispose if cutout and character.tile_id == 0 # If DLL was successful in rotating the Canvas, it returns 0 if result == 0 # If there is an old Image if @rotated_lights[character.id] # Erase any old Images @rotated_lights[character.id].dispose end # Store the Rotated Bitmap in a Hash for Caching and Performance @rotated_lights[character.id] = Cache_Light.new(lightCanvas) @rotated_lights[character.id].name = character.light.image_file @rotated_lights[character.id].angle = angle @rotated_lights[character.id].hue = character.light.hue # Free Memory lightCanvas.dispose # Return the Image rotated by the DLL (changed to light_bmp later) return @rotated_lights[character.id].bitmap elsif lightCanvas.is_a?(Bitmap) and not light_canvas.disposed? # Display Error when Editing print "Rotate Bitmap DLL Failure with Bitmap" if $DEBUG # Free Memory lightCanvas.dispose else # Display Error when Editing print "Rotate Bitmap DLL Failure no Bitmap" if $DEBUG end end #-------------------------------------------------------------------------- # * Fill Shadow - Spriteset_Map # - Uses DLL to fill Bitmap with RGB Color of Shadowmap Color # NOTE: This will dispose of any data in the Image except Alpha Channel # shadow_bmp : RPG Bitmap from Cache # character : Game_Character # color : Color - Color.new(R, G, B[, A*]) #-------------------------------------------------------------------------- def fill_shadow(shadow_src, character, color) # Return nothing if there is no Light, Bitmap, not Enabled or no Shadowmap return nil if not character.light return nil if not shadow_src return nil if not character.light.enabled return nil if not @shadowmap # If a Bitmap has already been Rotated and Color has not been changed if @shadowed_lights[character.id] and @shadowed_lights[character.id].name == character.light.image_file and @shadowed_lights[character.id].color == color # If the saved Image has not been Disposed and size matches if not @shadowed_lights[character.id].bitmap.disposed? # Use the Stored Image instead of calling the DLL again return @shadowed_lights[character.id].bitmap end end # Create an Array for Packing for DLL Usage color_array = [color.red, color.green, color.blue] # Clone the File so Original from RPG::Cache is Unmodified shadow_bmp = shadow_src.clone # Use DLL to fill the Clone with Shadowmap Color and ignore Alpha Channel result = Fill_Color_DLL.call(shadow_bmp.object_id, color_array.pack('F*')) # If DLL was successful in rotating the Canvas, it returns 0 if result == 0 # If there is an old Image if @shadowed_lights[character.id] # Erase any old Images @shadowed_lights[character.id].dispose end # Store the Rotated Bitmap in a Hash for Caching and Performance @shadowed_lights[character.id] = Cache_Light.new(shadow_bmp) @shadowed_lights[character.id].name = character.light.image_file @shadowed_lights[character.id].color = color # Free Memory shadow_bmp.dispose # Return the Image rotated by the DLL (changed to light_bmp later) return @shadowed_lights[character.id].bitmap # If Clone Image is in memory elsif shadow_bmp.is_a?(Bitmap) and not shadow_bmp.disposed? # Free Memory shadow_bmp.dispose end end end #============================================================================== # ** Game_System #============================================================================== class Game_System #-------------------------------------------------------------------------- # * Public Instance Variables - Game_System #-------------------------------------------------------------------------- attr_accessor :dynamic_lights # Enable / Disable Dynamic Lights attr_accessor :shadowmap_color # Color of Shadowmap (Inverted) attr_accessor :shadowmap_color_duration # Used during Dawn and Dusk attr_accessor :shadowmap_opacity # Opacity of Shadowmap for Lights attr_accessor :shadowmap_day_color # Color of Shadowmap when Day attr_accessor :shadowmap_night_color # Color of Shadowmap when Night attr_accessor :shadowmap_indoor_color # Color of Shadowmap when Indoor attr_accessor :shadowmap_lightning_color # Color of Lightning Flash attr_accessor :shadowmap_lightning_duration # Current Counter of Lightning attr_accessor :shadowmap_lightning_period # Total Duration of Lightning attr_accessor :lightning_default_duration # Used with Auto Storm attr_accessor :lightning_first_frames # Duration of First Flash attr_accessor :lightning_double_frames # Duration of Lightning Pause attr_accessor :fog_over_shadow # Determines Screen Z of Fog attr_accessor :weather_over_shadow # Determines Screen Z of Weather attr_accessor :dawn_time # Time (in Sec) when Dawn occurs attr_accessor :dusk_time # Time (in Sec) when Dusk occurs attr_accessor :dawn_dusk_duration # Duration of Transition (in Sec) attr_accessor :show_formatted_time # Display Clock with current Time attr_accessor :show_sunmoon_time # Display Rotating Sun / Moon attr_accessor :clock_match_tone # Match Tone of Clock to Screen attr_accessor :minutes_per_day # Number of Real Minutes per Day attr_accessor :min_inn_time # Minimum Time or Advance Days attr_accessor :pause_time_interpreter # Stops Clock when Interpreter attr_accessor :pause_time_in_battle # Stops Clock when In Battle attr_accessor :lightning_delay # Array [Min Delay, Max Delay] attr_accessor :thunder_sound # Sound File for Lightning attr_accessor :thunder_volume # [Outside Volume, Inside Volume] attr_accessor :thunder_delay # Array [Min Delay, Max Delay] attr_accessor :thunder_dampen_pitch # Dampens Pitch when Inside attr_accessor :transform_interpolate # Makes Images look less Bitty attr_reader :total_days # Number of Days since Game Start attr_reader :light_debug # Debugging of Light with F5 #-------------------------------------------------------------------------- # * Object Initialization - Game_System # - Adds Option for Dynamic Lighting and Weather Options #-------------------------------------------------------------------------- alias dynamic_lighting_system_initialize initialize unless $@ def initialize # Call Original or other Aliases dynamic_lighting_system_initialize # Run Dynamic Lights Property @dynamic_lights = Light_Config::Dynamic_Lights_Default # Shadowmap Color for use by Spriteset Map @shadowmap_color = Light_Config::Start_Color # Use for Transitioning Shadowmap Colors @shadowmap_color_target = @shadowmap_color @shadowmap_color_duration = 0 # Shadowmap Opacity for use by Spriteset Map @shadowmap_opacity = Light_Config::Shadow_Opacity # Use for Transitioning Shadowmap Opacity @shadowmap_opacity_target = @shadowmap_opacity @shadowmap_opacity_duration = 0 # Default Colors for Day / Night Transitions and Indoors @shadowmap_day_color = Light_Config::Shadow_Day_Color @shadowmap_night_color = Light_Config::Shadow_Night_Color @shadowmap_indoor_color = Light_Config::Shadow_Indoor_Color # Colors and Duration of Shadowmap Lightning Flashes Initial Values @shadowmap_lightning_color = Light_Config::Shadow_Lightning_Color @shadowmap_lightning_current_color = Color.new(0,0,0,0) @shadowmap_lightning_color_start = Light_Config::Shadow_Lightning_Color @shadowmap_lightning_duration = 0 @shadowmap_lightning_period = 0 # Auto Storm Variables @lightning_default_duration = Light_Config::Lightning_Default_Duration @lightning_delay = Light_Config::Lightning_Delay @thunder_sound = Light_Config::Thunder_Sound @thunder_volume = Light_Config::Thunder_Volume @thunder_delay = Light_Config::Thunder_Delay @thunder_dampen_ptich = Light_Config::Thunder_Dampen_Pitch # Used to check for Delays @storm_counter = 0 # Next Lightning and Sound Delay Defaults @next_lightning_delay = nil @next_thunder_delay = nil # Allows for a Hollywood Style Double Lightning Flash @lightning_first_frames = Light_Config::Lightning_First_Frames # Pause between First and Second Lightning Flash @lightning_double_frames = Light_Config::Lightning_Double_Frames # Allow for Transition of Indoor Color @shadowmap_indoor_color_target = @shadowmap_indoor_color @shadowmap_indoor_color_duration = 0 # Dawn Time (in Seconds) @dawn_time = Light_Config::Dawn_Time # Dusk Time (in Seconds) @dusk_time = Light_Config::Dusk_Time # Duration of Transition from Day to Night when Switches are turned on @dawn_dusk_duration = Light_Config::Dawn_Dusk_Duration # # Displays Clock with current Time @show_formatted_time = false # Displays Rotating Sun / Moon Graphic @show_sunmoon_time = true # Match Tone of Clock (SunMoon and Formatted Time) to Game Screen @clock_match_tone = Light_Config::Clock_Match_Tone # Dawn Faded Flag @shadowmap_dawn_faded = false # Dusk Faded Flag @shadowmap_dusk_faded = false # Number of Minutes in Real Time for one Day in Game Time @minutes_per_day = Light_Config::Minutes_Per_Day # Minimum Time required to fully rest at an Inn @min_inn_time = Light_Config::Min_Inn_Time # Remember if the Minutes Per Day has changed, start with Same Value @last_minutes_per_day = @minutes_per_day # Default for putting Fog Over the Shadowmap @fog_over_shadow = Light_Config::Fog_Over_Shadow # Default for putting Weather (Rain / Snow) Over the Shadowmap @weather_over_shadow = Light_Config::Weather_Over_Shadow # Whether to use Image Interpolation for better looking Stretch / Rotates @transform_interpolate = Light_Config::Transform_Interpolate # Total Number of Days in Game Time since game was started @total_days = 0 # Pauses advancement of the Game Clock when Interpreter is Running @pause_time_interpreter = Light_Config::Pause_Time_Interpreter # Pauses advancement of the Game Clock when In Battle @pause_time_in_battle = Light_Config::Pause_Time_In_Battle # Light Debug with F5 Key @light_debug = (Light_Config::Debug and $DEBUG) ? Light_Config::Debug : nil end #-------------------------------------------------------------------------- # * Update Light Duration Checks - Game_System # - Watches for changes to Minutes Per Day and recalculates Durations #-------------------------------------------------------------------------- def update_light_duration_checks # If Last Minutes Per Day has changed if @minutes_per_day != @last_minutes_per_day # Determine if Recalulation is needed if @shadowmap_color_duration > 0 # Determine number of Frames for Transition using previous values td = (@last_minutes_per_day * 60 * 20) / (24 / @dawn_dusk_duration) # Determine Percent Complete pc = @shadowmap_color_duration / td.to_f # Determine number of Frames for Transition using current values cd = (@minutes_per_day * 60 * 20) / (24 / @dawn_dusk_duration) # New duration as a Percent that the old Transition was complete @shadowmap_color_duration = [1, (cd * pc).ceil].max end end @last_minutes_per_day = @minutes_per_day end #-------------------------------------------------------------------------- # * Update - Game_System # - This alias updates Transitions and Game Clock #-------------------------------------------------------------------------- alias heretic_light_update update unless $@ def update # Call Original or other Aliases heretic_light_update # Update Shadowmap Color Transition update_shadowmap_color_transition # Update any Shadowmap Indoor Color Transitions update_shadowmap_indoor_color_transition # Update Shadowmap Opacity Transition update_shadowmap_opacity_transition # Updates Lightning Flashes update_shadowmap_lightning # Updates Storm for Automatic Lightning and Delayed Thunder update_auto_storm # Checks Durations when Minutes Per Day is changed update_light_duration_checks # Update the Game Clock (using a Game_Variable) update_game_clock end #-------------------------------------------------------------------------- # * Update Auto Storm - Game_System # - When Storm Switch is ON, this handles Lighting and Thunder #-------------------------------------------------------------------------- def update_auto_storm # If a Switch for Storm is set to ON if $game_switches[Light_Config::Storm_Switch_ID] # Increase the Storm Counter @storm_counter += 1 # If no Thunder Delay is set if not @next_thunder_delay # Determine the Next Thunder Delay @next_thunder_delay = rand(@thunder_delay[1]) + @thunder_delay[0] end # If no Lightning Delay is set if not @next_lightning_delay # Create the Delays if they are not set @next_lightning_delay = rand(@lightning_delay[1]) + @lightning_delay[0] end # If Counter matches Lightning Delay using Default Settings if @next_lightning_delay == @storm_counter # Cause Lightning to Flash flash_lightning() end # Add Lightning and Thunder Delays delay = @next_lightning_delay + @next_thunder_delay # If Counter matches the Lightning and Thunder Delay if delay == @storm_counter # Use these local variables for playing the Thunderclap name = "Audio/SE/" + @thunder_sound # Determine Volume of the Thunderclap if $game_switches[Light_Config::Switch_Indoor] volume = @thunder_volume[1] # Indoor Volume from Array else volume = @thunder_volume[0] # Outdoor Volume from Array end # The longer the Thunder Delay, the lower the Pitch (150 down to 50) pitch = [150, [150 - @next_thunder_delay, 50].max].min # If Option to Dampen Pitch when Indoors is ON if @thunder_dampen_pitch # Lower Pitch to Dampen Sound if Indoor pitch -= ($game_switches[Light_Config::Switch_Indoor]) ? 20 : 0 end # Play the Thunder Sound with these Variables Audio.se_play(name, volume, pitch) end # If we need to reset values for next Cycle (including Flash Period) if delay + @shadowmap_lightning_period == @storm_counter # Reset the Storm Counter for another Lightning and Thunder cycle @storm_counter = 0 # Reset the Storm Delay Counters @next_lightning_delay = nil @next_thunder_delay = nil end else # Set the Storm Counter to 0 @storm_counter = 0 end end #-------------------------------------------------------------------------- # * Lightinig Flash? - Game_System # - Returns True if a Lightning Flash is occuring # - Checks if Auto Storm will call Lightning on the Next Frame # - Events are updated before System so this it is necessary to check # if a Lightning Flash will occur on the next frame in order to properly # determine opacity values for lightning responsive events and lights. #-------------------------------------------------------------------------- def lightning_flash? # Flash is Occuring if Duration is more than 0 return true if @shadowmap_lightning_duration > 0 # If Next Lightning Delay (Auto Storm) is more than 0 if @next_lightning_delay and @next_lightning_delay > 0 # True if a Lightning Flash is to be Triggered Next Frame return true if @next_lightning_delay == @storm_counter + 1 end end #-------------------------------------------------------------------------- # * Initialize Game Clock - Game_System # - Sets value of Game Clock to a Game Variable for use with Event Calls # - $game_system is created before $game_variables so set the value of # the variable on the first update #-------------------------------------------------------------------------- def initialize_game_clock # Game Clock (24 Hour Day in Seconds, 86400) $game_variables[Light_Config::Clock_Variable] = Light_Config::Start_Time # Set initial value of Last Clock Variable to the Start Time of Game @last_clock_variable = Light_Config::Start_Time # Don't process if nothing in the Config return unless Light_Config::Day_Switch # Determine half of time it takes for Dawn / Dusk to complete (in Seconds) half = @dawn_dusk_duration * 60 * 60 / 2 # If current time is after Daybreak and before Nightfall if Light_Config::Start_Time >= (@dawn_time + half) and Light_Config::Start_Time < (@dawn_time + half) # Set the Game Switch to ON $game_switches[Light_Config::Day_Switch] = true end # Reset All Shadow Colors reset_shadowmap_dawn_dusk_color end #-------------------------------------------------------------------------- # * Update Time End of Day - Game_System # - Clears Flags used for fading the Shadowmap Day and Night # - Optional Array of Arguments for Aliases of this class # *args : Optional Arguments to use in Aliases of method #-------------------------------------------------------------------------- def update_time_end_of_day(*args) # Dawn Faded Flag @shadowmap_dawn_faded = false # Dusk Faded Flag @shadowmap_dusk_faded = false end #-------------------------------------------------------------------------- # * Call Dawn - Game_System # - Transitions Shadowmap Color to Day # - Sets Flag to prevent calling again until the next Day # - Can be Aliased for other things # *args : Array for Aliases #-------------------------------------------------------------------------- def call_dawn(*args) # Determine number of Frames for Transition duration = (@minutes_per_day * 60 * 20) / (24 / @dawn_dusk_duration) # Change the Color of the Shadowmap to Day shadowmap_color_to(@shadowmap_day_color, duration) # Set Flag that Transition to Night has been called @shadowmap_dawn_faded = true end #-------------------------------------------------------------------------- # * Call Dusk - Game_System # - Transitions Shadowmap Color to Night # - Sets Flag to prevent calling again until the next Day # - Can be Aliased for other things # *args : Array for Aliases #-------------------------------------------------------------------------- def call_dusk(*args) # Determine number of Frames for Transition duration = (@minutes_per_day * 60 * 20) / (24 / @dawn_dusk_duration) # Change the Color of the Shadowmap to Night shadowmap_color_to(@shadowmap_night_color, duration) # Set Flag that Transition to Night has been called @shadowmap_dusk_faded = true end #-------------------------------------------------------------------------- # * Update Formatted Time - Game_System # - Updates a Game Variable to display Formatted Time (Ex: 4:32PM) # # WARNING: Do NOT use ANY Event Conditions to check this Variable or the # game will crash! It works just fine using \v[n] to display # the Text in a Message Window. #-------------------------------------------------------------------------- def update_formatted_time # Game Variable ID from Config id = Light_Config::Formatted_Time_Variable # Current Clock (in Seconds) time = $game_variables[Light_Config::Clock_Variable] # Time in Hours hour = (time / 3600 % 12).floor #hour -= 12 if hour > 12 hour = 12 if hour == 0 # Time in Minutes min = (time / 60 % 60).floor min = "0", min.to_s if min < 10 # Determine AM or PM ampm = (time < 43200) ? " AM" : " PM" $game_variables[id] = hour.to_s, ":", min, ampm end #-------------------------------------------------------------------------- # * Check Day Night Switch - Game_System # - Changes a Game Switch to On during the Day and Off at Night # - Change occurs when half of Dawn / Dusk is complete # time : current Time in Seconds # args : Array of any additional arguments #-------------------------------------------------------------------------- def check_day_night_switch(time, *args) # Don't process if nothing in the Config return unless Light_Config::Day_Switch # ID of the Game Switch to change id = Light_Config::Day_Switch # Determine half of time it takes for Dawn / Dusk to complete (in Seconds) half = @dawn_dusk_duration * 60 * 60 / 2 # If current time is after Nightfall or before dawn if time >= (@dusk_time + half) or time < (@dawn_time + half) # If the Game Switch is currenty set to ON if $game_switches[id] # Set Flag to refresh the Game_Map $game_map.need_refresh = true # Set the Game Switch to Off $game_switches[id] = false end # If current time is after Daybreak elsif time >= (@dawn_time + half) # If the Game Switch is currenty set to OFF if not $game_switches[id] # Set Flag to refresh the Game_Map $game_map.need_refresh = true # Set the Game Switch to ON $game_switches[id] = true end end end #-------------------------------------------------------------------------- # * Dawn Dusk Setup - Game_System # - Takes Day and Night values and returns current values # a : Number used during the Day # b : Number used at Night # duration : Optional - Transition in Number of Frames #-------------------------------------------------------------------------- def dawn_dusk_setup(a, b, duration = nil, id = nil) # Return a value if they are the same return a if a == b # Current Time in Seconds time = $game_variables[Light_Config::Clock_Variable] # Duration of Dawn / Dusk in Seconds so Units match dds = @dawn_dusk_duration * 60 * 60 # Check for Division by 0 if dds == 0 or duration == 0 # Return Day or Night values when Division by 0 will occur return (time <= @dawn_time or time > @dusk_time) ? b : a end # If Time is before Dawn or After End of Dusk if time <= @dawn_time or time >= @dusk_time + dds # Use 2nd Value for Night Value return b # If Time is Day elsif time > @dawn_time + dds and time <= @dusk_time # Use 1st Value for Day Value return a end # Remember Original Duration of Arg original_duration = duration # Determine Total Duration Time daynight_duration = (@minutes_per_day * 1200) / (24 / @dawn_dusk_duration) # If Duration is specified if duration # If Duration is not expired if duration - (daynight_duration - @shadowmap_color_duration) > 0 # Total Duration is Argument total_duration = duration # Determine number of Frames remaining in Transition as Duration duration = duration - (daynight_duration - @shadowmap_color_duration) else # Duration is expired so 0 duration = 0 end else # Use Total Daynight Duration as Duration duration = daynight_duration end # If not the End of a Transition (prevents Division by 0) if duration > 1 # If Duration Argument is specified if original_duration # Determine Percent Complete of Transition as PC pc = duration / (original_duration - 1).to_f else # Determine Percent Complete of Transition as PC pc = @shadowmap_color_duration / (duration - 1).to_f end # Determine Difference between A and B dif = [a,b].max - [a,b].min # If Dawn if time < @dusk_time # Use Percent to determine current value for Dawn return (b > a) ? a + (dif * pc) : a - (dif * pc) # Then Dusk else # Use Percent to determine current value for Dusk return (b > a) ? b - (dif * pc) : b + (dif * pc) end # Duration is 0 or End of Transition else # Return either Day or Night value return (time <= @dawn_time or time >= @dusk_time) ? b : a end end #-------------------------------------------------------------------------- # * Reset Event Light Daynight Opacity - Game_System # - Resets Opacity of Lights and Events responsive to Day / Night Cycles #-------------------------------------------------------------------------- def reset_event_light_daynight_opacity # Return if no Game Map exists return unless $game_map # Iterate every Event for event in $game_map.events.values # Set the Event and any Lights to proper Opacity Levels event.reset_light_event_daynight_opacity end end #-------------------------------------------------------------------------- # * Reset Shadowmap Dawn Dusk Color - Game_System # - Determines Shadowmap Color when Time Variable changed by an Event Call #-------------------------------------------------------------------------- def reset_shadowmap_dawn_dusk_color # Do not process if Manual Dawn Dusk calls expected return if Light_Config::Manual_Dawn_Dusk # Current Time from Game Variable Limited to One Day t = $game_variables[Light_Config::Clock_Variable] % 86400 # Determine number of Seconds in a Day for Dawn / Dusk Transition as ddt ddt = @dawn_dusk_duration * 60 * 60 # Total Duration of number of Frames for Transition td = (@minutes_per_day * 60 * 20) / (24 / @dawn_dusk_duration) # If Time is before Dawn or after End of Dusk if t <= @dawn_time or t >= @dusk_time + ddt # Set Shadowmap Color (outside) to Night Color @shadowmap_color_target = @shadowmap_night_color # If Time is after Dusk Ends if t >= @dusk_time + ddt # Set Flags for Dawn / Dusk having been called due to Manual Call @shadowmap_dawn_faded = true @shadowmap_dusk_faded = true # Clear any Durations @shadowmap_color_duration = 0 end # If after End of Dawn and before Dusk elsif t >= @dawn_time + ddt and t <= @dusk_time # Set Shadowmap Color (outside) to Night Color @shadowmap_color_target = @shadowmap_day_color # Set Flags for Dawn / Dusk having been called due to Time Change @shadowmap_dawn_faded = true @shadowmap_dusk_faded = false # Clear any Durations @shadowmap_color_duration = 0 # Else if Dawn or Dusk is occuring elsif (t > @dawn_time and t < @dawn_time + ddt) or (t > @dusk_time and t < @dusk_time + ddt) # Set Current and Target Colors depending on Dawn or Dusk if t > @dusk_time # Dusk # Set Flags for Dawn / Dusk having been called due to Time Change @shadowmap_dawn_faded = true @shadowmap_dusk_faded = true # Set Color Target to Night Color @shadowmap_color_target = @shadowmap_night_color # Determine Percent Complete of Transition pc = (t - @dusk_time) / ddt.to_f else # Dawn # Set Flags for Dawn / Dusk having been called due to Time Change @shadowmap_dawn_faded = true @shadowmap_dusk_faded = false # Set Color Target to Day color @shadowmap_color_target = @shadowmap_day_color # Determine Percent Complete of Transition pc = (t - @dawn_time) / ddt.to_f end # Set the Shadowmap Color Duration to Remaining number of Frames @shadowmap_color_duration = td - (td * pc).round end # Shorthand for Day values rd = @shadowmap_day_color.red gd = @shadowmap_day_color.green bd = @shadowmap_day_color.blue ad = @shadowmap_day_color.alpha # Shorthand for Night values rn = @shadowmap_night_color.red gn = @shadowmap_night_color.green bn = @shadowmap_night_color.blue an = @shadowmap_night_color.alpha # Determine values for Day / Night for each Color Channel r = dawn_dusk_setup(rd, rn) g = dawn_dusk_setup(gd, gn) b = dawn_dusk_setup(bd, bn) a = dawn_dusk_setup(ad, an) # Set New Color to Shadowmap (affects Outdoor Color ONLY) @shadowmap_color = Color.new(r, g, b, a) # Set Opacity of Lights and Events responsive to Day / Night Cycles reset_event_light_daynight_opacity end #-------------------------------------------------------------------------- # * Update Game Clock - Game_System # - Updates the Game_Variable when Game_Switch is Enabled # - Calls Methods at Dawn, Dusk, and End of Day # - Value for the number of Seconds since 12:00 AM #-------------------------------------------------------------------------- def update_game_clock # If Game Switch to allow Advance of Time is Enabled or Manually Changed if $game_switches[Light_Config::Switch_Time] or @last_clock_variable != $game_variables[Light_Config::Clock_Variable] # Prevent advancing Time if Interpreter is running and option enabled if @pause_time_interpreter and $scene.is_a?(Scene_Map) and @last_clock_variable == $game_variables[Light_Config::Clock_Variable] # Prevent Processing if the Interpreter is running (Ignores Parallels) return if @map_interpreter.running? or @battle_interpreter.running? end # Current Time from Game Variable (in Seconds) t = $game_variables[Light_Config::Clock_Variable] # Determine Number of Seconds Per Frame as SPF (no, not Sunblock) spf = 86400 / (@minutes_per_day * 60 * 20) # If the Time was set by an Event to change Time Variable if @last_clock_variable != t # Minimum Time required to fully Rest at an Inn, Shorthand LCV min, lcv = @min_inn_time * 3600, @last_clock_variable # Determine Difference in Time to check against Minimum Rest Time td = (t > lcv) ? t - lcv + spf : t - lcv + spf + 86400 # Advance Total Days if Time is Next Day (from 6PM to 4PM for example @total_days += 1 if @last_clock_variable > t + spf # Advance Total Days if Insufficient Time to Rest at an Inn @total_days += 1 if td < min # Clear the Dawn Dusk Faded Flags @shadowmap_dawn_faded = false @shadowmap_dusk_faded = false # Calculate Shadowmap Color Values based on New Time reset_shadowmap_dawn_dusk_color # Set flag to Refresh Events $game_map.need_refresh = true end # Advance Time if Game Switch to allow Advance of Time is Enabled if $game_switches[Light_Config::Switch_Time] and not (@pause_time_in_battle and $game_temp.in_battle) # Increae Current Time by Seconds Per Frame t += spf end # Change Game Switch to ON during Day and OFF at Night check_day_night_switch(t) # If Dawn has not been Faded and Time is Dawn if not @shadowmap_dawn_faded and t > @dawn_time # Use Method to trigger Dawn if Option is enabled call_dawn(t) if not Light_Config::Manual_Dawn_Dusk end # If Dawn has not been Faded and Time is Dawn if not @shadowmap_dusk_faded and t > @dusk_time # Use Method to trigger Dawn if Option is enabled call_dusk(t) if not Light_Config::Manual_Dawn_Dusk end # If Time is greater than or equal to one Day (in Seconds) if t >= 86400 # Increase Number of Days by 1 @total_days += 1 # Reset Flags at End of the Day (Can be Aliased) update_time_end_of_day(t) # Limit to total number of Seconds in one day t %= 86400 end # Set Value (in Seconds) to Game Variable $game_variables[Light_Config::Clock_Variable] = t # Remember the value that the Clock has been set to @last_clock_variable = t end # Update String to display for Formatted Time update_formatted_time end #-------------------------------------------------------------------------- # * Shadowmap Color To - Game_System # - Transition Shadowmap Color (for Outdoor Lighting) # color : Target Color - Color.new(r, g, b, alpha) # duration : Duration of Transition in Frames #-------------------------------------------------------------------------- def shadowmap_color_to(color, duration) # Check for proper Object Type if not color.is_a?(Color) # If running game from Editor if $DEBUG # Explain the problem to the user print "Error: Problem with shadowmap_color_to(", color, ", ", duration, ")\n\n", "The color: \"", color.inspect, "\" is not a Color!\n\n", "Please use Color.new(red, green, blue, [alpha])", "for the Color!\n\n", "Example: c = Color.new(255, 255, 255, 255)\n", "shadowmap_color_to(c, 60)", "shadowmap_color_to(color, duration in frames)" end # Prevent Processing this Transition return end # Set Values Now if Transition Duration is 0 if duration == 0 @shadowmap_color = @shadowmap_color_target = color return end # Store Values of Transition @shadowmap_color_target = color @shadowmap_color_duration = duration end #-------------------------------------------------------------------------- # * Shadowmap Indoor Color To - Game_System # - Transition Shadowmap Indoor Color # color : Target Color - Color.new(r, g, b, alpha) # duration : Duration of Transition in Frames #-------------------------------------------------------------------------- def shadowmap_indoor_color_to(color, duration) # Check for proper Object Type if not color.is_a?(Color) # If running game from Editor if $DEBUG # Explain the problem to the user print "Error: Problem with shadowmap_indoor_color_to(", color, ", ", duration, ")\n\n", "The color: \"", color.inspect, "\" is not a Color!\n\n", "Please use Color.new(red, green, blue, [alpha])", "for the Color!\n\n", "Example: c = Color.new(255, 255, 255, 255)\n", "shadowmap_indoor_color_to(c, 60)", "shadowmap_indoor_color_to(color, duration in frames)" end # Prevent Processing this Transition return end # Set Values Now if Transition Duration is 0 if duration == 0 @shadowmap_indoor_color = @shadowmap_indoor_color_target = color return end # Store Values of Transition @shadowmap_indoor_color_target = color @shadowmap_indoor_color_duration = duration end #-------------------------------------------------------------------------- # * Flash Lightning - Game_System # - Causes a Lightning Flash for Outdoor Shadowmap Colors # - Lightning responsive Events and Lights also react Indoors # color : Color.new(RGBA) # d : Duration in Frames #-------------------------------------------------------------------------- def flash_lightning(color = nil, d = nil) # Default Value if no argument present color = @shadowmap_lightning_color if color.nil? # Use Default System Config if Duration is nil d = @lightning_default_duration if d.nil? # Make sure Duration is long enough to handle a Double Flash d = [d, @lightning_first_frames + @lightning_double_frames + 1].max # Remember the Color of Lightning Flash throughout the Transtion @shadowmap_lightning_color_start = color # Set the Duration of the Lightning Flash @shadowmap_lightning_duration = d # Remember the Duration of this Lightning Flash @shadowmap_lightning_period = d end #-------------------------------------------------------------------------- # * Update Shadowmap Color Transition - Game_System # - Updates Color Transition over Time # - Dawn / Dusk will Pause if Option enabled and Interpreter Running #-------------------------------------------------------------------------- def update_shadowmap_color_transition # If Flow of Time is not Enabled return if not $game_switches[Light_Config::Switch_Time] # Current Time from Game Variable Limited to One Day t = $game_variables[Light_Config::Clock_Variable] % 86400 # Determine number of Seconds in a Day for Dawn / Dusk Transition as ddt ddt = @dawn_dusk_duration * 60 * 60 # Prevent Color Transition if Target is Dawn or Dusk Color if @pause_time_interpreter and (not Light_Config::Manual_Dawn_Dusk and ((@shadowmap_color_target == @shadowmap_day_color and t > @dawn_time and t < @dawn_time + ddt) or (@shadowmap_color_target == @shadowmap_night_color and t > @dusk_time and t < @dusk_time + ddt)) ) # Prevent Processing if the Interpreter is running (Ignores Parallels) return if @map_interpreter.running? or @battle_interpreter.running? end # If a Transition is taking place if @shadowmap_color_duration >= 1 # Shorthand for current values rc = @shadowmap_color.red gc = @shadowmap_color.green bc = @shadowmap_color.blue ac = @shadowmap_color.alpha # Shorthand for new values rt = @shadowmap_color_target.red gt = @shadowmap_color_target.green bt = @shadowmap_color_target.blue at = @shadowmap_color_target.alpha # Shorthand for Duration d = @shadowmap_color_duration # Transition each Color and Alpha r = (rc * (d - 1) + rt) / d g = (gc * (d - 1) + gt) / d b = (bc * (d - 1) + bt) / d a = (ac * (d - 1) + at) / d # Set New Color to Shadowmap @shadowmap_color = Color.new(r, g, b, a) # Decrease Duration of Transition @shadowmap_color_duration -= 1 # If End of Color Transition if @shadowmap_color_duration == 0 # Set Color to Target if End of Transition @shadowmap_color = @shadowmap_color_target # Reset Grapics for Screen Tearing Graphics.frame_reset # Garbage Collection GC.start end end end #-------------------------------------------------------------------------- # * Update Shadowmap Indoor Color Transition - Game_System # - Updates Color Transition over Time # - Value here can be different than Outdoor Colors, even when a Color # Transition for Outdoor Color has a Transition is taking place #-------------------------------------------------------------------------- def update_shadowmap_indoor_color_transition # Current Time from Game Variable Limited to One Day t = $game_variables[Light_Config::Clock_Variable] % 86400 # Determine number of Seconds in a Day for Dawn / Dusk Transition as ddt ddt = @dawn_dusk_duration * 60 * 60 # Prevent Color Transition if Target is Dawn or Dusk Color if @pause_time_interpreter and (not Light_Config::Manual_Dawn_Dusk and ((@shadowmap_color_target == @shadowmap_day_color and t > @dawn_time and t < @dawn_time + ddt) or (@shadowmap_color_target == @shadowmap_night_color and t > @dusk_time and t < @dusk_time + ddt)) ) # Prevent Processing if the Interpreter is running (Ignores Parallels) return if @map_interpreter.running? or @battle_interpreter.running? end # If a Transition is taking place if @shadowmap_indoor_color_duration >= 1 # Shorthand for current values rc = @shadowmap_indoor_color.red gc = @shadowmap_indoor_color.green bc = @shadowmap_indoor_color.blue ac = @shadowmap_indoor_color.alpha # Shorthand for new values rt = @shadowmap_indoor_color_target.red gt = @shadowmap_indoor_color_target.green bt = @shadowmap_indoor_color_target.blue at = @shadowmap_indoor_color_target.alpha # Shorthand for Duration d = @shadowmap_indoor_color_duration # Transition each Color and Alpha r = (rc * (d - 1) + rt) / d g = (gc * (d - 1) + gt) / d b = (bc * (d - 1) + bt) / d a = (ac * (d - 1) + at) / d # Set New Color to Shadowmap @shadowmap_indoor_color = Color.new(r, g, b, a) # Decrease Duration of Transition @shadowmap_indoor_color_duration -= 1 # If End of Color Transition if @shadowmap_indoor_color_duration == 0 # Set Color to Target if End of Transition @shadowmap_indoor_color = @shadowmap_indoor_color_target # Reset Graphics for Screen Tearing Graphics.frame_reset # Garbage Collection GC.start end end end #-------------------------------------------------------------------------- # * Shadowmap Opacity To - Game_System # - Transition Shadowmap Opacity # opacity : Target Opacity # duration : Duration of Transition in Frames # # NOTE: This will affect both Indoor and Outdoor. Usually best to use the # alpha parameter when setting the Day / Night, Indoor Colors because # each Alpha can be different for all of those Colors. #-------------------------------------------------------------------------- def shadowmap_opacity_to(opacity, duration) # Set Values Now if Transition Duration is 0 if duration == 0 @shadowmap_opacity = @shadowmap_opacity_target = opacity return end # Store Values of Transition @shadowmap_opacity_target = opacity.to_f @shadowmap_opacity_duration = duration end #-------------------------------------------------------------------------- # * Update Shadowmap Opacity Transition - Game_System # - Updates Color Transition over Time #-------------------------------------------------------------------------- def update_shadowmap_opacity_transition # If a Transition is taking place if @shadowmap_opacity_duration >= 1 # Shorthand o = @shadowmap_opacity ot = @shadowmap_opacity_target d = @shadowmap_opacity_duration # Transition to New Shadowmap Opacity @shadowmap_opacity = (o * (d - 1) + ot) / d # Decrease Duration of Transition @shadowmap_opacity_duration -= 1 # If End of Transition if @shadowmap_opacity_duration == 0 # Set Opacity to Target if Transition is complete @shadowmap_opacity = @shadowmap_opacity_target end end end #-------------------------------------------------------------------------- # * Update Shadowmap Lightning - Game_System # - Handles Transtion of Color of Lightning Flash #-------------------------------------------------------------------------- def update_shadowmap_lightning # If a Lightning Flash is taking place if @shadowmap_lightning_duration > 0 # Shorthand for Period of Total Time of Lightning, First and Double Flash p = @shadowmap_lightning_period ff = @lightning_first_frames df = @lightning_double_frames # Shorthand for Color Values current_color = @shadowmap_color lightning_color = @shadowmap_lightning_color_start # If Duration is First Frames of Flash if @shadowmap_lightning_duration > p - ff # Determine Minimum Alpha alpha = [current_color.alpha, lightning_color.alpha].min # Set the Color to the Lighning Starting Color @shadowmap_lightning_current_color = @shadowmap_lightning_color_start # Use Lowest Alpha for Current Color Alpha @shadowmap_lightning_current_color.alpha = alpha # Else if Duration is the Pause between the Hollywood Double Flash elsif @shadowmap_lightning_duration > p - (ff + df) # Use the Shadowmap Color @shadowmap_lightning_current_color = @shadowmap_color # Standard Transition Processing except on 4th Frame to reset Color else # If First Frame of 2nd Flash if @shadowmap_lightning_duration == p - (ff + df) # Determine Minimum Alpha alpha = [current_color.alpha, lightning_color.alpha].min # Reset the Color to Start Color of Flash for Transitioning @shadowmap_lightning_current_color = @shadowmap_lightning_color_start # Use Lowest Alpha for Current Color Alpha @shadowmap_lightning_current_color.alpha = alpha end # Invert the current Shadowmap Color (due to Blend Type = 2, subtract) shadow_color = invert_color(@shadowmap_color) # Invert the current Lightning Color (due to Blend Type = 2, subtract) lightning_color = invert_color(@shadowmap_lightning_current_color) # Shorthand for Lightning Duration d = @shadowmap_lightning_duration # Shorthand for Shadowmap Color Channels as the Target Color sr = shadow_color.red sg = shadow_color.green sb = shadow_color.blue sa = shadow_color.alpha # Shorthand for current Shadowmap Color lr = lightning_color.red lg = lightning_color.green lb = lightning_color.blue la = lightning_color.alpha # Transition each Color and Alpha r = (lr * (d - 1) + sr) / d g = (lg * (d - 1) + sg) / d b = (lb * (d - 1) + sb) / d a = (la * (d - 1) + sa) / d # Set New Color to Shadowmap @shadowmap_lightning_current_color = invert_color(Color.new(r, g, b, a)) end # Reduce Lightning Flash Duration by one if greater than 0 @shadowmap_lightning_duration -= 1 if @shadowmap_lightning_duration # If End of Transition, set Lightning Color to current Shadowmap Color if @shadowmap_lightning_duration == 0 # Set Shadowmap Lightning Color to the Shadow Color at end of Lightning @shadowmap_lightning_current_color = shadow_color # Reset Graphics for Screen Tearing Graphics.frame_reset # Garbage Collection GC.start end end end #-------------------------------------------------------------------------- # * Invert Color - Game_System # - Returns Inverted Color for use in Shadowmap for Shadows #-------------------------------------------------------------------------- def invert_color(c) # Invert the Color so Black is Black and White is White for @blend_type = 2 return Color.new(255 - c.red, 255 - c.green, 255 - c.blue, c.alpha) end #-------------------------------------------------------------------------- # * Get Shadowmap Color - Game_System # - Returns Inverted Color for use in Shadowmap for Shadows #-------------------------------------------------------------------------- def get_shadowmap_color # If the Game_Switch set in Light_Config is set to On if $game_switches[Light_Config::Switch_Indoor] # Return an Inversion of the Indoor Color return invert_color(@shadowmap_indoor_color) else # If a Lightning Flash is taking place if @shadowmap_lightning_duration > 0 # Use Inverted Shadowmap Lightning Color return invert_color(@shadowmap_lightning_current_color) end # Returns Inversion of Color for Color Values to match expected Color return invert_color(@shadowmap_color) end end #-------------------------------------------------------------------------- # * Get Shadowmap Opacity - Game_System # - Returns Opacity for use in Shadowmap for Shadows # # NOTE: Opacity affects the Shadowmap so value will be used for both # the Outdoor Color (@shadowmap_color) and the value of the Color # used when Indoor and Game Switch is On (@shadowmap_indoor_color) #-------------------------------------------------------------------------- def get_shadowmap_opacity # Returns Shadowmap Opacity (adjusted for any Transitions) return @shadowmap_opacity end #-------------------------------------------------------------------------- # * Dawn? - Game_System # - Returns true if Time is during Dawn #-------------------------------------------------------------------------- def dawn? # Current Time from Game Variable Limited to One Day t = $game_variables[Light_Config::Clock_Variable] % 86400 # Determine number of Seconds in a Day for Dawn / Dusk Transition as ddt ddt = @dawn_dusk_duration * 60 * 60 # Evaluate if Dawn is occuring return true if t >= @dawn_time and t <= @dawn_time + ddt end #-------------------------------------------------------------------------- # * Day? - Game_System # - Returns true if Time is during Day #-------------------------------------------------------------------------- def day? # Current Time from Game Variable Limited to One Day t = $game_variables[Light_Config::Clock_Variable] % 86400 # Determine number of Seconds in a Day for Dawn / Dusk Transition as ddt ddt = @dawn_dusk_duration * 60 * 60 # Evaluate if Dawn is occuring return true if t > @dawn_time + ddt and t < @dusk_time end #-------------------------------------------------------------------------- # * Dusk? - Game_System # - Returns true if Time is during Dusk #-------------------------------------------------------------------------- def dusk? # Current Time from Game Variable Limited to One Day t = $game_variables[Light_Config::Clock_Variable] % 86400 # Determine number of Seconds in a Day for Dawn / Dusk Transition as ddt ddt = @dawn_dusk_duration * 60 * 60 # Evaluate if Dawn is occuring return true if t >= @dusk_time and t <= @dusk_time + ddt end #-------------------------------------------------------------------------- # * Night? - Game_System # - Returns true if Time is during Night #-------------------------------------------------------------------------- def night? # Current Time from Game Variable Limited to One Day t = $game_variables[Light_Config::Clock_Variable] % 86400 # Determine number of Seconds in a Day for Dawn / Dusk Transition as ddt ddt = @dawn_dusk_duration * 60 * 60 # Evaluate if Dawn is occuring return true if t > @dusk_time + ddt or t < @dawn_time end #-------------------------------------------------------------------------- # * Lightning? - Game_System # - Returns true if a Lightning Flash is occuring or sched for next frame #-------------------------------------------------------------------------- def lightning? # Uses Code from Lightning Flash to evaluate the code return lightning_flash? end end #============================================================================= # ** Game_Light - Class # - Manages internal conditions of the Light such as File, Zoom, and Opacity #============================================================================= class Game_Light #-------------------------------------------------------------------------- # * Public Instance Variables - Game_Light #-------------------------------------------------------------------------- attr_accessor :enabled # Enabled Property to Update attr_accessor :image_file # Image File to use as Light Source attr_accessor :up # Image when Character faces Up attr_accessor :down # Image when Character faces Down attr_accessor :left # Image when Character faces Left attr_accessor :right # Image when Character faces Right attr_accessor :zoom_xy # Scale the size of Light Image attr_accessor :zoom_duration # Array [X Zoom Dur, Y Zoom Dur] attr_accessor :width # Specify Width of Light Image attr_accessor :height # Specify Height of Light Image attr_accessor :offset_xy # Pixel Adjustment for XY Light Source attr_accessor :angle # Angle to Rotate Image attr_accessor :shadow # If Light renders as a Shadow attr_accessor :shadow_match_opacity # Match Opacity to Shadowmap attr_accessor :after_shadow # Renders this Light after Shadows attr_accessor :cutout # Cuts a Hole in Shadowmap of Character attr_accessor :cutout_name # Specified File Name of Cutout attr_accessor :no_jump # Lights dont track Jumps or Floats attr_accessor :spotlight # Array - Spotlight Target for Light attr_accessor :target_percent # Multiplier for Spotlights attr_accessor :flicker_size # Flickers the Zoom of the Light attr_accessor :opacity # Opacity of the Light attr_accessor :lightning_opacity # Opacity during Lightning Flash attr_accessor :flicker_opacity # Flickers the Opacity of the Light attr_accessor :daynight_opacity # Array Opacity [Day, Night] attr_accessor :match_direction # Flag Matches the Characters Direction attr_accessor :match_angle # Flag Matches the Character Angle attr_accessor :pendulum # Light swings from Pendulum attr_accessor :pendulum_y # Flag Tracks Pendulums on Y Coordinate attr_accessor :pendulum_opacity # Adjusts Opacity of Pendulum Lights attr_accessor :pendulum_zoom # Adjusts Zoom XY of Pendulum Lights attr_accessor :hue_speed # Rotates Hue of Light Color attr_reader :hue # Has Setter Method, hue= attr_reader :id # Character (Player or Event) ID #-------------------------------------------------------------------------- # * Initialize - Game_Light # - Creates Light Object # id : Character ID # name : Name of Light Image File in Graphics\Lights #-------------------------------------------------------------------------- def initialize(id, name = Light_Config::Default_Light_Image) # Character ID of Light (0 for Player, 1+ for Events) @id = id # Check that File Exists if not File.exist?("Graphics\\Lights\\" + name) or name == "" # If running game from Editor if $DEBUG and not name == "" # Display Error print "Warning: File \"Graphics\\Lights\\", name, "\"\ndoes not exist for Event ", id, "\n\nDefault Light has been used instead" end # Default Name from Light Config name = Light_Config::Default_Light_Image end # Light Properties @enabled = true @image_file = name # Direction Specific Images @up = nil @down = nil @left = nil @right = nil # Hue of the Light (does not allow Transitions due to Caching) @hue = 0 # Speed of Rotation of Hue @hue_speed = 0 # Rotation of Image @angle = 0 # Match the Parent Character's Direction (Down is 0 Degrees) @match_direction = nil # Match the Parent Character's Angle @match_angle = nil # Shadow Flag for rendering after Lights are drawn @shadow = nil # Match the Opacity of the Shadow when Drawn to Opacity of Shadowmap Color @shadow_mach_opacity = false # Flag to draw this Light after Shadows are Drawn @after_shadow = nil # Watches for Changes to Rescan Lights @last_after_shadow = @after_shadow # Flag to Cutout a Hole of Character @cutout = nil # Name of File to use for Cutout @cutout_name = nil # Light does not track the Jump Arc or Float Animations of a Character @no_jump = false # Allows changing the Size XY of the Light @zoom_xy = [1.0, 1.0] # Offers Override to Image size to specify Width and Height @width = nil @height = nil # Allow Transition in Size XY of Light @zoom_target = [@zoom_xy[0], @zoom_xy[1]] @zoom_duration = [0, 0] # Pixel Offsets for X and Y @offset_xy = [0, 0] # Allows changing Opacity of the Light @opacity = 255.0 # Allow Transition in Opacity of Light @opacity_target = @opacity @opacity_duration = 0 # Opacity during Lightning Flash set by \light_lightning_opacity[N] Comment @lightning_opacity = nil # Remembers Transitioned Opacity during a Lightning Flash @current_opacity = nil # Remembers Opacity before altering with a Lightning Flash @non_lightning_opacity = nil # Flicker Size Array [X Min, X Max, Y Min, Y Max, X Duration, Y Duration] @flicker_size = [] # Flicker Opacity Array [Opacity Min, Opacity Max, Duration] @flicker_opacity = [] # Opacity during Day and Night @daynight_opacity = [] # Spotlight - Adjusts the Height of Image to point a Light at a Target @spotlight = [] # Pendulum - Uses Rotate, Zoom, and Pendulum Script to Swing the Light @pendulum = nil # Pendulum Y - Tracks Pendulum on Y Coordinate, not just X @pendulum_y = nil # Pendulum Opacity - Opacity based on Angle, 0 is Full Opacity 90 is 0 @pendulum_opacity = nil # Pendulum Zoom - Lights render larger the closer to bottom of swing @pendulum_zoom = nil end #-------------------------------------------------------------------------- # * Hue= - Game_Light # - Setter Method for @hue for 360.0 degree rotation # - Has an attr_reader :hue so property may be read #-------------------------------------------------------------------------- def hue=(value) # Assign Value to Hue and clamp to 360 degrees, round for cache performance @hue = value % 360.0 end #-------------------------------------------------------------------------- # * Set Flicker Size - Game_Light # - Causes Light to Flicker in Size # - Uses same values for X and Y # args : Array - [Min Zoom, Max Zoom, Duration] #-------------------------------------------------------------------------- def set_flicker_size(args) # Iterate 0, 1, 2 for Array for i in 0..2 # If Argument is not a Number (Float or Integer) if not args[i].is_a?(Numeric) # If running game from Editor if $DEBUG # Display Warning to User print "Error in Set Flicker Size for Dynamic Lights\n", args[i], " is not a Number in\n", "\\light_flicker_size[", args[0], ", ", args[1], ", ", args[2], "]\n\n", "Expected Comment:\n", "\\light_flicker_size[min_zoom, max_zoom, duration]\n", "for Event ", @id end # Clear any assigned values @flicker_size = [] # Prevent further processing of this method return end # Assign Value in argument to Flicker array @flicker_size[i * 2], @flicker_size[i * 2 + 1] = args[i], args[i] end # Make sure that the Duration is a whole number by Rounding @flicker_size[4] = @flicker_size[4].round @flicker_size[5] = @flicker_size[5].round end #-------------------------------------------------------------------------- # * Set Flicker Size X - Game_Light # - Causes Light to Flicker in Size # - Only affects values for X # args : Array - [Min Zoom, Max Zoom, Duration] #-------------------------------------------------------------------------- def set_flicker_size_x(args) # Iterate min, max, duration for i in 0..2 # If Argument is not a Number (Float or Integer) if not args[i].is_a?(Numeric) # If running game from Editor if $DEBUG # Display Warning to User print "Error in Set Flicker Size X for Dynamic Lights\n", args[i], " is not a Number in\n", "\\light_flicker_size[", args[0], ", ", args[1], ", ", args[2], "]\n\n", "Expected Comment:\n", "\\light_flicker_size[min_zoom, max_zoom, duration]", "for Event ", @id end # Clear any assigned values @flicker_size = [] # Prevent further processing of this method return end # Assign Value in argument to Flicker array @flicker_size[i * 2] = args[i] # Increment Counter i += 1 end # Make sure that the Duration X is a whole number by Rounding @flicker_size[4] = @flicker_size[4].round end #-------------------------------------------------------------------------- # * Set Flicker Size Y - Game_Light # - Causes Light to Flicker in Size # - Only affects values for Y # args : Array - [Min Zoom, Max Zoom, Duration] #-------------------------------------------------------------------------- def set_flicker_size_y(args) # Iterate min, max, duration for i in 0..2 # If Argument is not a Number (Float or Integer) if not args[i].is_a?(Numeric) # If running game from Editor if $DEBUG # Display Warning to User print "Error in Set Flicker Size Y for Dynamic Lights\n", args[i], " is not a Number in\n", "\\light_flicker_size[", args[0], ", ", args[1], ", ", args[2], "]\n\n", "Expected Comment:\n", "\\light_flicker_size[min_zoom, max_zoom, duration]", "for Event ", @id end # Clear any assigned values @flicker_size = [] # Prevent further processing of this method return end # Assign Value in argument to Flicker array @flicker_size[i * 2 + 1] = args[i] # Increment Counter i += 1 end # Make sure that the Duration X is a whole number by Rounding @flicker_size[5] = @flicker_size[5].round end #-------------------------------------------------------------------------- # * Clear Flicker Size - Game_Light # - Clears Light Flickering in Size #-------------------------------------------------------------------------- def clear_flicker_size # Clear the Array @flicker_size = [] end #-------------------------------------------------------------------------- # * Zoom Light To - Game_Light # - Allows changing the Zoom Level of the Light over Time # - Affects both X and Y Zoom Levels with same values # z : New Zoom (Small floats work best, like 2.5 or 0.85) # d : Duration - Number of Frames for Transition #-------------------------------------------------------------------------- def zoom_light_to(z, d) # Zoom at the end of Transition @zoom_target[0], @zoom_target[1] = z.to_f, z.to_f # If Duration of Transition is 0 if d == 0 # Set Zoom XY to value right now @zoom_xy[0], @zoom_xy[1] = z.to_f, z.to_f # No further processing return end # Set the Duration for the number of Frames for the Zoom to Transition over @zoom_duration[0], @zoom_duration[1] = [0, d].max, [0, d].max end #-------------------------------------------------------------------------- # * Zoom X Light To - Game_Light # - Allows changing the Zoom X Level of the Light over Time # - Affects ONLY X Zoom Levels # z : New Zoom (Small floats work best, like 2.5 or 0.85) # d : Duration - Number of Frames for Transition #-------------------------------------------------------------------------- def zoom_x_light_to(z, d) # Zoom X at the end of Transition @zoom_target[0] = z.to_f # If Duration of Transition is 0 if d == 0 # Set Zoom XY to value right now @zoom_xy[0] = z.to_f # No further processing return end # Set the Duration for the number of Frames for the Zoom to Transition over @zoom_duration[0] = [0, d].max end #-------------------------------------------------------------------------- # * Zoom Y Light To - Game_Light # - Allows changing the Zoom Y Level of the Light over Time # - Affects ONLY Y Zoom Levels # z : New Zoom (Small floats work best, like 2.5 or 0.85) # d : Duration - Number of Frames for Transition #-------------------------------------------------------------------------- def zoom_y_light_to(z, d) # Zoom Y at the end of Transition @zoom_target[1] = z.to_f # If Duration of Transition is 0 if d == 0 # Set Zoom XY to value right now @zoom_xy[1] = z.to_f # No further processing return end # Set the Duration for the number of Frames for the Zoom to Transition over @zoom_duration[1] = [0, d].max end #-------------------------------------------------------------------------- # * Update Zoom To - Game_Light # - Updates values of Zoom Transition independently on X and Y [0, 1] #-------------------------------------------------------------------------- def update_zoom_to # Shorthand for @flicker_size fs = @flicker_size # If a Zoom X Transition is taking place if @zoom_duration[0] >= 1 # Shorthand for X and Y Duration xd = @zoom_duration[0] # Update Zoom X Transition @zoom_xy[0] = (@zoom_xy[0] * (xd - 1) + @zoom_target[0]) / xd # Decrease Duration each Frame of Update @zoom_duration[0] -= 1 # Set Zoom X to Target X if End of Transition @zoom_xy[0] = @zoom_target[0] if @zoom_duration[0] == 0 # If Light is Flickering in Size X elsif @flicker_size[4].is_a?(Numeric) # Decide Zoom X Target t = (fs[0] == @zoom_xy[0]) ? fs[2] : fs[0] # Transition a Zoom using 5th Item in Array as the Duration zoom_x_light_to(t, @flicker_size[4]) end # If a Zoom Y Transition is taking place if @zoom_duration[1] >= 1 # Shorthand for Y Duration yd = @zoom_duration[1] # Update Zoom Y Transition @zoom_xy[1] = (@zoom_xy[1] * (yd - 1) + @zoom_target[1]) / yd # Decrease Duration each Frame of Update @zoom_duration[1] -= 1 # Set Zoom Y to Target Y if End of Transition @zoom_xy[1] = @zoom_target[1] if @zoom_duration[1] == 0 # If Light is Flickering in Size Y elsif @flicker_size[5].is_a?(Numeric) # Decide Zoom Y Target t = (fs[1] == @zoom_xy[1]) ? fs[3] : fs[1] # Transition a Zoom using 6th Item in Array as the Duration zoom_y_light_to(t, @flicker_size[5]) end end #-------------------------------------------------------------------------- # * Set Flicker Opacity - Game_Light # - Causes Light to Flicker in Opacity # args : Array - [Min Opacity, Max Opacity, Duration] #-------------------------------------------------------------------------- def set_flicker_opacity(args) # Iterate 0, 1, 2 for Array for i in 0..2 # If Argument is not a Number (Float or Integer) if not args[i].is_a?(Numeric) # If running game from Editor if $DEBUG # Display Warning to User print "Error in Set Flicker Opacity for Dynamic Lights\n", args[i], " is not a Number in\n", "\\light_flicker_opacity[", args[0], ", ", args[1], ", ", args[2], "]\n\n", "Expected Comment:\n", "\\light_flicker_opacity[min_opacity, max_opacity, duration]", "for Event ", @id end # Clear any assigned values @flicker_opacity = [] # Prevent further processing of this method return end # Assign Value in argument to Flicker array @flicker_opacity[i] = args[i] end # Make sure that the Duration is a whole number by Rounding @flicker_opacity[2] = @flicker_opacity[2].round end #-------------------------------------------------------------------------- # * Clear Flicker Opacity - Game_Light # - Clears Light Flickering in Opacity #-------------------------------------------------------------------------- def clear_flicker_opacity # Clear the Array @flicker_opacity = [] end #-------------------------------------------------------------------------- # * Opacity Light To - Game_Light # - Allows changing the Opacity Level of the Light over Time # z : New Opacity (Number between 0 and 255) # d : Duration - Number of Frames for Transition #-------------------------------------------------------------------------- def opacity_light_to(o, d) # Opacity at the end of Transition @opacity_target = o.to_f # If Duration of Transition is 0 if d == 0 # Set Zoom to value right now @opacity = o.to_f # No further processing return end # Set the Duration for the number of Frames for the Zoom to Transition over @opacity_duration = [0, d].max end #-------------------------------------------------------------------------- # * Update Opacity Transition - Game_Light # - Updates values of Opacity Transition #-------------------------------------------------------------------------- def update_opacity_transition # Shorthand for Opacity, Opacity Target, Opacity Duration, Flicker Opacity o = @opacity ot = @opacity_target od = @opacity_duration fo = @flicker_opacity # If a Opacity Transition is taking place if od >= 1 # Update Opacity Transition @opacity = (o * (od - 1) + ot) / od # Decrease Duration each Frame of Update @opacity_duration -= 1 # Set Zoom to Target if End of Transition @opacity = @opacity_target if @opacity_duration == 0 # If Light is Flickering in Opacity elsif @flicker_opacity[2].is_a?(Numeric) # Decide Opacity Target ot = (fo[0] == @opacity) ? fo[1] : fo[0] # Transition Opacity using 3rd Item in Array as the Duration opacity_light_to(ot, fo[2]) end end #-------------------------------------------------------------------------- # * Daynight Opacity Setup - Game_Light # - Used for Events with \light_daynight_opacity[0, 255] Comments # - Determines Initial Opacity of Light #-------------------------------------------------------------------------- def daynight_opacity_setup # Shorthand dno = @daynight_opacity # Call Game System Method to determine current Number Value for Opacity @opacity = $game_system.dawn_dusk_setup(dno[0], dno[1], dno[2]) end #-------------------------------------------------------------------------- # * Update Daynight Opacity - Game_Light # - Changes Opacity of Light depending on Day or Night (useful Indoors) #-------------------------------------------------------------------------- def update_daynight_opacity # Do not Update if Light Flickers Opacity (can still flicker the size) return if @flicker_opacity[2] # If Option to prevent Prevent Altering Opacity is enabled if $game_system.pause_time_interpreter and ($game_system.map_interpreter.running? or $game_system.battle_interpreter.running?) # Prevent Processing when the Interpreter is running (Ignores Parallels) return end # If a Flag is set to match Opacity of Day / Night if $game_system.shadowmap_color_duration > 0 and @daynight_opacity[1] # If Flow of Time is not Enabled return if not $game_switches[Light_Config::Switch_Time] # Current Game Time (in Seconds) time = $game_variables[Light_Config::Clock_Variable] # Determine Target Opacity if (time > $game_system.dusk_time or time < $game_system.dawn_time) # Use 2nd Array Element as Opacity Target for Night target = @daynight_opacity[1] elsif (time > $game_system.dawn_time) # Use 1st Array Element as Opacity Target for Day target = @daynight_opacity[0] else return end # Determine Duration of Transition if @daynight_opacity_duration d = @daynight_opacity_duration elsif @daynight_opacity[2] d = @daynight_opacity_duration = @daynight_opacity[2] else d = $game_system.shadowmap_color_duration end # Transition the Event Opacity, Prevent Division by 0 @opacity = (@opacity * ([d, 1].max - 1) + target) / [d, 1].max # Set Final Value if End of Transition @opacity = target if d - 1 < 1 # Clear Property or Decrement if @daynight_opacity_duration @daynight_opacity_duration = (d <= 1) ? nil : d - 1 end end end #-------------------------------------------------------------------------- # * Update Angle - Game_Light # - Sets the Light Angle to match character.angle # - Subtracts 180 degrees as adjustment for another script where Up # is used for pointing at characters #-------------------------------------------------------------------------- def update_angle # Get the Parent Character char = (@id == 0) ? $game_player : $game_map.events[@id] # If a Match Direction Flag is set (usually Comment \light_match_direction if @match_direction and not char.direction_fix and char.tile_id == 0 # If Character is Player and Transitioning if char.id == 0 and $game_temp.transition_processing and $game_temp.player_new_direction != 0 # Use the Temporary Direction d = $game_temp.player_new_direction else # Use current Direction d = char.direction end # Rotate the Light for each Direction @angle = d == 8 ? 180 : d == 6 ? 90 : d == 4 ? 270 : d == 2 ? 0 : 0 # If a Flag is set to match the Character's Angle elsif @match_angle # If the Character responds to an Angle command if char.respond_to?(:angle) # Set the Angle of the Light to the Angle of the Character @angle = char.angle # Adjust the Angle for Script Compatability except for Pendulums @angle -= 180 unless @pendulum or @cutout end end end #-------------------------------------------------------------------------- # * Update Direction Images - Game_Light # - Switches Files depending on Character Direction #-------------------------------------------------------------------------- def update_direction_images # Get the Parent Character char = (@id == 0) ? $game_player : $game_map.events[@id] # If Character is Player and Transitioning if char.id == 0 and $game_temp.transition_processing and $game_temp.player_new_direction != 0 # Use the Temporary Direction d = $game_temp.player_new_direction else # Use current Direction d = char.direction end # If a File for a Direction is assigned and character is that Direction if @up and d == 8 @image_file = @up elsif @down and d == 2 @image_file = @down elsif @left and d == 4 @image_file = @left elsif @right and d == 6 @image_file = @right end end #------------------------------------------------------------------------ # * Real Distance - Game_Light # - Distance in Real Coordinates (no Loop Map Round) # c : Character of Parent of Light # rx, ry : Map Coordinates of Target in Real Values (128) #------------------------------------------------------------------------ def real_distance(c, rx, ry) return [c.real_x - rx, c.real_y - ry] end #-------------------------------------------------------------------------- # If Heretic's Loop Maps is installed #-------------------------------------------------------------------------- if Game_Map.method_defined?(:map_loop_passable?) #---------------------------------------------------------------------- # * Real Distance - Game_Light - Loop Map Redefinition # - Distance in Real Coordinates # c : Character of Parent of Light # rx, ry : Map Coordinates of Target in Real Values (128) #---------------------------------------------------------------------- def real_distance(c, rx, ry) # Shorthand for Width and Height in Real Values w = $game_map.width * 128 h = $game_map.height * 128 # Round Positions for Looping Maps rx = ($game_map.loop_horizontal?) ? rx % w : rx ry = ($game_map.loop_vertical?) ? ry % h : ry # Round Self Positions for Looping Maps srx = ($game_map.loop_horizontal?) ? c.real_x % w : c.real_x sry = ($game_map.loop_vertical?) ? c.real_y % h : c.real_y # Determine Initial Distance rx_dist = srx - rx ry_dist = sry - ry # If Looping and Distance is more than half Width of Map if $game_map.loop_horizontal? and rx_dist.abs > w / 2 rx_dist += (rx_dist < 0) ? w : w * -1 end # If Looping and Distance is more than half Height of Map / 2 if $game_map.loop_vertical? and ry_dist.abs > h / 2 ry_dist += (ry_dist < 0) ? h : h * -1 end # Return Distance XY as an Array - x, y = method(x, y) return [rx_dist, ry_dist] end end # End Loop Map Redefinitions #-------------------------------------------------------------------------- # * Update Spotlight - Game_Light # - Adjusts the Zoom Height of Image to allow pointing on a Target # - This code is used to cause a "Spot Light" to "Focus" on a "Target" # - When used with my Rotate, Zoom, and Pendulum Script, a Spotlight will # focus on a Target Character and adjust the Length of the Image so # that character is contained within the Spotlight. Think of it like # a Stage Light which focuses on an Actor presenting lines in a play. # NOTE: This feature ONLY works if you also use Rotate, Zoom, and Pendulum # script with \rotate_target[char_id] #-------------------------------------------------------------------------- def update_spotlight # Get the Parent Character p = (@id == 0) ? $game_player : $game_map.events[@id] # If using Features provided by Rotate, Zoom, and Pendulum Script if @spotlight[0] and p.rotate_target.is_a?(Numeric) # Get the target Character, 0 for Player, 1+ for Events target = (p.rotate_target == 0) ? $game_player : $game_map.events[p.rotate_target] # Check that Target Character exists return if not target # Target can not be Self return if p == target # If Rotate Min and Max are set to limit Rotation (Rotate Zoom Script) if p.rotate_target_min and p.rotate_target_max # Prevent changing @target_percent until Angle is Fixed return if p.angle_fix end # Shorthand for RPG::Cache cache = RPG::Cache # Get the Height of the Light Bitmap lh = cache.light(@image_file, @hue).height # If Target has a Character Name (Graphic Image) if target.character_name != "" # Get Cached Size of Target Bitmap h = cache.character(target.character_name, target.character_hue).height # Divide by 4 for Size of Sprite Sheet h /= 4 else # Default to 16 for Center of Tile h = 16 end # If using Rotate, Zoom, and Pendulum Script, and Sprite Y is Zoomed if target.sprite_zoom_y # Adjust Target Height for Sprite Zoom Y h *= target.sprite_zoom_y end # Determine Target X and Y values in Pixels tx = target.real_x / 4 ty = (target.real_y / 4) - (h / 2) # Determine Real Distance from Light Center to Target dx, dy = real_distance(p, tx * 4, ty * 4) # Convert values of Real Distance to Pixels dx, dy = dx / 4 + @offset_xy[0], dy / 4 + @offset_xy[1] # Distance Hypotenuse dhyp = Math.sqrt(dx ** 2 + dy ** 2) # If features of Rotate, Zoom, and Pendulum are available and in use if p.rotate_target_dist # If Distance to Target is exceeded if dhyp > p.rotate_target_dist * 32 - 1 return end end # Disance from Center of Light to Center of Spotlight as Hypotenuse hyp = lh - @spotlight[0] # If Distance to Target greater than Dist if hyp < dhyp # Set Pre Rotation Zoom Y as Percentage, Clamp to 2nd Value @target_percent = [@spotlight[1],[1.0, dhyp / hyp].max].min else # Set Pre Rotation Zoom Y to Minimum Zoom of 1.0 @target_percent = 1.0 end end end #-------------------------------------------------------------------------- # * Reset Lightning Opacity - Game_Light # - Restores value of Opacity prior to setting Lightning Flash Opacity #-------------------------------------------------------------------------- def reset_lightning_opacity # If Non Opacity Lightning is Stored if not @non_lightning_opacity.nil? # Reset Opacity to Non Lightning Opacity @opacity = @non_lightning_opacity # Clear Non Lightning Opacity to allow other Transitions @non_lightning_opacity = nil end end #-------------------------------------------------------------------------- # * Update Lightning Opacity - Game_Light # - Updates Opacity of Light during a Lightning Flash # - Allows a "Moving Target" so other Opacity Transitions are allowed #-------------------------------------------------------------------------- def update_lightning_opacity # If a Lightning Flash is taking place if @lightning_opacity and $game_system.lightning_flash? # Remember Opacity not affected by Lightning @non_lightning_opacity = @opacity # Shorthand for Duration and Period d = $game_system.shadowmap_lightning_duration p = $game_system.shadowmap_lightning_period # If Duration is 0 (Auto Storm) if d == 0 # Use the Default Duratin since other value not yet declared d = $game_system.lightning_default_duration p = $game_system.lightning_default_duration end # This syncrhonizes the Duration since Events are updated before System d -= 1 if d > 1 # Get the Flash Frams and Double Frames for Hollywood Lightning Flash ff = $game_system.lightning_first_frames df = $game_system.lightning_double_frames # If Initial Lightning Flash (-1 because System already updated once) if d >= p - ff # Set Opacity to the Lightning Opacity as a Static Value @opacity = [@lightning_opacity, @non_lightning_opacity].max # Transition the Current Lightning Opacity @current_opacity = [@lightning_opacity, @non_lightning_opacity].max # If this is the Pause between the Hollywood Double Lightning Flash elsif d >= p - (ff + df) # Set Opacity to the Non Lightning Opacity @opacity = @non_lightning_opacity else # If Current Opacity is set if @current_opacity # Transition the Current Opacity to Non Lightning Opacity @opacity = (@current_opacity * (d - 1) + @non_lightning_opacity) / d # Current Opacity not set (happens on Map Transfer during Lightning) else # Transition the Current Opacity to Non Lightning Opacity @opacity = (@lightning_opacity * (d - 1) + @opacity) / d end # Remember the Transitioned Value @current_opacity = @opacity end end end #-------------------------------------------------------------------------- # * Check After Shadow - Game_Light # - Causes Flag to be set so Lights are Rescanned on Change #-------------------------------------------------------------------------- def check_after_shadow # If Values do not match if @after_shadow != @last_after_shadow # Set a Flag to Rescan all Lights $game_map.need_refresh = true # Remember Value @last_after_shadow = @after_shadow end end #-------------------------------------------------------------------------- # * Update Hue Rotation - Game_Light # - Updates Hue Rotation when Light has a Hue Speed set #-------------------------------------------------------------------------- def update_hue_rotation # If Hue Speed is not 0 if @hue_speed and @hue_speed != 0 # Adjust current Hue Value by Hue Speed @hue += @hue_speed # Round to 360 Degrees @hue %= 360.0 end end #-------------------------------------------------------------------------- # * Update - Game_Light # - Main Update, calls all of the Update Methods for Lights #-------------------------------------------------------------------------- def update # Checks if After Shadow Flag has changed check_after_shadow # Resets Opacity to Non Lightning Value prior to calcuating any adjustments reset_lightning_opacity # Updates Hue Changes update_hue_rotation # Update Zoom Transitions update_zoom_to # Update Opacity Transitions update_opacity_transition # Update Angle update_angle # Update Direction Images update_direction_images # Updates Light Target update_spotlight # Updates Lights with different Opacity during Day and Night update_daynight_opacity # Updates Opacity of Light during Lightning Flash update_lightning_opacity end end #============================================================================= # ** Game_Map - Class #============================================================================= class Game_Map #-------------------------------------------------------------------------- # * Public Instance Variables - Game_Map #-------------------------------------------------------------------------- attr_accessor :scan_lights # Flag to Recheck Events for Lights #-------------------------------------------------------------------------- # * Initialize - Game_Map # - Initializes values in Game Variables and Switches before creating Map #-------------------------------------------------------------------------- alias light_time_game_map_initialize initialize unless $@ def initialize # Set initial values for the Game Clock and Game Switches $game_system.initialize_game_clock # Call Original or other Aliases light_time_game_map_initialize end #-------------------------------------------------------------------------- # * Setup - Game_Map # - Sets a Flag on Setup to Scan Events which may be Lights #-------------------------------------------------------------------------- alias dynamic_lights_setup setup unless $@ def setup(map_id) # If Map ID is Valid and does not match current Map ID if map_id > 0 and map_id != @map_id # Sets a Flagh to Scan Lights for Spriteset Map @scan_lights = true end # Call Original or other Aliases dynamic_lights_setup(map_id) end #-------------------------------------------------------------------------- # * Update - Game_Map # - Alias updates the Clock Window #-------------------------------------------------------------------------- alias light_time_update update unless $@ def update # Call Original or other Aliases light_time_update # Update the Clock Window $scene.window_clock.update if $scene.is_a?(Scene_Map) end #-------------------------------------------------------------------------- # * Refresh - Game_Map # - Sets Flag to Rescan Lights #-------------------------------------------------------------------------- alias dynamic_lights_map_refresh refresh unless $@ def refresh # Set Flat to Rescan Light Events @scan_lights = true # Call Original or other Aliases dynamic_lights_map_refresh end end #============================================================================= # ** Game_Character - Class #============================================================================= class Game_Character #-------------------------------------------------------------------------- # * Public Instance Variables - Game_Character #-------------------------------------------------------------------------- attr_accessor :direction_fix # Direction Fix Flag attr_accessor :rotate_target # Feature of Rotate, Zoom, Pendulum Script attr_accessor :rotate_target_dist # Feature of Rotate, Zoom, Pendulum Script attr_accessor :sprite_zoom_y # Feature of Rotate, Zoom, Pendulum Script attr_accessor :rotate_target_min # Feature of Rotate, Zoom, Pendulum Script attr_accessor :rotate_target_max # Feature of Rotate, Zoom, Pendulum Script attr_accessor :angle_fix # Feature of Rotate, Zoom, Pendulum Script attr_accessor :sprite_zoom_x # Feature of Rotate, Zoom, Pendulum Script attr_accessor :sprite_zoom_y # Feature of Rotate, Zoom, Pendulum Script attr_reader :light # Light Object attr_reader :pendulum # Feature of Rotate, Zoom, Pendulum Script #-------------------------------------------------------------------------- # * Initialize - Game_Character #-------------------------------------------------------------------------- alias dynamic_light_character_initialize initialize unless $@ def initialize # Call Original or other Aliases dynamic_light_character_initialize # Create a Light if Player @light = Game_Light.new(0) if self.is_a?(Game_Player) end #-------------------------------------------------------------------------- # * Rotate Center - Game_Character (Getter Method) # NOTE: Identical Method to Rotate, Zoom, and Pendulum Script # - Determines Vertical Center of Sprite Rotation # - 0 is Top, 1 is Center (Default), 2 is Bottom #-------------------------------------------------------------------------- def rotate_center return @rotate_center ||= 1 end #-------------------------------------------------------------------------- # * Set Flicker Size - Game_Character # - Causes Light to Flicker in Size # args : Array - [Min Zoom, Max Zoom, Duration] #-------------------------------------------------------------------------- def set_flicker_size(*args) # Shorthand to the Light Object Method of same name @light.set_flicker_size(args) if @light end #-------------------------------------------------------------------------- # * Set Flicker Size X - Game_Character # - Causes Light to Flicker in Size X # args : Array - [Min Zoom, Max Zoom, Duration] #-------------------------------------------------------------------------- def set_flicker_size_x(*args) # Shorthand to the Light Object Method of same name @light.set_flicker_size_x(args) if @light end #-------------------------------------------------------------------------- # * Set Flicker Size Y - Game_Character # - Causes Light to Flicker in Size Y # args : Array - [Min Zoom, Max Zoom, Duration] #-------------------------------------------------------------------------- def set_flicker_size_y(*args) # Shorthand to the Light Object Method of same name @light.set_flicker_size_y(args) if @light end #-------------------------------------------------------------------------- # * Clear Flicker Size - Game_Light # - Clears Light Flickering of Size / Zoom # NOTE: Does not reset zoom values #-------------------------------------------------------------------------- def clear_flicker_size # Shorthand to the Light Object Method of same name @light.clear_flicker_size if @light end #-------------------------------------------------------------------------- # * Zoom Light To - Game_Character # - Allows changing the Zoom Level X and Y of the Light over Time # z : New Zoom (Small floats work best, like 2.5 or 0.85) # d : Duration - Number of Frames for Transition #-------------------------------------------------------------------------- def zoom_light_to(z, d) # Shorthand to the Light Object Method of same name @light.zoom_light_to(z, d) if @light end #-------------------------------------------------------------------------- # * Set Flicker Opacity - Game_Character # - Causes Light to Flicker in Opacity # args : Array - [Min Opacity, Max Opacity, Duration] #-------------------------------------------------------------------------- def set_flicker_opacity(*args) # Shorthand to the Light Object Method of same name @light.set_flicker_opacity(args) if @light end #-------------------------------------------------------------------------- # * Clear Flicker Opacity - Game_Light # - Clears Light Flickering of Opacity # NOTE: Does not reset Opacity #-------------------------------------------------------------------------- def clear_flicker_opacity # Shorthand to the Light Object Method of same name @light.clear_flicker_opacity if @light end #-------------------------------------------------------------------------- # * Opacity Light To - Game_Character # - Allows changing the Opacity of the Light over Time # z : New Opacity (Number between 0.0 and 255.0) # d : Duration - Number of Frames for Transition #-------------------------------------------------------------------------- def opacity_light_to(z, d) # Shorthand to the Light Object Method of same name @light.opacity_light_to(z, d) if @light end #-------------------------------------------------------------------------- # * Angle - Game_Character (Getter Method) # - Duplicate Method provided by Rotate, Zoom, and Pendulum Script #-------------------------------------------------------------------------- def angle # Return or Initialize the property for Save Games return @angle ||= 0 end # If Heretic's Vehicles and Looping Maps is installed if Game_Character.method_defined?(:vehicle_screen_y) and Game_Map.method_defined?(:loop_vertical?) #------------------------------------------------------------------------ # * Light Screen Y - Game_Character # - Determines Y Position of Light ignoring Looping Map Adjustments #------------------------------------------------------------------------ def light_screen_y # If a No Jump Flag has not been set if not @light.no_jump # Determine Jump Height jh = (@jump_peak ** 2 - (@jump_count - @jump_peak).abs ** 2) / 2 # Adjust for any Floating Values (used to make characters Float) jh -= float_screen_y_adjust else # Jump Height forced to 0 due to No Jump Flag jh = 0 end # Adjust Value for Float & Sprite Offsets ignoring Vertical Looping Maps return ($game_map.adjust_y(@real_y / 128.0) * 32 + 32 - jh).ceil end # If Looping Maps is installed elsif Game_Map.method_defined?(:loop_vertical?) #------------------------------------------------------------------------ # * Light Screen Y - Game_Character # - Returns Original Value of Screen Y #------------------------------------------------------------------------ def light_screen_y # If a No Jump Flag has not been set if not @light.no_jump # Adjust Returned Value for Float and Sprite Offsets return screen_y else # Adjust for Looping Maps and Ceil to prevent Screen Event Tearing return ($game_map.adjust_y(@real_y / 128.0) * 32 + 32).ceil end end # Standard Non Looping No Vehicles Definition else #------------------------------------------------------------------------ # * Light Screen Y - Game_Character # - Returns Original Value of Screen Y #------------------------------------------------------------------------ def light_screen_y # If a No Jump Flag has not been set if not @light.no_jump # Return Unmodified Value return screen_y else # Determine Y Coordinate without Jump Values return (@real_y - $game_map.display_y + 3) / 4 + 32 end end end #-------------------------------------------------------------------------- # * Dawn? - Game_Character # - Returns true if Time is during Dawn from $game_system #-------------------------------------------------------------------------- def dawn? return $game_system.dawn? end #-------------------------------------------------------------------------- # * Day? - Game_Character # - Returns true if Time is during Day from $game_system #-------------------------------------------------------------------------- def day? return $game_system.day? end #-------------------------------------------------------------------------- # * Dusk? - Game_Character # - Returns true if Time is during Dusk from $game_system #-------------------------------------------------------------------------- def dusk? return $game_system.dusk? end #-------------------------------------------------------------------------- # * Night? - Game_Character # - Returns true if Time is during Night from $game_system #-------------------------------------------------------------------------- def night? return $game_system.night? end #-------------------------------------------------------------------------- # * Lightning? Game_Character # - Returns true if Lightning is flashing or scheduled for the next frame #-------------------------------------------------------------------------- def lightning? return $game_system.lightning_flash? end #-------------------------------------------------------------------------- # * In Light? - Game_Character # - Returns true when Character's Center is in a Light # arg : can be an Event ID (or 0 for Player), Character, or nil # tolerance : Percent to Add so Light registers as false if in tolerance #-------------------------------------------------------------------------- def in_light?(arg = nil, tolerance = 0) # False if Scene is not a Map return nil if not $scene.is_a?(Scene_Map) # Use Self if Argument is not provided arg = self if arg.nil? # Return value of Method from Map Interpreter return $game_system.map_interpreter.in_light?(arg, tolerance) end #========================================================================== # * NOTE - The following definitions are borrowed from my "Heretic's # Advanced Camera System". The definitions are identical so # they are perfectly compatible with the script. One of the # features of the Camera System is to "Lock Scrolling" hence # these methods use "lock_" in the name. These methods are # used for determining the position of a character so another # feature of this script which allows checking if a character # is being illuminated can function. #========================================================================== #-------------------------------------------------------------------------- # * Lock Real Distance - Game_Character # - Distance in Real Coordinates (no Loop Map Round) #-------------------------------------------------------------------------- def lock_real_distance(real_x1, real_y1, real_x2, real_y2) return [real_x1 - real_x2, real_y1 - real_y2] end #-------------------------------------------------------------------------- # * Lock Real Distance X - Game_Character # - Distance X in Real Coordinates (no Loop Map Round) #-------------------------------------------------------------------------- def lock_real_distance_x(real_x1, real_x2) return real_x1 - real_x2 end #-------------------------------------------------------------------------- # * Lock Real Distance Y - Game_Character # - Distance Y in Real Coordinates (no Loop Map Round) #-------------------------------------------------------------------------- def lock_real_distance_y(real_y1, real_y2) return real_y1 - real_y2 end #-------------------------------------------------------------------------- # If Heretic's Loop Maps is installed #-------------------------------------------------------------------------- if Game_Map.method_defined?(:map_loop_passable?) #------------------------------------------------------------------------ # * Platform Real Distance - Game_Character - Loop Map Redefinition # - Distance in Real Coordinates #------------------------------------------------------------------------ def lock_real_distance(rx1, ry1, rx2, ry2) # Determine Real XY Distance rx_dist = lock_real_distance_x(rx1, rx2) ry_dist = lock_real_distance_y(ry1, ry2) # Return Distance XY as an Array - x, y = method(x, y) return [rx_dist, ry_dist] end #------------------------------------------------------------------------ # * Platform Real Distance X - Game_Character - Loop Map Redefinition # - Distance X in Real Coordinates #------------------------------------------------------------------------ def lock_real_distance_x(rx1, rx2) # Shorthand for Width in Real Values w = $game_map.width * 128 # Round Positions for Looping Maps rx1 = ($game_map.loop_horizontal?) ? rx1 % w : rx1 rx2 = ($game_map.loop_horizontal?) ? rx2 % w : rx2 # Determine Initial Distance rx_dist = rx2 - rx1 # If Looping and Distance is more than half Width of Map if $game_map.loop_horizontal? and rx_dist.abs > w / 2 rx_dist += (rx_dist < 0) ? w : w * -1 end # Return Distance X return rx_dist end #------------------------------------------------------------------------ # * Platform Real Distance - Game_Character - Loop Map Redefinition # - Distance in Real Coordinates #------------------------------------------------------------------------ def lock_real_distance_y(ry1, ry2) # Shorthand for Width and Height in Real Values h = $game_map.height * 128 # Round Positions for Looping Maps ry1 = ($game_map.loop_vertical?) ? ry1 % h : ry1 ry2 = ($game_map.loop_vertical?) ? ry2 % h : ry2 # Determine Initial Distance ry_dist = ry2 - ry1 # If Looping and Distance is more than half Height of Map / 2 if $game_map.loop_vertical? and ry_dist.abs > h / 2 ry_dist += (ry_dist < 0) ? h : h * -1 end # Return Distance Y return ry_dist end end end #============================================================================== # ** Game_Player - Class #============================================================================== class Game_Player < Game_Character #-------------------------------------------------------------------------- # * Update - Game_Player #-------------------------------------------------------------------------- alias light_player_update update unless $@ def update # Call Original or other Aliases light_player_update # Update Light Object @light.update if @light end end #============================================================================== # ** Game_Event #============================================================================== class Game_Event < Game_Character #-------------------------------------------------------------------------- # * Public Instance Variables - Game_Character #-------------------------------------------------------------------------- attr_accessor :lightning_event_opacity # Event Opacity during Lightning attr_accessor :light_opacity_duration # Duration of Transition* #-------------------------------------------------------------------------- # * Update Event Light Opacity - Game_Event # - Changes Opacity of Character / Tile to match Shadowmap # - Use a \light_event_opacity Comment # ** \light_event_opacity[day opacity, night opacity, [duration] ] #-------------------------------------------------------------------------- def update_event_light_opacity # If a Flag is set to match Opacity of Day if $game_system.shadowmap_color_duration > 0 and @light_event_opacity # If Option to prevent Prevent Altering Opacity is enabled if $game_system.pause_time_interpreter and ($game_system.map_interpreter.running? or $game_system.battle_interpreter.running?) # Prevent Processing when the Interpreter is running (ignore Parallels) return end # If Flow of Time is not Enabled return if not $game_switches[Light_Config::Switch_Time] # Current Game Time (in Seconds) time = $game_variables[Light_Config::Clock_Variable] # Determine Target Opacity if (time > $game_system.dusk_time or time < $game_system.dawn_time) # Use 2nd Array Element as Opacity Target target = @light_event_opacity[1] elsif (time > $game_system.dawn_time) # Use 1st Array Element as Opacity Target target = @light_event_opacity[0] else # No Day or Night Opacity Targets so quit processing return end # Determine Duration of Transition if @light_opacity_duration d = @light_opacity_duration elsif @light_event_opacity[2] d = @light_opacity_duration = @light_event_opacity[2] else d = $game_system.shadowmap_color_duration end # Transition the Event Opacity, Prevent Division by 0 @opacity = (@opacity * ([d, 1].max - 1) + target) / [d, 1].max # Set Final Value if End of Transition @opacity = target if d - 1 < 1 # Clear Property or Decrement if @light_opacity_duration @light_opacity_duration = (d <= 1) ? nil : d - 1 end end end #-------------------------------------------------------------------------- # * Reset Lightning Opacity - Game_Event # - Resets Values used during Lightning Flashes for proper calculations #-------------------------------------------------------------------------- def reset_lightning_opacity # If a Non Lightning Opacity is stored if @non_lightning_opacity # Reset the Opacity of the Event to Non Lightning Opacity @opacity = @non_lightning_opacity # Clear the Non Lightning Opacity for proper display of Event @non_lightning_opacity = nil end end #-------------------------------------------------------------------------- # * Update - Game_Event # - Main Update Method #-------------------------------------------------------------------------- alias light_event_update update unless $@ def update # Reset Opacity to values prior to Lightning for proper calculations reset_lightning_opacity # Call Original or other Aliases light_event_update # Update Light Object @light.update if @light # Updates Event Opacity for Day / Night Opacity update_event_light_opacity # Update the Opacity of the Event during Lightning Flashes update_lightning_opacity end #-------------------------------------------------------------------------- # * Update Lightning Opacity - Game_Event # - This handles Events with \light_event_lightning_opacity[N] Comments #-------------------------------------------------------------------------- def update_lightning_opacity # Shorthand for Game System s = $game_system # If Event is Lightning Sensitive and a Lightning Flash is taking place if @lightning_event_opacity and s.lightning_flash? # Remember Opacity not affected by Lightning @non_lightning_opacity = @opacity # Shorthand for Duration, Period, Frames and Non Lightning Opacity d, p = s.shadowmap_lightning_duration, s.shadowmap_lightning_period # If Duration is 0 (Auto Storm) if d == 0 # Use the Default Duratin since other value not yet declared d = s.lightning_default_duration p = s.lightning_default_duration end # This syncrhonizes the Duration since Events are updated before System d -= 1 if d > 1 # Get the Flash Frams and Double Frames for Hollywood Lightning Flash ff, df = s.lightning_first_frames, s.lightning_double_frames # If Initial Lightning Flash if d >= p - ff # Set Opacity to the Lightning Opacity as a Static Value @opacity = @lightning_event_opacity # Transition the Current Lightning Opacity @lightning_current_opacity = @lightning_event_opacity # If this is the Pause between the Hollywood Double Lightning Flash elsif d >= p - (ff + df) # Set Opacity to the Non Lightning Opacity @opacity = @non_lightning_opacity else # If Lighting Opacity is set if @lightning_current_opacity # Transition the Current Opacity to Non Lightning Opacity @opacity = (@lightning_current_opacity * (d - 1) + @opacity) / d # Current Opacity not set (happens when Transfer Maps during Lightning) else # Transition the Current Opacity to Non Lightning Opacity @opacity = (@lightning_event_opacity * (d - 1) + @opacity) / d end # Remember the Transitioned Value @lightning_current_opacity = @opacity end end end #-------------------------------------------------------------------------- # * Event Light Opacity Setup - Game_Event # - Used for Events with \light_event_opacity[0, 255] Comments # - Determines Initial Opacity of Event when Transferring Maps #-------------------------------------------------------------------------- def event_light_opacity_setup # Shorthand leo = @light_event_opacity # Call Game System Method to determine current Number Value for Opacity @opacity = $game_system.dawn_dusk_setup(leo[0], leo[1], leo[2], @id) end #-------------------------------------------------------------------------- # * Reset Light Event Daynight Opacity - Game_Event # - Resets Lights and Events to proper Opacities # - Used when an Event sets the Game_Variable that holds Time #-------------------------------------------------------------------------- def reset_light_event_daynight_opacity # If this Event's Opacity responds to Day / Night Cycles if not @light_event_opacity.nil? and @light_event_opacity[1].is_a?(Numeric) # Reset any Lightning Opacities reset_lightning_opacity # Clear Property @light_opacity_duration = nil # Call Setup Method to determine Opacity event_light_opacity_setup end # If Event has a Light and reacts to Daynight Transitions if @light and @light.daynight_opacity and @light.daynight_opacity[1] # Reset any Lightning Opacities @light.reset_lightning_opacity # Call Setup Method to determine Opacity @light.daynight_opacity_setup end end #-------------------------------------------------------------------------- # * Light Read Event Comment - Game_Event # - Reads Comments in List of Event Commands for Event Configuration #-------------------------------------------------------------------------- def light_read_event_comment(comment, count) # Looks for "\light[File.png]" in the Comment on List of Event Commands comment.gsub(/^\\light\[([a-z0-9_.\/ ]*)\]\z/i){ # Create the Light using the String in Comment as the Image File @light = Game_Light.new(@id, $1.to_s) # Return Count of Number of Lines for other Aliases to read this Comment return count; } # Looks for "\light_up[]" in the Comment on List of Event Commands comment.gsub(/^\\light_up\[([a-z0-9_.\/ ]*)\]\z/i){ # Assigns a File to use when Character faces UP if Event is a Light @light.up = $1.to_s if @light; return count } # Looks for "\light_down[]" in the Comment on List of Event Commands comment.gsub(/^\\light_down\[([a-z0-9_.\/ ]*)\]\z/i){ # Assigns a File to use when Character faces UP if Event is a Light @light.down = $1.to_s if @light; return count } # Looks for "\light_left[]" in the Comment on List of Event Commands comment.gsub(/^\\light_left\[([a-z0-9_.\/ ]*)\]\z/i){ # Assigns a File to use when Character faces UP if Event is a Light @light.left = $1.to_s if @light; return count } # Looks for "\light_up[]" in the Comment on List of Event Commands comment.gsub(/^\\light_right\[([a-z0-9_.\/ ]*)\]\z/i){ # Assigns a File to use when Character faces UP if Event is a Light @light.right = $1.to_s if @light; return count } # Looks for "\light_hue[180]" in the Comment on List of Event Commands comment.gsub(/^\\light_hue\[([0-9.]+)\]\z/i){ # Set the Hue of the Light Image using the Number in Comment as the Hue @light.hue = $1.to_f if @light # Return Count of Number of Lines for other Aliases to read this Comment return count; } # Looks for "\light_hue_speed[n]" in the Comment on List of Event Commands comment.gsub(/^\\light_hue_speed\[([-0-9.]+)\]\z/i){ # Set the Hue Speed of the Light using the Number in Comment as Speed @light.hue_speed = $1.to_f if @light # Return Count of Number of Lines for other Aliases to read this Comment return count; } # Looks for "\light_disabled" in the Comment on List of Event Commands comment.gsub(/^\\light_disabled\z/i){ # Disables a Light upon Creation still reading all Comments @light.enabled = false if @light; return count } # Looks for "\light_no_jump" in the Comment on List of Event Commands comment.gsub(/^\\light_no_jump\z/i){ # Disables Lights following Jumps (also affects Floats) @light.no_jump = true if @light; return count } # Looks for "\light_zoom_xy[1.1]" in the Comment on List of Event Commands comment.gsub(/^\\light_zoom_xy\[([0-9., ]+)\]\z/i){ # If Event is a Light if @light # Set Light Zoom using the Number in Comment as the Zoom @light.zoom_xy = ($1.split(",").map { |s| s.to_f }) # Reset the Array if values are improper @light.zoom_xy = [1.0, 1.0] if @light.zoom_xy.size < 2 end # Return Count of Number of Lines for other Aliases to read this Comment return count; } # Looks for "\light_width[64]" in the Comment on List of Event Commands comment.gsub(/^\\light_width\[([0-9]+)\]\z/i){ # Set the Width of the Light Image using the Number from the Comment @light.width = $1.to_i if @light # Return Count of Number of Lines for other Aliases to read this Comment return count; } # Looks for "\light_height[64]" in the Comment on List of Event Commands comment.gsub(/^\\light_height\[([0-9]+)\]\z/i){ # Set the Height of the Light Image using the Number from the Comment @light.height = $1.to_i if @light # Return Count of Number of Lines for other Aliases to read this Comment return count; } # Looks for "\light_offset_xy[16, 32]" in the Comment comment.gsub(/^\\light_offset_xy\[([0-9,\- ]+)\]\z/i){ # If Event is a Light if @light # Set Light Property using the Number in Comment as the Offset XY @light.offset_xy = ($1.split(",").map { |s| s.to_i }) # Reset the Array if values are improper @light.offset_xy = [0, 0] if @light.offset_xy.size < 2 end # Return Count of Number of Lines for other Aliases to read this Comment return count; } # Looks for "\light_opacity[128.0" in the Comment on List of Event Commands comment.gsub(/^\\light_opacity\[([0-9.]+)\]\z/i){ # Create the Light using the Number in Comment as the Opacity @light.opacity = $1.to_f if @light # Return Count of Number of Lines for other Aliases to read this Comment return count; } # Looks for "\light_flicker_size[Zoom Min, Zoom Max, Zoom Duration]" comment.gsub(/^\\light_flicker_size\[([0-9,. ]+)\]\z/i){ # Set the Flicker Values from the $1 capture group @light.set_flicker_size($1.split(",").map { |s| s.to_f }) if @light # Return Count of Number of Lines for other Aliases to read this Comment return count; } # Looks for "\light_flicker_size_x[Zoom Min, Zoom Max, Zoom Duration]" comment.gsub(/^\\light_flicker_size_x\[([0-9,. ]+)\]\z/i){ # Set the Flicker Values from the $1 capture group @light.set_flicker_size_x($1.split(",").map { |s| s.to_f }) if @light # Return Count of Number of Lines for other Aliases to read this Comment return count; } # Looks for "\light_flicker_size_y[Zoom Min, Zoom Max, Zoom Duration]" comment.gsub(/^\\light_flicker_size_y\[([0-9,. ]+)\]\z/i){ # Set the Flicker Values from the $1 capture group @light.set_flicker_size_y($1.split(",").map { |s| s.to_f }) if @light # Return Count of Number of Lines for other Aliases to read this Comment return count; } # Looks for "\light_flicker_opacity[192.5, 255, 20]" in the Comment comment.gsub(/^\\light_flicker_opacity\[([0-9,. ]+)\]\z/i){ # Set the Flicker Values from the $1 capture group @light.set_flicker_opacity($1.split(",").map { |s| s.to_f }) if @light # Return Count of Number of Lines for other Aliases to read this Comment return count; } # Looks for "\light_event_opacity[0, 255, 20*]" in the Comment comment.gsub(/^\\light_event_opacity\[([0-9., ]*)\]\z/i){ # Array of Opacity in Day and Night @light_event_opacity = $1.split(",").map { |s|s.to_f } # Clear the Property if a Range is not given in the Comment @light_event_opacity = nil if @light_event_opacity.size < 2 # Sets a Flag for Always Update for Optimization Scripts @al_update = true if not @light_event_opacity.nil? # Setup the Current Light Opacity if Parameters are accepted event_light_opacity_setup if not @light_event_opacity.nil? # Return Count of Number of Lines for other Aliases to read this Comment return count; } # Looks for "\light_daynight_opacity[0, 255]" in the Comment comment.gsub(/^\\light_daynight_opacity\[([0-9., ]*)\]\z/i){ # If Event is a Light if @light # Set a Flag so Opacity of Character / Tile Sprite matches Shadowmap @light.daynight_opacity = $1.split(",").map { |s|s.to_f } # Clear the Property if a Range is not given in the Comment @light.daynight_opacity = [] if @light.daynight_opacity.size < 2 # Sets a Flag for Always Update for Optimization Scripts @al_update = true if @light.daynight_opacity.size < 2 # Determine current Opacity based on current Time and Shadowmap @light.daynight_opacity_setup if @light.daynight_opacity.size > 1 end # Return Count of Number of Lines for other Aliases to read this Comment return count; } # Looks for \light_match_direction Comments comment.gsub(/^\\light_match_direction\z/i){ # Set a Flag to in the Light Object if Event is a Light @light.match_direction = true if @light # Return Count of Number of Lines for other Aliases to read this Comment return count; } # Looks for \light_angle[180] Comments comment.gsub(/^\\light_angle\[([-0-9.]*)\]\z/i){ # Set the Light using the Number in Comment as the Angle @light.angle = $1.to_f if @light # Return Count of Number of Lines for other Aliases to read this Comment return count; } # Looks for \light_match_angle Comments comment.gsub(/^\\light_match_angle\z/i){ # Set a Flag to in the Light Object if Event is a Light @light.match_angle = true if @light # Return Count of Number of Lines for other Aliases to read this Comment return count; } # Looks for \light_cutout[] Comments comment.gsub(/^\\light_cutout\[([a-z0-9_.\-\/ ]*)\]\z/i){ # If Event is a Light if @light # Set Name to the Capture Group for Cutouts @light.cutout_name = $1 # Set Flag to render Light as a Cutout @light.cutout = true end # Return Count of Number of Lines for other Aliases to read this Comment return count; } # Looks for \light_shadow Comments comment.gsub(/^\\light_shadow\[([0-9]*)\]\z/i){ # Set a Flag to in the Light Object if Event is a Light @light.shadow = ($1) ? $1.to_i : 0 if @light # Return Count of Number of Lines for other Aliases to read this Comment return count; } # Looks for \light_shadow_match_opacity Comments comment.gsub(/^\\light_shadow_match_opacity\z/i){ # Set a Flag to in the Light Object if Event is a Light @light.shadow_match_opacity = true if @light # Return Count of Number of Lines for other Aliases to read this Comment return count; } # Looks for \light_after_shadow Comments comment.gsub(/^\\light_after_shadow\z/i){ # Set a Flag to in the Light Object if Event is a Light @light.after_shadow = true if @light # Return Count of Number of Lines for other Aliases to read this Comment return count; } # Looks for \light_target[offset, max_zoom] Comments comment.gsub(/^\\light_spotlight\[([-0-9., ]*)\]\z/i){ # Spotlight for Light (Target of Spotlight set by Rotate Zoom Script) @light.spotlight = $1.split(",").map { |s|s.to_f } if @light # Return Count of Number of Lines for other Aliases to read this Comment return count; } # Looks for \light_pendulum Comments comment.gsub(/^\\light_pendulum\[?([-0-9]+)?\]?\z/i){ # Pendulum for Light (Target of Spotlight set by Rotate Zoom Script) @light.pendulum = ($1.nil?) ? true : $1.to_i if @light # Sets a Flag for Always Update for Optimization Scripts @al_update = true if @light # Return Count of Number of Lines for other Aliases to read this Comment return count; } # Looks for \light_pendulum_y Comments comment.gsub(/^\\light_pendulum_y\z/i){ # Set a Flag to in the Light Object if Event is a Light @light.pendulum_y = true if @light # Return Count of Number of Lines for other Aliases to read this Comment return count; } # Looks for \light_pendulum_opacity Comments comment.gsub(/^\\light_pendulum_opacity\z/i){ # Set a Flag to in the Light Object if Event is a Light @light.pendulum_opacity = true if @light # Return Count of Number of Lines for other Aliases to read this Comment return count; } # Looks for \light_pendulum_zoom[N] Comments comment.gsub(/^\\light_pendulum_zoom\[([0-9.]+)\]\z/i){ # Pendulum Zoom - The closer to 90 Degrees, the closer to N it will be @light.pendulum_zoom = $1.to_f if @light # Return Count of Number of Lines for other Aliases to read this Comment return count; } # Looks for \light_lightning_opacity[N] Comments comment.gsub(/^\\light_lightning_opacity\[([0-9.]+)\]\z/i){ # Pendulum Zoom - The closer to 90 Degrees, the closer to N it will be @light.lightning_opacity = $1.to_f if @light # Sets a Flag for Always Update for Optimization Scripts @al_update = true if @light # Return Count of Number of Lines for other Aliases to read this Comment return count; } # Looks for \lightning_event_opacity[N] Comments comment.gsub(/^\\lightning_event_opacity\[([0-9.]+)\]\z/i){ # Pendulum Zoom - The closer to 90 Degrees, the closer to N it will be @lightning_event_opacity = $1.to_f # Return Count of Number of Lines for other Aliases to read this Comment return count; } # Return the Line Counter if it has not yet been returned already return count end #-------------------------------------------------------------------------- # * Reset Event Light Config - Game_Event # - Resets Platforms on Refresh or Page Change #-------------------------------------------------------------------------- def reset_event_light_config # Clears Light Object @light = nil # Clear Light related properties @light_event_opacity = nil @lightning_event_opacity = nil @non_lightning_opacity = nil @lightning_current_opacity = nil end #-------------------------------------------------------------------------- # * Refresh - Game_Event # - Reads Event Comments for Light Configuration #-------------------------------------------------------------------------- alias dynamic_light_event_refresh refresh unless $@ def refresh # Initial State of current @page before Refresh page = @page # Call Original or Other Aliases of Refresh dynamic_light_event_refresh # If Event Not Erased unless @erased # If Page is Invalid if @page.nil? # Clear any Lights or Light Properties reset_event_light_config # If Page is not Nil (Page Conditions) and Page Change is occuring elsif not @page.nil? and @page != page # Reset to set again by Comment Conditions (@option = nil) reset_event_light_config # For Performance on checking each Command in Page List when Refreshed count = 0 # For each Event Command as 'command' in Page List of Event Commands @page.list.each {|command| # If Command Code is a Comment (Code 108, 408 is Next Line Code) if command.code == 108 # Check Comment for Configuration Values and adjust Counter count = light_read_event_comment(command.parameters[0], count) end # Increment Counter count += 1 # Stop Iterating after Limit reached break if count >= Light_Config::Comment_Limit } # End |command| loop (Event Command List) # Check if Light exists in Spriteset check_light end end end #-------------------------------------------------------------------------- # * Reset Page Comment Config - Game_Event # - Resets Platforms on Refresh or Page Change #-------------------------------------------------------------------------- alias dynamic_light_erase_event erase unless $@ def erase # Call Original or other Aliases dynamic_light_erase_event # Erase the Light @light = nil # Clear Properties to match Event Opacity to Day / Night Color Alpha @light_event_opacity = nil @lightning_event_opacity = nil @non_lightning_opacity = nil @lightning_current_opacity = nil end #-------------------------------------------------------------------------- # * Make Light - Game_Event # - Creates a Light Object #-------------------------------------------------------------------------- def make_light # Create a new Light Object if one does not already exist @light = Game_Light.new(@id) unless @light # Refresh the Map so Lights are properly added in the Spriteset_Map $game_map.need_refresh = true end #-------------------------------------------------------------------------- # * Check Light - Game_Event # - Checks if Event is Refreshed by itself without Map Refreshing Event # - Needed for compatability with Unlimited Event Page Conditions where # the Event is refreshed without using $game_map.need_refresh #-------------------------------------------------------------------------- def check_light # If Event Page has a Light if @light # If Map does not have a Scan Lights Flag if not $game_map.scan_lights # If Scene is the Map if $scene.is_a?(Scene_Map) # Make sure Event is properly Sorted into Light Hashes $scene.light_spriteset.sort_light(self) else # Set a Flag that Lights need to be Rescanned for Spriteset $game_map.scan_lights = true end end end end end #============================================================================== # ** Interpreter - Class #============================================================================== class Interpreter #-------------------------------------------------------------------------- # * Dawn? - Interpreter # - Returns true if Time is during Dawn from $game_system #-------------------------------------------------------------------------- def dawn? return $game_system.dawn? end #-------------------------------------------------------------------------- # * Day? - Interpreter # - Returns true if Time is during Day from $game_system #-------------------------------------------------------------------------- def day? return $game_system.day? end #-------------------------------------------------------------------------- # * Dusk? - Interpreter # - Returns true if Time is during Dusk from $game_system #-------------------------------------------------------------------------- def dusk? return $game_system.dusk? end #-------------------------------------------------------------------------- # * Night? - Interpreter # - Returns true if Time is during Night from $game_system #-------------------------------------------------------------------------- def night? return $game_system.night? end #-------------------------------------------------------------------------- # * Lightning? Game_Character # - Returns true if Lightning is flashing or scheduled for the next frame #-------------------------------------------------------------------------- def lightning? return $game_system.lightning_flash? end #-------------------------------------------------------------------------- # * In Light? - Interpreter # - Returns true / false if a character is illuminated or nil if failure # arg : Event ID (use 0 for Player) or Character or nil for self # tolerance : Percent to Add so Light registers as false if in tolerance #-------------------------------------------------------------------------- def in_light?(arg = nil, tolerance = 0) # Failure if Indoor Switch is not specified return nil if not Light_Config::Switch_Indoor # Failure if Dynamic Lighting is not enabled return nil if not $game_system.dynamic_lights # Failure if Scene is not a Map return nil if not $scene.is_a?(Scene_Map) # Get the Shadowmap from the Map (Bitmap that covers the screen) shadowmap = $scene.get_shadowmap # Failure if there is no Shadowmap return nil if not shadowmap # Use Event ID of Event running the call if not specified arg = @event_id if arg.nil? # If in battle if $game_temp.in_battle # Depends on the Battle System. Allows Interpreter to Continue return nil end # Check if Argument is a Number or a Character if arg.is_a?(Game_Character) # Set Local Variable to Character char = arg # If Argument provided was a Number elsif arg.is_a?(Numeric) # Set Local Variable to Character char = (arg > 0) ? $game_map.events[arg] : $game_player end # Check that Character is valid if not char # If Game is being run from the Editor and not a released Game if $DEBUG print "Warning - Problem in in_light(", arg, ") for Event ", @event_id, "The character ", arg, " does not exist on this map." end # Prevent further processing because an Error was detected return nil end # Return nil if Character is not on the screen return nil if not character_on_screen?(char) # Night Color nc = $game_system.shadowmap_night_color # Night Average night_avg = (nc.red + nc.green + nc.blue + nc.alpha) / 4 # If the Indoor Switch is ON if $game_switches[Light_Config::Switch_Indoor] # Get Indoor Color ic = $game_system.shadowmap_indoor_color # Determine Map Average in_avg = (ic.red + ic.green + ic.blue) / 3 # Get Shadowmap Color mc = $game_system.invert_color($game_system.get_shadowmap_color) # Determine Map Average map_avg = (mc.red + mc.green + mc.blue) / 3 # True if current Color is more than Map Color return true if map_avg > in_avg + tolerance # Swap Color Avg for Character Detection map_avg = in_avg else # Get Shadowmap Color mc = $game_system.invert_color($game_system.get_shadowmap_color) # Determine Map Average map_avg = (mc.red + mc.green + mc.blue) / 3 # Character is In Light if Shadowmap does not match Night Color return true if map_avg > night_avg + tolerance end # If Character Name is not an empty string if char.character_name != "" # Get the Height of the Character's Sprite from the Cache h = RPG::Cache.character(char.character_name,char.character_hue).height # Divide Character Height by 4 for Sprite h /= 4 else # 32 Pixels per Tile h = 32 end # Determine Two Points on Character to check h1, h2 = (h * 0.8).round, (h * 0.2).round # Use the Character's Screen Position adjusted for Height as XY x = char.screen_x y = char.screen_y # Get One Pixel from the Shadowmap at center of Character's Position cc1 = $game_system.invert_color(shadowmap.bitmap.get_pixel(x, y - h1)) cc2 = $game_system.invert_color(shadowmap.bitmap.get_pixel(x, y - h2)) # Average Character Color for Character char_avg = cc1.red + cc2.red + cc1.green + cc2.green + cc1.blue + cc2.blue char_avg /= 6 # Character is In Light if the Pixel Color Average is more than Map Average return (char_avg > map_avg + tolerance) ? true : false end #-------------------------------------------------------------------------- # * Character On Screen? - Interpreter # - Returns true if the specified character is not in the Map Display # arg : Number or Character #-------------------------------------------------------------------------- def character_on_screen?(arg) # If in battle if $game_temp.in_battle # Depends on the Battle System. Allows Interpreter to Continue return true end # Check if Argument is a Number or a Character if arg.is_a?(Game_Character) # Set Local Variable to Character char = arg # If Argument provided was a Number elsif arg.is_a?(Numeric) # Set Local Variable to Character char = (arg > 0) ? $game_map.events[arg] : $game_player end # Check that Character is valid if not char # If Game is being run from the Editor and not a released Game if $DEBUG print "Warning - Problem in character_on_screen\n", "The character ", arg, " does not exist on this map." end # Prevent further processing because an Error was detected return false end # Check that Character is within the Display Ranges of the Game Map if character_on_screen_left?(char) and character_on_screen_right?(char) and character_on_screen_up?(char) and character_on_screen_down?(char) return true end # Default to False return false end #-------------------------------------------------------------------------- # * Character On Screen Down? - Interpreter # - Returns true / false if Character is not over the Bottom Display Edge # arg : Number or Character #-------------------------------------------------------------------------- def character_on_screen_down?(arg) # If in battle if $game_temp.in_battle # Depends on the Battle System. Allows Interpreter to Continue return true end # Check if Argument is a Number or a Character if arg.is_a?(Game_Character) # Set Local Variable to Character char = arg # If Argument provided was a Number elsif arg.is_a?(Numeric) # Set Local Variable to Character char = (arg > 0) ? $game_map.events[arg] : $game_player end # Not on screen if Character is not a valid character, sorry, no error msg return false if not char # Shorthand for Player Center, for Center Distances to Edge of Screen cy = Game_Player::CENTER_Y # Determine Map Display as Center of Screen my = $game_map.display_y + cy # Get the Distance X between Self and Center of Screen dy = char.lock_real_distance_y(char.real_y, my) # Distance to Center (with adjustment) less than Dist from Center to Edge return (-dy < cy + 128) end #-------------------------------------------------------------------------- # * Character On Screen Left? - Interpreter # - Returns true / false if Character is not over the Left Screen Edge # arg : Number or Character #-------------------------------------------------------------------------- def character_on_screen_left?(arg) # If in battle if $game_temp.in_battle # Depends on the Battle System. Allows Interpreter to Continue return true end # Check if Argument is a Number or a Character if arg.is_a?(Game_Character) # Set Local Variable to Character char = arg # If Argument provided was a Number elsif arg.is_a?(Numeric) # Set Local Variable to Character char = (arg > 0) ? $game_map.events[arg] : $game_player end # Not on screen if Character is not a valid character, sorry, no error msg return false if not char # Shorthand for Player Center, for Center Distances to Edge of Screen cx = Game_Player::CENTER_X # Determine Map Display as Center of Screen mx = $game_map.display_x + cx # Get the Distance between Self and Center of Screen dx = char.lock_real_distance_x(char.real_x, mx) # Distance to Center (with adjustment) less than Dist from Center to Edge return (dx < cx + 128) end #-------------------------------------------------------------------------- # * Character On Screen Right? - Interpreter # - Returns true / false if Character is not over the Right Screen Edge # arg : Number or Character #-------------------------------------------------------------------------- def character_on_screen_right?(arg) # If in battle if $game_temp.in_battle # Depends on the Battle System. Allows Interpreter to Continue return true end # Check if Argument is a Number or a Character if arg.is_a?(Game_Character) # Set Local Variable to Character char = arg # If Argument provided was a Number elsif arg.is_a?(Numeric) # Set Local Variable to Character char = (arg > 0) ? $game_map.events[arg] : $game_player end # Not on screen if Character is not a valid character, sorry, no error msg return false if not char # Shorthand for Player Center, for Center Distances to Edge of Screen cx = Game_Player::CENTER_X # Determine Map Display as Center of Screen mx = $game_map.display_x + cx # Get the Distance between Self and Center of Screen dx = char.lock_real_distance_x(char.real_x, mx) # Distance to Center (with adjustment) less than Dist from Center to Edge return (-dx < cx + 128) end #-------------------------------------------------------------------------- # * Character On Screen Up? - Interpreter # - Returns true / false if Character is lower than the Top Screen Edge # arg : Number or Character #-------------------------------------------------------------------------- def character_on_screen_up?(arg) # If in battle if $game_temp.in_battle # Depends on the Battle System. Allows Interpreter to Continue return true end # Check if Argument is a Number or a Character if arg.is_a?(Game_Character) # Set Local Variable to Character char = arg # If Argument provided was a Number elsif arg.is_a?(Numeric) # Set Local Variable to Character char = (arg > 0) ? $game_map.events[arg] : $game_player end # Not on screen if Character is not a valid character, sorry, no error msg return false if not char # Shorthand for Player Center, for Center Distances to Edge of Screen cy = Game_Player::CENTER_Y # Determine Map Display as Center of Screen my = $game_map.display_y + cy # Get the Distance between Self and Center of Screen dy = char.lock_real_distance_y(char.real_y, my) # Distance to Center (with adjustment) less than Dist from Center to Edge return (dy < cy + 128) end #-------------------------------------------------------------------------- # * Set Day Color - Interpreter # - Allows changing the Day Color from a Script # color : Color.new(r,g,b,a) #-------------------------------------------------------------------------- def set_day_color(color) # If color is not a color type if not color.is_a?(Color) # If running Game from Editor if $DEBUG # Explain the problem print "Warning: Problem in script call set_day_color(color)\n", "The color ", color, " is not a Color!\n\n", "Only use Colors in the Argument.\n\n", "Example: color = Color.new(32, 64, 128, 192)\n" " Color.new(red, green, blue, Alpha*)" end # Prevent Crash return true end # Set System Color to New Color $game_system.shadowmap_day_color = color # Return that Script call was successful for Interpreter return true end #-------------------------------------------------------------------------- # * Set Night Color - Interpreter # - Allows changing the Day Color from a Script # color : Color.new(r,g,b,a) #-------------------------------------------------------------------------- def set_night_color(color) # If color is not a color type if not color.is_a?(Color) # If running Game from Editor if $DEBUG # Explain the problem print "Warning: Problem in script call set_night_color(color)\n", "The color ", color, " is not a Color!\n\n", "Only use Colors in the Argument.\n\n", "Example: color = Color.new(32, 64, 128, 192)\n" " Color.new(red, green, blue, Alpha*)" end # Prevent Crash return true end # Set System Color to New Color $game_system.shadowmap_night_color = color # Return that Script call was successful for Interpreter return true end #-------------------------------------------------------------------------- # * Set Indoor Color - Interpreter # - Allows changing the Day Color from a Script # color : Color.new(r,g,b,a) #-------------------------------------------------------------------------- def set_indoor_color(color) # If color is not a color type if not color.is_a?(Color) # If running Game from Editor if $DEBUG # Explain the problem print "Warning: Problem in script call set_indoor_color(color)\n", "The color ", color, " is not a Color!\n\n", "Only use Colors in the Argument.\n\n", "Example: color = Color.new(32, 64, 128, 192)\n" " Color.new(red, green, blue, Alpha*)" end # Prevent Crash return true end # Set System Color to New Color $game_system.shadowmap_indoor_color = color # Return that Script call was successful for Interpreter return true end #-------------------------------------------------------------------------- # * Get Shadow Color - Interpreter # - Returns the inverted Shadowmap Color, including Lighting Flashes # - Inversion is due to the use of Blend Type 2 for the Shadowmap #-------------------------------------------------------------------------- def get_shadow_color # Return current Color used by the Shadowmap in Game System for drawing return $game_system.invert_color($game_system.get_shadowmap_color) end end #============================================================================== # ** Sprite_Sun_Moon - Class # - Used to draw a Sun / Moon Icon for a Day / Night indicator # - I did this because I thought it looked better than 4:32 PM as Text #============================================================================== class Sprite_Sun_Moon < RPG::Sprite #-------------------------------------------------------------------------- # * Public Instance Variables - Sprite_Sun_Moon #-------------------------------------------------------------------------- attr_accessor :graphic # Graphic File for Sun and Moon #-------------------------------------------------------------------------- # * Object Initialization - Sprite_Sun_Moon # graphic : graphic (Graphic Image File) # viewport : viewport #-------------------------------------------------------------------------- def initialize(graphic, viewport = nil) super(viewport) # Remember the Graphic Name @graphic_name = graphic # If file name is not empty if @graphic_name != "" # Get Sun and Moon Image graphic self.bitmap = RPG::Cache.picture(@graphic_name) end # Set Center to Picture Center self.ox = self.bitmap.rect.width / 2 self.oy = self.bitmap.rect.height / 2 # Set Z to display in the Window Clock self.z = 2000 # Set Opacity of Sun Moon Graphic self.opacity = 224 # Update the Angle update end #-------------------------------------------------------------------------- # * Dispose - Sprite_Sun_Moon #-------------------------------------------------------------------------- def dispose if self.bitmap != nil self.bitmap.dispose end super end #-------------------------------------------------------------------------- # * Frame Update - Sprite_Sun_Moon # - Updates Rotation of Sun and Moon Graphic #-------------------------------------------------------------------------- def update # If the Game Switch for displaying Clock is OFF or System Option is OFF if not $game_switches[Light_Config::Display_Clock] or not $game_system.show_sunmoon_time # Visibility if the System Option is on or not self.visible = false else self.visible = true end # Current Time time = $game_variables[Light_Config::Clock_Variable] # Calculate Angle, * -1 causes clockwise rotation self.angle = 360.0 * ((time / 86400.0) * -1) - 180 # Limit Rotation to 360.0 self.angle %= 360.0 end end #============================================================================== # ** Window_Clock #============================================================================== class Window_Clock < Window_Base #-------------------------------------------------------------------------- # * Object Initialization - Window_Clock # - NOTES FOR EDITING - # The values 0, 0, 180, 64 are x, y, width, height # The x and y values are the top left corner. Most likely these are the # values you want to mess with to reposition this window. The internal # viewport is only needed to clip the Sun / Moon Sprite if you use it. # You won't need to reposition the viewport if you do use it should you # reposition the window as the viewport and sun and moon sprite are set # up relative to this window. #-------------------------------------------------------------------------- def initialize super(0, 0, 180, 64) self.contents = Bitmap.new(width - 32, height - 32) self.contents.font.bold = true self.contents.font.size = 20 self.contents.font.color = get_color self.opacity = 0 self.x = Light_Config::Width - width + 16 # Right Side of Screen self.y = -8 # Top of Screen self.z = 2000 # Create a tiny Viewport to "Clip" the bottom of the Sun Moon Sprite @viewport = Viewport.new(self.x + width - 80, self.y + 6, 64, 32) @viewport.z = 2000 # Sprite for Sun and Moon Display @sun_moon = Sprite_Sun_Moon.new(Light_Config::Sun_Moon_Image, @viewport) # If Option to match Tone to the Tone of the Game Screen if $game_system.clock_match_tone # Set Tone of containing Viewport to Tone of Game Screen @sun_moon.tone = $game_screen.tone elsif @sun_moon.tone != Tone.new(0,0,0) # Set Tone of containing Viewport to normal @sun_moon.tone = Tone.new(0,0,0) end # Position the Sprite relative to its containing Viewport @sun_moon.x = 32 @sun_moon.y = 32 # Variable to hold Formatted Time Text @time = "" end #-------------------------------------------------------------------------- # * Get Color - Window Clock # - Checks Options and returns Color based on Options #-------------------------------------------------------------------------- def get_color # If Option for Clock is set to Match the Screen Tone if $game_system.clock_match_tone # Shorthand for Game Screen Tone tone = $game_screen.tone # Determine RGB Values for Color from Tone (Clamp to 0 to 255) red = [[tone.red + 255, 255].min, 0].max green = [[tone.green + 255, 255].min, 0].max blue = [[tone.blue + 255, 255].min, 0].max # Set the Color to the Tone of the Game Screen (Inversion) color = Color.new(red, green, blue) else # Set Color to White (Default Normal Color) color = Color.new(255, 255, 255) end end #-------------------------------------------------------------------------- # * Update - Window_Clock # - Updates the Formatted Time in Display #-------------------------------------------------------------------------- def update # Determine Visibility if Switch is On or Off self.visible = $game_switches[Light_Config::Display_Clock] # Update the Sun and Moon Sprite (for Rotation) @sun_moon.update # If Option to match Tone to the Tone of the Game Screen if $game_system.clock_match_tone # Set Tone of containing Viewport to Tone of Game Screen @sun_moon.tone = $game_screen.tone elsif @sun_moon.tone != Tone.new(0,0,0) # Set Tone of containing Viewport to normal @sun_moon.tone = Tone.new(0,0,0) end # Get the Color based on Script and System Options color = get_color # Shorthand for Game System s = $game_system # Refresh if Time has Changed or Tone has changed also if @time != $game_variables[Light_Config::Formatted_Time_Variable].to_s or @color != color or @last_show_formatted_time != s.show_formatted_time # Remember the Color of the Text @color = color # Draw Contents of Clock Text refresh end # Remember System Values @last_show_formatted_time = $game_system.show_formatted_time end #-------------------------------------------------------------------------- # * Refresh - Window_Clock # - Draws the Contents of the Clock (12:24 AM) for example #-------------------------------------------------------------------------- def refresh # Clear the Text (does not affect the Sun / Moon Sprite or Viewport) self.contents.clear # If System Option allows showing the Formatted time if $game_system.show_formatted_time # Store the Formatted Time converted to String @time = $game_variables[Light_Config::Formatted_Time_Variable].to_s # Set the Color to the Contents of the Clock Text self.contents.font.color = @color # Draw the Text of the Formatted Time self.contents.draw_text(0, 0, 80, 32, @time, 2) end end #-------------------------------------------------------------------------- # * Dispose - Window_Clock # - Gets rid of Graphics #-------------------------------------------------------------------------- def dispose # Dispose of Sun Moon Graphic @sun_moon.dispose # Dispose the Sun Moon Viewport (used for Clipping the rotated Sprite) @viewport.dispose # Dispose of Self via Parent Class Method for whatever it does super end end #============================================================================== # ** Scene_Map #============================================================================== class Scene_Map #-------------------------------------------------------------------------- # * Public Instance Variables - Game_System #-------------------------------------------------------------------------- attr_reader :window_clock # Displays the current Formatted Time #-------------------------------------------------------------------------- # * Main Processing - Scene_Map #-------------------------------------------------------------------------- alias clock_map_main main unless $@ def main # Create the Clock Window @window_clock = Window_Clock.new # Call Original or other Aliases clock_map_main # Dispose of the Clock Window @window_clock.dispose end #-------------------------------------------------------------------------- # * Get Shadowmap - Scene_Map # - Allows access to the Shadowmap Object in the Spriteset #-------------------------------------------------------------------------- def get_shadowmap # Return the Shadowmap from the Spriteset return @spriteset.shadowmap end #-------------------------------------------------------------------------- # * Light Spriteset - Scene_Map # - Allows access to the Spriteset Object in Scene Map #-------------------------------------------------------------------------- def light_spriteset # Return the Spriteset return @spriteset end end # Version Number $heretics_dynamic_lights = 1.01 #============================================================================ # *** --- Heretic's Caterpillar Compatability --- *** # # Everything below here will be used with Heretic's Caterpillar 2.0+ to # make the Caterpillar Movements on Platforms perform correctly. It is NOT # required if you do not use Heretics Caterpillar. It will not # work with Heretic's Caterpillar version 1.99.5 and below. #============================================================================ if defined?(Game_Caterpillar) and Game_Character.method_defined?('cat_moves_list_other_passable?') #========================================================================== # ** Game_Caterpillar # - Specific to Heretic's Caterpillar ONLY if it is used. #========================================================================== class Game_Caterpillar #------------------------------------------------------------------------ # * Center - Game_Caterpillar # - Centers Caterpillar Events to Player Position on Transfer # - This alias is used to set Lights based on Config settings #------------------------------------------------------------------------ alias dynamic_lights_caterpillar_center center unless $@ def center # Call Original or other Aliases dynamic_lights_caterpillar_center # Iterator i = 0 # Iterate each Caterpillar Actor for actor in @actors # Give each Caterpillar Actor or Follower a Light actor.make_light if not actor.light # If Caterpillar is Active and Event is following if $game_switches[Game_Caterpillar::CATERPILLAR_ACTIVE_SWITCH] and $game_system.caterpillar.actors.include?(actor) # If Caterpillar is not Paused if not $game_switches[Game_Caterpillar::CATERPILLAR_PAUSE_SWITCH] # Turn ON Lights for Caterpillar Actors and Followers that follow actor.light.enabled = $game_player.light.enabled # Set Actor Light Params based on Iteration s = i == 0 ? 16 : i == 1 ? 17 : i == 2 ? 15 : 12 + rand(5) actor.set_flicker_size(2.5, 2.25, s) # Use the Player's Light Opacity actor.light.opacity = $game_player.light.opacity end else # Turn off Lights for Caterpillar Actors and Followers not following actor.light.enabled = false end # Increment Iterator i += 1 end end end #========================================================================== # ** Interpreter - Class #========================================================================== class Game_Character #------------------------------------------------------------------------ # * Cat In Light? - Interpreter # - Returns true if the Player or any Caterpillar Members are Illuminated #------------------------------------------------------------------------ def cat_in_light? # Quit if Scene is not a Map return unless $scene.is_a?(Scene_Map) # Call same method from Map Interpreter return $game_system.map_interpreter.cat_in_light? end end #========================================================================== # ** Interpreter - Class #========================================================================== class Interpreter #------------------------------------------------------------------------ # * Cat In Light? - Interpreter # - Returns true if the Player or any Caterpillar Members are Illuminated #------------------------------------------------------------------------ def cat_in_light? # True if the Player is in light return true if in_light?($game_player) # Iterate each Caterpillar Actor and Follower for actor in $game_system.caterpillar.actors # If Caterpillar is Active and not Paused if not $game_switches[Game_Caterpillar::CATERPILLAR_PAUSE_SWITCH] and $game_switches[Game_Caterpillar::CATERPILLAR_ACTIVE_SWITCH] # True if any of the Caterpillar Members is in light return true if in_light?(actor) end end end end end