Downloads containing SaLLiB-v2.0.asc

Downloads
Name Author Game Mode Rating
TSF with JJ2+ Only: Devan's Revenge (Intro)Featured Download sAlAmAnDeR Single player 9.6 Download file

File preview

  1. //SaLLiB v2.0
  2. //By sAlAmAnDeR
  3. #pragma require "sepalwiz-1.1.asc"
  4. #include "sepalwiz-1.1.asc"
  5.  
  6. //An unused object constant, to mark custom stuff
  7. OBJECT::Object NIL = OBJECT::AMBIENTSOUND;
  8.  
  9. //BEHAVIOR::INACTIVE actually calls the objects behavior function, but BEHAVIOR::BEES actually does nothing.
  10. jjBEHAVIOR DO_NOTHING = BEHAVIOR::BEES;
  11.  
  12. float PI = 3.1416;
  13. float HALF_PI = 0.5*PI;
  14. float ONE_AND_HALF_PI = 1.5*PI;
  15. float TWO_PI = 2*PI;
  16. float E = 2.71828;
  17.  
  18. float distance(float xStart, float yStart, float xEnd, float yEnd) {return sqrt((xEnd - xStart)*(xEnd - xStart) + (yEnd - yStart)*(yEnd - yStart));}
  19.  
  20. float RAND_MAX = 4294967295;
  21.  
  22. float random(float a, float b) {
  23.     float random =  jjRandom() / RAND_MAX;
  24.     float diff = b - a;
  25.     float r = random * diff;
  26.     return a + r;
  27. }
  28.  
  29. funcdef void OBJECTHITHOOK(jjOBJ@ obj, jjOBJ@ bullet, jjPLAYER@ player, int force);
  30. funcdef void CANVASHOOKVOID(jjPLAYER@ player, jjCANVAS@ canvas);
  31.  
  32. OBJECTHITHOOK@  onObjectHitHook           = null;
  33. CANVASHOOKVOID@ onDrawAmmoHook            = null;
  34. CANVASHOOKVOID@ onDrawHealthHook          = null;
  35. CANVASHOOKVOID@ onDrawLivesHook           = null;
  36. CANVASHOOKVOID@ onDrawScoreHook           = null;
  37. CANVASHOOKVOID@ onDrawPlayerTimerHook = null;
  38.  
  39. string chr(uint8 value) {
  40.   string str="\0";
  41.   str[0]=value;
  42.   return str;
  43. }
  44.  
  45. string stripPipes(string str) {
  46.         string result = "";
  47.         for(uint i = 0; i < str.length(); i++)
  48.                 if(str[i] != 124)
  49.                         result += chr(str[i]);
  50.         return result;
  51. }
  52.  
  53. namespace COORDINATES {
  54.         enum CoordinateType {
  55.                 LAYER = 0,
  56.                 SCREEN = 1
  57.         }
  58. }
  59.  
  60. namespace COMMON {
  61.         interface IFocalPoint {
  62.                 float get_xPos() const;
  63.                 float get_yPos() const;
  64.                 bool  get_isActive() const;
  65.         }
  66.  
  67.         class Position : IFocalPoint {
  68.                 float x, y;
  69.                 Position(float xPos, float yPos) {this.x = xPos;
  70.                                                                                   this.y = yPos;}
  71.                                                                                  
  72.                 float get_xPos() const                   {return this.x;}
  73.                 float get_yPos() const                   {return this.y;}
  74.                
  75.                 void  set_xPos(float xPos)               {this.x=xPos;}
  76.                 void  set_yPos(float yPos)               {this.y=yPos;}
  77.                
  78.                 bool  get_isActive() const               {return true;}
  79.         }
  80.  
  81.         //TODO: Maybe set a variable when gameTicks == 0 to indicate timers are safe to use?
  82.         funcdef int TIME_UNIT();
  83.        
  84.         int jjGameTicks() { return ::jjGameTicks; }
  85.         int jjRenderFrame() { return ::jjRenderFrame; }
  86.        
  87.         class Timer {
  88.                 int inter, startTime, end;
  89.                 bool running;
  90.                 TIME_UNIT@ timeUnit = COMMON::jjGameTicks;
  91.                
  92.                 Timer() {
  93.                         this.running = false;
  94.                 }
  95.                
  96.                 Timer start(int interval) {
  97.                         if(interval < 0)
  98.                                 return start();
  99.                         this.inter = interval;
  100.                         this.startTime = timeUnit();
  101.                         this.end = this.startTime + this.inter;
  102.                         this.running = true;
  103.                         return this;
  104.                 }
  105.                
  106.                 Timer start() {
  107.                         this.startTime = timeUnit();
  108.                         this.running = true;
  109.                         this.end = -1;
  110.                         this.inter = -1;
  111.                         return this;
  112.                 }
  113.                
  114.                 Timer stop() {
  115.                         this.running = false;
  116.                         return this;
  117.                 }
  118.                
  119.                 bool isFinished() const {
  120.                         if(!this.running) return false;
  121.                         if(this.end == -1) return false;
  122.                         return timeUnit() >= this.end;
  123.                 }
  124.                
  125.                 int elapsedTime() const {
  126.                         return timeUnit()-this.startTime;
  127.                 }
  128.                
  129.                 int endTime() const {
  130.                         return end;
  131.                 }
  132.                
  133.                 int remainingTime() const {
  134.                         if(this.interval() == -1) return -1;
  135.                         return this.interval() - this.elapsedTime();
  136.                 }
  137.                
  138.                 int interval() const {
  139.                         return this.inter;
  140.                 }
  141.                
  142.                 Timer reset() {
  143.                         start(this.inter);
  144.                         return this;
  145.                 }
  146.                
  147.                 bool isStarted() const {
  148.                         return this.running;
  149.                 }
  150.                
  151.                 Timer useGameTicks() {
  152.                         @timeUnit = @COMMON::jjGameTicks;
  153.                         return this;
  154.                 }
  155.                
  156.                 Timer useRenderFrames() {
  157.                         @timeUnit = @COMMON::jjRenderFrame;
  158.                         return this;
  159.                 }
  160.         }
  161. }
  162.  
  163. namespace CUSTOM {
  164.         bool SHOW_WARNINGS = true;
  165.         bool SALLIB_DEBUG = true;
  166.        
  167.         void CustomObjectBehavior(jjOBJ@ jjo) {
  168.                 Object@ co = objects[jjo.objectID];
  169.                 if(co != jjo) {//jjo is active or the behavior would not be called
  170.                         if(SALLIB_DEBUG) jjDebug("SaLLiB DEBUG: Object behavior changed.");
  171.                         return;
  172.                 }
  173.                
  174.                 co.age = co.age + 1;
  175.                 co.behavior();
  176.                 if(!co.isActive) return;
  177.                 co.move();
  178.                 co.rotate();
  179.                 co.color();
  180.                 co.scale();
  181.                 co.patrol();
  182.                 co.warp();
  183.                 co.updateDefaultBehavior();
  184.         }
  185.        
  186.         int theoreticalMaxObjects = jjGameMode == GAME::SP ? 512:2560;
  187.         array<CUSTOM::Object@> objects(jjObjectMax == 0 ? theoreticalMaxObjects : jjObjectMax);
  188.         int nextID = 1;
  189.        
  190.         interface ICustomObject : COMMON::IFocalPoint,MIXIN::IMovable,MIXIN::IScalable,MIXIN::IRotatable,MIXIN::IColorable,MIXIN::IPatrollable,MIXIN::IWarpable {
  191.                 void behavior();
  192.                 void onObjectHit(jjOBJ@ bullet, jjPLAYER@ player, int force);
  193.                 bool get_isActive() const;
  194.                
  195.                 jjBEHAVIOR get_standardBehavior();
  196.                
  197.                 int get_age() const;
  198.                 void set_age(int age);
  199.                
  200.                 //Sprite accessors/mutators
  201.                 int                     get_spriteAngle() const;
  202.                 float                   get_spriteScaleX() const;
  203.                 float                   get_spriteScaleY() const;
  204.                 SPRITE::Mode    get_spriteMode() const;
  205.                 uint8                   get_spriteParam() const;
  206.                 uint8                   get_spriteLayerZ() const;
  207.                 uint8                   get_spriteLayerXY()     const;
  208.                 int8                    get_spritePlayerID() const;
  209.                 bool                    get_spriteBob() const;
  210.                 ANIM::Set               get_set() const;
  211.                 int                             get_animation() const;
  212.                 int                             get_startFrame() const;
  213.                 int                             get_numFrames() const;
  214.                 COMMON::Timer@  get_animationTimer();
  215.                 int                             get_ticksPerFrame()     const;
  216.                 bool                    get_cycleAnimation() const;
  217.                 bool                    get_invisible() const;
  218.                 int                             get_frame()     const;
  219.                
  220.                 void                    set_spriteAngle(int angle);
  221.                 void                    set_spriteScaleX(float scaleX);
  222.                 void                    set_spriteScaleY(float scaleY);
  223.                 void                    set_spriteMode(SPRITE::Mode mode);
  224.                 void                    set_spriteParam(uint8 param);
  225.                 void                    set_spriteLayerZ(uint8 layerZ);
  226.                 void                    set_spriteLayerXY(uint8 layerXY);
  227.                 void                    set_spritePlayerID(int8 playerID);
  228.                 void                    set_spriteBob(bool bob);
  229.                 void                    set_set(ANIM::Set set);
  230.                 void                    set_animation(int animation);
  231.                 void                    set_startFrame(int startFrame);
  232.                 void                    set_numFrames(int numFrames);
  233.                 void                    set_ticksPerFrame(int ticksPerFrame);
  234.                 void                    set_cycleAnimation(bool cycleAnimation);
  235.                 void                    set_invisible(bool invisible);
  236.                
  237.                 void playAnimation(ANIM::Set set, int animation, int startFrame, int numFrames, int ticksPerFrame = 7, bool cycle=true);
  238.                
  239.                 bool isPlayingAnimation();
  240.                
  241.                 void stopAnimation();
  242.                
  243.                 void freezeFrame(ANIM::Set, int animation, int frame);
  244.                
  245.                 //Custom object methods
  246.                 float distancePixels(COMMON::IFocalPoint@ focus);
  247.                 float distanceTiles(COMMON::IFocalPoint@ focus);
  248.        
  249.                 bool opEquals(jjOBJ@ jjObject);
  250.                 bool opEquals(CUSTOM::Object object);
  251.                
  252.                 void updateDefaultBehavior();
  253.                 //jjOBJ accessors
  254.                 int get_animSpeed() const;
  255.                 HANDLING::Bullet get_bulletHandling() const;
  256.                 bool get_causesRicochet() const;
  257.                 int get_counter() const;
  258.                 uint8 get_counterEnd() const;
  259.                 int get_creator() const;
  260.                 int get_creatorID() const;
  261.                 CREATOR::Type get_creatorType() const;
  262.                 int16 get_curAnim() const;
  263.                 uint get_curFrame() const;
  264.                 bool get_deactivates() const;
  265.                 int8 get_direction() const;
  266.                 uint8 get_doesHurt() const;
  267.                 int get_energy() const;
  268.                 uint8 get_eventID() const;
  269.                 int8 get_frameID() const;
  270.                 uint8 get_freeze() const;
  271.                 bool get_isBlastable() const;
  272.                 bool get_isFreezable() const;
  273.                 bool get_isTarget() const;
  274.                 uint8 get_justHit() const;
  275.                 int16 get_killAnim() const;
  276.                 int8 get_light() const;
  277.                 uint8 get_lightType() const;
  278.                 int get_oldState() const;
  279.                 HANDLING::Player get_playerHandling() const;
  280.                 uint16 get_points() const;
  281.                 int8 get_noHit() const;
  282.                 uint16 get_objectID() const;
  283.                 uint8 get_objType() const;
  284.                 bool get_scriptedCollisions() const;
  285.                 int get_special() const;
  286.                 int get_state() const;
  287.                 bool get_triggersTNT() const;
  288.                 int get_var(int index) const;
  289.                 float get_xAcc() const;
  290.                 float get_xOrg() const;
  291.                 float get_xPos() const;
  292.                 float get_xSpeed() const;
  293.                 float get_yAcc() const;
  294.                 float get_yPos() const;
  295.                 float get_yOrg() const;
  296.                 float get_ySpeed() const;
  297.                
  298.                
  299.                 void set_animSpeed(int animSpeed);
  300.                 void set_bulletHandling(HANDLING::Bullet bulletHandling);
  301.                 void set_causesRicochet(bool causesRicochet);
  302.                 void set_counter(int counter);
  303.                 void set_counterEnd(uint8 counterEnd);
  304.                 void set_creator(int creator);
  305.                 void set_creatorID(int creatorID);
  306.                 void set_creatorType(CREATOR::Type creatorType);
  307.                 void set_curAnim(int16 curAnim);
  308.                 void set_curFrame(uint curFrame);
  309.                 void set_deactivates(bool deactivates);
  310.                 void set_direction(int8 direction);
  311.                 void set_doesHurt(uint8 doesHurt);
  312.                 void set_energy(int energy);
  313.                 void set_eventID(uint8 eventID);
  314.                 void set_frameID(int8 frameID);
  315.                 void set_freeze(uint8 freeze);
  316.                 void set_isBlastable(bool isBlastable);
  317.                 void set_isFreezable(bool isFreezable);
  318.                 void set_isTarget(bool isTarget);
  319.                 void set_justHit(uint8 justHit);
  320.                 void set_killAnim(int16 killAnim);
  321.                 void set_light(int8 light);
  322.                 void set_lightType(uint8 lightType);
  323.                 void set_oldState(STATE::State state);
  324.                 void set_playerHandling(HANDLING::Player playerHandling);
  325.                 void set_points(uint16 points);
  326.                 void set_noHit(int8 noHit);
  327.                 void set_objType(uint8 objType);
  328.                 void set_scriptedCollisions(bool scriptedCollisions);
  329.                 void set_special(int special);
  330.                 void set_state(int state);
  331.                 void set_triggersTNT(bool triggersTNT);
  332.                 void set_var(int index, int value);
  333.                 void set_xAcc(float xAcc);
  334.                 void set_xOrg(float xOrg);
  335.                 void set_xPos(float xPos);
  336.                 void set_xSpeed(float xSpeed);
  337.                 void set_ySpeed(float ySpeed);
  338.                 void set_yAcc(float yAcc);
  339.                 void set_yPos(float yPos);
  340.                 void set_yOrg(float yOrg);
  341.                
  342.                
  343.                 void clearPlatform() const;
  344.                 void grantPickup(jjPLAYER@ player, int frequency) const;
  345.                 void particlePixelExplosion(int style) const;
  346.                 void pathMovement() const;
  347.                 void putOnGround(bool precise = false) const;
  348.                 void delete();
  349.                
  350.                 int     beSolid() const;
  351.                 int16   determineCurAnim(uint8 setID, uint8 animation, bool change = true) const;
  352.                 int16   determineCurAnim(ANIM::Set setID, uint8 animation, bool change = true) const;
  353.                 uint    determineCurFrame(bool change = true) const;
  354.                 int     findNearestPlayer(int maxDistance) const;
  355.                 int     findNearestPlayer(int maxDistance, int &out foundDistance) const;
  356.                 int     fireBullet(OBJECT::Object eventID) const;
  357.                 int     unfreeze(int style) const;
  358.                 bool    ricochet() const;
  359.        
  360.                
  361.                 void deactivate();
  362.                 void behave(BEHAVIOR::Behavior behavior, bool draw = true, bool useCustomSpriteProperties = true) const;
  363.                 void behave(jjVOIDFUNCOBJ@ behavior, bool draw = true, bool useCustomSpriteProperties = true) const;
  364.                 void behave(jjBEHAVIOR behavior, bool draw = true, bool useCustomSpriteProperties = true) const;
  365.                 int draw(bool useCustomSpriteProperties = true) const;
  366.                 void printPos();
  367.         }
  368.  
  369.         class Object : ICustomObject, MIXIN::Movable, MIXIN::Scalable,
  370.                                    MIXIN::Rotatable, MIXIN::Colorable, MIXIN::Patrollable,
  371.                                    MIXIN::Warpable {
  372.                 //Object Properties
  373.                 jjOBJ@                          jjObject = null;
  374.                 jjBEHAVIOR                      defaultBehavior;
  375.                 int                             customAge = 0, cid;
  376.                 bool isNil = false;
  377.                
  378.                 //Sprite properties
  379.                 int angle = 0;
  380.                 float scaleX = 1, scaleY = 1;
  381.                 SPRITE::Mode mode = SPRITE::NORMAL;
  382.                 uint8 param = 0, layerZ = 4, layerXY = 4;
  383.                 int8 playerID = -1;
  384.                 bool bob = false;
  385.                 COMMON::Timer bobTimer(), anmTimer();
  386.                 ANIM::Set st;
  387.                 int anim, fram, numFrams, startFram, ticksPerFram;
  388.                 bool cycleAnim = false, inv = false;
  389.                 INTERPOLATION::Sinusoidal@ bobInterpolator;
  390.                
  391.                 Object(OBJECT::Object objectTemplate, float xPos, float yPos) {
  392.                         @this.jjObject = @jjObjects[jjAddObject(objectTemplate,xPos,yPos,0,CREATOR::OBJECT)];
  393.                         if(objectTemplate == NIL) {
  394.                                 isNil = true;
  395.                                 this.jjObject.playerHandling = HANDLING::PARTICLE;
  396.                                 this.jjObject.bulletHandling = HANDLING::IGNOREBULLET;
  397.                                 this.defaultBehavior = DO_NOTHING;
  398.                         } else
  399.                                 initDefaultBehavior();
  400.                         initialize();
  401.                 }
  402.                
  403.                 Object(jjOBJ@ jjObject) {
  404.                         @this.jjObject = jjObject;
  405.                         initDefaultBehavior();
  406.                         initialize();
  407.                 }
  408.                
  409.                 void initDefaultBehavior() {
  410.                         if(this.jjObject.behavior == BEHAVIOR::DEFAULT) {
  411.                                 if(SALLIB_DEBUG) jjDebug("SaLLiB DEBUG: jjObject passed to custom object uses BEHAVIOR::DEFAULT");
  412.                                 this.defaultBehavior = BEHAVIOR::BULLET;
  413.                         } else if(this.jjObject.behavior == CustomBulletBehavior1) this.defaultBehavior = BEHAVIOR::BULLET;
  414.                         else if(this.jjObject.behavior == CustomBulletBehavior2) this.defaultBehavior = BEHAVIOR::BOUNCERBULLET;
  415.                         else if(this.jjObject.behavior == CustomBulletBehavior3) this.defaultBehavior = BEHAVIOR::BULLET;
  416.                         else if(this.jjObject.behavior == CustomBulletBehavior4) this.defaultBehavior = BEHAVIOR::SEEKERBULLET;
  417.                         else if(this.jjObject.behavior == CustomBulletBehavior5) this.defaultBehavior = BEHAVIOR::RFBULLET;
  418.                         else if(this.jjObject.behavior == CustomBulletBehavior6) this.defaultBehavior = BEHAVIOR::TOASTERBULLET;
  419.                         else if(this.jjObject.behavior == CustomBulletBehavior7) this.defaultBehavior = BEHAVIOR::TNT;
  420.                         else if(this.jjObject.behavior == CustomBulletBehavior8) this.defaultBehavior = BEHAVIOR::PEPPERBULLET;
  421.                         else if(this.jjObject.behavior == CustomBulletBehavior9) this.defaultBehavior = BEHAVIOR::ELECTROBULLET;
  422.                         else this.defaultBehavior = this.jjObject.behavior;
  423.                 }
  424.                
  425.                 void initialize() {
  426.                         this.jjObject.behavior = CustomObjectBehavior;
  427.                        
  428.                         @CUSTOM::objects[jjObject.objectID] = @this;
  429.                         this.jjObject.age = nextID;
  430.                         this.cid = nextID++;
  431.  
  432.                         @this.bobInterpolator = INTERPOLATION::Sinusoidal(2,3,jjRandom()%100,65);
  433.                         this.bobTimer.start();
  434.                 }
  435.                
  436.                 //Methods made to be overridden
  437.                 void behavior() {
  438.                         behave(this.defaultBehavior);
  439.                 }
  440.                
  441.                 void onObjectHit(jjOBJ@ bullet, jjPLAYER@ player, int force)    {}
  442.                
  443.                 bool get_isActive() const {return this.jjObject.isActive && this.jjObject.age == this.cid;}
  444.                
  445.                 //Handling jjObject changing out from under you
  446.                 bool objectChanged(jjOBJ@ jjo) {
  447.                         if(!isActive) {
  448.                                 if(jjo.behavior == CustomObjectBehavior)
  449.                                         jjo.behavior = DO_NOTHING;
  450.                                 return true;
  451.                         }
  452.                         return false;
  453.                 }
  454.                
  455.                 jjBEHAVIOR get_standardBehavior() { if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via get_standardBehavior()");}}return defaultBehavior; }
  456.                
  457.                 int get_age() const     {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via get_age()");}}                                return this.customAge;}
  458.                 void set_age(int age)   {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via set_age()");}return;} this.customAge=age;}
  459.                
  460.                 //Sprite accessors/mutators
  461.                 int                     get_spriteAngle() const         {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via get_spriteAngle()");}}                return angle;}
  462.                 //float                 get_spriteAngleRadians()        {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via get_spriteAngleRadians()");}} return angle;}
  463.                 //float                 get_spriteAngleDegrees()        {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via get_spriteAngleDegrees()");}} return angle;}
  464.                 float                   get_spriteScaleX() const        {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via get_spriteScaleX()");}}               return scaleX;}
  465.                 float                   get_spriteScaleY() const        {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via get_spriteScaleY()");}}               return scaleY;}
  466.                 SPRITE::Mode    get_spriteMode() const          {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via get_spriteMode()");}}                 return mode;}
  467.                 uint8                   get_spriteParam() const         {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via get_spriteParam()");}}                return param;}
  468.                 uint8                   get_spriteLayerZ() const        {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via get_spriteLayerZ()");}}               return layerZ;}
  469.                 uint8                   get_spriteLayerXY()     const   {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via get_spriteLayerXY()");}}              return layerXY;}
  470.                 int8                    get_spritePlayerID() const      {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via get_spritePlayerID()");}}             return playerID;}
  471.                 bool                    get_spriteBob() const           {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via get_spriteBob()");}}                  return bob;}
  472.                 ANIM::Set               get_set() const                         {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via get_set()");}}                                return st;}
  473.                 int                             get_animation() const           {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via get_animation()");}}                  return anim;}
  474.                 int                             get_startFrame() const          {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via get_startFrame()");}}                 return startFram;}
  475.                 int                             get_numFrames() const           {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via get_numFrames()");}}                  return numFrams;}
  476.                 COMMON::Timer@  get_animationTimer()            {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via get_animationTimer()");}}             return anmTimer;}
  477.                 int                             get_ticksPerFrame()     const   {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via get_ticksPerFrame()");}}              return ticksPerFram;}
  478.                 bool                    get_cycleAnimation() const      {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via get_cycleAnimation()");}}             return cycleAnim;}
  479.                 bool                    get_invisible() const           {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via set_invisible()");}}                  return inv;}
  480.                
  481.                 int                             get_frame()     const                   {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via get_frame()");}}
  482.                                                                                                          return startFrame + (anmTimer.elapsedTime()/ticksPerFrame) % numFrames;}
  483.                
  484.                
  485.                 void                    set_spriteAngle(int angle)                              {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via set_spriteAngle()");}return;}         this.angle = angle;}
  486.                 void                    set_spriteScaleX(float scaleX)                  {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via set_spriteScaleX()");}return;}        this.scaleX = scaleX;}
  487.                 void                    set_spriteScaleY(float scaleY)                  {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via set_spriteScaleY()");}return;}        this.scaleY = scaleY;}
  488.                 void                    set_spriteMode(SPRITE::Mode mode)               {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via set_spriteMode()");}return;}          this.mode = mode;}
  489.                 void                    set_spriteParam(uint8 param)                    {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via set_spriteParam()");}return;}         this.param = param;}
  490.                 void                    set_spriteLayerZ(uint8 layerZ)                  {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via set_spriteLayerZ()");}return;}        this.layerZ = layerZ;}
  491.                 void                    set_spriteLayerXY(uint8 layerXY)                {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via set_spriteLayerXY()");}return;}       this.layerXY = layerXY;}
  492.                 void                    set_spritePlayerID(int8 playerID)               {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via set_spritePlayerID()");}return;}      this.playerID = playerID;}     
  493.                 void                    set_spriteBob(bool bob)                                 {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via set_spriteBob()");}return;}           this.bob = bob;}
  494.                 void                    set_set(ANIM::Set set)                                  {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via get_set()");}return;}                         this.st = set;}
  495.                 void                    set_animation(int animation)                    {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via get_animation()");}return;}           this.anim = animation;}
  496.                 void                    set_startFrame(int startFrame)                  {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via set_startFame()");}return;}           this.startFram = startFrame;}
  497.                 void                    set_numFrames(int numFrames)                    {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via set_numFrames()");}return;}           this.numFrams = numFrames;}
  498.                 void                    set_ticksPerFrame(int ticksPerFrame)    {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via set_ticksPerFrame()");}return;}       this.ticksPerFram = ticksPerFrame;}
  499.                 void                    set_cycleAnimation(bool cycleAnimation) {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via set_cycleAnimation()");}return;}      this.cycleAnim = cycleAnimation;}
  500.                 void                    set_invisible(bool invisible)                   {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via set_invisible()");}return;}           this.inv = invisible;}
  501.                
  502.                 void playAnimation(ANIM::Set set, int animation, int startFrame, int numFrames, int ticksPerFrame = 7, bool cycle=true) {
  503.                         if(this.anmTimer.isFinished() || set != st || animation != anim || startFrame != startFram || numFrames != numFrams || ticksPerFrame != ticksPerFram) {
  504.                                 this.st = set;
  505.                                 this.anim = animation;
  506.                                 this.startFram = startFrame;
  507.                                 this.numFrams = numFrames;
  508.                                 this.ticksPerFram = ticksPerFrame;
  509.                                 this.anmTimer.start(numFrams*ticksPerFram);
  510.                         }
  511.                         this.cycleAnim = cycle;
  512.                 }
  513.                
  514.                 bool isPlayingAnimation() {
  515.                         return (this.anmTimer.isStarted() && !this.anmTimer.isFinished()) || cycleAnim;
  516.                 }
  517.                
  518.                 void stopAnimation() {
  519.                         this.anmTimer.stop();
  520.                 }
  521.                
  522.                 void freezeFrame(ANIM::Set, int animation, int frame) {
  523.                         playAnimation(set,animation,frame,1,1,true);
  524.                 }
  525.                
  526.                 //Custom object methods
  527.                 float distancePixels(COMMON::IFocalPoint@ focus) {return distance(this.xPos, this.yPos, focus.xPos, focus.yPos);}
  528.                 float distanceTiles(COMMON::IFocalPoint@ focus)  {return distancePixels(focus)/32;}    
  529.        
  530.                 bool opEquals(jjOBJ@ jjObject) {return this.cid == jjObject.age;}
  531.                 bool opEquals(CUSTOM::Object object) {return object.objectID == this.objectID && object.cid == this.cid;}
  532.                
  533.                 void updateDefaultBehavior() {
  534.                         if(this.jjObject.behavior != CustomObjectBehavior) {
  535.                                 if(!isNil && this.jjObject.behavior != BEHAVIOR::DEFAULT) {
  536.                                         this.defaultBehavior = this.jjObject.behavior;
  537.                                 } else {
  538.                                         this.defaultBehavior = DO_NOTHING;
  539.                                 }
  540.                         }
  541.                 }
  542.                
  543.                 //jjOBJ accessors
  544.                 int get_animSpeed() const                                       {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via get_animSpeed()");}}                          return this.jjObject.animSpeed;}
  545.                 HANDLING::Bullet get_bulletHandling() const {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via get_bulletHandling()");}}                 return this.jjObject.bulletHandling;}
  546.                 bool get_causesRicochet() const                         {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via get_causesRicochet()");}}                     return this.jjObject.causesRicochet;}
  547.                 int get_counter() const                                         {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via get_counter()");}}                            return this.jjObject.counter;}
  548.                 uint8 get_counterEnd() const                            {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via get_counterEnd()");}}                         return this.jjObject.counterEnd;}
  549.                 int get_creator() const                                         {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via get_creator()");}}                            return this.jjObject.creator;}
  550.                 int get_creatorID() const                                       {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via get_creatorID()");}}                          return this.jjObject.creatorID;}
  551.                 CREATOR::Type get_creatorType() const           {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via get_creatorType()");}}                        return this.jjObject.creatorType;}
  552.                 int16 get_curAnim() const                                       {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via get_curAnim()");}}                            return this.jjObject.curAnim;}
  553.                 uint get_curFrame() const                                       {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via get_curFrame()");}}                           return this.jjObject.curFrame;}
  554.                 bool get_deactivates() const                            {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via get_deactivates()");}}                        return this.jjObject.deactivates;}
  555.                 int8 get_direction() const                                      {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via get_direction()");}}                          return this.jjObject.direction;}
  556.                 uint8 get_doesHurt() const                                      {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via get_doesHurt()");}}                           return this.jjObject.doesHurt;}
  557.                 int get_energy() const                                          {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via get_energy()");}}                                     return this.jjObject.energy;}
  558.                 uint8 get_eventID() const                                       {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via get_eventID()");}}                            return this.jjObject.eventID;}
  559.                 int8 get_frameID() const                                        {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via get_frameID()");}}                            return this.jjObject.frameID;}
  560.                 uint8 get_freeze() const                                        {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via get_freeze()");}}                                     return this.jjObject.freeze;}
  561.                 bool get_isBlastable() const                            {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via get_isBlastable()");}}                        return this.jjObject.isBlastable;}
  562.                 bool get_isFreezable() const                            {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via get_isFreezable()");}}                        return this.jjObject.isFreezable;}
  563.                 bool get_isTarget() const                                       {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via get_isTarget()");}}                           return this.jjObject.isTarget;}
  564.                 uint8 get_justHit() const                                       {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via get_justHit()");}}                            return this.jjObject.justHit;}
  565.                 int16 get_killAnim() const                                      {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via get_killAnim()");}}                           return this.jjObject.killAnim;}
  566.                 int8 get_light() const                                          {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via get_light()");}}                                      return this.jjObject.light;}
  567.                 uint8 get_lightType() const                             {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via get_lightType()");}}                          return this.jjObject.lightType;}
  568.                 int get_oldState() const                                        {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via get_oldState()");}}                           return this.jjObject.oldState;}
  569.                 HANDLING::Player get_playerHandling() const {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via get_playerHandling()");}}                 return this.jjObject.playerHandling;}
  570.                 uint16 get_points() const                                       {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via get_points()");}}                                     return this.jjObject.points;}
  571.                 int8 get_noHit() const                                          {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via get_noHit()");}}                                      return this.jjObject.noHit;}
  572.                 uint16 get_objectID() const                             {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via get_objectID()");}}                           return this.jjObject.objectID;}
  573.                 uint8 get_objType() const                                       {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via get_objType()");}}                            return this.jjObject.objType;}
  574.                 bool get_scriptedCollisions() const             {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via get_scriptedCollisions()");}}         return this.jjObject.scriptedCollisions;}
  575.                 int get_special() const                                         {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via get_special()");}}                            return this.jjObject.special;}
  576.                 int get_state() const                                           {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via get_state()");}}                                      return this.jjObject.state;}
  577.                 bool get_triggersTNT() const                            {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via get_triggersTNT()");}}                        return this.jjObject.triggersTNT;}
  578.                 int get_var(int index) const                            {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via get_var()");}}                                        return this.jjObject.var[index];}
  579.                 float get_xAcc() const                                          {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via get_xAcc()");}}                                       return this.jjObject.xAcc;}
  580.                 float get_xOrg() const                                          {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via get_xOrg()");}}                                       return this.jjObject.xOrg;}
  581.                 float get_xPos() const                                          {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via get_xPos()");}}                                       return this.jjObject.xPos;}
  582.                 float get_xSpeed() const                                        {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via get_xSpeed()");}}                                     return this.jjObject.xSpeed;}
  583.                 float get_yAcc() const                                          {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via get_yAcc()");}}                                       return this.jjObject.yAcc;}
  584.                 float get_yPos() const                                          {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via get_yPos()");}}                                       return this.jjObject.yPos;}
  585.                 float get_yOrg() const                                          {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via get_yOrg()");}}                                       return this.jjObject.yOrg;}
  586.                 float get_ySpeed() const                                        {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via get_ySpeed()");}}                                     return this.jjObject.ySpeed;}
  587.                
  588.                 //jjOBJ mutators
  589.                 void set_animSpeed(int animSpeed)                                                       {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via set_animSpeed()");}return;}                   this.jjObject.animSpeed=animSpeed;}
  590.                 void set_bulletHandling(HANDLING::Bullet bulletHandling)        {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via set_bulletHandling()");}return;}              this.jjObject.bulletHandling=bulletHandling;}
  591.                 void set_causesRicochet(bool causesRicochet)                            {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via set_causesRicochet()");}return;}              this.jjObject.causesRicochet=causesRicochet;}
  592.                 void set_counter(int counter)                                                           {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via set_counter()");}return;}                             this.jjObject.counter=counter;}
  593.                 void set_counterEnd(uint8 counterEnd)                                           {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via set_counterEnd()");}return;}                  this.jjObject.counterEnd=counterEnd;}
  594.                 void set_creator(int creator)                                                   {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via set_creator()");}return;}                             this.jjObject.creator=creator;}
  595.                 void set_creatorID(int creatorID)                                                       {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via set_creatorID()");}return;}                   this.jjObject.creatorID=creatorID;}
  596.                 void set_creatorType(CREATOR::Type creatorType)                         {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via set_creatorType()");}return;}                 this.jjObject.creatorType=creatorType;}
  597.                 void set_curAnim(int16 curAnim)                                                         {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via set_curAnim()");}return;}                             this.jjObject.curAnim=curAnim;}
  598.                 void set_curFrame(uint curFrame)                                                        {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via set_curFrame()");}return;}                    this.jjObject.curFrame=curFrame;}
  599.                 void set_deactivates(bool deactivates)                                          {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via set_deactivates()");}return;}                 this.jjObject.deactivates=deactivates;}
  600.                 void set_direction(int8 direction)                                                      {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via set_direction()");}return;}                   this.jjObject.direction=direction;}
  601.                 void set_doesHurt(uint8 doesHurt)                                                       {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via set_doesHurt()");}return;}                    this.jjObject.doesHurt=doesHurt;}
  602.                 void set_energy(int energy)                                                             {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via set_energy()");}return;}                              this.jjObject.energy=energy;}
  603.                 void set_eventID(uint8 eventID)                                                         {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via set_eventID()");}return;}                             this.jjObject.eventID=eventID;}
  604.                 void set_frameID(int8 frameID)                                                          {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via set_frameID()");}return;}                             this.jjObject.frameID=frameID;}
  605.                 void set_freeze(uint8 freeze)                                                           {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via set_freeze()");}return;}                              this.jjObject.freeze=freeze;}
  606.                 void set_isBlastable(bool isBlastable)                                          {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via set_isBlastable()");}return;}                 this.jjObject.isBlastable=isBlastable;}
  607.                 void set_isFreezable(bool isFreezable)                                          {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via set_isFreezable()");}return;}                 this.jjObject.isFreezable=isFreezable;}
  608.                 void set_isTarget(bool isTarget)                                                        {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via set_isTarget()");}return;}                    this.jjObject.isTarget=isTarget;}
  609.                 void set_justHit(uint8 justHit)                                                         {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via set_justHit()");}return;}                             this.jjObject.justHit=justHit;}
  610.                 void set_killAnim(int16 killAnim)                                                       {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via set_killAnim()");}return;}                    this.jjObject.killAnim=killAnim;}
  611.                 void set_light(int8 light)                                                                      {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via set_light()");}return;}                               this.jjObject.light=light;}
  612.                 void set_lightType(uint8 lightType)                                             {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via set_lightType()");}return;}                   this.jjObject.lightType=lightType;}
  613.                 void set_oldState(STATE::State state)                                           {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via set_oldState()");}return;}                    this.jjObject.oldState = state;}
  614.                 void set_playerHandling(HANDLING::Player playerHandling)        {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via set_playerHandling()");}return;}              this.jjObject.playerHandling=playerHandling;}
  615.                 void set_points(uint16 points)                                                          {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via set_points()");}return;}                              this.jjObject.points=points;}
  616.                 void set_noHit(int8 noHit)                                                                      {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via set_noHit()");}return;}                               this.jjObject.noHit=noHit;}
  617.                 void set_objType(uint8 objType)                                                         {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via set_objType()");}return;}                             this.jjObject.objType=objType;}
  618.                 void set_scriptedCollisions(bool scriptedCollisions)            {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via set_scriptedCollisions()");}return;}  this.jjObject.scriptedCollisions=scriptedCollisions;}
  619.                 void set_special(int special)                                                           {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via set_special()");}return;}                             this.jjObject.special=special;}
  620.                 void set_state(int state)                                                                       {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via set_state()");}return;}                               this.jjObject.state = state;}
  621.                 void set_triggersTNT(bool triggersTNT)                                          {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via set_triggersTNT()");}return;}                 this.jjObject.triggersTNT=triggersTNT;}
  622.                 void set_var(int index, int value)                                                      {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via set_var()");}return;}                                 this.jjObject.var[index]=value;}
  623.                 void set_xAcc(float xAcc)                                                                       {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via set_xAcc()");}return;}                                this.jjObject.xAcc=xAcc;}
  624.                 void set_xOrg(float xOrg)                                                                       {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via set_xOrg()");}return;}                                this.jjObject.xOrg=xOrg;}
  625.                 void set_xPos(float xPos)                                                                       {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via set_xPos()");}return;}                                this.jjObject.xPos=xPos;}
  626.                 void set_xSpeed(float xSpeed)                                                           {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via set_xSpeed()");}return;}                              this.jjObject.xSpeed=xSpeed;}
  627.                 void set_ySpeed(float ySpeed)                                                           {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via set_ySpeed()");}return;}                              this.jjObject.ySpeed=ySpeed;}
  628.                 void set_yAcc(float yAcc)                                                                       {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via set_yAcc()");}return;}                                this.jjObject.yAcc=yAcc;}
  629.                 void set_yPos(float yPos)                                                                       {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via set_yPos()");}return;}                                this.jjObject.yPos=yPos;}
  630.                 void set_yOrg(float yOrg)                                                                       {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via set_yOrg()");}return;}                                this.jjObject.yOrg=yOrg;}
  631.                
  632.                 //jjOBJ methods
  633.                 void clearPlatform() const                                                                      {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via clearPlatform()");}return;}                   this.jjObject.clearPlatform();}
  634.                 void grantPickup(jjPLAYER@ player, int frequency) const         {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via grantPickup()");}return;}                             this.jjObject.grantPickup(player, frequency);}
  635.                 void particlePixelExplosion(int style) const                            {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via particlePixelExplosion()");}return;}  this.jjObject.particlePixelExplosion(style);}
  636.                 void pathMovement() const                                                                       {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via pathMovement()");}return;}                    this.jjObject.pathMovement();}
  637.                 void putOnGround(bool precise = false) const                            {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via putOnGround()");}return;}                             this.jjObject.putOnGround(precise);}
  638.                 void delete()                                                                                           {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via delete()");}return;}                                  this.jjObject.delete();}
  639.                
  640.                 void deleteNoWarn() {if(!objectChanged(this.jjObject)) this.jjObject.delete();}
  641.                
  642.                 int     beSolid() const                                                                                                                                  {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via beSolid()");}return  0;}                     return this.jjObject.beSolid();}
  643.                 int16   determineCurAnim(uint8 setID, uint8 animation, bool change = true) const                 {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via determineCurAnim()");}return -1;}    return this.jjObject.determineCurAnim(setID,animation,change);}
  644.                 int16   determineCurAnim(ANIM::Set setID, uint8 animation, bool change = true) const     {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via determineCurAnim()");}return -1;}    return this.jjObject.determineCurAnim(setID,animation,change);}
  645.                 uint    determineCurFrame(bool change = true) const                                                                              {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via determineCurFrame()");}return 0;}    return this.jjObject.determineCurFrame(change);}
  646.                 int     findNearestPlayer(int maxDistance) const                                                                                 {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via findNearestPlayer()");}return -1;}   return this.jjObject.findNearestPlayer(maxDistance);}
  647.                 int     findNearestPlayer(int maxDistance, int &out foundDistance) const                                 {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via findNearestPlayer()");}return -1;}   return this.jjObject.findNearestPlayer(maxDistance, foundDistance);}
  648.                 int     fireBullet(OBJECT::Object eventID) const                                                                                 {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via fireBullet()");}return -1;}                  return this.jjObject.fireBullet(eventID);}
  649.                 int     unfreeze(int style) const                                                                                                                {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via unfreeze()");}return -1;}                    return this.jjObject.unfreeze(style);}
  650.                 bool    ricochet() const                                                                                                                                 {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via ricochet()");}return false;}         return this.jjObject.ricochet();}
  651.        
  652.                 //Reimplemented deactivate to explicitly call delete, so if delete is overridden it will still work.
  653.                 //Also prevents having to override both deactivate and delete (just delete is enough)
  654.                 void deactivate() {
  655.                         if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via deactivate()");}return;}
  656.                         this.jjObject.delete();
  657.                         if(this.jjObject.creatorType == CREATOR::LEVEL) {
  658.                           jjEventSet(int(this.jjObject.xOrg/32), int(this.jjObject.yOrg/32), this.jjObject.eventID);
  659.                           jjParameterSet(int(this.jjObject.xOrg/32), int(this.jjObject.yOrg/32), -1, 1, 0);
  660.                         }
  661.                 }
  662.                
  663.                 //Reimplemented behave to explicitly call draw()
  664.                 void behave(BEHAVIOR::Behavior behavior, bool draw = true, bool useCustomSpriteProperties = true) const {
  665.                         if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via behave()");}return;}
  666.                         if(!isWarping()) this.jjObject.behave(behavior, false);
  667.                         if(draw && isActive) this.draw(useCustomSpriteProperties);
  668.                 }
  669.                
  670.                 void behave(jjVOIDFUNCOBJ@ behavior, bool draw = true, bool useCustomSpriteProperties = true) const {
  671.                         if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via behave()");}return;}
  672.                         if(!isWarping()) this.jjObject.behave(behavior, false);
  673.                         if(draw && isActive) this.draw(useCustomSpriteProperties);
  674.                 }
  675.                
  676.                 void behave(jjBEHAVIOR behavior, bool draw = true, bool useCustomSpriteProperties = true) const {
  677.                         if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via behave()");}return;}
  678.                         if(!isWarping()) this.jjObject.behave(behavior, false);
  679.                         if(draw && isActive) this.draw(useCustomSpriteProperties);
  680.                 }
  681.  
  682.                 //Reimplemented draw to use custom sprite properties
  683.                 int draw(bool useCustomSpriteProperties = true) const {
  684.                         if(objectChanged(this.jjObject)) {
  685.                                 if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via draw()");}
  686.                                 return -1;
  687.                         }
  688.                         if(invisible) return -1;
  689.                         if(isPlayingAnimation() || useCustomSpriteProperties) {
  690.                                 float offset = bob ? this.bobInterpolator.value(bobTimer.elapsedTime()) : 0;
  691.                                 SPRITE::Mode mode = justHit != 0 ? SPRITE::SINGLECOLOR : this.mode;
  692.                                 int param = justHit != 0 ? 64 : this.param;
  693.                                 int angleDirection = direction > 0 ? angle : -angle;
  694.                                 float xScaleDirection = direction > 0 ? scaleX : -1*scaleX;
  695.                                 if(angle != 0) {
  696.                                         if(isPlayingAnimation())
  697.                                                 jjDrawRotatedSprite(xPos,yPos+offset,st,anim,frame,angleDirection,xScaleDirection,scaleY,mode,param,layerZ,layerXY,playerID);
  698.                                         else
  699.                                                 jjDrawRotatedSpriteFromCurFrame(xPos,yPos+offset,curFrame,angleDirection,xScaleDirection,scaleY,mode,param,layerZ,layerXY,playerID);
  700.                                         return -1;
  701.                                 } else if(scaleX != 1 || scaleY != 1) {
  702.                                         if(isPlayingAnimation())
  703.                                                 jjDrawResizedSprite(xPos,yPos+offset,st,anim,frame,xScaleDirection,scaleY,mode,param,layerZ,layerXY,playerID);
  704.                                         else
  705.                                                 jjDrawResizedSpriteFromCurFrame(xPos,yPos+offset,curFrame,xScaleDirection,scaleY,mode,param,layerZ,layerXY,playerID);
  706.                                         return -1;
  707.                                 } else if(isPlayingAnimation() || bob != false || mode != SPRITE::NORMAL || layerZ != 4 || layerXY != 4 || playerID != -1) {
  708.                                         if(isPlayingAnimation())
  709.                                                 jjDrawSprite(xPos,yPos+offset,st,anim,frame,direction,mode,param,layerZ,layerXY,playerID);
  710.                                         else
  711.                                                 jjDrawSpriteFromCurFrame(xPos,yPos+offset,curFrame,direction,mode,param,layerZ,layerXY,playerID);
  712.                                         return -1;
  713.                                 } else { return this.jjObject.draw(); }
  714.                         } else { return this.jjObject.draw(); }
  715.                 }
  716.                
  717.                 //Debug helpers
  718.                 void printPos() {
  719.                         jjDebug((xPos/32) + "," + (yPos/32));
  720.                 }
  721.         }
  722.        
  723.         class Boss : CUSTOM::Object {
  724.                 int oldPresetEnergy;
  725.                 jjPLAYER@ player;
  726.                 bool activated = false;
  727.                 GAME::IBossMeter@ meter = null;
  728.                
  729.                 Boss(OBJECT::Object objectTemplate, float xPixel, float yPixel) {
  730.                         super(objectTemplate, xPixel, yPixel);
  731.                         jjObjectPresets[255].determineCurAnim(ANIM::BOSS, 0);
  732.                         this.playerHandling = HANDLING::SPECIAL;
  733.                         this.bulletHandling = HANDLING::DETECTBULLET;
  734.                         this.scriptedCollisions = true;
  735.                 }
  736.                
  737.                 int get_energy() {
  738.                         if(meter is null) return Object::get_energy();
  739.                         return meter.energy;
  740.                 }
  741.                
  742.                 void set_energy(int energy) {
  743.                         if(meter is null) Object::set_energy(energy);
  744.                         else meter.energy = energy;
  745.                 }
  746.                
  747.                 void activateBoss(int maxEnergy) {
  748.                         activateBossWithUIMeter(maxEnergy,10*32,0);
  749.                 }
  750.                
  751.                 void activateBossWithUIMeter(int maxEnergy, float xPos=10*32, float yPos=0) {
  752.                         if(!activated) {
  753.                                 GAME::BossMeterUI@ meterUI = @GAME::BossMeterUI(xPos,yPos);
  754.                                 @meter = @meterUI;
  755.                                 CUSTOM::UI.addElement(meterUI);
  756.                                 meter.maxEnergy = maxEnergy;
  757.                                 meter.energy = maxEnergy;
  758.                                 activated = true;
  759.                         }
  760.                 }
  761.                
  762.                 void activateBossWithFloatingMeter(int maxEnergy, int yOffset = 0) {
  763.                         if(!activated) {
  764.                                 @meter = @GAME::BossMeterFloating(this,yOffset);
  765.                                 meter.maxEnergy = maxEnergy;
  766.                                 meter.energy = maxEnergy;
  767.                                 activated = true;
  768.                         }
  769.                 }
  770.                
  771.                 void deactivateBoss() {
  772.                         if(activated) {
  773.                                 GAME::BossMeterUI@ casted = cast<GAME::BossMeterUI@>(meter);
  774.                                 if(casted !is null) {
  775.                                         CUSTOM::UI.removeElement(casted);
  776.                                 }
  777.                                 meter.delete();
  778.                                 @meter = null;
  779.                                 activated = false;
  780.                         }
  781.                 }
  782.                
  783.                 GAME::IBossMeter@ bossMeter() {
  784.                         return meter;
  785.                 }
  786.                
  787.                 void delete() {
  788.                         deactivateBoss();
  789.                         CUSTOM::Object::delete();
  790.                 }
  791.                
  792.                 void onObjectHit(jjOBJ@ bullet, jjPLAYER@ player, int force) {
  793.                         if(bullet !is null && player !is null) {
  794.                                 if(meter !is null) {
  795.                                         meter.energy = meter.energy - force;
  796.                                         justHit = 5;
  797.                                         bullet.state = STATE::EXPLODE;
  798.                                         if(meter.energy <= 0) {
  799.                                                 particlePixelExplosion(0);
  800.                                                 delete();
  801.                                         }
  802.                                 } else {
  803.                                         bullet.state = STATE::EXPLODE;
  804.                                 }
  805.                         } else if (player !is null) {
  806.                                 player.hurt();
  807.                         }
  808.                 }
  809.         }
  810.        
  811.         funcdef se::colorEffect@ COLOR_EFFECT(jjPALCOLOR,float);
  812.  
  813.         se::colorEffect@ effectBrighten(jjPALCOLOR color, float value) {return se::colorBrighten(value);}
  814.         se::colorEffect@ effectContrast(jjPALCOLOR color, float value) {return se::colorContrast(value);}
  815.         se::colorEffect@ effectFade(jjPALCOLOR color, float value)         {return se::colorFade(color,value);}
  816.         se::colorEffect@ effectGamma(jjPALCOLOR color,float value)     {return se::colorGamma(value);}
  817.         se::colorEffect@ effectShiftHue(jjPALCOLOR color,float value)  {return se::colorShiftHue(int(value));}
  818.         se::colorEffect@ effectSketch(jjPALCOLOR color,float value)    {return se::colorSketch(int(value));}
  819.        
  820.         class Palette : CUSTOM::Object {
  821.                 INTERPOLATION::Interpolator@ interpolatorEffect;
  822.                 COMMON::Timer effectTimer();
  823.                 bool effectCycle;
  824.                 jjPAL palette;
  825.                 jjPALCOLOR palcolor;
  826.                 COLOR_EFFECT@ effectFunction;
  827.                 int minColor, maxColor;
  828.                 int updateDelay = 3;
  829.  
  830.                 Palette() {
  831.                         super(NIL,-1,-1);
  832.                 }
  833.                
  834.                 void set_delay(int delay) { this.updateDelay = delay; }
  835.                 int  get_delay()                  { return this.updateDelay;  }
  836.                
  837.                 void colorGrayscale(int minColor = 0, int maxColor = 255)                                               {applyColorEffect(se::colorGrayscale(), minColor, maxColor);}
  838.                 void colorSolarize(int minColor = 0, int maxColor = 255)                                                {applyColorEffect(se::colorSolarize(), minColor, maxColor);}
  839.                 void colorSepia(int minColor = 0, int maxColor = 255)                                                   {applyColorEffect(se::colorSepia(), minColor, maxColor);}
  840.                 void colorNegative(int minColor = 0, int maxColor = 255)                                                {applyColorEffect(se::colorNegative(), minColor, maxColor);}
  841.                 void colorDecompositionMin(int minColor = 0, int maxColor = 255)                                {applyColorEffect(se::colorDecompositionMin(), minColor, maxColor);}
  842.                 void colorDecompositionMax(int minColor = 0, int maxColor = 255)                                {applyColorEffect(se::colorDecompositionMax(), minColor, maxColor);}
  843.                 void colorGlow(jjPALCOLOR color, int minColor = 0, int maxColor = 255)                  {applyColorEffect(se::colorGlow(color), minColor, maxColor);}
  844.                 void colorReduction(int r, int g, int b,int minColor = 0, int maxColor = 255)   {applyColorEffect(se::colorReduction(r,g,b), minColor, maxColor);}
  845.                 void colorReplace(jjPALCOLOR color, int minColor = 0, int maxColor = 255)               {applyColorEffect(se::colorReplace(color), minColor, maxColor);}
  846.                
  847.                 void restore() {
  848.                         jjBackupPalette.apply();
  849.                 }
  850.                
  851.                 void applyColorEffect(se::colorEffect@ effect, uint minColor = 1, uint maxColor = 256) {
  852.                         jjPAL newPalette = jjBackupPalette;
  853.                         for(uint i = minColor; i < maxColor; i++) {
  854.                                 newPalette.color[i] = effect.getResult(jjBackupPalette.color[i]);
  855.                         }
  856.                         newPalette.apply();
  857.                 }
  858.                
  859.                 void brightenLinear(jjPAL palette, float min, float max, int duration, uint minColor = 1, uint maxColor = 256, bool cycle = false) {
  860.                         effectInterpolator(effectBrighten,
  861.                                                            palette,
  862.                                                            jjPALCOLOR(),
  863.                                                            INTERPOLATION::Linear(min,max,duration),
  864.                                                            duration,minColor,maxColor,cycle);
  865.                 }
  866.                
  867.                 void brightenOscillate(jjPAL palette, jjPALCOLOR color, float min, float max, int duration, float frequency, int offset, uint minColor = 1, uint maxColor = 256, bool cycle = false) {
  868.                         float dist = (max-min)/2;
  869.                         float mid  = min + dist;
  870.                         effectInterpolator(effectBrighten,
  871.                                                            palette,
  872.                                                            color,
  873.                                                            INTERPOLATION::PointPlusSine(COMMON::Position(mid,mid),INTERPOLATION::getX,frequency,dist,offset,duration),
  874.                                                            duration,minColor,maxColor,cycle);
  875.                 }
  876.                
  877.                 void contrastLinear(jjPAL palette, float min, float max, int duration, uint minColor = 1, uint maxColor = 256, bool cycle = false) {
  878.                         effectInterpolator(effectContrast,
  879.                                                            palette,
  880.                                                            jjPALCOLOR(),
  881.                                                            INTERPOLATION::Linear(min,max,duration),
  882.                                                            duration,minColor,maxColor,cycle);
  883.                 }
  884.                
  885.                 void contrastOscillate(jjPAL palette, jjPALCOLOR color, float min, float max, int duration, float frequency, int offset, uint minColor = 1, uint maxColor = 256, bool cycle = false) {
  886.                         float dist = (max-min)/2;
  887.                         float mid  = min + dist;
  888.                         effectInterpolator(effectContrast,
  889.                                                            palette,
  890.                                                            color,
  891.                                                            INTERPOLATION::PointPlusSine(COMMON::Position(mid,mid),INTERPOLATION::getX,frequency,dist,offset,duration),
  892.                                                            duration,minColor,maxColor,cycle);
  893.                 }
  894.                
  895.                 void fadeLinear(jjPAL palette, jjPALCOLOR color, float min, float max, int duration, uint minColor = 1, uint maxColor = 256, bool cycle = false) {
  896.                         effectInterpolator(effectFade,
  897.                                                            palette,
  898.                                                            color,
  899.                                                            INTERPOLATION::Linear(min,max,duration),
  900.                                                            duration,minColor,maxColor,cycle);
  901.                 }
  902.                
  903.                 void fadeOscillate(jjPAL palette, jjPALCOLOR color, float min, float max, int duration, float frequency, int offset, uint minColor = 1, uint maxColor = 256, bool cycle = false) {
  904.                         float dist = (max-min)/2;
  905.                         float mid  = min + dist;
  906.                         effectInterpolator(effectFade,
  907.                                                            palette,
  908.                                                            color,
  909.                                                            INTERPOLATION::PointPlusSine(COMMON::Position(mid,mid),INTERPOLATION::getX,frequency,dist,offset,duration),
  910.                                                            duration,minColor,maxColor,cycle);
  911.                 }
  912.                
  913.                 void gammaLinear(jjPAL palette, float min, float max, int duration, uint minColor = 1, uint maxColor = 256, bool cycle = false) {
  914.                         effectInterpolator(effectGamma,
  915.                                                            palette,
  916.                                                            jjPALCOLOR(),
  917.                                                            INTERPOLATION::Linear(min,max,duration),
  918.                                                            duration,minColor,maxColor,cycle);
  919.                 }
  920.                
  921.                 void gammaOscillate(jjPAL palette, float min, float max, int duration, float frequency, int offset, uint minColor = 1, uint maxColor = 256, bool cycle = false) {
  922.                         float dist = (max-min)/2;
  923.                         float mid  = min + dist;
  924.                         effectInterpolator(effectGamma,
  925.                                                            palette,
  926.                                                            jjPALCOLOR(),
  927.                                                            INTERPOLATION::PointPlusSine(COMMON::Position(mid,mid),INTERPOLATION::getX,frequency,dist,offset,duration),
  928.                                                            duration,minColor,maxColor,cycle);
  929.                 }
  930.                
  931.                 void shiftHueLinear(jjPAL palette, float min, float max, int duration, uint minColor = 1, uint maxColor = 256, bool cycle = false) {
  932.                         effectInterpolator(effectShiftHue,
  933.                                                            palette,
  934.                                                            jjPALCOLOR(),
  935.                                                            INTERPOLATION::Linear(min,max,duration),
  936.                                                            duration,minColor,maxColor,cycle);
  937.                 }
  938.                
  939.                 void shiftHueOscillate(jjPAL palette, float min, float max, int duration, float frequency, int offset, uint minColor = 1, uint maxColor = 256, bool cycle = false) {
  940.                         float dist = (max-min)/2;
  941.                         float mid  = min + dist;
  942.                         effectInterpolator(effectShiftHue,
  943.                                                            palette,
  944.                                                            jjPALCOLOR(),
  945.                                                            INTERPOLATION::PointPlusSine(COMMON::Position(mid,mid),INTERPOLATION::getX,frequency,dist,offset,duration),
  946.                                                            duration,minColor,maxColor,cycle);
  947.                 }
  948.                
  949.                 void sketchLinear(jjPAL palette, float min, float max, int duration, uint minColor = 1, uint maxColor = 256, bool cycle = false) {
  950.                         effectInterpolator(effectSketch,
  951.                                                            palette,
  952.                                                            jjPALCOLOR(),
  953.                                                            INTERPOLATION::Linear(min,max,duration),
  954.                                                            duration,minColor,maxColor,cycle);
  955.                 }
  956.                
  957.                 void sketchOscillate(jjPAL palette, float min, float max, int duration, float frequency, int offset, uint minColor = 1, uint maxColor = 256, bool cycle = false) {
  958.                         float dist = (max-min)/2;
  959.                         float mid  = min + dist;
  960.                         effectInterpolator(effectSketch,
  961.                                                            palette,
  962.                                                            jjPALCOLOR(),
  963.                                                            INTERPOLATION::PointPlusSine(COMMON::Position(mid,mid),INTERPOLATION::getX,frequency,dist,offset,duration),
  964.                                                            duration,minColor,maxColor,cycle);
  965.                 }
  966.                
  967.                 void effectInterpolator(COLOR_EFFECT@ colorEffect, jjPAL palette, jjPALCOLOR color, INTERPOLATION::Interpolator@ interpolator, int duration, int minColor = 0, int maxColor = 255, bool cycle = false) {
  968.                         @effectFunction = @colorEffect;
  969.                         this.minColor = minColor;
  970.                         this.maxColor = maxColor;
  971.                         this.palcolor = color;
  972.                         this.palette = palette;
  973.                         effectTimer.start(duration);
  974.                         @this.interpolatorEffect = interpolator;
  975.                         effectCycle = cycle;
  976.                 }
  977.                        
  978.                 bool effect() {
  979.                         if(effectTimer.isStarted()) {
  980.                                 if(effectTimer.elapsedTime() == 0 || effectTimer.elapsedTime() == effectTimer.interval() || effectTimer.elapsedTime() % updateDelay == 0) {
  981.                                         if(effectTimer.elapsedTime() <= effectTimer.interval() || effectTimer.interval() == -1) {
  982.                                                 se::colorEffect@ ceffect = effectFunction(this.palcolor,interpolatorEffect.value(effectTimer.elapsedTime()));
  983.                                                 applyColorEffect(ceffect,minColor,maxColor);
  984.                                                 return true;
  985.                                         } else if(effectCycle && effectTimer.elapsedTime() > effectTimer.interval()) {
  986.                                                 effectTimer.reset();
  987.                                                 se::colorEffect@ ceffect = effectFunction(this.palcolor,interpolatorEffect.value(effectTimer.elapsedTime()));
  988.                                                 applyColorEffect(ceffect,minColor,maxColor);
  989.                                                 return true;
  990.                                         }
  991.                                 }
  992.                         }
  993.                         return false;
  994.                 }
  995.                
  996.                 bool isPerformingColorEffect() {
  997.                         return effectTimer.isStarted() && !effectTimer.isFinished();
  998.                 }
  999.                
  1000.                 void stopEffect() {
  1001.                         effectTimer.stop();
  1002.                 }
  1003.                
  1004.                 void behavior() {
  1005.                         effect();
  1006.                 }
  1007.         }
  1008.  
  1009.         class Player : COMMON::IFocalPoint {
  1010.                 jjPLAYER@ jjPlayer;
  1011.                
  1012.                 Player(jjPLAYER@ player)  {@this.jjPlayer = @player;}
  1013.                
  1014.                 void set_xPos(float xPos) {this.jjPlayer.xPos=xPos;}
  1015.                 void set_yPos(float yPos) {this.jjPlayer.yPos=yPos;}
  1016.                
  1017.                 float get_xPos() const    {return this.jjPlayer.xPos;}
  1018.                 float get_yPos() const    {return this.jjPlayer.yPos;}
  1019.                
  1020.                 bool get_isActive() const {return this.jjPlayer.isActive;}
  1021.         }
  1022.        
  1023.         funcdef CUSTOM::Object@ BULLET(jjOBJ@ bullet);
  1024.        
  1025.         void CustomBulletBehavior1(jjOBJ@ bullet) { CustomBulletBehavior(bullet, 1); }
  1026.         void CustomBulletBehavior2(jjOBJ@ bullet) { CustomBulletBehavior(bullet, 2); }
  1027.         void CustomBulletBehavior3(jjOBJ@ bullet) { CustomBulletBehavior(bullet, 3); }
  1028.         void CustomBulletBehavior4(jjOBJ@ bullet) { CustomBulletBehavior(bullet, 4); }
  1029.         void CustomBulletBehavior5(jjOBJ@ bullet) { CustomBulletBehavior(bullet, 5); }
  1030.         void CustomBulletBehavior6(jjOBJ@ bullet) { CustomBulletBehavior(bullet, 6); }
  1031.         void CustomBulletBehavior7(jjOBJ@ bullet) { CustomBulletBehavior(bullet, 7); }
  1032.         void CustomBulletBehavior8(jjOBJ@ bullet) { CustomBulletBehavior(bullet, 8); }
  1033.         void CustomBulletBehavior9(jjOBJ@ bullet) { CustomBulletBehavior(bullet, 9); }
  1034.        
  1035.         void CustomBulletBehavior(jjOBJ@ bullet, int weaponNumber) {
  1036.                 CUSTOM::weapons.get(weaponNumber).getBullet()(bullet);
  1037.         }
  1038.        
  1039.         class Weapon {
  1040.                 int gun;
  1041.                 BULLET@ makeBullet;
  1042.                 IInterfaceElement@ element;
  1043.                
  1044.                 Weapon(int gun, BULLET@ bullet, IInterfaceElement@ element = null) {
  1045.                         this.gun = gun;
  1046.                         @this.makeBullet = @bullet;
  1047.                         @this.element = @element;
  1048.                 }
  1049.                
  1050.                 //Can't use virtual accessor with function handle or it crashes.
  1051.                 BULLET@                         getBullet()                             { return makeBullet; }
  1052.                 IInterfaceElement@      get_interfaceElement()          { return element; }
  1053.                
  1054.                 bool              get_comesFromGunCrates()  { return jjWeapons[gun].comesFromGunCrates; }
  1055.                 bool              get_infinite()                    { return jjWeapons[gun].infinite; }
  1056.                 int               get_maximum()                     { return jjWeapons[gun].maximum; }
  1057.                 int               get_multiplier()                      { return jjWeapons[gun].multiplier; }
  1058.                 bool              get_replacedByBubbles()       { return jjWeapons[gun].replacedByBubbles; }
  1059.                 bool              get_replenishes()                     { return jjWeapons[gun].replenishes; }
  1060.                 int                       get_style()                           { return jjWeapons[gun].style; }
  1061.                
  1062.                 void                    set_comesFromGunCrates(bool comesFromGunCrates) { jjWeapons[gun].comesFromGunCrates = comesFromGunCrates; }
  1063.                 void                    set_infinite(bool infinite)                                             { jjWeapons[gun].infinite = infinite; }
  1064.                 void                    set_maximum(int maximum)                                                { jjWeapons[gun].maximum = maximum; }
  1065.                 void                    set_multiplier(int multiplier)                                  { jjWeapons[gun].multiplier = multiplier; }
  1066.                 void                    set_replacedByBubbles(bool replacedByBubbles)   { jjWeapons[gun].replacedByBubbles = replacedByBubbles; }
  1067.                 void                    set_replenishes(bool replenishes)                               { jjWeapons[gun].replenishes = replenishes; }
  1068.                 void                    set_style(int style)                                                    { jjWeapons[gun].style = style; }
  1069.         }
  1070.        
  1071.         class Weapons {
  1072.                 array<CUSTOM::Weapon@> weapons(10);
  1073.                
  1074.                 Weapons() {
  1075.                         for(uint i = 1; i <= 9; i++)
  1076.                                 @weapons[i] = @Weapon(i, null, null);
  1077.                 }
  1078.                
  1079.                 void registerWeapon(int gun, BULLET@ bullet, IInterfaceElement@ element = null) {
  1080.                         if(gun < 1 || gun > 9)
  1081.                                 if(SHOW_WARNINGS)
  1082.                                         jjDebug("SaLLiB Warning: Button out of range in custom weapon!");
  1083.                         @weapons[gun] = @CUSTOM::Weapon(gun,bullet,element);
  1084.                         if(gun == 1)      jjObjectPresets[OBJECT::BLASTERBULLET].behavior = CustomBulletBehavior1;
  1085.                         else if(gun == 2) jjObjectPresets[OBJECT::BOUNCERBULLET].behavior = CustomBulletBehavior2;
  1086.                         else if(gun == 3) jjObjectPresets[OBJECT::ICEBULLET].behavior = CustomBulletBehavior3;
  1087.                         else if(gun == 4) jjObjectPresets[OBJECT::SEEKERBULLET].behavior = CustomBulletBehavior4;
  1088.                         else if(gun == 5) jjObjectPresets[OBJECT::RFBULLET].behavior = CustomBulletBehavior5;
  1089.                         else if(gun == 6) jjObjectPresets[OBJECT::TOASTERBULLET].behavior = CustomBulletBehavior6;
  1090.                         else if(gun == 7) jjObjectPresets[OBJECT::TNT].behavior = CustomBulletBehavior7;
  1091.                         else if(gun == 8) jjObjectPresets[OBJECT::FIREBALLBULLET].behavior = CustomBulletBehavior8;
  1092.                         else if(gun == 9) jjObjectPresets[OBJECT::ELECTROBULLET].behavior = CustomBulletBehavior9;
  1093.                 }
  1094.                
  1095.                 CUSTOM::Weapon@ get(int gun) {
  1096.                         return weapons[gun];
  1097.                 }
  1098.         }
  1099.        
  1100.         Weapons weapons;
  1101.        
  1102.         interface IInterfaceElement {
  1103.                 void draw(jjPLAYER@ player, jjCANVAS@ canvas);
  1104.         }
  1105.        
  1106.         class WeaponDisplayInterfaceElement : IInterfaceElement {
  1107.                 void draw(jjPLAYER@ player, jjCANVAS@ canvas) {
  1108.                         IInterfaceElement@ element = @CUSTOM::weapons.get(player.currWeapon).interfaceElement;
  1109.                         if(element !is null) {
  1110.                                 CUSTOM::UI.drawAmmo = false;
  1111.                                 element.draw(player,canvas);
  1112.                         } else {
  1113.                                 CUSTOM::UI.drawAmmo = true;
  1114.                         }
  1115.                 }
  1116.         }
  1117.        
  1118.         interface IInterface {
  1119.                 bool get_drawScore() const;
  1120.                 bool get_drawHealth() const;
  1121.                 bool get_drawLives() const;
  1122.                 bool get_drawAmmo() const;
  1123.                 bool get_drawPlayerTimer() const;
  1124.                
  1125.                 int get_numElements() const;
  1126.                
  1127.                 void set_drawScore(bool drawScore);
  1128.                 void set_drawHealth(bool drawHealth);
  1129.                 void set_drawLives(bool drawLives);
  1130.                 void set_drawAmmo(bool drawAmmo);
  1131.                 void set_drawPlayerTimer(bool drawPlayerTimer);
  1132.                
  1133.                 void addElement(IInterfaceElement@ element);
  1134.                 void removeElement(IInterfaceElement@ element);
  1135.                
  1136.                 void draw(jjPLAYER@ player,jjCANVAS@ canvas);
  1137.         }
  1138.        
  1139.         class Interface : IInterface {
  1140.                 array<CUSTOM::IInterfaceElement@> ui();
  1141.        
  1142.                 bool drawScore { get const {return drawScore;} set {drawScore = value; }}
  1143.                 bool drawScore = true;
  1144.                
  1145.                 bool drawHealth { get const {return drawHealth;} set {drawHealth = value; }}
  1146.                 bool drawHealth = true;
  1147.                
  1148.                 bool drawLives { get const {return drawLives;} set {drawLives = value;}}
  1149.                 bool drawLives = true;
  1150.                
  1151.                 bool drawAmmo { get const {return drawAmmo;} set {drawAmmo = value;}}
  1152.                 bool drawAmmo = true;
  1153.                
  1154.                 bool drawPlayerTimer { get const { return drawPlayerTimer;} set {drawPlayerTimer = value;}}
  1155.                 bool drawPlayerTimer = true;
  1156.        
  1157.                 int numElements { get const {return ui.length;}}
  1158.        
  1159.                 void addElement(IInterfaceElement@ element) {ui.insertLast(element);}
  1160.                
  1161.                 void removeElement(IInterfaceElement@ element) {
  1162.                         //Workaround bug in angelscript's array.find
  1163.                         int index = -1;
  1164.                         for(uint i = 0; i < ui.length; i++)
  1165.                                 if(ui[i] is element) {
  1166.                                         index = i;
  1167.                                         break;
  1168.                                 }
  1169.                         if(index >= 0)
  1170.                                 ui.removeAt(index);
  1171.                 }
  1172.        
  1173.                 void draw(jjPLAYER@ player,jjCANVAS@ canvas) {
  1174.                         for(uint i = 0; i < ui.length; i++) {
  1175.                                 ui[i].draw(player,canvas);
  1176.                         }
  1177.                 }
  1178.         }
  1179.        
  1180.         class EnhancedUI : Interface {
  1181.                 EnhancedUI() {
  1182.                         this.addElement(WeaponDisplayInterfaceElement());
  1183.                 }
  1184.         }
  1185.        
  1186.         IInterface@ UI = @Interface();
  1187. }
  1188.  
  1189. bool onDrawAmmo(jjPLAYER@ player, jjCANVAS@ canvas) {
  1190.         CUSTOM::UI.draw(player,canvas);
  1191.         if(onDrawAmmoHook !is null) onDrawAmmoHook(player,canvas);
  1192.         return !CUSTOM::UI.drawAmmo;
  1193. }
  1194.  
  1195. bool onDrawHealth(jjPLAYER@ player, jjCANVAS@ canvas) {
  1196.         if(onDrawHealthHook !is null) onDrawHealthHook(player,canvas);
  1197.         return !CUSTOM::UI.drawHealth;
  1198. }
  1199.  
  1200. bool onDrawLives(jjPLAYER@ player, jjCANVAS@ canvas) {
  1201.         if(onDrawLivesHook !is null) onDrawLivesHook(player,canvas);
  1202.         return !CUSTOM::UI.drawLives;
  1203. }
  1204.  
  1205. bool onDrawPlayerTimer(jjPLAYER@ player, jjCANVAS@ canvas) {
  1206.         if(onDrawPlayerTimerHook !is null) onDrawPlayerTimerHook(player,canvas);
  1207.         return !CUSTOM::UI.drawPlayerTimer;
  1208. }
  1209.  
  1210. bool onDrawScore(jjPLAYER@ player, jjCANVAS@ canvas) {
  1211.         if(onDrawScoreHook !is null) onDrawScoreHook(player,canvas);
  1212.         return !CUSTOM::UI.drawScore;
  1213. }
  1214.  
  1215. void onObjectHit(jjOBJ@ obj, jjOBJ@ bullet, jjPLAYER@ player, int force) {
  1216.         CUSTOM::objects[obj.objectID].onObjectHit(bullet,player,force);
  1217.         if(onObjectHitHook !is null) onObjectHitHook(obj,bullet,player,force);
  1218. }
  1219.  
  1220. namespace INTERPOLATION {
  1221.         interface Interpolator {
  1222.                 float value(int time);
  1223.         }
  1224.        
  1225.         interface PathInterpolator : Interpolator {
  1226.                 void  reset(float start, float end, int duration);
  1227.         }
  1228.  
  1229.         class Linear : PathInterpolator {
  1230.                 float start, end;
  1231.                 int duration;
  1232.                
  1233.                 Linear(float start, float end, int duration) {
  1234.                         reset(start,end,duration);
  1235.                 }
  1236.                
  1237.                 void reset(float start, float end, int duration) {
  1238.                         this.start = start;
  1239.                         this.end = end;
  1240.                         this.duration = duration;
  1241.                 }
  1242.                
  1243.                 float value(int time) {
  1244.                         return ((end-start)/duration)*time + start;
  1245.                 }
  1246.         }
  1247.        
  1248.         class Exponential : PathInterpolator {
  1249.                 float start, end, base;
  1250.                 int duration;
  1251.                
  1252.                 Exponential(float start, float end, float base, int duration) {
  1253.                         this.base = base;
  1254.                         reset(start,end,duration);
  1255.                 }
  1256.                
  1257.                 void reset(float start, float end, int duration) {
  1258.                         this.start = start;
  1259.                         this.end = end;
  1260.                         this.duration = duration;
  1261.                 }
  1262.                
  1263.                 float logb(float x,float b){
  1264.                   return log(x)/log(b);
  1265.                 }
  1266.                
  1267.                 float value(int time) {
  1268.                         //Same as this equation, but uses logs to avoid underflow when dividing by pow
  1269.                         //return ((end-start)/pow(base,duration))*pow(base,time) + start;
  1270.                         return pow(base,logb(end-start,base)-duration+time)+start;
  1271.                 }
  1272.         }
  1273.        
  1274.         class Logarithmic : PathInterpolator {
  1275.                 float start, end, base;
  1276.                 int duration;
  1277.                
  1278.                 Logarithmic(float start, float end, float base, int duration) {
  1279.                         this.base = base;
  1280.                         reset(start,end,duration);
  1281.                 }
  1282.                
  1283.                 void reset(float start, float end, int duration) {
  1284.                         this.start = start;
  1285.                         this.end = end;
  1286.                         this.duration = duration;
  1287.                 }
  1288.                
  1289.                 float logb(float x,float b){
  1290.                   return log(x)/log(b);
  1291.                 }
  1292.                
  1293.                 float value(int time) {
  1294.                         return ((end-start)/logb(duration,base))*logb(time,base) + start;
  1295.                 }
  1296.         }
  1297.        
  1298.         class Sinusoidal : Interpolator {
  1299.                 float frequency, amplitude, offset;
  1300.                 int duration;
  1301.                
  1302.                 Sinusoidal(float frequency, float amplitude, float offset, int duration) {
  1303.                         reset(frequency,amplitude,offset,duration);
  1304.                 }
  1305.                
  1306.                 void reset(float frequency, float amplitude, float offset, int duration) {
  1307.                         this.frequency = frequency;
  1308.                         this.amplitude = amplitude;
  1309.                         this.offset = offset;
  1310.                         this.duration = duration;
  1311.                 }
  1312.                
  1313.                 float value(int time) {
  1314.                         return amplitude*sin(time*frequency*PI/duration + offset);
  1315.                 }
  1316.         }
  1317.        
  1318.         class LinePlusSine : PathInterpolator {
  1319.                 Linear@ line;
  1320.                 Sinusoidal@ sine;
  1321.                
  1322.                 LinePlusSine(float start, float end, int duration, float frequency, float amplitude, float offset) {
  1323.                         @line = @Linear(start, end, duration);
  1324.                         @sine = @Sinusoidal(frequency,amplitude,offset,duration);
  1325.                 }
  1326.                
  1327.                 void reset(float start,float end,int duration) {
  1328.                         line.reset(start, end, duration);
  1329.                         sine.duration = duration;
  1330.                 }
  1331.                
  1332.                 float value(int time) {
  1333.                         return line.value(time) + sine.value(time);
  1334.                 }
  1335.         }
  1336.        
  1337.         funcdef float SELECTOR(COMMON::IFocalPoint@);
  1338.        
  1339.         float getX(COMMON::IFocalPoint@ focus) {return focus.xPos;}
  1340.         float getY(COMMON::IFocalPoint@ focus) {return focus.yPos;}
  1341.        
  1342.         class Point : Interpolator {
  1343.                 COMMON::IFocalPoint@ focus;
  1344.                 SELECTOR@ selector;
  1345.                
  1346.                 Point(COMMON::IFocalPoint@ focus, SELECTOR@ selector) {
  1347.                         @this.focus = @focus;
  1348.                         @this.selector = @selector;
  1349.                 }
  1350.                
  1351.                 float value(int time) {
  1352.                         return selector(focus);
  1353.                 }
  1354.         }
  1355.        
  1356.         class PointPlusSine : Interpolator {
  1357.                 COMMON::IFocalPoint@ focus;
  1358.                 SELECTOR@ selector;
  1359.                 Sinusoidal@ sine;
  1360.                
  1361.                 PointPlusSine(COMMON::IFocalPoint@ focus, SELECTOR@ selector, float frequency, float amplitude, float offset, int duration) {
  1362.                         @sine = @Sinusoidal(frequency,amplitude,offset,duration);
  1363.                         @this.selector = @selector;
  1364.                         @this.focus = @focus;
  1365.                 }
  1366.                
  1367.                 void reset(float frequency, float amplitude, float offset, int duration) {
  1368.                         sine.reset(frequency,amplitude,offset,duration);
  1369.                 }
  1370.                
  1371.                 float value(int time) {
  1372.                         return selector(focus) + sine.value(time);
  1373.                 }
  1374.         }
  1375. }
  1376.  
  1377. namespace MIXIN {
  1378.         interface IMovable {
  1379.                 void moveLinear(float xStart, float yStart, float xEnd, float yEnd, int duration, bool cycle = false);
  1380.                 void moveLinear(float xEnd, float yEnd, int duration, bool cycle = false);
  1381.                 void moveAccelerate(float xStart, float yStart, float xEnd, float yEnd, int duration, float acceleration, bool cycle = false);
  1382.                 void moveAccelerate(float xEnd, float yEnd, int duration, float acceleration, bool cycle = false);
  1383.                 void moveDeccelerate(float xStart, float yStart, float xEnd, float yEnd, int duration, float decceleration, bool cycle = false);
  1384.                 void moveDeccelerate(float xEnd, float yEnd, int duration, float decceleration, bool cycle = false);
  1385.                 void moveSinusoidal(float xStart, float yStart, float xEnd, float yEnd, int duration, float frequency, float amplitude, float offset, bool cycle = false);
  1386.                 void moveSinusoisal(float xEnd, float yEnd, int duration, float frequency, float amplitude, float offset, bool cycle = false);
  1387.                 void moveSpiral(float xStart, float yStart, float xEnd, float yEnd, int duration, float frequency, float amplitude, float offset, bool cycle = false);
  1388.                 void moveSpiral(float xEnd, float yEnd, int duration, float frequency, float amplitude, float offset, bool cycle = false);
  1389.                 void moveRevolve(COMMON::IFocalPoint@ focus, int duration, float radius, float offset, bool clockwise, bool cycle = false);
  1390.                 void moveOscillate(COMMON::IFocalPoint@ focus, int duration, float xDist, float yDist, float frequency, float offset, bool cycle = false);
  1391.                 void moveFollow(COMMON::IFocalPoint@ focus);
  1392.                 void moveInterpolator(INTERPOLATION::Interpolator@ xInterpolator, INTERPOLATION::Interpolator@ yInterpolator, int duration, bool cycle = false);
  1393.                 bool move();
  1394.                 bool isMoving();
  1395.                 void stopMove();
  1396.         }
  1397.        
  1398.         mixin class Movable : IMovable {
  1399.                 INTERPOLATION::Interpolator@ xInterpolatorMove;
  1400.                 INTERPOLATION::Interpolator@ yInterpolatorMove;
  1401.                 COMMON::Timer moveTimer();
  1402.                 bool moveCycle;
  1403.        
  1404.                 void moveLinear(float xStart, float yStart, float xEnd, float yEnd, int duration, bool cycle = false) {
  1405.                         moveInterpolator(INTERPOLATION::Linear(xStart,xEnd,duration),
  1406.                                                          INTERPOLATION::Linear(yStart,yEnd,duration),
  1407.                                                          duration,
  1408.                                                          cycle);
  1409.                        
  1410.                 }
  1411.                
  1412.                 void moveLinear(float xEnd, float yEnd, int duration, bool cycle = false) {
  1413.                         moveLinear(xPos,yPos,xEnd,yEnd,duration,cycle);
  1414.                 }
  1415.                
  1416.                 void moveAccelerate(float xStart, float yStart, float xEnd, float yEnd, int duration, float acceleration, bool cycle = false) {
  1417.                         moveInterpolator(INTERPOLATION::Exponential(xStart,xEnd,acceleration,duration),
  1418.                                                          INTERPOLATION::Exponential(yStart,yEnd,acceleration,duration),
  1419.                                                          duration,
  1420.                                                          cycle);
  1421.                 }
  1422.                
  1423.                 void moveAccelerate(float xEnd, float yEnd, int duration, float acceleration, bool cycle = false) {
  1424.                         moveAccelerate(xPos, yPos, xEnd, yEnd, duration, acceleration, cycle);
  1425.                 }
  1426.                
  1427.                 void moveDeccelerate(float xStart, float yStart, float xEnd, float yEnd, int duration, float decceleration, bool cycle = false) {
  1428.                         moveInterpolator(INTERPOLATION::Logarithmic(xStart,xEnd,decceleration,duration),
  1429.                                                          INTERPOLATION::Logarithmic(yStart,yEnd,decceleration,duration),
  1430.                                                          duration,
  1431.                                                          cycle);
  1432.                 }
  1433.                
  1434.                 void moveDeccelerate(float xEnd, float yEnd, int duration, float decceleration, bool cycle = false) {
  1435.                         moveDeccelerate(xPos, yPos, xEnd, yEnd, duration, decceleration, cycle);
  1436.                 }
  1437.                
  1438.                 void moveSinusoidal(float xStart, float yStart, float xEnd, float yEnd, int duration, float frequency, float amplitude, float offset, bool cycle = false) {
  1439.                         moveInterpolator(INTERPOLATION::LinePlusSine(xStart,xEnd,duration,frequency,amplitude,offset),
  1440.                                                          INTERPOLATION::LinePlusSine(yStart,yEnd,duration,frequency,amplitude,offset),
  1441.                                                          duration,
  1442.                                                          cycle);
  1443.                 }
  1444.                
  1445.                 void moveSinusoisal(float xEnd, float yEnd, int duration, float frequency, float amplitude, float offset, bool cycle = false) {
  1446.                         moveSinusoidal(xPos,yPos,xEnd,yEnd,duration,frequency,amplitude,offset,cycle);
  1447.                 }
  1448.                
  1449.                 void moveSpiral(float xStart, float yStart, float xEnd, float yEnd, int duration, float frequency, float amplitude, float offset, bool cycle = false) {
  1450.                         moveInterpolator(INTERPOLATION::LinePlusSine(xStart,xEnd,duration,frequency,amplitude,offset),    //sine
  1451.                                                          INTERPOLATION::LinePlusSine(yStart,yEnd,duration,frequency,amplitude,offset+PI), //cosine
  1452.                                                          duration,
  1453.                                                          cycle);
  1454.                 }
  1455.                
  1456.                 void moveSpiral(float xEnd, float yEnd, int duration, float frequency, float amplitude, float offset, bool cycle = false) {
  1457.                         moveSpiral(xPos,yPos,xEnd,yEnd,duration,frequency,amplitude,offset,cycle);
  1458.                 }
  1459.                
  1460.                 void moveRevolve(COMMON::IFocalPoint@ focus, int duration, float radius, float offset, bool clockwise, bool cycle = false) {
  1461.                         int frequency = 2;
  1462.                         if(clockwise) frequency *= -1;
  1463.                         moveInterpolator(INTERPOLATION::PointPlusSine(focus,INTERPOLATION::getX,frequency,radius,offset,duration),
  1464.                                                          INTERPOLATION::PointPlusSine(focus,INTERPOLATION::getY,frequency,radius,offset+HALF_PI,duration),
  1465.                                                          duration,
  1466.                                                          cycle);
  1467.                 }
  1468.                
  1469.                 void moveOscillate(COMMON::IFocalPoint@ focus, int duration, float xDist, float yDist, float frequency, float offset, bool cycle = false) {
  1470.                         moveInterpolator(INTERPOLATION::PointPlusSine(focus,INTERPOLATION::getX,frequency,xDist,offset,duration),
  1471.                                                          INTERPOLATION::PointPlusSine(focus,INTERPOLATION::getY,frequency,yDist,offset,duration),
  1472.                                                          duration,
  1473.                                                          cycle);
  1474.                 }
  1475.                
  1476.                 void moveFollow(COMMON::IFocalPoint@ focus) {
  1477.                         moveInterpolator(INTERPOLATION::Point(focus,INTERPOLATION::getX),
  1478.                                                          INTERPOLATION::Point(focus,INTERPOLATION::getY),
  1479.                                                          -1,
  1480.                                                          true);
  1481.                 }
  1482.                
  1483.                 void moveInterpolator(INTERPOLATION::Interpolator@ xInterpolatorMove, INTERPOLATION::Interpolator@ yInterpolatorMove, int duration, bool cycle = false) {
  1484.                         moveTimer.start(duration);
  1485.                         @this.xInterpolatorMove = @xInterpolatorMove;
  1486.                         @this.yInterpolatorMove = @yInterpolatorMove;
  1487.                         moveCycle = cycle;
  1488.                 }
  1489.                
  1490.                 bool move() {
  1491.                         if(isActive && moveTimer.isStarted())
  1492.                                 if(moveTimer.elapsedTime() <= moveTimer.interval() || moveTimer.interval() == -1) {
  1493.                                         this.xPos = xInterpolatorMove.value(moveTimer.elapsedTime());
  1494.                                         this.yPos = yInterpolatorMove.value(moveTimer.elapsedTime());
  1495.                                         return true;
  1496.                                 } else if(moveCycle && moveTimer.elapsedTime() > moveTimer.interval()) {
  1497.                                         moveTimer.reset();
  1498.                                         this.xPos = xInterpolatorMove.value(moveTimer.elapsedTime());
  1499.                                         this.yPos = yInterpolatorMove.value(moveTimer.elapsedTime());
  1500.                                         return true;
  1501.                                 }
  1502.                         return false;
  1503.                 }
  1504.                
  1505.                 bool isMoving() {
  1506.                         return moveTimer.isStarted() && !moveTimer.isFinished();
  1507.                 }
  1508.                
  1509.                 void stopMove() {
  1510.                         moveTimer.stop();
  1511.                 }
  1512.         }
  1513.        
  1514.         interface IScalable {
  1515.                 void scaleLinear(float scaleInit, float scaleEnd, int duration, bool cycle = false);
  1516.                 void scaleAccelerate(float scaleInit, float scaleEnd, int duration, float acceleration, bool cycle = false);
  1517.                 void scaleDeccelerate(float scaleInit, float scaleEnd, int duration, float decceleration, bool cycle = false);
  1518.                 void scaleOscillate(float xMin, float xMax, float yMin, float yMax, int duration, float frequency, int offset, bool cycle = false);
  1519.                 void scaleInterpolator(INTERPOLATION::Interpolator@ xInterpolator, INTERPOLATION::Interpolator@ yInterpolator, int duration, bool cycle = false);
  1520.                 bool scale();
  1521.                 bool isScaling();
  1522.                 void stopScaling();
  1523.         }
  1524.        
  1525.         mixin class Scalable : IScalable {
  1526.                 INTERPOLATION::Interpolator@ xInterpolatorScale;
  1527.                 INTERPOLATION::Interpolator@ yInterpolatorScale;
  1528.                 COMMON::Timer scaleTimer();
  1529.                 bool scaleCycle;
  1530.        
  1531.                 void scaleLinear(float scaleStart, float scaleEnd, int duration, bool cycle = false) {
  1532.                         scaleInterpolator(INTERPOLATION::Linear(scaleStart,scaleEnd,duration),
  1533.                                                           INTERPOLATION::Linear(scaleStart,scaleEnd,duration),
  1534.                                                           duration,
  1535.                                                           cycle);
  1536.  
  1537.                 }
  1538.                
  1539.                 void scaleAccelerate(float scaleStart, float scaleEnd, int duration, float acceleration, bool cycle = false) {
  1540.                         scaleInterpolator(INTERPOLATION::Exponential(scaleStart,scaleEnd,acceleration,duration),
  1541.                                                           INTERPOLATION::Exponential(scaleStart,scaleEnd,acceleration,duration),
  1542.                                                           duration,
  1543.                                                           cycle);
  1544.                 }
  1545.                
  1546.                 void scaleDeccelerate(float scaleStart, float scaleEnd, int duration, float decceleration, bool cycle = false) {
  1547.                         scaleInterpolator(INTERPOLATION::Logarithmic(scaleStart,scaleEnd,decceleration,duration),
  1548.                                                           INTERPOLATION::Logarithmic(scaleStart,scaleEnd,decceleration,duration),
  1549.                                                           duration,
  1550.                                                           cycle);
  1551.                 }
  1552.                
  1553.                 void scaleOscillate(float xMin, float xMax, float yMin, float yMax, int duration, float frequency, int offset, bool cycle = false) {
  1554.                         float xDist = (xMax-xMin)/2;
  1555.                         float xMid  = xMin + xDist;
  1556.                         float yDist = (yMax-yMin)/2;
  1557.                         float yMid  = yMin + yDist;
  1558.                         scaleInterpolator(INTERPOLATION::PointPlusSine(COMMON::Position(xMid,yMid),INTERPOLATION::getX,frequency,xDist,offset,duration),
  1559.                                                           INTERPOLATION::PointPlusSine(COMMON::Position(xMid,yMid),INTERPOLATION::getY,frequency,yDist,offset,duration),
  1560.                                                           duration,
  1561.                                                           cycle);
  1562.                 }
  1563.                
  1564.                 void scaleInterpolator(INTERPOLATION::Interpolator@ xInterpolator, INTERPOLATION::Interpolator@ yInterpolator, int duration, bool cycle = false) {
  1565.                         scaleTimer.start(duration);
  1566.                         @this.xInterpolatorScale = @xInterpolator;
  1567.                         @this.yInterpolatorScale = @yInterpolator;
  1568.                         scaleCycle = cycle;
  1569.                 }
  1570.                
  1571.                 bool scale() {
  1572.                         if(isActive && scaleTimer.isStarted())
  1573.                                 if(scaleTimer.elapsedTime() <= scaleTimer.interval() || scaleTimer.interval() == -1) {
  1574.                                         this.spriteScaleX = xInterpolatorScale.value(scaleTimer.elapsedTime());
  1575.                                         this.spriteScaleY = yInterpolatorScale.value(scaleTimer.elapsedTime());
  1576.                                         return true;
  1577.                                 } else if(scaleCycle && scaleTimer.elapsedTime() > scaleTimer.interval()) {
  1578.                                         scaleTimer.reset();
  1579.                                         this.spriteScaleX = xInterpolatorScale.value(scaleTimer.elapsedTime());
  1580.                                         this.spriteScaleY = yInterpolatorScale.value(scaleTimer.elapsedTime());
  1581.                                         return true;
  1582.                                 }
  1583.                         return false;
  1584.                 }
  1585.                
  1586.                 bool isScaling() {
  1587.                         return scaleTimer.isStarted() && !scaleTimer.isFinished();
  1588.                 }
  1589.                
  1590.                 void stopScaling() {
  1591.                         scaleTimer.stop();
  1592.                 }
  1593.         }
  1594.        
  1595.         interface IRotatable {
  1596.                 void rotate(float numRotations, int duration, int offset, bool clockwise, bool cycle = false);
  1597.                 bool rotate();
  1598.                 bool isRotating();
  1599.                 void stopRotating();
  1600.         }
  1601.        
  1602.         mixin class Rotatable : IRotatable {
  1603.                 INTERPOLATION::Interpolator@ interpolatorRotate;
  1604.                 COMMON::Timer rotateTimer();
  1605.                 bool rotateCycle;
  1606.        
  1607.                 void rotate(float numRotations, int duration, int offset, bool clockwise, bool cycle = false) {
  1608.                         float start, end, point = 1024*numRotations+offset;
  1609.                         if(clockwise) {
  1610.                                 start = offset;
  1611.                                 end = point;
  1612.                         } else {
  1613.                                 start = point;
  1614.                                 end = offset;
  1615.                         }
  1616.                        
  1617.                         rotateInterpolator(INTERPOLATION::Linear(start,end,duration),
  1618.                                                            duration,
  1619.                                                            cycle);
  1620.                 }
  1621.                
  1622.                 void rotateInterpolator(INTERPOLATION::Interpolator@ interpolator, int duration, bool cycle = false) {
  1623.                         rotateTimer.start(duration);
  1624.                         @this.interpolatorRotate = interpolator;
  1625.                         rotateCycle = cycle;
  1626.                 }
  1627.                
  1628.                 bool rotate() {
  1629.                         if(isActive && rotateTimer.isStarted())
  1630.                                 if(rotateTimer.elapsedTime() <= rotateTimer.interval() || rotateTimer.interval() == -1) {
  1631.                                         this.spriteAngle = int(interpolatorRotate.value(rotateTimer.elapsedTime()));
  1632.                                         return true;
  1633.                                 } else if(rotateCycle && rotateTimer.elapsedTime() > rotateTimer.interval()) {
  1634.                                         rotateTimer.reset();
  1635.                                         this.spriteAngle = int(interpolatorRotate.value(rotateTimer.elapsedTime()));
  1636.                                         return true;
  1637.                                 }
  1638.                         return false;
  1639.                 }
  1640.                
  1641.                 bool isRotating() {
  1642.                         return rotateTimer.isStarted() && !rotateTimer.isFinished();
  1643.                 }
  1644.                
  1645.                 void stopRotating() {
  1646.                         rotateTimer.stop();
  1647.                 }
  1648.         }
  1649.        
  1650.         interface IColorable {
  1651.                 void tintLinear(int startTint, int endTint, int duration, bool cycle = false);
  1652.                 void tintOscillate(float min, float max, int duration, float frequency, int offset, bool cycle = false);
  1653.                 void palshiftLinear(int startTint, int endTint, int duration, bool cycle = false);
  1654.                 void palshiftOscillate(float min, float max, int duration, float frequency, int offset, bool cycle = false);
  1655.                 bool color();
  1656.                 bool isColoring();
  1657.                 void stopColoring();
  1658.         }
  1659.        
  1660.         mixin class Colorable : IColorable {
  1661.                 INTERPOLATION::Interpolator@ interpolatorColor;
  1662.                 COMMON::Timer colorTimer();
  1663.                 bool colorCycle;
  1664.                
  1665.                 void tintLinear(int startTint, int endTint, int duration, bool cycle = false) {
  1666.                         spriteMode = SPRITE::TINTED;
  1667.                         colorInterpolator(INTERPOLATION::Linear(startTint,endTint,duration),
  1668.                                                           duration,
  1669.                                                           cycle);
  1670.                 }
  1671.                
  1672.                 void tintOscillate(float min, float max, int duration, float frequency, int offset, bool cycle = false) {
  1673.                         float halfDist = (max-min)/2;
  1674.                         float mid  = min + halfDist;
  1675.                         spriteMode = SPRITE::TINTED;
  1676.                         colorInterpolator(INTERPOLATION::PointPlusSine(COMMON::Position(mid,mid),INTERPOLATION::getX,frequency,halfDist,offset,duration),
  1677.                                                           duration,
  1678.                                                           cycle);
  1679.                 }
  1680.                
  1681.                 void palshiftLinear(int startTint, int endTint, int duration, bool cycle = false) {
  1682.                         spriteMode = SPRITE::PALSHIFT;
  1683.                         colorInterpolator(INTERPOLATION::Linear(startTint,endTint,duration),
  1684.                                                           duration,
  1685.                                                           cycle);
  1686.                 }
  1687.                
  1688.                 void palshiftOscillate(float min, float max, int duration, float frequency, int offset, bool cycle = false) {
  1689.                         float dist = (min-max)/2;
  1690.                         float mid  = min + dist;
  1691.                         spriteMode = SPRITE::PALSHIFT;
  1692.                         colorInterpolator(INTERPOLATION::PointPlusSine(COMMON::Position(mid,mid),INTERPOLATION::getX,frequency,dist,offset,duration),
  1693.                                                           duration,
  1694.                                                           cycle);
  1695.                 }
  1696.                
  1697.                 void colorInterpolator(INTERPOLATION::Interpolator@ interpolator, int duration, bool cycle = false) {
  1698.                         colorTimer.start(duration);
  1699.                         @this.interpolatorColor = interpolator;
  1700.                         colorCycle = cycle;
  1701.                 }
  1702.                
  1703.                 bool color() {
  1704.                         if(isActive && colorTimer.isStarted()) {
  1705.                                 if(colorTimer.elapsedTime() <= colorTimer.interval() || colorTimer.interval() == -1) {
  1706.                                         this.spriteParam = int(interpolatorColor.value(colorTimer.elapsedTime()));
  1707.                                         return true;
  1708.                                 } else if(colorCycle && colorTimer.elapsedTime() > colorTimer.interval()) {
  1709.                                         colorTimer.reset();
  1710.                                         this.spriteParam = int(interpolatorColor.value(colorTimer.elapsedTime()));
  1711.                                         return true;
  1712.                                 }
  1713.                         }
  1714.                         return false;
  1715.                 }
  1716.                
  1717.                 bool isColoring() {
  1718.                         return colorTimer.isStarted() && !colorTimer.isFinished();
  1719.                 }
  1720.                
  1721.                 void stopColoring() {
  1722.                         colorTimer.stop();
  1723.                 }
  1724.         }
  1725.        
  1726.         interface IPatrollable {
  1727.                 void patrolLinear(array<float>@ pathX, array<float>@ pathY, int duration, bool cycle = true);
  1728.                 //void patrolSinusoidal(array<float>@ pathX, array<float>@ pathY, int duration, bool cycle = true,float frequency = 1, float amplitude=32, float offset=0);
  1729.                 void patrolInterpolator(array<float>@ pathX, array<float>@ pathY, INTERPOLATION::PathInterpolator@ xInterpolator, INTERPOLATION::PathInterpolator@ yInterpolator, int duration, bool cycle = true);
  1730.                 bool patrol();
  1731.                 bool isPatrolling();
  1732.         }
  1733.        
  1734.         mixin class Patrollable : IPatrollable, IMovable {
  1735.                 array<float>@ pathX;
  1736.                 array<float>@ pathY;
  1737.                 int duration;
  1738.                 bool pathCycle = false;
  1739.                 float pathTotalDist;
  1740.                 float percentDist;
  1741.                 int portionDuration;
  1742.                 int pathIndex;
  1743.                 COMMON::Timer patrolTimer();
  1744.                 INTERPOLATION::PathInterpolator@ xInterpolator;
  1745.                 INTERPOLATION::PathInterpolator@ yInterpolator;
  1746.                
  1747.                 void patrolLinear(array<float>@ pathX, array<float>@ pathY, int duration, bool cycle = true) {
  1748.                         patrolInterpolator(pathX,
  1749.                                                            pathY,
  1750.                                                            INTERPOLATION::Linear(pathX[0],pathX[1],portionDuration),
  1751.                                                            INTERPOLATION::Linear(pathY[0],pathY[1],portionDuration),
  1752.                                                            duration,
  1753.                                                            cycle);
  1754.                 }
  1755.                
  1756.                 /*void patrolSinusoidal(array<float>@ pathX, array<float>@ pathY, int duration, bool cycle = true,float frequency = 1, float amplitude=32, float offset=0) {
  1757.                         initPatrol(pathX,pathY,duration,cycle);
  1758.                         @this.xInterpolator = INTERPOLATION::LinePlusSine(pathX[0],pathX[1],duration,frequency,amplitude,offset);
  1759.                         @this.yInterpolator = INTERPOLATION::LinePlusSine(pathY[0],pathY[1],duration,frequency,amplitude,offset);
  1760.                         moveInterpolator(xInterpolator,yInterpolator,portionDuration);
  1761.                 }*/
  1762.                
  1763.                 void patrolInterpolator(array<float>@ pathX, array<float>@ pathY, INTERPOLATION::PathInterpolator@ xInterpolator, INTERPOLATION::PathInterpolator@ yInterpolator, int duration, bool cycle = true) {
  1764.                         initPatrol(pathX,pathY,duration,cycle);
  1765.                         @this.xInterpolator = @xInterpolator;
  1766.                         @this.yInterpolator = @yInterpolator;
  1767.                         moveInterpolator(xInterpolator,yInterpolator,portionDuration);
  1768.                 }
  1769.                
  1770.                 void initPatrol(array<float>@ pathX, array<float>@ pathY, int duration, bool cycle = true) {
  1771.                         this.duration = duration;
  1772.                         @this.pathX = @pathX;
  1773.                         @this.pathY = @pathY;
  1774.                         this.pathCycle = cycle;
  1775.                        
  1776.                         uint i = 0;
  1777.                         pathTotalDist = 0;
  1778.                         for(; i < pathX.length-1; i++) pathTotalDist += distance(pathX[i],pathY[i],pathX[i+1],pathY[i+1]);;
  1779.                         if(pathCycle) pathTotalDist += distance(pathX[i],pathY[i],pathX[0],pathY[0]);
  1780.                         this.pathIndex = 1;
  1781.                         percentDist = distance(pathX[0],pathY[0],pathX[1],pathY[1]) / pathTotalDist;
  1782.                        
  1783.                         patrolTimer.start(duration);
  1784.                         portionDuration = int(percentDist * duration);
  1785.                 }
  1786.                
  1787.                 bool patrol() {
  1788.                         if(isActive && isPatrolling()) {
  1789.                                 if(!move()) {
  1790.                                         int oldIndex = pathIndex;
  1791.                                         pathIndex = (pathIndex + 1) % pathX.length();
  1792.                                         percentDist = distance(pathX[oldIndex],pathY[oldIndex],pathX[pathIndex],pathY[pathIndex]) / pathTotalDist;
  1793.                                         portionDuration = int(percentDist * duration);
  1794.                                         if(!pathCycle && pathIndex == 0) return true;
  1795.                                         xInterpolator.reset(pathX[oldIndex],pathX[pathIndex],portionDuration);
  1796.                                         yInterpolator.reset(pathY[oldIndex],pathY[pathIndex],portionDuration);
  1797.                                         moveInterpolator(xInterpolator,yInterpolator,portionDuration);
  1798.                                 }
  1799.                                 return true;
  1800.                         }
  1801.                         return false;
  1802.                 }
  1803.                
  1804.                 bool isPatrolling() {
  1805.                         return pathCycle || (patrolTimer.isStarted() && !patrolTimer.isFinished());
  1806.                 }
  1807.         }
  1808.        
  1809.         interface IWarpable {
  1810.                 void warpIn(int warpColor = 90);
  1811.                 void warpOut(int warpColor = 90);
  1812.                 void warpTo(float xEnd, float yEnd, int warpColor = 90);
  1813.                 bool warp();
  1814.                 bool isWarping();
  1815.                 bool isWarpingIn();
  1816.                 bool isWarpingOut();
  1817.                 bool isWarpedOut();
  1818.         }
  1819.  
  1820.         mixin class Warpable : IWarpable {
  1821.                 COMMON::Timer warpTimer();
  1822.                 bool warpInOrOut; //true=in,false=out
  1823.                 int  warpAnim;
  1824.                 int  warpFrameStart;
  1825.                 int  warpColor;
  1826.                 bool warpedOut = false;
  1827.                 bool warpingTo = false;
  1828.                 HANDLING::Bullet warpPrevBulletHandling;
  1829.                 HANDLING::Player warpPrevPlayerHandling;
  1830.                 float warpTargetX, warpTargetY;
  1831.                
  1832.                 int WARP_FRAMES = 25;
  1833.                
  1834.                 void warpIn(int warpColor = 90) {
  1835.                         this.warpInOrOut = true;
  1836.                         this.warpColor = warpColor;
  1837.                         this.initWarp(this.warpColor, SOUND::COMMON_TELPORT2,76,0);
  1838.                 }
  1839.                
  1840.                 void warpOut(int warpColor = 90) {
  1841.                         this.warpInOrOut = false;
  1842.                         this.warpColor = warpColor;
  1843.                         this.initWarp(this.warpColor, SOUND::COMMON_TELPORT1,75,2);
  1844.                 }
  1845.                
  1846.                 void warpTo(float xEnd, float yEnd, int warpColor = 90) {
  1847.                         this.warpingTo = true;
  1848.                         this.warpTargetX = xEnd;
  1849.                         this.warpTargetY = yEnd;
  1850.                         this.warpOut(warpColor);
  1851.                 }
  1852.                
  1853.                 void initWarp(int warpColor, SOUND::Sample sound,int anim,int frameStart) {
  1854.                         this.stopWarpingHelper();
  1855.                         this.warpAnim = anim;
  1856.                         this.warpFrameStart = frameStart;
  1857.                         this.warpColor = warpColor;
  1858.                         if(!warpingTo && warpInOrOut) {
  1859.                                 /*this.warpPrevBulletHandling = this.bulletHandling;
  1860.                                 this.warpPrevPlayerHandling = this.playerHandling;
  1861.                                 this.bulletHandling = HANDLING::IGNOREBULLET;
  1862.                                 this.playerHandling = HANDLING::SPECIAL;*/
  1863.                         }
  1864.                         this.warpedOut = !this.warpInOrOut;
  1865.                         warpTimer.start(WARP_FRAMES);
  1866.                         jjSample(xPos, yPos, sound);
  1867.                 }
  1868.                
  1869.                 bool warp() {
  1870.                         if(isActive && warpTimer.isStarted()) {
  1871.                                 if(warpTimer.elapsedTime() < WARP_FRAMES) {
  1872.                                         jjDrawSprite(xPos, yPos, ANIM::SPAZ,this.warpAnim, this.warpFrameStart+(warpTimer.elapsedTime()/5), this.direction, SPRITE::SINGLECOLOR, this.warpColor);
  1873.                                         return true;
  1874.                                 } else if(warpTimer.elapsedTime() == WARP_FRAMES) {
  1875.                                         if(warpingTo && !warpInOrOut) {
  1876.                                                 xPos = warpTargetX;
  1877.                                                 yPos = warpTargetY;
  1878.                                                 warpIn(warpColor);
  1879.                                                 return true;
  1880.                                         } else {
  1881.                                                 stopWarping();
  1882.                                                 return false;
  1883.                                         }
  1884.                                 }
  1885.                         }
  1886.                         return false;
  1887.                 }
  1888.                
  1889.                 void stopWarpingHelper() {
  1890.                         if(warpTimer.elapsedTime() <= WARP_FRAMES) {
  1891.                                 /*this.bulletHandling = this.warpPrevBulletHandling;
  1892.                                 this.playerHandling = this.warpPrevPlayerHandling;*/
  1893.                                 this.warpTimer.stop();
  1894.                         }
  1895.                 }
  1896.                
  1897.                 void stopWarping() {
  1898.                         if(warpTimer.elapsedTime() <= WARP_FRAMES) {
  1899.                                 this.warpingTo = false;
  1900.                         }
  1901.                         stopWarpingHelper();
  1902.                 }
  1903.                
  1904.                 bool isWarping() {
  1905.                         return warpTimer.isStarted() && !warpTimer.isFinished();
  1906.                 }
  1907.                
  1908.                 bool isWarpingIn() {
  1909.                         return this.isWarping(