Advertisement
Guest User

ShadowedCharacters.js

a guest
Oct 24th, 2015
338
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. //=============================================================================
  2. // ShadowedCharacters.js
  3. //=============================================================================
  4.  
  5. /*:
  6.  * @plugindesc Shadows characters depending on shadow brush
  7.  * @author EvilCat
  8.  * @param Shadow Color
  9.  * @desc Hex RGB value of default shadow color.
  10.  * @default 0x646464
  11.  * @help
  12.  * Characters walking through shadows are shadowed.
  13.  * Shadow color can be tweaked with <Shadow Color:0xFFAA00> tags in notes of
  14.  * maps, events, and actors. By default, tiles and objects are not affected
  15.  * by shadows. Shadows can be explicitly turned off and on by placing
  16.  * <Is Shadowed:on> or <Is Shadowed:off> tag in event's or actor's notes.
  17.  * Creative Commons 3.0 Attribution
  18.  * See http://www.w3schools.com/tags/ref_colorpicker.asp for hex color picker.
  19.  */
  20.  
  21. "use strict";
  22.  
  23.  {
  24.     let parameters = PluginManager.parameters('ShadowedCharacters');
  25.    
  26.     let parse_Boolean=function(b)
  27.     {
  28.         b=b.toLowerCase().trim();
  29.         if (b==='true' || b==='y' || b==='yes' || b==='on' || b==='1') return true;
  30.         if (b==='false' || b==='n' || b==='no' || b==='off' || b==='0') return false;
  31.         throw new Error('unknown boolean value: '+b);
  32.     }
  33.    
  34.     // A class to handle color transformations - couldn't find an existing one...
  35.     class Color
  36.     {
  37.         constructor(r, g, b)
  38.         {
  39.             this.r=Math.floor(r);
  40.             this.g=Math.floor(g);
  41.             this.b=Math.floor(b);
  42.         }
  43.        
  44.         static parse(input)
  45.         {
  46.             return Color.create_from_hex(Number(input));
  47.         }
  48.        
  49.         static create_from_hex(hex)
  50.         {
  51.             return new Color(
  52.                 hex % 0x100,
  53.                 Math.floor(hex/0x100) % 0x100,
  54.                 Math.floor(hex/0x10000)
  55.             );
  56.         }
  57.  
  58.         merge_with_color(other_color, value)
  59.         {
  60.             return new Color(
  61.                 this.r+(other_color.r-this.r)*value,
  62.                 this.g+(other_color.g-this.g)*value,
  63.                 this.b+(other_color.b-this.b)*value
  64.             );
  65.         }
  66.        
  67.         merge_with_white(value) { return this.merge_with_color(Color.create_from_hex(0xFFFFFF), value); }
  68.        
  69.         to_hex()
  70.         {
  71.             return this.r+this.g*0x100+this.b*0x10000;
  72.         }
  73.     }
  74.    
  75.     let DEFAULT_SHADOW_COLOR=Color.create_from_hex(0x646464);
  76.    
  77.     // a character now also stores shadow value, depending on how surrounded by shadows the character is.
  78.     let oldInit=Game_Character.prototype.initMembers;
  79.     Game_Character.prototype.initMembers=function()
  80.     {
  81.         oldInit.apply(this, arguments);
  82.         this.shadowValue=null;
  83.     }
  84.    
  85.     // determines whether the character is affected by shadows
  86.     Game_Character.prototype.shadowedOn=function()
  87.     {
  88.         if (this.getMeta)
  89.         {
  90.             var param=this.getMeta('Is Shadowed');
  91.             if (param!==undefined) return parse_Boolean(param);
  92.         }
  93.         return !this.isTile() && !this.isObjectCharacter();
  94.     }
  95.    
  96.     // determines character's "center" coordinates inside the grid square.
  97.     Game_Character.prototype.realCenterX=function() { return this._realX+0.5; }
  98.     Game_Character.prototype.realCenterY=function() { return this._realY+0.5; }
  99.    
  100.     // generic function for getting metadata from meta-array
  101.     let getMeta=function(meta, param)
  102.     {
  103.         if (!meta) return undefined;
  104.         if (param===undefined) return meta;
  105.         if (meta[param]) return meta[param];
  106.         return undefined;
  107.     }
  108.    
  109.     // determines shadow color; probably expensive.
  110.     Game_Character.prototype.shadowColor=function()
  111.     {
  112.         if (this.getMeta)
  113.         {
  114.             var param=this.getMeta('Shadow Color');
  115.             if (param!==undefined) return Color.parse(param);
  116.         }
  117.         if ($dataMap.meta['Shadow Color']) return Color.parse($dataMap.meta['Shadow Color']);
  118.         if (parameters['Shadow Color']) return Color.parse(parameters['Shadow Color']);
  119.         return DEFAULT_SHADOW_COLOR;
  120.     }
  121.    
  122.     // couldn't find a generic function for getting metadata from various game objects.
  123.     Game_Player.prototype.getMeta=function(param)
  124.     {
  125.         var actor=$gameParty.leader();
  126.         if (actor) return actor.getMeta(param);
  127.         return undefined;
  128.     }
  129.     Game_Follower.prototype.getMeta=function(param)
  130.     {
  131.         var actor=this.actor();
  132.         if (actor) return actor.getMeta(param);
  133.         return undefined;
  134.     }
  135.     Game_Actor.prototype.getMeta=function(param)
  136.     {
  137.         return getMeta($dataActors[this._actorId].meta, param);
  138.     }
  139.     Game_Event.prototype.getMeta=function(param)
  140.     {
  141.         // metadata is not page-specific presently and can't be changed by page flipping.
  142.         return getMeta(this.event().meta, param);
  143.     }
  144.    
  145.     // determines shadow value; probably expensive;
  146.     Game_Character.prototype.updateShadowing=function()
  147.     {
  148.         var shadow_points=[], shadow_x, shadow_y;
  149.         var center_x=this.realCenterX(), center_y=this.realCenterY();
  150.        
  151.         if (center_x % 1 <0.25) shadow_x=Math.floor(center_x)-0.25;
  152.         else shadow_x=Math.floor(center_x)+0.25;
  153.         if (center_y % 1 <0.25) shadow_y=Math.floor(center_y)-0.25;
  154.         else shadow_y=Math.floor(center_y)+0.25;
  155.        
  156.         this.appendShadowPoint(shadow_points, shadow_x, shadow_y);
  157.         this.appendShadowPoint(shadow_points, shadow_x+0.5, shadow_y);
  158.         this.appendShadowPoint(shadow_points, shadow_x, shadow_y+0.5);
  159.         this.appendShadowPoint(shadow_points, shadow_x+0.5, shadow_y+0.5);
  160.        
  161.         if (shadow_points.length==0) this.shadowValue=0;
  162.         else
  163.         {
  164.             var sum=shadow_points.reduce(function(s, data) { return s+data.weight; }, 0);
  165.             this.shadowValue=shadow_points.reduce(function(s, data) { return s+data.value*(data.weight/sum); }, 0);
  166.         }
  167.     }
  168.    
  169.     // adds a point data to the array of shadow points, if the point is valid.
  170.     Game_Character.prototype.appendShadowPoint=function(target, shadow_x, shadow_y)
  171.     {
  172.         if (shadow_x<0 || shadow_y<0 || shadow_x>$gameMap.width()+1 || shadow_y>$gameMap.height()+1) return;
  173.         if (Math.abs(shadow_x-this.realCenterX())>1 || Math.abs(shadow_y-this.realCenterY())>1) return;
  174.         target.push({
  175.             x:      shadow_x,
  176.             y:      shadow_y,
  177.             weight: 1-pointDistance(shadow_x, shadow_y, this.realCenterX(), this.realCenterY()),
  178.             value:  Number($gameMap.hasShadow(shadow_x, shadow_y))
  179.         });
  180.     }
  181.      
  182.      // update() now also updates shadow value.
  183.     let oldCharUpdate=Game_Character.prototype.update;
  184.     Game_Character.prototype.update=function()
  185.     {
  186.         var prevX=this._realX, prevY=this._realY;
  187.         oldCharUpdate.apply(this, arguments);
  188.         if (this._needsShadowRefresh) this._needsShadowRefresh = this._needsShadowRefresh==1 ? 2 : false;
  189.         // since this param is set to "true" for at least one full update, all sprites should handle their refreshes no matter at which point the request was made. sometimes they will do this twice, but the refreshing doesn't happen often.
  190.         if (this.shadowedOn() && (this.shadowValue===null || prevX!=this._realX || prevY!=this._realY) ) this.updateShadowing();
  191.         // shadows are only updated if the character has moved.
  192.     }
  193.    
  194.    
  195.     // some Game_Character subclasses call refresh() on major changes (such as page flip of an Event) which can also affect shadows.
  196.    
  197.     Game_Character.prototype.requestShadowRefresh=function()
  198.     {
  199.         this._needsShadowRefresh=1;
  200.         // when this param is set, Sprites are informed that they need to update their shadow data.
  201.     }
  202.    
  203.     let oldFollowerRefresh=Game_Follower.prototype.refresh;
  204.     Game_Follower.prototype.refresh=function()
  205.     {
  206.         oldFollowerRefresh.apply(this, arguments);
  207.         this.requestShadowRefresh();
  208.     }
  209.    
  210.     let oldPlayerRefresh=Game_Player.prototype.refresh;
  211.     Game_Player.prototype.refresh=function()
  212.     {
  213.         oldPlayerRefresh.apply(this, arguments);
  214.         this.requestShadowRefresh();
  215.     }
  216.    
  217.     // it seems that presently Vehicle can't change image or meta during the game, but if some plugin does that, it should also be compatible.
  218.     let oldVehicleRefresh=Game_Vehicle.prototype.refresh;
  219.     Game_Vehicle.prototype.refresh=function()
  220.     {
  221.         oldVehicleRefresh.apply(this, arguments);
  222.         this.requestShadowRefresh();
  223.     }
  224.    
  225.     let oldEventRefresh=Game_Event.prototype.refresh;
  226.     Game_Event.prototype.refresh=function()
  227.     {
  228.         oldEventRefresh.apply(this, arguments);
  229.         this.requestShadowRefresh();
  230.     }
  231.    
  232.    
  233.     // sprite now stores shadow data.
  234.     let oldSpriteInitMembers=Sprite_Character.prototype.initMembers;
  235.     Sprite_Character.prototype.initMembers=function()
  236.     {
  237.         oldSpriteInitMembers.apply(this, arguments);
  238.         this.shadowValue=null;
  239.         this.shadowColor=null;
  240.         this.shadowedOn=null;
  241.     }
  242.    
  243.     // shadow data is refreshed on character's init or change.
  244.     let oldSpriteSetCharacter=Sprite_Character.prototype.setCharacter;
  245.     Sprite_Character.prototype.setCharacter=function()
  246.     {
  247.         oldSpriteSetCharacter.apply(this, arguments);
  248.         this.refreshShadowing();
  249.     }
  250.    
  251.     Sprite_Character.prototype.refreshShadowing=function()
  252.     {
  253.         if (!this._character) return;
  254.         this.shadowedOn=this._character.shadowedOn();
  255.         this.shadowColor=this._character.shadowColor();
  256.         this.shadowValue=null; // forgets previous shadow value (if any) and forces to update.
  257.         if (!this.shadowedOn) this.tint=0xFFFFFF;
  258.     }
  259.    
  260.     // updates tint only if something has changed.
  261.     // tint doesn't seem to be in use by any RPG Maker components, so this plugin can safely usurp it... however, if another plugin or effect will require tint manipulation, they will cancel each other. there's no interface or convention about it, so I have no solution yet.
  262.     let oldSpriteUpdate=Sprite_Character.prototype.update;
  263.     Sprite_Character.prototype.update=function()
  264.     {
  265.         oldSpriteUpdate.apply(this, arguments);
  266.         if (this._character._needsShadowRefresh || !this.shadowColor) this.refreshShadowing();
  267.         if (this.shadowedOn && this.shadowValue!==this._character.shadowValue)
  268.         {
  269.             this.shadowValue=this._character.shadowValue;
  270.             this.tint=this.shadowColor.merge_with_white(1-this.shadowValue).to_hex();
  271.         }
  272.     }
  273.    
  274.     // an utility function (no such function seems to exist by default).
  275.     let pointDistance=function(x1, y1, x2, y2)
  276.     {
  277.         return Math.sqrt(Math.pow(x1-x2, 2)+Math.pow(y1-y2, 2));
  278.     }
  279.    
  280.     // determines whether a point on the map has shadow. the coords should end in .25 or .75.
  281.     Game_Map.prototype.hasShadow=function(x, y)
  282.     {
  283.         if (x<0 || y<0 || x>$dataMap.width+1 || y>$dataMap.height+1) return false;
  284.         var bytemask;
  285.              if (x % 1 == 0.25 && y % 1 == 0.25) bytemask=0b00000001;
  286.         else if (x % 1 == 0.75 && y % 1 == 0.25) bytemask=0b00000010;
  287.         else if (x % 1 == 0.25 && y % 1 == 0.75) bytemask=0b00000100;
  288.         else if (x % 1 == 0.75 && y % 1 == 0.75) bytemask=0b00001000;
  289.         else throw new Error('bad shadow coordinates');
  290.        
  291.         var offset=$dataMap.width*$dataMap.height*4;
  292.         return Boolean($dataMap.data[offset+Math.floor(x)+Math.floor(y)*$dataMap.width] & bytemask);
  293.     }
  294.    
  295.  }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement