Name | Author | Game Mode | Rating | |||||
---|---|---|---|---|---|---|---|---|
Devan's Revenge (Intro) | sAlAmAnDeR | Single player | 9.6 |
//SaLLiB v2.0
//By sAlAmAnDeR
#pragma require "sepalwiz-1.1.asc"
#include "sepalwiz-1.1.asc"
//An unused object constant, to mark custom stuff
OBJECT::Object NIL = OBJECT::AMBIENTSOUND;
//BEHAVIOR::INACTIVE actually calls the objects behavior function, but BEHAVIOR::BEES actually does nothing.
jjBEHAVIOR DO_NOTHING = BEHAVIOR::BEES;
float PI = 3.1416;
float HALF_PI = 0.5*PI;
float ONE_AND_HALF_PI = 1.5*PI;
float TWO_PI = 2*PI;
float E = 2.71828;
float distance(float xStart, float yStart, float xEnd, float yEnd) {return sqrt((xEnd - xStart)*(xEnd - xStart) + (yEnd - yStart)*(yEnd - yStart));}
float RAND_MAX = 4294967295;
float random(float a, float b) {
float random = jjRandom() / RAND_MAX;
float diff = b - a;
float r = random * diff;
return a + r;
}
funcdef void OBJECTHITHOOK(jjOBJ@ obj, jjOBJ@ bullet, jjPLAYER@ player, int force);
funcdef void CANVASHOOKVOID(jjPLAYER@ player, jjCANVAS@ canvas);
OBJECTHITHOOK@ onObjectHitHook = null;
CANVASHOOKVOID@ onDrawAmmoHook = null;
CANVASHOOKVOID@ onDrawHealthHook = null;
CANVASHOOKVOID@ onDrawLivesHook = null;
CANVASHOOKVOID@ onDrawScoreHook = null;
CANVASHOOKVOID@ onDrawPlayerTimerHook = null;
string chr(uint8 value) {
string str="\0";
str[0]=value;
return str;
}
string stripPipes(string str) {
string result = "";
for(uint i = 0; i < str.length(); i++)
if(str[i] != 124)
result += chr(str[i]);
return result;
}
namespace COORDINATES {
enum CoordinateType {
LAYER = 0,
SCREEN = 1
}
}
namespace COMMON {
interface IFocalPoint {
float get_xPos() const;
float get_yPos() const;
bool get_isActive() const;
}
class Position : IFocalPoint {
float x, y;
Position(float xPos, float yPos) {this.x = xPos;
this.y = yPos;}
float get_xPos() const {return this.x;}
float get_yPos() const {return this.y;}
void set_xPos(float xPos) {this.x=xPos;}
void set_yPos(float yPos) {this.y=yPos;}
bool get_isActive() const {return true;}
}
//TODO: Maybe set a variable when gameTicks == 0 to indicate timers are safe to use?
funcdef int TIME_UNIT();
int jjGameTicks() { return ::jjGameTicks; }
int jjRenderFrame() { return ::jjRenderFrame; }
class Timer {
int inter, startTime, end;
bool running;
TIME_UNIT@ timeUnit = COMMON::jjGameTicks;
Timer() {
this.running = false;
}
Timer start(int interval) {
if(interval < 0)
return start();
this.inter = interval;
this.startTime = timeUnit();
this.end = this.startTime + this.inter;
this.running = true;
return this;
}
Timer start() {
this.startTime = timeUnit();
this.running = true;
this.end = -1;
this.inter = -1;
return this;
}
Timer stop() {
this.running = false;
return this;
}
bool isFinished() const {
if(!this.running) return false;
if(this.end == -1) return false;
return timeUnit() >= this.end;
}
int elapsedTime() const {
return timeUnit()-this.startTime;
}
int endTime() const {
return end;
}
int remainingTime() const {
if(this.interval() == -1) return -1;
return this.interval() - this.elapsedTime();
}
int interval() const {
return this.inter;
}
Timer reset() {
start(this.inter);
return this;
}
bool isStarted() const {
return this.running;
}
Timer useGameTicks() {
@timeUnit = @COMMON::jjGameTicks;
return this;
}
Timer useRenderFrames() {
@timeUnit = @COMMON::jjRenderFrame;
return this;
}
}
}
namespace CUSTOM {
bool SHOW_WARNINGS = true;
bool SALLIB_DEBUG = true;
void CustomObjectBehavior(jjOBJ@ jjo) {
Object@ co = objects[jjo.objectID];
if(co != jjo) {//jjo is active or the behavior would not be called
if(SALLIB_DEBUG) jjDebug("SaLLiB DEBUG: Object behavior changed.");
return;
}
co.age = co.age + 1;
co.behavior();
if(!co.isActive) return;
co.move();
co.rotate();
co.color();
co.scale();
co.patrol();
co.warp();
co.updateDefaultBehavior();
}
int theoreticalMaxObjects = jjGameMode == GAME::SP ? 512:2560;
array<CUSTOM::Object@> objects(jjObjectMax == 0 ? theoreticalMaxObjects : jjObjectMax);
int nextID = 1;
interface ICustomObject : COMMON::IFocalPoint,MIXIN::IMovable,MIXIN::IScalable,MIXIN::IRotatable,MIXIN::IColorable,MIXIN::IPatrollable,MIXIN::IWarpable {
void behavior();
void onObjectHit(jjOBJ@ bullet, jjPLAYER@ player, int force);
bool get_isActive() const;
jjBEHAVIOR get_standardBehavior();
int get_age() const;
void set_age(int age);
//Sprite accessors/mutators
int get_spriteAngle() const;
float get_spriteScaleX() const;
float get_spriteScaleY() const;
SPRITE::Mode get_spriteMode() const;
uint8 get_spriteParam() const;
uint8 get_spriteLayerZ() const;
uint8 get_spriteLayerXY() const;
int8 get_spritePlayerID() const;
bool get_spriteBob() const;
ANIM::Set get_set() const;
int get_animation() const;
int get_startFrame() const;
int get_numFrames() const;
COMMON::Timer@ get_animationTimer();
int get_ticksPerFrame() const;
bool get_cycleAnimation() const;
bool get_invisible() const;
int get_frame() const;
void set_spriteAngle(int angle);
void set_spriteScaleX(float scaleX);
void set_spriteScaleY(float scaleY);
void set_spriteMode(SPRITE::Mode mode);
void set_spriteParam(uint8 param);
void set_spriteLayerZ(uint8 layerZ);
void set_spriteLayerXY(uint8 layerXY);
void set_spritePlayerID(int8 playerID);
void set_spriteBob(bool bob);
void set_set(ANIM::Set set);
void set_animation(int animation);
void set_startFrame(int startFrame);
void set_numFrames(int numFrames);
void set_ticksPerFrame(int ticksPerFrame);
void set_cycleAnimation(bool cycleAnimation);
void set_invisible(bool invisible);
void playAnimation(ANIM::Set set, int animation, int startFrame, int numFrames, int ticksPerFrame = 7, bool cycle=true);
bool isPlayingAnimation();
void stopAnimation();
void freezeFrame(ANIM::Set, int animation, int frame);
//Custom object methods
float distancePixels(COMMON::IFocalPoint@ focus);
float distanceTiles(COMMON::IFocalPoint@ focus);
bool opEquals(jjOBJ@ jjObject);
bool opEquals(CUSTOM::Object object);
void updateDefaultBehavior();
//jjOBJ accessors
int get_animSpeed() const;
HANDLING::Bullet get_bulletHandling() const;
bool get_causesRicochet() const;
int get_counter() const;
uint8 get_counterEnd() const;
int get_creator() const;
int get_creatorID() const;
CREATOR::Type get_creatorType() const;
int16 get_curAnim() const;
uint get_curFrame() const;
bool get_deactivates() const;
int8 get_direction() const;
uint8 get_doesHurt() const;
int get_energy() const;
uint8 get_eventID() const;
int8 get_frameID() const;
uint8 get_freeze() const;
bool get_isBlastable() const;
bool get_isFreezable() const;
bool get_isTarget() const;
uint8 get_justHit() const;
int16 get_killAnim() const;
int8 get_light() const;
uint8 get_lightType() const;
int get_oldState() const;
HANDLING::Player get_playerHandling() const;
uint16 get_points() const;
int8 get_noHit() const;
uint16 get_objectID() const;
uint8 get_objType() const;
bool get_scriptedCollisions() const;
int get_special() const;
int get_state() const;
bool get_triggersTNT() const;
int get_var(int index) const;
float get_xAcc() const;
float get_xOrg() const;
float get_xPos() const;
float get_xSpeed() const;
float get_yAcc() const;
float get_yPos() const;
float get_yOrg() const;
float get_ySpeed() const;
void set_animSpeed(int animSpeed);
void set_bulletHandling(HANDLING::Bullet bulletHandling);
void set_causesRicochet(bool causesRicochet);
void set_counter(int counter);
void set_counterEnd(uint8 counterEnd);
void set_creator(int creator);
void set_creatorID(int creatorID);
void set_creatorType(CREATOR::Type creatorType);
void set_curAnim(int16 curAnim);
void set_curFrame(uint curFrame);
void set_deactivates(bool deactivates);
void set_direction(int8 direction);
void set_doesHurt(uint8 doesHurt);
void set_energy(int energy);
void set_eventID(uint8 eventID);
void set_frameID(int8 frameID);
void set_freeze(uint8 freeze);
void set_isBlastable(bool isBlastable);
void set_isFreezable(bool isFreezable);
void set_isTarget(bool isTarget);
void set_justHit(uint8 justHit);
void set_killAnim(int16 killAnim);
void set_light(int8 light);
void set_lightType(uint8 lightType);
void set_oldState(STATE::State state);
void set_playerHandling(HANDLING::Player playerHandling);
void set_points(uint16 points);
void set_noHit(int8 noHit);
void set_objType(uint8 objType);
void set_scriptedCollisions(bool scriptedCollisions);
void set_special(int special);
void set_state(int state);
void set_triggersTNT(bool triggersTNT);
void set_var(int index, int value);
void set_xAcc(float xAcc);
void set_xOrg(float xOrg);
void set_xPos(float xPos);
void set_xSpeed(float xSpeed);
void set_ySpeed(float ySpeed);
void set_yAcc(float yAcc);
void set_yPos(float yPos);
void set_yOrg(float yOrg);
void clearPlatform() const;
void grantPickup(jjPLAYER@ player, int frequency) const;
void particlePixelExplosion(int style) const;
void pathMovement() const;
void putOnGround(bool precise = false) const;
void delete();
int beSolid() const;
int16 determineCurAnim(uint8 setID, uint8 animation, bool change = true) const;
int16 determineCurAnim(ANIM::Set setID, uint8 animation, bool change = true) const;
uint determineCurFrame(bool change = true) const;
int findNearestPlayer(int maxDistance) const;
int findNearestPlayer(int maxDistance, int &out foundDistance) const;
int fireBullet(OBJECT::Object eventID) const;
int unfreeze(int style) const;
bool ricochet() const;
void deactivate();
void behave(BEHAVIOR::Behavior behavior, bool draw = true, bool useCustomSpriteProperties = true) const;
void behave(jjVOIDFUNCOBJ@ behavior, bool draw = true, bool useCustomSpriteProperties = true) const;
void behave(jjBEHAVIOR behavior, bool draw = true, bool useCustomSpriteProperties = true) const;
int draw(bool useCustomSpriteProperties = true) const;
void printPos();
}
class Object : ICustomObject, MIXIN::Movable, MIXIN::Scalable,
MIXIN::Rotatable, MIXIN::Colorable, MIXIN::Patrollable,
MIXIN::Warpable {
//Object Properties
jjOBJ@ jjObject = null;
jjBEHAVIOR defaultBehavior;
int customAge = 0, cid;
bool isNil = false;
//Sprite properties
int angle = 0;
float scaleX = 1, scaleY = 1;
SPRITE::Mode mode = SPRITE::NORMAL;
uint8 param = 0, layerZ = 4, layerXY = 4;
int8 playerID = -1;
bool bob = false;
COMMON::Timer bobTimer(), anmTimer();
ANIM::Set st;
int anim, fram, numFrams, startFram, ticksPerFram;
bool cycleAnim = false, inv = false;
INTERPOLATION::Sinusoidal@ bobInterpolator;
Object(OBJECT::Object objectTemplate, float xPos, float yPos) {
@this.jjObject = @jjObjects[jjAddObject(objectTemplate,xPos,yPos,0,CREATOR::OBJECT)];
if(objectTemplate == NIL) {
isNil = true;
this.jjObject.playerHandling = HANDLING::PARTICLE;
this.jjObject.bulletHandling = HANDLING::IGNOREBULLET;
this.defaultBehavior = DO_NOTHING;
} else
initDefaultBehavior();
initialize();
}
Object(jjOBJ@ jjObject) {
@this.jjObject = jjObject;
initDefaultBehavior();
initialize();
}
void initDefaultBehavior() {
if(this.jjObject.behavior == BEHAVIOR::DEFAULT) {
if(SALLIB_DEBUG) jjDebug("SaLLiB DEBUG: jjObject passed to custom object uses BEHAVIOR::DEFAULT");
this.defaultBehavior = BEHAVIOR::BULLET;
} else if(this.jjObject.behavior == CustomBulletBehavior1) this.defaultBehavior = BEHAVIOR::BULLET;
else if(this.jjObject.behavior == CustomBulletBehavior2) this.defaultBehavior = BEHAVIOR::BOUNCERBULLET;
else if(this.jjObject.behavior == CustomBulletBehavior3) this.defaultBehavior = BEHAVIOR::BULLET;
else if(this.jjObject.behavior == CustomBulletBehavior4) this.defaultBehavior = BEHAVIOR::SEEKERBULLET;
else if(this.jjObject.behavior == CustomBulletBehavior5) this.defaultBehavior = BEHAVIOR::RFBULLET;
else if(this.jjObject.behavior == CustomBulletBehavior6) this.defaultBehavior = BEHAVIOR::TOASTERBULLET;
else if(this.jjObject.behavior == CustomBulletBehavior7) this.defaultBehavior = BEHAVIOR::TNT;
else if(this.jjObject.behavior == CustomBulletBehavior8) this.defaultBehavior = BEHAVIOR::PEPPERBULLET;
else if(this.jjObject.behavior == CustomBulletBehavior9) this.defaultBehavior = BEHAVIOR::ELECTROBULLET;
else this.defaultBehavior = this.jjObject.behavior;
}
void initialize() {
this.jjObject.behavior = CustomObjectBehavior;
@CUSTOM::objects[jjObject.objectID] = @this;
this.jjObject.age = nextID;
this.cid = nextID++;
@this.bobInterpolator = INTERPOLATION::Sinusoidal(2,3,jjRandom()%100,65);
this.bobTimer.start();
}
//Methods made to be overridden
void behavior() {
behave(this.defaultBehavior);
}
void onObjectHit(jjOBJ@ bullet, jjPLAYER@ player, int force) {}
bool get_isActive() const {return this.jjObject.isActive && this.jjObject.age == this.cid;}
//Handling jjObject changing out from under you
bool objectChanged(jjOBJ@ jjo) {
if(!isActive) {
if(jjo.behavior == CustomObjectBehavior)
jjo.behavior = DO_NOTHING;
return true;
}
return false;
}
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; }
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;}
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;}
//Sprite accessors/mutators
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;}
//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;}
//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;}
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;}
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;}
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;}
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;}
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;}
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;}
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;}
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;}
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;}
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;}
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;}
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;}
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;}
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;}
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;}
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;}
int get_frame() const {if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via get_frame()");}}
return startFrame + (anmTimer.elapsedTime()/ticksPerFrame) % numFrames;}
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;}
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;}
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;}
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;}
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;}
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;}
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;}
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;}
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;}
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;}
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;}
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;}
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;}
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;}
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;}
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;}
void playAnimation(ANIM::Set set, int animation, int startFrame, int numFrames, int ticksPerFrame = 7, bool cycle=true) {
if(this.anmTimer.isFinished() || set != st || animation != anim || startFrame != startFram || numFrames != numFrams || ticksPerFrame != ticksPerFram) {
this.st = set;
this.anim = animation;
this.startFram = startFrame;
this.numFrams = numFrames;
this.ticksPerFram = ticksPerFrame;
this.anmTimer.start(numFrams*ticksPerFram);
}
this.cycleAnim = cycle;
}
bool isPlayingAnimation() {
return (this.anmTimer.isStarted() && !this.anmTimer.isFinished()) || cycleAnim;
}
void stopAnimation() {
this.anmTimer.stop();
}
void freezeFrame(ANIM::Set, int animation, int frame) {
playAnimation(set,animation,frame,1,1,true);
}
//Custom object methods
float distancePixels(COMMON::IFocalPoint@ focus) {return distance(this.xPos, this.yPos, focus.xPos, focus.yPos);}
float distanceTiles(COMMON::IFocalPoint@ focus) {return distancePixels(focus)/32;}
bool opEquals(jjOBJ@ jjObject) {return this.cid == jjObject.age;}
bool opEquals(CUSTOM::Object object) {return object.objectID == this.objectID && object.cid == this.cid;}
void updateDefaultBehavior() {
if(this.jjObject.behavior != CustomObjectBehavior) {
if(!isNil && this.jjObject.behavior != BEHAVIOR::DEFAULT) {
this.defaultBehavior = this.jjObject.behavior;
} else {
this.defaultBehavior = DO_NOTHING;
}
}
}
//jjOBJ accessors
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;}
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;}
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;}
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;}
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;}
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;}
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;}
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;}
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;}
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;}
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;}
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;}
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;}
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;}
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;}
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;}
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;}
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;}
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;}
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;}
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;}
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;}
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;}
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;}
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;}
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;}
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;}
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;}
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;}
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;}
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;}
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;}
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;}
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;}
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];}
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;}
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;}
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;}
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;}
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;}
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;}
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;}
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;}
//jjOBJ mutators
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;}
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;}
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;}
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;}
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;}
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;}
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;}
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;}
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;}
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;}
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;}
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;}
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;}
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;}
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;}
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;}
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;}
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;}
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;}
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;}
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;}
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;}
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;}
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;}
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;}
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;}
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;}
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;}
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;}
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;}
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;}
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;}
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;}
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;}
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;}
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;}
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;}
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;}
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;}
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;}
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;}
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;}
//jjOBJ methods
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();}
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);}
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);}
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();}
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);}
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();}
void deleteNoWarn() {if(!objectChanged(this.jjObject)) this.jjObject.delete();}
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();}
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);}
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);}
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);}
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);}
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);}
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);}
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);}
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();}
//Reimplemented deactivate to explicitly call delete, so if delete is overridden it will still work.
//Also prevents having to override both deactivate and delete (just delete is enough)
void deactivate() {
if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via deactivate()");}return;}
this.jjObject.delete();
if(this.jjObject.creatorType == CREATOR::LEVEL) {
jjEventSet(int(this.jjObject.xOrg/32), int(this.jjObject.yOrg/32), this.jjObject.eventID);
jjParameterSet(int(this.jjObject.xOrg/32), int(this.jjObject.yOrg/32), -1, 1, 0);
}
}
//Reimplemented behave to explicitly call draw()
void behave(BEHAVIOR::Behavior behavior, bool draw = true, bool useCustomSpriteProperties = true) const {
if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via behave()");}return;}
if(!isWarping()) this.jjObject.behave(behavior, false);
if(draw && isActive) this.draw(useCustomSpriteProperties);
}
void behave(jjVOIDFUNCOBJ@ behavior, bool draw = true, bool useCustomSpriteProperties = true) const {
if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via behave()");}return;}
if(!isWarping()) this.jjObject.behave(behavior, false);
if(draw && isActive) this.draw(useCustomSpriteProperties);
}
void behave(jjBEHAVIOR behavior, bool draw = true, bool useCustomSpriteProperties = true) const {
if(objectChanged(this.jjObject)){if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via behave()");}return;}
if(!isWarping()) this.jjObject.behave(behavior, false);
if(draw && isActive) this.draw(useCustomSpriteProperties);
}
//Reimplemented draw to use custom sprite properties
int draw(bool useCustomSpriteProperties = true) const {
if(objectChanged(this.jjObject)) {
if(CUSTOM::SHOW_WARNINGS){jjDebug("SaLLiB WARNING: Tried to access a deleted object via draw()");}
return -1;
}
if(invisible) return -1;
if(isPlayingAnimation() || useCustomSpriteProperties) {
float offset = bob ? this.bobInterpolator.value(bobTimer.elapsedTime()) : 0;
SPRITE::Mode mode = justHit != 0 ? SPRITE::SINGLECOLOR : this.mode;
int param = justHit != 0 ? 64 : this.param;
int angleDirection = direction > 0 ? angle : -angle;
float xScaleDirection = direction > 0 ? scaleX : -1*scaleX;
if(angle != 0) {
if(isPlayingAnimation())
jjDrawRotatedSprite(xPos,yPos+offset,st,anim,frame,angleDirection,xScaleDirection,scaleY,mode,param,layerZ,layerXY,playerID);
else
jjDrawRotatedSpriteFromCurFrame(xPos,yPos+offset,curFrame,angleDirection,xScaleDirection,scaleY,mode,param,layerZ,layerXY,playerID);
return -1;
} else if(scaleX != 1 || scaleY != 1) {
if(isPlayingAnimation())
jjDrawResizedSprite(xPos,yPos+offset,st,anim,frame,xScaleDirection,scaleY,mode,param,layerZ,layerXY,playerID);
else
jjDrawResizedSpriteFromCurFrame(xPos,yPos+offset,curFrame,xScaleDirection,scaleY,mode,param,layerZ,layerXY,playerID);
return -1;
} else if(isPlayingAnimation() || bob != false || mode != SPRITE::NORMAL || layerZ != 4 || layerXY != 4 || playerID != -1) {
if(isPlayingAnimation())
jjDrawSprite(xPos,yPos+offset,st,anim,frame,direction,mode,param,layerZ,layerXY,playerID);
else
jjDrawSpriteFromCurFrame(xPos,yPos+offset,curFrame,direction,mode,param,layerZ,layerXY,playerID);
return -1;
} else { return this.jjObject.draw(); }
} else { return this.jjObject.draw(); }
}
//Debug helpers
void printPos() {
jjDebug((xPos/32) + "," + (yPos/32));
}
}
class Boss : CUSTOM::Object {
int oldPresetEnergy;
jjPLAYER@ player;
bool activated = false;
GAME::IBossMeter@ meter = null;
Boss(OBJECT::Object objectTemplate, float xPixel, float yPixel) {
super(objectTemplate, xPixel, yPixel);
jjObjectPresets[255].determineCurAnim(ANIM::BOSS, 0);
this.playerHandling = HANDLING::SPECIAL;
this.bulletHandling = HANDLING::DETECTBULLET;
this.scriptedCollisions = true;
}
int get_energy() {
if(meter is null) return Object::get_energy();
return meter.energy;
}
void set_energy(int energy) {
if(meter is null) Object::set_energy(energy);
else meter.energy = energy;
}
void activateBoss(int maxEnergy) {
activateBossWithUIMeter(maxEnergy,10*32,0);
}
void activateBossWithUIMeter(int maxEnergy, float xPos=10*32, float yPos=0) {
if(!activated) {
GAME::BossMeterUI@ meterUI = @GAME::BossMeterUI(xPos,yPos);
@meter = @meterUI;
CUSTOM::UI.addElement(meterUI);
meter.maxEnergy = maxEnergy;
meter.energy = maxEnergy;
activated = true;
}
}
void activateBossWithFloatingMeter(int maxEnergy, int yOffset = 0) {
if(!activated) {
@meter = @GAME::BossMeterFloating(this,yOffset);
meter.maxEnergy = maxEnergy;
meter.energy = maxEnergy;
activated = true;
}
}
void deactivateBoss() {
if(activated) {
GAME::BossMeterUI@ casted = cast<GAME::BossMeterUI@>(meter);
if(casted !is null) {
CUSTOM::UI.removeElement(casted);
}
meter.delete();
@meter = null;
activated = false;
}
}
GAME::IBossMeter@ bossMeter() {
return meter;
}
void delete() {
deactivateBoss();
CUSTOM::Object::delete();
}
void onObjectHit(jjOBJ@ bullet, jjPLAYER@ player, int force) {
if(bullet !is null && player !is null) {
if(meter !is null) {
meter.energy = meter.energy - force;
justHit = 5;
bullet.state = STATE::EXPLODE;
if(meter.energy <= 0) {
particlePixelExplosion(0);
delete();
}
} else {
bullet.state = STATE::EXPLODE;
}
} else if (player !is null) {
player.hurt();
}
}
}
funcdef se::colorEffect@ COLOR_EFFECT(jjPALCOLOR,float);
se::colorEffect@ effectBrighten(jjPALCOLOR color, float value) {return se::colorBrighten(value);}
se::colorEffect@ effectContrast(jjPALCOLOR color, float value) {return se::colorContrast(value);}
se::colorEffect@ effectFade(jjPALCOLOR color, float value) {return se::colorFade(color,value);}
se::colorEffect@ effectGamma(jjPALCOLOR color,float value) {return se::colorGamma(value);}
se::colorEffect@ effectShiftHue(jjPALCOLOR color,float value) {return se::colorShiftHue(int(value));}
se::colorEffect@ effectSketch(jjPALCOLOR color,float value) {return se::colorSketch(int(value));}
class Palette : CUSTOM::Object {
INTERPOLATION::Interpolator@ interpolatorEffect;
COMMON::Timer effectTimer();
bool effectCycle;
jjPAL palette;
jjPALCOLOR palcolor;
COLOR_EFFECT@ effectFunction;
int minColor, maxColor;
int updateDelay = 3;
Palette() {
super(NIL,-1,-1);
}
void set_delay(int delay) { this.updateDelay = delay; }
int get_delay() { return this.updateDelay; }
void colorGrayscale(int minColor = 0, int maxColor = 255) {applyColorEffect(se::colorGrayscale(), minColor, maxColor);}
void colorSolarize(int minColor = 0, int maxColor = 255) {applyColorEffect(se::colorSolarize(), minColor, maxColor);}
void colorSepia(int minColor = 0, int maxColor = 255) {applyColorEffect(se::colorSepia(), minColor, maxColor);}
void colorNegative(int minColor = 0, int maxColor = 255) {applyColorEffect(se::colorNegative(), minColor, maxColor);}
void colorDecompositionMin(int minColor = 0, int maxColor = 255) {applyColorEffect(se::colorDecompositionMin(), minColor, maxColor);}
void colorDecompositionMax(int minColor = 0, int maxColor = 255) {applyColorEffect(se::colorDecompositionMax(), minColor, maxColor);}
void colorGlow(jjPALCOLOR color, int minColor = 0, int maxColor = 255) {applyColorEffect(se::colorGlow(color), minColor, maxColor);}
void colorReduction(int r, int g, int b,int minColor = 0, int maxColor = 255) {applyColorEffect(se::colorReduction(r,g,b), minColor, maxColor);}
void colorReplace(jjPALCOLOR color, int minColor = 0, int maxColor = 255) {applyColorEffect(se::colorReplace(color), minColor, maxColor);}
void restore() {
jjBackupPalette.apply();
}
void applyColorEffect(se::colorEffect@ effect, uint minColor = 1, uint maxColor = 256) {
jjPAL newPalette = jjBackupPalette;
for(uint i = minColor; i < maxColor; i++) {
newPalette.color[i] = effect.getResult(jjBackupPalette.color[i]);
}
newPalette.apply();
}
void brightenLinear(jjPAL palette, float min, float max, int duration, uint minColor = 1, uint maxColor = 256, bool cycle = false) {
effectInterpolator(effectBrighten,
palette,
jjPALCOLOR(),
INTERPOLATION::Linear(min,max,duration),
duration,minColor,maxColor,cycle);
}
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) {
float dist = (max-min)/2;
float mid = min + dist;
effectInterpolator(effectBrighten,
palette,
color,
INTERPOLATION::PointPlusSine(COMMON::Position(mid,mid),INTERPOLATION::getX,frequency,dist,offset,duration),
duration,minColor,maxColor,cycle);
}
void contrastLinear(jjPAL palette, float min, float max, int duration, uint minColor = 1, uint maxColor = 256, bool cycle = false) {
effectInterpolator(effectContrast,
palette,
jjPALCOLOR(),
INTERPOLATION::Linear(min,max,duration),
duration,minColor,maxColor,cycle);
}
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) {
float dist = (max-min)/2;
float mid = min + dist;
effectInterpolator(effectContrast,
palette,
color,
INTERPOLATION::PointPlusSine(COMMON::Position(mid,mid),INTERPOLATION::getX,frequency,dist,offset,duration),
duration,minColor,maxColor,cycle);
}
void fadeLinear(jjPAL palette, jjPALCOLOR color, float min, float max, int duration, uint minColor = 1, uint maxColor = 256, bool cycle = false) {
effectInterpolator(effectFade,
palette,
color,
INTERPOLATION::Linear(min,max,duration),
duration,minColor,maxColor,cycle);
}
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) {
float dist = (max-min)/2;
float mid = min + dist;
effectInterpolator(effectFade,
palette,
color,
INTERPOLATION::PointPlusSine(COMMON::Position(mid,mid),INTERPOLATION::getX,frequency,dist,offset,duration),
duration,minColor,maxColor,cycle);
}
void gammaLinear(jjPAL palette, float min, float max, int duration, uint minColor = 1, uint maxColor = 256, bool cycle = false) {
effectInterpolator(effectGamma,
palette,
jjPALCOLOR(),
INTERPOLATION::Linear(min,max,duration),
duration,minColor,maxColor,cycle);
}
void gammaOscillate(jjPAL palette, float min, float max, int duration, float frequency, int offset, uint minColor = 1, uint maxColor = 256, bool cycle = false) {
float dist = (max-min)/2;
float mid = min + dist;
effectInterpolator(effectGamma,
palette,
jjPALCOLOR(),
INTERPOLATION::PointPlusSine(COMMON::Position(mid,mid),INTERPOLATION::getX,frequency,dist,offset,duration),
duration,minColor,maxColor,cycle);
}
void shiftHueLinear(jjPAL palette, float min, float max, int duration, uint minColor = 1, uint maxColor = 256, bool cycle = false) {
effectInterpolator(effectShiftHue,
palette,
jjPALCOLOR(),
INTERPOLATION::Linear(min,max,duration),
duration,minColor,maxColor,cycle);
}
void shiftHueOscillate(jjPAL palette, float min, float max, int duration, float frequency, int offset, uint minColor = 1, uint maxColor = 256, bool cycle = false) {
float dist = (max-min)/2;
float mid = min + dist;
effectInterpolator(effectShiftHue,
palette,
jjPALCOLOR(),
INTERPOLATION::PointPlusSine(COMMON::Position(mid,mid),INTERPOLATION::getX,frequency,dist,offset,duration),
duration,minColor,maxColor,cycle);
}
void sketchLinear(jjPAL palette, float min, float max, int duration, uint minColor = 1, uint maxColor = 256, bool cycle = false) {
effectInterpolator(effectSketch,
palette,
jjPALCOLOR(),
INTERPOLATION::Linear(min,max,duration),
duration,minColor,maxColor,cycle);
}
void sketchOscillate(jjPAL palette, float min, float max, int duration, float frequency, int offset, uint minColor = 1, uint maxColor = 256, bool cycle = false) {
float dist = (max-min)/2;
float mid = min + dist;
effectInterpolator(effectSketch,
palette,
jjPALCOLOR(),
INTERPOLATION::PointPlusSine(COMMON::Position(mid,mid),INTERPOLATION::getX,frequency,dist,offset,duration),
duration,minColor,maxColor,cycle);
}
void effectInterpolator(COLOR_EFFECT@ colorEffect, jjPAL palette, jjPALCOLOR color, INTERPOLATION::Interpolator@ interpolator, int duration, int minColor = 0, int maxColor = 255, bool cycle = false) {
@effectFunction = @colorEffect;
this.minColor = minColor;
this.maxColor = maxColor;
this.palcolor = color;
this.palette = palette;
effectTimer.start(duration);
@this.interpolatorEffect = interpolator;
effectCycle = cycle;
}
bool effect() {
if(effectTimer.isStarted()) {
if(effectTimer.elapsedTime() == 0 || effectTimer.elapsedTime() == effectTimer.interval() || effectTimer.elapsedTime() % updateDelay == 0) {
if(effectTimer.elapsedTime() <= effectTimer.interval() || effectTimer.interval() == -1) {
se::colorEffect@ ceffect = effectFunction(this.palcolor,interpolatorEffect.value(effectTimer.elapsedTime()));
applyColorEffect(ceffect,minColor,maxColor);
return true;
} else if(effectCycle && effectTimer.elapsedTime() > effectTimer.interval()) {
effectTimer.reset();
se::colorEffect@ ceffect = effectFunction(this.palcolor,interpolatorEffect.value(effectTimer.elapsedTime()));
applyColorEffect(ceffect,minColor,maxColor);
return true;
}
}
}
return false;
}
bool isPerformingColorEffect() {
return effectTimer.isStarted() && !effectTimer.isFinished();
}
void stopEffect() {
effectTimer.stop();
}
void behavior() {
effect();
}
}
class Player : COMMON::IFocalPoint {
jjPLAYER@ jjPlayer;
Player(jjPLAYER@ player) {@this.jjPlayer = @player;}
void set_xPos(float xPos) {this.jjPlayer.xPos=xPos;}
void set_yPos(float yPos) {this.jjPlayer.yPos=yPos;}
float get_xPos() const {return this.jjPlayer.xPos;}
float get_yPos() const {return this.jjPlayer.yPos;}
bool get_isActive() const {return this.jjPlayer.isActive;}
}
funcdef CUSTOM::Object@ BULLET(jjOBJ@ bullet);
void CustomBulletBehavior1(jjOBJ@ bullet) { CustomBulletBehavior(bullet, 1); }
void CustomBulletBehavior2(jjOBJ@ bullet) { CustomBulletBehavior(bullet, 2); }
void CustomBulletBehavior3(jjOBJ@ bullet) { CustomBulletBehavior(bullet, 3); }
void CustomBulletBehavior4(jjOBJ@ bullet) { CustomBulletBehavior(bullet, 4); }
void CustomBulletBehavior5(jjOBJ@ bullet) { CustomBulletBehavior(bullet, 5); }
void CustomBulletBehavior6(jjOBJ@ bullet) { CustomBulletBehavior(bullet, 6); }
void CustomBulletBehavior7(jjOBJ@ bullet) { CustomBulletBehavior(bullet, 7); }
void CustomBulletBehavior8(jjOBJ@ bullet) { CustomBulletBehavior(bullet, 8); }
void CustomBulletBehavior9(jjOBJ@ bullet) { CustomBulletBehavior(bullet, 9); }
void CustomBulletBehavior(jjOBJ@ bullet, int weaponNumber) {
CUSTOM::weapons.get(weaponNumber).getBullet()(bullet);
}
class Weapon {
int gun;
BULLET@ makeBullet;
IInterfaceElement@ element;
Weapon(int gun, BULLET@ bullet, IInterfaceElement@ element = null) {
this.gun = gun;
@this.makeBullet = @bullet;
@this.element = @element;
}
//Can't use virtual accessor with function handle or it crashes.
BULLET@ getBullet() { return makeBullet; }
IInterfaceElement@ get_interfaceElement() { return element; }
bool get_comesFromGunCrates() { return jjWeapons[gun].comesFromGunCrates; }
bool get_infinite() { return jjWeapons[gun].infinite; }
int get_maximum() { return jjWeapons[gun].maximum; }
int get_multiplier() { return jjWeapons[gun].multiplier; }
bool get_replacedByBubbles() { return jjWeapons[gun].replacedByBubbles; }
bool get_replenishes() { return jjWeapons[gun].replenishes; }
int get_style() { return jjWeapons[gun].style; }
void set_comesFromGunCrates(bool comesFromGunCrates) { jjWeapons[gun].comesFromGunCrates = comesFromGunCrates; }
void set_infinite(bool infinite) { jjWeapons[gun].infinite = infinite; }
void set_maximum(int maximum) { jjWeapons[gun].maximum = maximum; }
void set_multiplier(int multiplier) { jjWeapons[gun].multiplier = multiplier; }
void set_replacedByBubbles(bool replacedByBubbles) { jjWeapons[gun].replacedByBubbles = replacedByBubbles; }
void set_replenishes(bool replenishes) { jjWeapons[gun].replenishes = replenishes; }
void set_style(int style) { jjWeapons[gun].style = style; }
}
class Weapons {
array<CUSTOM::Weapon@> weapons(10);
Weapons() {
for(uint i = 1; i <= 9; i++)
@weapons[i] = @Weapon(i, null, null);
}
void registerWeapon(int gun, BULLET@ bullet, IInterfaceElement@ element = null) {
if(gun < 1 || gun > 9)
if(SHOW_WARNINGS)
jjDebug("SaLLiB Warning: Button out of range in custom weapon!");
@weapons[gun] = @CUSTOM::Weapon(gun,bullet,element);
if(gun == 1) jjObjectPresets[OBJECT::BLASTERBULLET].behavior = CustomBulletBehavior1;
else if(gun == 2) jjObjectPresets[OBJECT::BOUNCERBULLET].behavior = CustomBulletBehavior2;
else if(gun == 3) jjObjectPresets[OBJECT::ICEBULLET].behavior = CustomBulletBehavior3;
else if(gun == 4) jjObjectPresets[OBJECT::SEEKERBULLET].behavior = CustomBulletBehavior4;
else if(gun == 5) jjObjectPresets[OBJECT::RFBULLET].behavior = CustomBulletBehavior5;
else if(gun == 6) jjObjectPresets[OBJECT::TOASTERBULLET].behavior = CustomBulletBehavior6;
else if(gun == 7) jjObjectPresets[OBJECT::TNT].behavior = CustomBulletBehavior7;
else if(gun == 8) jjObjectPresets[OBJECT::FIREBALLBULLET].behavior = CustomBulletBehavior8;
else if(gun == 9) jjObjectPresets[OBJECT::ELECTROBULLET].behavior = CustomBulletBehavior9;
}
CUSTOM::Weapon@ get(int gun) {
return weapons[gun];
}
}
Weapons weapons;
interface IInterfaceElement {
void draw(jjPLAYER@ player, jjCANVAS@ canvas);
}
class WeaponDisplayInterfaceElement : IInterfaceElement {
void draw(jjPLAYER@ player, jjCANVAS@ canvas) {
IInterfaceElement@ element = @CUSTOM::weapons.get(player.currWeapon).interfaceElement;
if(element !is null) {
CUSTOM::UI.drawAmmo = false;
element.draw(player,canvas);
} else {
CUSTOM::UI.drawAmmo = true;
}
}
}
interface IInterface {
bool get_drawScore() const;
bool get_drawHealth() const;
bool get_drawLives() const;
bool get_drawAmmo() const;
bool get_drawPlayerTimer() const;
int get_numElements() const;
void set_drawScore(bool drawScore);
void set_drawHealth(bool drawHealth);
void set_drawLives(bool drawLives);
void set_drawAmmo(bool drawAmmo);
void set_drawPlayerTimer(bool drawPlayerTimer);
void addElement(IInterfaceElement@ element);
void removeElement(IInterfaceElement@ element);
void draw(jjPLAYER@ player,jjCANVAS@ canvas);
}
class Interface : IInterface {
array<CUSTOM::IInterfaceElement@> ui();
bool drawScore { get const {return drawScore;} set {drawScore = value; }}
bool drawScore = true;
bool drawHealth { get const {return drawHealth;} set {drawHealth = value; }}
bool drawHealth = true;
bool drawLives { get const {return drawLives;} set {drawLives = value;}}
bool drawLives = true;
bool drawAmmo { get const {return drawAmmo;} set {drawAmmo = value;}}
bool drawAmmo = true;
bool drawPlayerTimer { get const { return drawPlayerTimer;} set {drawPlayerTimer = value;}}
bool drawPlayerTimer = true;
int numElements { get const {return ui.length;}}
void addElement(IInterfaceElement@ element) {ui.insertLast(element);}
void removeElement(IInterfaceElement@ element) {
//Workaround bug in angelscript's array.find
int index = -1;
for(uint i = 0; i < ui.length; i++)
if(ui[i] is element) {
index = i;
break;
}
if(index >= 0)
ui.removeAt(index);
}
void draw(jjPLAYER@ player,jjCANVAS@ canvas) {
for(uint i = 0; i < ui.length; i++) {
ui[i].draw(player,canvas);
}
}
}
class EnhancedUI : Interface {
EnhancedUI() {
this.addElement(WeaponDisplayInterfaceElement());
}
}
IInterface@ UI = @Interface();
}
bool onDrawAmmo(jjPLAYER@ player, jjCANVAS@ canvas) {
CUSTOM::UI.draw(player,canvas);
if(onDrawAmmoHook !is null) onDrawAmmoHook(player,canvas);
return !CUSTOM::UI.drawAmmo;
}
bool onDrawHealth(jjPLAYER@ player, jjCANVAS@ canvas) {
if(onDrawHealthHook !is null) onDrawHealthHook(player,canvas);
return !CUSTOM::UI.drawHealth;
}
bool onDrawLives(jjPLAYER@ player, jjCANVAS@ canvas) {
if(onDrawLivesHook !is null) onDrawLivesHook(player,canvas);
return !CUSTOM::UI.drawLives;
}
bool onDrawPlayerTimer(jjPLAYER@ player, jjCANVAS@ canvas) {
if(onDrawPlayerTimerHook !is null) onDrawPlayerTimerHook(player,canvas);
return !CUSTOM::UI.drawPlayerTimer;
}
bool onDrawScore(jjPLAYER@ player, jjCANVAS@ canvas) {
if(onDrawScoreHook !is null) onDrawScoreHook(player,canvas);
return !CUSTOM::UI.drawScore;
}
void onObjectHit(jjOBJ@ obj, jjOBJ@ bullet, jjPLAYER@ player, int force) {
CUSTOM::objects[obj.objectID].onObjectHit(bullet,player,force);
if(onObjectHitHook !is null) onObjectHitHook(obj,bullet,player,force);
}
namespace INTERPOLATION {
interface Interpolator {
float value(int time);
}
interface PathInterpolator : Interpolator {
void reset(float start, float end, int duration);
}
class Linear : PathInterpolator {
float start, end;
int duration;
Linear(float start, float end, int duration) {
reset(start,end,duration);
}
void reset(float start, float end, int duration) {
this.start = start;
this.end = end;
this.duration = duration;
}
float value(int time) {
return ((end-start)/duration)*time + start;
}
}
class Exponential : PathInterpolator {
float start, end, base;
int duration;
Exponential(float start, float end, float base, int duration) {
this.base = base;
reset(start,end,duration);
}
void reset(float start, float end, int duration) {
this.start = start;
this.end = end;
this.duration = duration;
}
float logb(float x,float b){
return log(x)/log(b);
}
float value(int time) {
//Same as this equation, but uses logs to avoid underflow when dividing by pow
//return ((end-start)/pow(base,duration))*pow(base,time) + start;
return pow(base,logb(end-start,base)-duration+time)+start;
}
}
class Logarithmic : PathInterpolator {
float start, end, base;
int duration;
Logarithmic(float start, float end, float base, int duration) {
this.base = base;
reset(start,end,duration);
}
void reset(float start, float end, int duration) {
this.start = start;
this.end = end;
this.duration = duration;
}
float logb(float x,float b){
return log(x)/log(b);
}
float value(int time) {
return ((end-start)/logb(duration,base))*logb(time,base) + start;
}
}
class Sinusoidal : Interpolator {
float frequency, amplitude, offset;
int duration;
Sinusoidal(float frequency, float amplitude, float offset, int duration) {
reset(frequency,amplitude,offset,duration);
}
void reset(float frequency, float amplitude, float offset, int duration) {
this.frequency = frequency;
this.amplitude = amplitude;
this.offset = offset;
this.duration = duration;
}
float value(int time) {
return amplitude*sin(time*frequency*PI/duration + offset);
}
}
class LinePlusSine : PathInterpolator {
Linear@ line;
Sinusoidal@ sine;
LinePlusSine(float start, float end, int duration, float frequency, float amplitude, float offset) {
@line = @Linear(start, end, duration);
@sine = @Sinusoidal(frequency,amplitude,offset,duration);
}
void reset(float start,float end,int duration) {
line.reset(start, end, duration);
sine.duration = duration;
}
float value(int time) {
return line.value(time) + sine.value(time);
}
}
funcdef float SELECTOR(COMMON::IFocalPoint@);
float getX(COMMON::IFocalPoint@ focus) {return focus.xPos;}
float getY(COMMON::IFocalPoint@ focus) {return focus.yPos;}
class Point : Interpolator {
COMMON::IFocalPoint@ focus;
SELECTOR@ selector;
Point(COMMON::IFocalPoint@ focus, SELECTOR@ selector) {
@this.focus = @focus;
@this.selector = @selector;
}
float value(int time) {
return selector(focus);
}
}
class PointPlusSine : Interpolator {
COMMON::IFocalPoint@ focus;
SELECTOR@ selector;
Sinusoidal@ sine;
PointPlusSine(COMMON::IFocalPoint@ focus, SELECTOR@ selector, float frequency, float amplitude, float offset, int duration) {
@sine = @Sinusoidal(frequency,amplitude,offset,duration);
@this.selector = @selector;
@this.focus = @focus;
}
void reset(float frequency, float amplitude, float offset, int duration) {
sine.reset(frequency,amplitude,offset,duration);
}
float value(int time) {
return selector(focus) + sine.value(time);
}
}
}
namespace MIXIN {
interface IMovable {
void moveLinear(float xStart, float yStart, float xEnd, float yEnd, int duration, bool cycle = false);
void moveLinear(float xEnd, float yEnd, int duration, bool cycle = false);
void moveAccelerate(float xStart, float yStart, float xEnd, float yEnd, int duration, float acceleration, bool cycle = false);
void moveAccelerate(float xEnd, float yEnd, int duration, float acceleration, bool cycle = false);
void moveDeccelerate(float xStart, float yStart, float xEnd, float yEnd, int duration, float decceleration, bool cycle = false);
void moveDeccelerate(float xEnd, float yEnd, int duration, float decceleration, bool cycle = false);
void moveSinusoidal(float xStart, float yStart, float xEnd, float yEnd, int duration, float frequency, float amplitude, float offset, bool cycle = false);
void moveSinusoisal(float xEnd, float yEnd, int duration, float frequency, float amplitude, float offset, bool cycle = false);
void moveSpiral(float xStart, float yStart, float xEnd, float yEnd, int duration, float frequency, float amplitude, float offset, bool cycle = false);
void moveSpiral(float xEnd, float yEnd, int duration, float frequency, float amplitude, float offset, bool cycle = false);
void moveRevolve(COMMON::IFocalPoint@ focus, int duration, float radius, float offset, bool clockwise, bool cycle = false);
void moveOscillate(COMMON::IFocalPoint@ focus, int duration, float xDist, float yDist, float frequency, float offset, bool cycle = false);
void moveFollow(COMMON::IFocalPoint@ focus);
void moveInterpolator(INTERPOLATION::Interpolator@ xInterpolator, INTERPOLATION::Interpolator@ yInterpolator, int duration, bool cycle = false);
bool move();
bool isMoving();
void stopMove();
}
mixin class Movable : IMovable {
INTERPOLATION::Interpolator@ xInterpolatorMove;
INTERPOLATION::Interpolator@ yInterpolatorMove;
COMMON::Timer moveTimer();
bool moveCycle;
void moveLinear(float xStart, float yStart, float xEnd, float yEnd, int duration, bool cycle = false) {
moveInterpolator(INTERPOLATION::Linear(xStart,xEnd,duration),
INTERPOLATION::Linear(yStart,yEnd,duration),
duration,
cycle);
}
void moveLinear(float xEnd, float yEnd, int duration, bool cycle = false) {
moveLinear(xPos,yPos,xEnd,yEnd,duration,cycle);
}
void moveAccelerate(float xStart, float yStart, float xEnd, float yEnd, int duration, float acceleration, bool cycle = false) {
moveInterpolator(INTERPOLATION::Exponential(xStart,xEnd,acceleration,duration),
INTERPOLATION::Exponential(yStart,yEnd,acceleration,duration),
duration,
cycle);
}
void moveAccelerate(float xEnd, float yEnd, int duration, float acceleration, bool cycle = false) {
moveAccelerate(xPos, yPos, xEnd, yEnd, duration, acceleration, cycle);
}
void moveDeccelerate(float xStart, float yStart, float xEnd, float yEnd, int duration, float decceleration, bool cycle = false) {
moveInterpolator(INTERPOLATION::Logarithmic(xStart,xEnd,decceleration,duration),
INTERPOLATION::Logarithmic(yStart,yEnd,decceleration,duration),
duration,
cycle);
}
void moveDeccelerate(float xEnd, float yEnd, int duration, float decceleration, bool cycle = false) {
moveDeccelerate(xPos, yPos, xEnd, yEnd, duration, decceleration, cycle);
}
void moveSinusoidal(float xStart, float yStart, float xEnd, float yEnd, int duration, float frequency, float amplitude, float offset, bool cycle = false) {
moveInterpolator(INTERPOLATION::LinePlusSine(xStart,xEnd,duration,frequency,amplitude,offset),
INTERPOLATION::LinePlusSine(yStart,yEnd,duration,frequency,amplitude,offset),
duration,
cycle);
}
void moveSinusoisal(float xEnd, float yEnd, int duration, float frequency, float amplitude, float offset, bool cycle = false) {
moveSinusoidal(xPos,yPos,xEnd,yEnd,duration,frequency,amplitude,offset,cycle);
}
void moveSpiral(float xStart, float yStart, float xEnd, float yEnd, int duration, float frequency, float amplitude, float offset, bool cycle = false) {
moveInterpolator(INTERPOLATION::LinePlusSine(xStart,xEnd,duration,frequency,amplitude,offset), //sine
INTERPOLATION::LinePlusSine(yStart,yEnd,duration,frequency,amplitude,offset+PI), //cosine
duration,
cycle);
}
void moveSpiral(float xEnd, float yEnd, int duration, float frequency, float amplitude, float offset, bool cycle = false) {
moveSpiral(xPos,yPos,xEnd,yEnd,duration,frequency,amplitude,offset,cycle);
}
void moveRevolve(COMMON::IFocalPoint@ focus, int duration, float radius, float offset, bool clockwise, bool cycle = false) {
int frequency = 2;
if(clockwise) frequency *= -1;
moveInterpolator(INTERPOLATION::PointPlusSine(focus,INTERPOLATION::getX,frequency,radius,offset,duration),
INTERPOLATION::PointPlusSine(focus,INTERPOLATION::getY,frequency,radius,offset+HALF_PI,duration),
duration,
cycle);
}
void moveOscillate(COMMON::IFocalPoint@ focus, int duration, float xDist, float yDist, float frequency, float offset, bool cycle = false) {
moveInterpolator(INTERPOLATION::PointPlusSine(focus,INTERPOLATION::getX,frequency,xDist,offset,duration),
INTERPOLATION::PointPlusSine(focus,INTERPOLATION::getY,frequency,yDist,offset,duration),
duration,
cycle);
}
void moveFollow(COMMON::IFocalPoint@ focus) {
moveInterpolator(INTERPOLATION::Point(focus,INTERPOLATION::getX),
INTERPOLATION::Point(focus,INTERPOLATION::getY),
-1,
true);
}
void moveInterpolator(INTERPOLATION::Interpolator@ xInterpolatorMove, INTERPOLATION::Interpolator@ yInterpolatorMove, int duration, bool cycle = false) {
moveTimer.start(duration);
@this.xInterpolatorMove = @xInterpolatorMove;
@this.yInterpolatorMove = @yInterpolatorMove;
moveCycle = cycle;
}
bool move() {
if(isActive && moveTimer.isStarted())
if(moveTimer.elapsedTime() <= moveTimer.interval() || moveTimer.interval() == -1) {
this.xPos = xInterpolatorMove.value(moveTimer.elapsedTime());
this.yPos = yInterpolatorMove.value(moveTimer.elapsedTime());
return true;
} else if(moveCycle && moveTimer.elapsedTime() > moveTimer.interval()) {
moveTimer.reset();
this.xPos = xInterpolatorMove.value(moveTimer.elapsedTime());
this.yPos = yInterpolatorMove.value(moveTimer.elapsedTime());
return true;
}
return false;
}
bool isMoving() {
return moveTimer.isStarted() && !moveTimer.isFinished();
}
void stopMove() {
moveTimer.stop();
}
}
interface IScalable {
void scaleLinear(float scaleInit, float scaleEnd, int duration, bool cycle = false);
void scaleAccelerate(float scaleInit, float scaleEnd, int duration, float acceleration, bool cycle = false);
void scaleDeccelerate(float scaleInit, float scaleEnd, int duration, float decceleration, bool cycle = false);
void scaleOscillate(float xMin, float xMax, float yMin, float yMax, int duration, float frequency, int offset, bool cycle = false);
void scaleInterpolator(INTERPOLATION::Interpolator@ xInterpolator, INTERPOLATION::Interpolator@ yInterpolator, int duration, bool cycle = false);
bool scale();
bool isScaling();
void stopScaling();
}
mixin class Scalable : IScalable {
INTERPOLATION::Interpolator@ xInterpolatorScale;
INTERPOLATION::Interpolator@ yInterpolatorScale;
COMMON::Timer scaleTimer();
bool scaleCycle;
void scaleLinear(float scaleStart, float scaleEnd, int duration, bool cycle = false) {
scaleInterpolator(INTERPOLATION::Linear(scaleStart,scaleEnd,duration),
INTERPOLATION::Linear(scaleStart,scaleEnd,duration),
duration,
cycle);
}
void scaleAccelerate(float scaleStart, float scaleEnd, int duration, float acceleration, bool cycle = false) {
scaleInterpolator(INTERPOLATION::Exponential(scaleStart,scaleEnd,acceleration,duration),
INTERPOLATION::Exponential(scaleStart,scaleEnd,acceleration,duration),
duration,
cycle);
}
void scaleDeccelerate(float scaleStart, float scaleEnd, int duration, float decceleration, bool cycle = false) {
scaleInterpolator(INTERPOLATION::Logarithmic(scaleStart,scaleEnd,decceleration,duration),
INTERPOLATION::Logarithmic(scaleStart,scaleEnd,decceleration,duration),
duration,
cycle);
}
void scaleOscillate(float xMin, float xMax, float yMin, float yMax, int duration, float frequency, int offset, bool cycle = false) {
float xDist = (xMax-xMin)/2;
float xMid = xMin + xDist;
float yDist = (yMax-yMin)/2;
float yMid = yMin + yDist;
scaleInterpolator(INTERPOLATION::PointPlusSine(COMMON::Position(xMid,yMid),INTERPOLATION::getX,frequency,xDist,offset,duration),
INTERPOLATION::PointPlusSine(COMMON::Position(xMid,yMid),INTERPOLATION::getY,frequency,yDist,offset,duration),
duration,
cycle);
}
void scaleInterpolator(INTERPOLATION::Interpolator@ xInterpolator, INTERPOLATION::Interpolator@ yInterpolator, int duration, bool cycle = false) {
scaleTimer.start(duration);
@this.xInterpolatorScale = @xInterpolator;
@this.yInterpolatorScale = @yInterpolator;
scaleCycle = cycle;
}
bool scale() {
if(isActive && scaleTimer.isStarted())
if(scaleTimer.elapsedTime() <= scaleTimer.interval() || scaleTimer.interval() == -1) {
this.spriteScaleX = xInterpolatorScale.value(scaleTimer.elapsedTime());
this.spriteScaleY = yInterpolatorScale.value(scaleTimer.elapsedTime());
return true;
} else if(scaleCycle && scaleTimer.elapsedTime() > scaleTimer.interval()) {
scaleTimer.reset();
this.spriteScaleX = xInterpolatorScale.value(scaleTimer.elapsedTime());
this.spriteScaleY = yInterpolatorScale.value(scaleTimer.elapsedTime());
return true;
}
return false;
}
bool isScaling() {
return scaleTimer.isStarted() && !scaleTimer.isFinished();
}
void stopScaling() {
scaleTimer.stop();
}
}
interface IRotatable {
void rotate(float numRotations, int duration, int offset, bool clockwise, bool cycle = false);
bool rotate();
bool isRotating();
void stopRotating();
}
mixin class Rotatable : IRotatable {
INTERPOLATION::Interpolator@ interpolatorRotate;
COMMON::Timer rotateTimer();
bool rotateCycle;
void rotate(float numRotations, int duration, int offset, bool clockwise, bool cycle = false) {
float start, end, point = 1024*numRotations+offset;
if(clockwise) {
start = offset;
end = point;
} else {
start = point;
end = offset;
}
rotateInterpolator(INTERPOLATION::Linear(start,end,duration),
duration,
cycle);
}
void rotateInterpolator(INTERPOLATION::Interpolator@ interpolator, int duration, bool cycle = false) {
rotateTimer.start(duration);
@this.interpolatorRotate = interpolator;
rotateCycle = cycle;
}
bool rotate() {
if(isActive && rotateTimer.isStarted())
if(rotateTimer.elapsedTime() <= rotateTimer.interval() || rotateTimer.interval() == -1) {
this.spriteAngle = int(interpolatorRotate.value(rotateTimer.elapsedTime()));
return true;
} else if(rotateCycle && rotateTimer.elapsedTime() > rotateTimer.interval()) {
rotateTimer.reset();
this.spriteAngle = int(interpolatorRotate.value(rotateTimer.elapsedTime()));
return true;
}
return false;
}
bool isRotating() {
return rotateTimer.isStarted() && !rotateTimer.isFinished();
}
void stopRotating() {
rotateTimer.stop();
}
}
interface IColorable {
void tintLinear(int startTint, int endTint, int duration, bool cycle = false);
void tintOscillate(float min, float max, int duration, float frequency, int offset, bool cycle = false);
void palshiftLinear(int startTint, int endTint, int duration, bool cycle = false);
void palshiftOscillate(float min, float max, int duration, float frequency, int offset, bool cycle = false);
bool color();
bool isColoring();
void stopColoring();
}
mixin class Colorable : IColorable {
INTERPOLATION::Interpolator@ interpolatorColor;
COMMON::Timer colorTimer();
bool colorCycle;
void tintLinear(int startTint, int endTint, int duration, bool cycle = false) {
spriteMode = SPRITE::TINTED;
colorInterpolator(INTERPOLATION::Linear(startTint,endTint,duration),
duration,
cycle);
}
void tintOscillate(float min, float max, int duration, float frequency, int offset, bool cycle = false) {
float halfDist = (max-min)/2;
float mid = min + halfDist;
spriteMode = SPRITE::TINTED;
colorInterpolator(INTERPOLATION::PointPlusSine(COMMON::Position(mid,mid),INTERPOLATION::getX,frequency,halfDist,offset,duration),
duration,
cycle);
}
void palshiftLinear(int startTint, int endTint, int duration, bool cycle = false) {
spriteMode = SPRITE::PALSHIFT;
colorInterpolator(INTERPOLATION::Linear(startTint,endTint,duration),
duration,
cycle);
}
void palshiftOscillate(float min, float max, int duration, float frequency, int offset, bool cycle = false) {
float dist = (min-max)/2;
float mid = min + dist;
spriteMode = SPRITE::PALSHIFT;
colorInterpolator(INTERPOLATION::PointPlusSine(COMMON::Position(mid,mid),INTERPOLATION::getX,frequency,dist,offset,duration),
duration,
cycle);
}
void colorInterpolator(INTERPOLATION::Interpolator@ interpolator, int duration, bool cycle = false) {
colorTimer.start(duration);
@this.interpolatorColor = interpolator;
colorCycle = cycle;
}
bool color() {
if(isActive && colorTimer.isStarted()) {
if(colorTimer.elapsedTime() <= colorTimer.interval() || colorTimer.interval() == -1) {
this.spriteParam = int(interpolatorColor.value(colorTimer.elapsedTime()));
return true;
} else if(colorCycle && colorTimer.elapsedTime() > colorTimer.interval()) {
colorTimer.reset();
this.spriteParam = int(interpolatorColor.value(colorTimer.elapsedTime()));
return true;
}
}
return false;
}
bool isColoring() {
return colorTimer.isStarted() && !colorTimer.isFinished();
}
void stopColoring() {
colorTimer.stop();
}
}
interface IPatrollable {
void patrolLinear(array<float>@ pathX, array<float>@ pathY, int duration, bool cycle = true);
//void patrolSinusoidal(array<float>@ pathX, array<float>@ pathY, int duration, bool cycle = true,float frequency = 1, float amplitude=32, float offset=0);
void patrolInterpolator(array<float>@ pathX, array<float>@ pathY, INTERPOLATION::PathInterpolator@ xInterpolator, INTERPOLATION::PathInterpolator@ yInterpolator, int duration, bool cycle = true);
bool patrol();
bool isPatrolling();
}
mixin class Patrollable : IPatrollable, IMovable {
array<float>@ pathX;
array<float>@ pathY;
int duration;
bool pathCycle = false;
float pathTotalDist;
float percentDist;
int portionDuration;
int pathIndex;
COMMON::Timer patrolTimer();
INTERPOLATION::PathInterpolator@ xInterpolator;
INTERPOLATION::PathInterpolator@ yInterpolator;
void patrolLinear(array<float>@ pathX, array<float>@ pathY, int duration, bool cycle = true) {
patrolInterpolator(pathX,
pathY,
INTERPOLATION::Linear(pathX[0],pathX[1],portionDuration),
INTERPOLATION::Linear(pathY[0],pathY[1],portionDuration),
duration,
cycle);
}
/*void patrolSinusoidal(array<float>@ pathX, array<float>@ pathY, int duration, bool cycle = true,float frequency = 1, float amplitude=32, float offset=0) {
initPatrol(pathX,pathY,duration,cycle);
@this.xInterpolator = INTERPOLATION::LinePlusSine(pathX[0],pathX[1],duration,frequency,amplitude,offset);
@this.yInterpolator = INTERPOLATION::LinePlusSine(pathY[0],pathY[1],duration,frequency,amplitude,offset);
moveInterpolator(xInterpolator,yInterpolator,portionDuration);
}*/
void patrolInterpolator(array<float>@ pathX, array<float>@ pathY, INTERPOLATION::PathInterpolator@ xInterpolator, INTERPOLATION::PathInterpolator@ yInterpolator, int duration, bool cycle = true) {
initPatrol(pathX,pathY,duration,cycle);
@this.xInterpolator = @xInterpolator;
@this.yInterpolator = @yInterpolator;
moveInterpolator(xInterpolator,yInterpolator,portionDuration);
}
void initPatrol(array<float>@ pathX, array<float>@ pathY, int duration, bool cycle = true) {
this.duration = duration;
@this.pathX = @pathX;
@this.pathY = @pathY;
this.pathCycle = cycle;
uint i = 0;
pathTotalDist = 0;
for(; i < pathX.length-1; i++) pathTotalDist += distance(pathX[i],pathY[i],pathX[i+1],pathY[i+1]);;
if(pathCycle) pathTotalDist += distance(pathX[i],pathY[i],pathX[0],pathY[0]);
this.pathIndex = 1;
percentDist = distance(pathX[0],pathY[0],pathX[1],pathY[1]) / pathTotalDist;
patrolTimer.start(duration);
portionDuration = int(percentDist * duration);
}
bool patrol() {
if(isActive && isPatrolling()) {
if(!move()) {
int oldIndex = pathIndex;
pathIndex = (pathIndex + 1) % pathX.length();
percentDist = distance(pathX[oldIndex],pathY[oldIndex],pathX[pathIndex],pathY[pathIndex]) / pathTotalDist;
portionDuration = int(percentDist * duration);
if(!pathCycle && pathIndex == 0) return true;
xInterpolator.reset(pathX[oldIndex],pathX[pathIndex],portionDuration);
yInterpolator.reset(pathY[oldIndex],pathY[pathIndex],portionDuration);
moveInterpolator(xInterpolator,yInterpolator,portionDuration);
}
return true;
}
return false;
}
bool isPatrolling() {
return pathCycle || (patrolTimer.isStarted() && !patrolTimer.isFinished());
}
}
interface IWarpable {
void warpIn(int warpColor = 90);
void warpOut(int warpColor = 90);
void warpTo(float xEnd, float yEnd, int warpColor = 90);
bool warp();
bool isWarping();
bool isWarpingIn();
bool isWarpingOut();
bool isWarpedOut();
}
mixin class Warpable : IWarpable {
COMMON::Timer warpTimer();
bool warpInOrOut; //true=in,false=out
int warpAnim;
int warpFrameStart;
int warpColor;
bool warpedOut = false;
bool warpingTo = false;
HANDLING::Bullet warpPrevBulletHandling;
HANDLING::Player warpPrevPlayerHandling;
float warpTargetX, warpTargetY;
int WARP_FRAMES = 25;
void warpIn(int warpColor = 90) {
this.warpInOrOut = true;
this.warpColor = warpColor;
this.initWarp(this.warpColor, SOUND::COMMON_TELPORT2,76,0);
}
void warpOut(int warpColor = 90) {
this.warpInOrOut = false;
this.warpColor = warpColor;
this.initWarp(this.warpColor, SOUND::COMMON_TELPORT1,75,2);
}
void warpTo(float xEnd, float yEnd, int warpColor = 90) {
this.warpingTo = true;
this.warpTargetX = xEnd;
this.warpTargetY = yEnd;
this.warpOut(warpColor);
}
void initWarp(int warpColor, SOUND::Sample sound,int anim,int frameStart) {
this.stopWarpingHelper();
this.warpAnim = anim;
this.warpFrameStart = frameStart;
this.warpColor = warpColor;
if(!warpingTo && warpInOrOut) {
/*this.warpPrevBulletHandling = this.bulletHandling;
this.warpPrevPlayerHandling = this.playerHandling;
this.bulletHandling = HANDLING::IGNOREBULLET;
this.playerHandling = HANDLING::SPECIAL;*/
}
this.warpedOut = !this.warpInOrOut;
warpTimer.start(WARP_FRAMES);
jjSample(xPos, yPos, sound);
}
bool warp() {
if(isActive && warpTimer.isStarted()) {
if(warpTimer.elapsedTime() < WARP_FRAMES) {
jjDrawSprite(xPos, yPos, ANIM::SPAZ,this.warpAnim, this.warpFrameStart+(warpTimer.elapsedTime()/5), this.direction, SPRITE::SINGLECOLOR, this.warpColor);
return true;
} else if(warpTimer.elapsedTime() == WARP_FRAMES) {
if(warpingTo && !warpInOrOut) {
xPos = warpTargetX;
yPos = warpTargetY;
warpIn(warpColor);
return true;
} else {
stopWarping();
return false;
}
}
}
return false;
}
void stopWarpingHelper() {
if(warpTimer.elapsedTime() <= WARP_FRAMES) {
/*this.bulletHandling = this.warpPrevBulletHandling;
this.playerHandling = this.warpPrevPlayerHandling;*/
this.warpTimer.stop();
}
}
void stopWarping() {
if(warpTimer.elapsedTime() <= WARP_FRAMES) {
this.warpingTo = false;
}
stopWarpingHelper();
}
bool isWarping() {
return warpTimer.isStarted() && !warpTimer.isFinished();
}
bool isWarpingIn() {
return this.isWarping() && this.warpInOrOut;
}
bool isWarpingOut() {
return this.isWarping() && !this.warpInOrOut;
}
bool isWarpedOut() {
return this.warpedOut;
}
}
interface IComposite {
int addObject(CUSTOM::Object@ obj);
void removeObject(CUSTOM::Object@ obj);
void set_xPos(float xPos);
void set_yPos(float yPos);
void delete();
}
mixin class Composite : IComposite {
array<CUSTOM::Object@> compositeObjects();
int addObject(CUSTOM::Object@ obj) {
int len = compositeObjects.length();
compositeObjects.insertLast(obj);
return len;
}
void removeObject(CUSTOM::Object@ obj) {
int index = -1;
for(uint i = 0; i < compositeObjects.length; i++)
if(compositeObjects[i] is obj) {
index = i;
break;
}
if(index >= 0)
compositeObjects.removeAt(index);
}
void set_xPos(float xPos) {
float diff = xPos - this.xPos;
for(uint i = 0; i < compositeObjects.length(); i++) {
if(compositeObjects[i].isActive)
compositeObjects[i].xPos = compositeObjects[i].xPos + diff;
}
if(isActive)
CUSTOM::Object::set_xPos(xPos);
}
void set_yPos(float yPos) {
float diff = yPos - this.yPos;
for(uint i = 0; i < compositeObjects.length(); i++) {
if(compositeObjects[i].isActive)
compositeObjects[i].yPos = compositeObjects[i].yPos + diff;
}
if(isActive)
CUSTOM::Object::set_yPos(yPos);
}
void cleanup() {
for(uint i = 0; i < compositeObjects.length(); i++) {
if(compositeObjects[i].isActive)
compositeObjects[i].delete();
}
}
}
}
namespace GAME {
funcdef CUSTOM::Object@ ENCOUNTER_STAGE();
class Encounter : CUSTOM::Object {
array<ENCOUNTER_STAGE@> stages;
CUSTOM::Object@ curStage = null;
bool startFromCurStage;
bool replayIntro;
int cur = -1;
int max = -1;
Encounter(array<ENCOUNTER_STAGE@> stages, bool startFromCurStage = true) {
super(NIL,-32,-32);
this.stages = stages;
this.startFromCurStage = startFromCurStage;
this.replayIntro = replayIntro;
startNextStage();
}
void onDeath() {
if(!this.isActive) return;
if(curStage.isActive)
curStage.delete();
if(startFromCurStage) cur--;
else cur = -1;
startNextStage();
}
void startNextStage() {
if(!this.isActive) return;
if(curStage !is null && curStage.isActive) curStage.delete();
if(int(cur++) >= int(stages.length()) || stages.length() == 0) {delete();return;}
if(cur > max) max = cur;
@curStage = @stages[cur]();
}
void behavior() {
if(curStage !is null && !curStage.isActive)
startNextStage();
}
void delete() {
if(curStage !is null && curStage.isActive)
curStage.delete();
CUSTOM::Object::delete();
}
int stageNumber() {return cur;}
CUSTOM::Object@ currentStage() {return curStage;}
}
class String : CUSTOM::Object {
string txt;
STRING::Size stringS;
STRING::Mode stringM;
SPRITE::Mode spriteM;
int par;
String(float xPos, float yPos, string text, STRING::Size stringSize = STRING::MEDIUM, STRING::Mode stringMode = STRING::NORMAL, SPRITE::Mode spriteMode = SPRITE::NORMAL, int param = 0) {
super(NIL,xPos,yPos);
this.txt = text;
this.stringS = stringSize;
this.stringM = stringMode;
this.spriteM = spriteMode;
this.par = param;
}
void set_text(string text) {this.txt = txt;}
string get_text() {return this.txt;}
void set_stringSize(STRING::Size stringSize) {this.stringS = stringSize;}
STRING::Size get_stringSize() {return this.stringS;}
void set_stringMode(STRING::Mode stringMode) {this.stringM = stringMode;}
STRING::Mode get_stringMode() {return this.stringM;}
void set_spriteMode(SPRITE::Mode spriteMode) {this.spriteM = spriteMode;}
SPRITE::Mode get_spriteMode() {return this.spriteM;}
void set_param(int param) {this.par = param;}
int get_param() {return this.par;}
void behavior() {draw();}
int draw() {
if(spriteM != SPRITE::NORMAL)
jjDrawString(xPos,yPos,txt,stringS,jjTEXTAPPEARANCE(stringM),param,spriteM);
else
jjDrawString(xPos,yPos,text,stringS,stringM,param,1);
return -1;
}
}
class FloatingString : String {
COMMON::Timer timer();
COMMON::IFocalPoint@ focus;
float xOffset;
FloatingString(string text, COMMON::IFocalPoint@ focus, float xOffset, float yOffset, int duration) {
super(focus.xPos+xOffset,focus.yPos+yOffset,text,STRING::SMALL);
this.xOffset = xOffset;
@this.focus = @focus;
this.timer.start(duration);
}
void behavior() {
if(!focus.isActive || timer.isFinished()) {delete(); return;}
xPos = focus.xPos + xOffset;
if(jjGameTicks & 1 == 0) yPos = yPos - 1;
draw();
}
}
class Speech {
COMMON::IFocalPoint@ focus;
string text;
int textWidth;
Speech(COMMON::IFocalPoint@ focus, string text, int textWidth) {
@this.focus = @focus; this.text = text; this.textWidth = textWidth;
}
COMMON::IFocalPoint@ getPos() { return focus; }
string getText() { return text; }
int getTextWidth() { return textWidth; }
}
class Dialogue : CUSTOM::Object {
array<GAME::Speech@> dialogue;
int index = -1;
bool endOnDeath;
Dialogue(array<GAME::Speech@> dialogue, bool endOnDeath = true) {
super(NIL,-32,-32);
this.dialogue = dialogue;
this.endOnDeath = endOnDeath;
}
void showNext() {
if(endOnDeath) {
for(uint i = 0; i < dialogue.length(); i++) {
if(!dialogue[i].getPos().isActive) {
delete();
return;
}
}
}
if(uint(++index) < dialogue.length()) {
GAME::Speech@ speech = @dialogue[index];
if(speech.getPos().isActive) {
GAME::FloatingString(speech.getText(),
speech.getPos(),
-speech.getTextWidth()/2,
-32,
getDelay());
}
} else
delete();
}
bool displayNextMessage() {
return false; //Override me
}
int getDelay() {
return 0;
}
void behavior() {
if(displayNextMessage()) showNext();
}
}
class DialogueFixedDelay : Dialogue {
uint delay;
COMMON::Timer timer();
DialogueFixedDelay(array<GAME::Speech@> dialogue, int delay, bool endOnDeath = true) {
super(dialogue,endOnDeath);
this.delay = delay;
}
bool displayNextMessage() {
bool display = false;
if(!timer.isStarted() || timer.isFinished()) {
display = true;
this.timer.start(delay);
}
return display;
}
int getDelay() {
return delay;
}
}
class DialogueScaledDelay : Dialogue {
COMMON::Timer timer();
int delay;
int minDelay;
DialogueScaledDelay(array<GAME::Speech@> dialogue, int minDelay = 0, bool endOnDeath = true) {
super(dialogue,endOnDeath);
this.minDelay = minDelay;
}
bool displayNextMessage() {
bool display = false;
if(!timer.isStarted() || timer.isFinished()) {
display = true;
if(uint(index+1) < dialogue.length()) {
delay = dialogue[index+1].getText().length()*7;
delay = delay < minDelay ? minDelay : delay;
this.timer.start(delay);
}
}
return display;
}
int getDelay() {
return delay;
}
}
class Camera : CUSTOM::Object {
jjPLAYER@ play;
Camera(jjPLAYER@ player) {
super(NIL,player.xPos,player.yPos);
@this.play = @player;
}
float get_xPos() const { return play.cameraX+10*32;}
float get_yPos() const { return play.cameraY+7.5*32;}
void unfreeze() {
play.cameraUnfreeze();
}
void behavior() {
if(isMoving() && moveTimer.elapsedTime() > 1) {
play.cameraFreeze(this.jjObject.xPos,this.jjObject.yPos,true,true);
}
}
}
interface IBossMeter : COMMON::IFocalPoint, MIXIN::IMovable, MIXIN::IPatrollable {
void set_maxEnergy(int maxEnergy);
int get_maxEnergy();
void set_energy(int energy);
int get_energy() const;
void delete();
}
class AbstractBossMeter : CUSTOM::Object, IBossMeter {
int health;
int maxHealth;
COMMON::IFocalPoint@ focus;
int BAR_WIDTH = 136;
int BAR_OFFSET_Y = 17;
int BAR_HEIGHT = 4;
int BAR_COLOR = 0;
AbstractBossMeter(COMMON::IFocalPoint@ focus) {
super(NIL, focus.xPos, focus.yPos);
@this.focus = @focus;
determineCurAnim(ANIM::BOSS,0);
}
void set_maxEnergy(int maxEnergy) {this.maxHealth = maxEnergy;}
int get_maxEnergy() {return maxHealth;}
void set_energy(int energy) {if(energy > maxHealth) this.health = maxHealth;
else if(energy > 0) this.health = energy;
else this.health = 0;}
int get_energy() const {return this.health;}
}
class BossMeterUI : AbstractBossMeter, CUSTOM::IInterfaceElement {
BossMeterUI(float xPos, float yPos) {
super(COMMON::Position(xPos,yPos));
}
//TODO: Sux that we need two different drawing functions.
//Maybe the object draw function should take a canvas somehow?
void behavior() {}
int draw(bool useCustomSpriteProperties = true) const { return -1;}
void draw(jjPLAYER@ player, jjCANVAS@ canvas) {
float percentHealth = float(health)/float(maxHealth);
float xBar = int(xPos-(BAR_WIDTH/2)+BAR_WIDTH*percentHealth);
float yBar = yPos+BAR_OFFSET_Y;
int lenBar = int(xPos+(BAR_WIDTH/2)-xBar);
canvas.drawSprite(int(xPos),int(yPos),ANIM::BOSS,0,0,0);
canvas.drawSprite(int(xPos),int(yPos),ANIM::BOSS,0,1,0);
canvas.drawRectangle(int(xBar),int(yBar),lenBar,BAR_HEIGHT,BAR_COLOR);
}
void delete() {
CUSTOM::Object::delete();
CUSTOM::UI.removeElement(this);
}
}
class BossMeterFloating : AbstractBossMeter {
int yOffset;
BossMeterFloating(COMMON::IFocalPoint@ focus, int yOffset = 0) {
super(focus);
this.yOffset = yOffset;
}
void behavior() {
if(!focus.isActive) delete();
xPos = focus.xPos;
yPos = focus.yPos+yOffset;
draw();
}
int draw(bool useCustomSpriteProperties = true) const {
float percentHealth = float(health)/float(maxHealth);
float xBar = int(xPos-(BAR_WIDTH/2)+BAR_WIDTH*percentHealth);
float yBar = yPos+BAR_OFFSET_Y;
int lenBar = int(xPos+(BAR_WIDTH/2)-xBar);
jjDrawSprite(xPos,yPos,ANIM::BOSS,0,0,0);
jjDrawSprite(xPos,yPos,ANIM::BOSS,0,1,0);
jjDrawRectangle(xBar,yBar,lenBar,BAR_HEIGHT,BAR_COLOR);
return -1;
}
}
}
//--------------------------------------------------------
//END OF SaLLiB
//--------------------------------------------------------
//TODO: Make Particle class
//TODO: Replace patrol with general interpolation chaining
//TODO: Builder style set methods
//TODO: Allow camera/objects to be created globally
//TODO: Consider removing set_xPos/yPos from MIXIN::Composite (since I override them in pileofcheese)
//TODO: Angle offset conventions.
//TODO: Documentation
//TODO: Consider composable interpolations
//TODO: Make boss meter work with multiple local players
//TODO: call delete or some kind of cleanup on death - bug violet even more about delete hook
//TODO: Write full-fledged player class
//TODO: Consider interpolateVar function handle to replace move(), rotate(), scale() etc. Can we make the interpolation mixins more abstract?
Jazz2Online © 1999-INFINITY (Site Credits). We have a Privacy Policy. Jazz Jackrabbit, Jazz Jackrabbit 2, Jazz Jackrabbit Advance and all related trademarks and media are ™ and © Epic Games. Lori Jackrabbit is © Dean Dodrill. J2O development powered by Loops of Fury and Chemical Beats.
Eat your lima beans, Johnny.