Downloads containing academy_entities.asc

Downloads
Name Author Game Mode Rating
TSF with JJ2+ Only: Anniversary Bash 21 Levels Jazz2Online Multiple N/A Download file

File preview

  1. class AbstractBullet : jjBEHAVIORINTERFACE {
  2.         private jjBEHAVIOR nativeBehavior;
  3.         private HANDLING::Bullet nativeBulletHandling;
  4.         private HANDLING::Player nativePlayerHandling;
  5.     AbstractBullet(const jjBEHAVIOR &in setNativeBehavior,
  6.                         const HANDLING::Bullet &in setNativeBulletHandling,
  7.                         const HANDLING::Player &in setNativePlayerHandling) {
  8.                 nativeBehavior = setNativeBehavior;
  9.                 nativeBulletHandling = setNativeBulletHandling;
  10.                 nativePlayerHandling = setNativePlayerHandling;
  11.         }
  12.         void onBehave(jjOBJ@ obj) {
  13.                 initBullet(obj);
  14.         }
  15.         bool onIsRFBullet(jjOBJ@ obj) {
  16.         return nativeBehavior == BEHAVIOR::RFBULLET;
  17.     }
  18.         void initBullet(jjOBJ@ obj) {
  19.                 int ammoCount = jjPlayers[obj.creatorID].ammo[WEAPON::BLASTER];
  20.                 obj.bulletHandling = HANDLING::IGNOREBULLET;
  21.                 obj.playerHandling = HANDLING::SPECIAL;
  22.                 if (debugModeOn) jjAlert("ammoCount: " + ammoCount);
  23.                 if (ammoCount >= BULLET_MAGIC_ARROW-1 && ammoCount <= BULLET_MAGIC_ARROW) {
  24.                         obj.scriptedCollisions = true;
  25.                         obj.behavior = MagicArrow();
  26.                 } else if (ammoCount >= BULLET_ICE_BOLT-1 && ammoCount <= BULLET_ICE_BOLT) {
  27.                         obj.scriptedCollisions = true;
  28.                         obj.behavior = IceBolt();
  29.                 } else if (ammoCount >= BULLET_FIREBALL-1 && ammoCount <= BULLET_FIREBALL) {
  30.                         obj.behavior = Fireball();
  31.                 } else if (fullFeatures) {
  32.                         obj.scriptedCollisions = true;
  33.                         obj.behavior = BulletWrapper(obj, nativeBehavior, nativePlayerHandling);
  34.                 } else {
  35.                         obj.behavior = nativeBehavior;
  36.                         obj.bulletHandling = nativeBulletHandling;
  37.                         obj.playerHandling = nativePlayerHandling;
  38.                 }
  39.                 jjPlayers[obj.creatorID].ammo[WEAPON::BLASTER] = 50;
  40.         }
  41. }
  42.  
  43. abstract class AreaOfEffect {
  44.         float xPos;
  45.         float yPos;
  46.         float radius;
  47.         int damage;
  48.         uint elapsed;
  49.         uint duration;
  50.         jjPLAYER@ caster;
  51.         AreaOfEffect(float setXPos, float setYPos, float setRadius, int setDamage, uint setDuration, jjPLAYER@ setCaster) {
  52.                 xPos = setXPos;
  53.                 yPos = setYPos;
  54.                 radius = setRadius;
  55.                 damage = setDamage;
  56.                 elapsed = duration = setDuration;
  57.                 @caster = setCaster;
  58.         }
  59.         void animate() {}
  60.         void control(jjPLAYER@ play) {
  61.                 float scaledRadius = acUtils::getScaledRadius(radius, ANIM::FLARE, 5);
  62.                 float xDistance = play.xPos - xPos;
  63.                 float yDistance = play.yPos - yPos;
  64.                 if (xDistance*xDistance + yDistance*yDistance < scaledRadius*scaledRadius && acUtils::gameIsRunning()) {
  65.                         play.hurt(damage - players[play.playerID].magicResist, false, caster);
  66.                 }
  67.         }
  68. }
  69.  
  70. class Armageddon : AreaOfEffect {
  71.         int frame;
  72.         float meteorY;
  73.         bool exploding;
  74.         Armageddon(float setXPos, float setYPos, float setRadius, int setDamage, uint setDuration, jjPLAYER@ setCaster) {
  75.                 super(setXPos, setYPos, setRadius, setDamage, setDuration, setCaster);
  76.                 meteorY = setYPos - 640;
  77.                 exploding = false;
  78.                 frame = 4;
  79.         }
  80.         void animate() override {
  81.                 if (exploding) {
  82.                         jjDrawResizedSprite(xPos, yPos, ANIM::AMMO, 81, frame, 14, 14, SPRITE::NORMAL, 0, 1);
  83.                         if (jjGameTicks % 7 == 0) {
  84.                                 if (frame < 11) {
  85.                                         frame++;
  86.                                 }
  87.                         }
  88.                 } else {
  89.                         jjDrawRotatedSprite(xPos, meteorY, ANIM::ROCK, 0, 0, 0, 5, 5, SPRITE::TINTED, 24, 1);
  90.                 }
  91.                 if (meteorY < yPos) {
  92.                         meteorY += 32;
  93.                 } else if (!exploding) {
  94.                         exploding = true;
  95.                         jjSamplePriority(SOUND::INTRO_BOEM2);
  96.                 }
  97.         }
  98.         void control(jjPLAYER@ play) override {
  99.                 if (playerInRange(play, true) && exploding) {
  100.                         play.hurt(damage - players[play.playerID].magicResist, false, caster);
  101.                 }
  102.         }
  103.         bool playerInRange(jjPLAYER@ play, bool vulnerable) {
  104.                 float scaledRadius = acUtils::getScaledRadius(radius, ANIM::FLARE, 5);
  105.                 float xDistance = play.xPos - xPos;
  106.                 float yDistance = play.yPos - yPos;
  107.                 if (vulnerable) {
  108.                         return xDistance*xDistance + yDistance*yDistance < scaledRadius*scaledRadius && acUtils::gameIsRunning()
  109.                                 && acUtils::checkIfPlayerIsVulnerable(caster, play, cast<Spell@>(spells["A"]));
  110.                 }
  111.                 return xDistance*xDistance + yDistance*yDistance < scaledRadius*scaledRadius;
  112.         }
  113. }
  114.  
  115. class Bless : Effect {
  116.         Bless(jjPLAYER@ setPlay, string setName, SPELL setEnumValue, SPELL setCounterEffect,
  117.                         uint setDuration) {
  118.                 super(setPlay, setName, setEnumValue, setCounterEffect, setDuration);
  119.         }
  120.         void affect() override {}
  121. }
  122.  
  123. class BloodLust : Effect {
  124.         int originalFastFire;
  125.         BloodLust(jjPLAYER@ setPlay, string setName, SPELL setEnumValue, SPELL setCounterEffect,
  126.                         uint setDuration, bool setIsLocal) {
  127.                 super(setPlay, setName, setEnumValue, setCounterEffect, setDuration, setIsLocal);
  128.                 originalFastFire = setPlay.fastfire;
  129.         }
  130.         ~BloodLust() {
  131.                 play.fastfire = originalFastFire;
  132.         }
  133.         void affect() override {
  134.                 play.fastfire = 6;
  135.         }
  136. }
  137.  
  138. class BulletWrapper : jjBEHAVIORINTERFACE {
  139.     private jjBEHAVIOR nativeBehavior;
  140.         private HANDLING::Player nativePlayerHandling;
  141.     BulletWrapper(jjOBJ@ obj, const jjBEHAVIOR &in setNativeBehavior, const HANDLING::Player &in setNativeHandling) {
  142.                 nativeBehavior = setNativeBehavior;
  143.                 nativePlayerHandling = setNativeHandling;
  144.         }
  145.     void onBehave(jjOBJ@ obj) {
  146.                 if (obj.state == STATE::KILL) {
  147.                         obj.delete();
  148.                 }
  149.                 else {
  150.                         controlObjectCollision(obj);
  151.                         obj.behave(nativeBehavior);
  152.                 }
  153.     }
  154.         bool onObjectHit(jjOBJ@ obj, jjOBJ@ bullet, jjPLAYER@ play, int force) {
  155.                 if (@play != null && acUtils::gameIsRunning()) {
  156.                         jjPLAYER@ creator = jjPlayers[obj.creatorID];
  157.                         if (obj.creatorType == CREATOR::PLAYER && creator.isEnemy(play) &&
  158.                                         play.blink == 0 && creator.blink == 0) {
  159.                                 int8 damage = obj.var[6] & 8 != 0 ? 2 : 1;
  160.                                 if (jjPlayers[creator.playerID].isZombie) damage++;
  161.                                 if (debugModeOn) jjAlert("damage now: " + damage);
  162.                                
  163.                                 damage += acUtils::getDamageModifier(players[creator.playerID], players[play.playerID]);
  164.                                
  165.                                 if (damage < 0) damage = 0;
  166.                                 if (debugModeOn) jjAlert("damage then: " + damage);
  167.                                
  168.                                 play.hurt(damage, false, creator);
  169.                                 obj.state = STATE::KILL;
  170.                         }
  171.                         return true;
  172.                 }
  173.                 return false;
  174.         }
  175. }
  176.  
  177. class ChainLightningTarget {
  178.         int8 id;
  179.         int8 parentID;
  180.         int8 playerID;
  181.         int8 damage;
  182.         uint elapsed;
  183.         uint duration;
  184.         ChainLightningTarget(int8 setID, int8 setPlayerID, int8 setDamage, uint setDuration, int8 setParentID = -1) {
  185.                 id = setID;
  186.                 playerID = setPlayerID;
  187.                 damage = setDamage;
  188.                 duration = setDuration;
  189.                 elapsed = setDuration;
  190.                 parentID = setParentID;
  191.         }
  192. }
  193.  
  194. class Chunk {
  195.         array<jjOBJ@> objects;
  196.         void addObject(jjOBJ@ obj) {
  197.                 objects.insertLast(obj);
  198.         }
  199.         void clearObjects() {
  200.                 objects.removeRange(0, objects.length);
  201.         }
  202. }
  203.  
  204. class DeathRipple : AreaOfEffect {
  205.         int frame;
  206.         float range;
  207.         DeathRipple(float setXPos, float setYPos, float setRadius, int setDamage, uint setDuration, jjPLAYER@ setCaster) {
  208.                 super(setXPos, setYPos, setRadius, setDamage, setDuration, setCaster);
  209.                 frame = 0;
  210.         }
  211.         void animate() override {
  212.                 for (int i = 0; i < 32; i++) {
  213.                         jjPLAYER@ play = jjPlayers[i];
  214.                         if (playerInRange(play, false)) {
  215.                                 jjDrawRotatedSprite(play.xPos, play.yPos, ANIM::AMMO, 66, frame, 0, 2, 2, SPRITE::SINGLECOLOR, 24);
  216.                         }
  217.                 }
  218.                 if (jjGameTicks % 5 == 0) {
  219.                         if (frame < 8) {
  220.                                 frame++;
  221.                         } else {
  222.                                 frame = 0;
  223.                         }
  224.                 }
  225.         }
  226.         void control(jjPLAYER@ play) override {
  227.                 if (playerInRange(play, true)) {
  228.                         play.hurt(damage - players[play.playerID].magicResist, false, caster);
  229.                 }
  230.         }
  231.         bool playerInRange(jjPLAYER@ play, bool vulnerable) {
  232.                 float scaledRadius = acUtils::getScaledRadius(radius, ANIM::FLARE, 5);
  233.                 float xDistance = play.xPos - xPos;
  234.                 float yDistance = play.yPos - yPos;
  235.                 if (vulnerable) {
  236.                         return xDistance*xDistance + yDistance*yDistance < scaledRadius*scaledRadius && acUtils::gameIsRunning()
  237.                                 && acUtils::checkIfPlayerIsVulnerable(caster, play, cast<Spell@>(spells["H"]));
  238.                 }
  239.                 return xDistance*xDistance + yDistance*yDistance < scaledRadius*scaledRadius;
  240.         }
  241. }
  242.  
  243. class DisruptingRay : Effect {
  244.         DisruptingRay(jjPLAYER@ setPlay, string setName, SPELL setEnumValue, SPELL setCounterEffect,
  245.                         uint setDuration, bool setIsLocal, bool setIsCurse) {
  246.                 super(setPlay, setName, setEnumValue, setCounterEffect, setDuration, setIsLocal, setIsCurse);
  247.         }
  248.         void affect() override {}
  249. }
  250.  
  251. abstract class Effect {
  252.         bool isLocal;
  253.         bool isCurse;
  254.         jjPLAYER@ play;
  255.         string name;
  256.         SPELL enumValue;
  257.         SPELL counterEffect;
  258.         uint elapsed;
  259.         uint duration;
  260.         Effect() {}
  261.         Effect(jjPLAYER@ setPlay, string setName, SPELL setEnumValue, SPELL setCounterEffect, uint setDuration,
  262.                         bool setIsLocal = false, bool setIsCurse = false) {
  263.                 @play = setPlay;
  264.                 name = setName;
  265.                 enumValue = setEnumValue;
  266.                 counterEffect = setCounterEffect;
  267.                 duration = setDuration;
  268.                 isLocal = setIsLocal;
  269.                 isCurse = setIsCurse;
  270.                 elapsed = setDuration;
  271.         }
  272.         void affect() {}
  273. }
  274.  
  275. class Fireball : jjBEHAVIORINTERFACE {
  276.         uint8 frame;
  277.         Fireball() {
  278.                 frame = 0;
  279.         }
  280.         void onBehave(jjOBJ@ obj) {
  281.                 switch(obj.state) {
  282.                         case STATE::START:
  283.                                 jjSample(obj.xPos, obj.yPos, SOUND::AMMO_FIREGUN1A, 0, 7500);
  284.                                 obj.direction = jjPlayers[obj.creatorID].direction;
  285.                                 obj.xPos += obj.direction >= 0 ? TILE : -TILE;
  286.                                 obj.state = STATE::FLY;
  287.                                 obj.light = 125;
  288.                                 break;
  289.                         case STATE::FLY:
  290.                         {
  291.                                 jjPLAYER@ caster = jjPlayers[obj.creatorID];
  292.                                 for (int i = 0; i < 32; i++) {
  293.                                         jjPLAYER@ play = jjPlayers[i];
  294.                                         if (obj.doesCollide(play, true) && play !is caster) {
  295.                                                 explode(obj, caster);
  296.                                         }
  297.                                 }
  298.                                 obj.xPos += obj.direction >= 0 ? 10 : -10;
  299.                                 if (obj.xPos < 0 || obj.xPos > jjLayerWidth[4] * TILE) {
  300.                                         obj.delete();
  301.                                 } else if ((jjLayers[4].maskedVLine(int(obj.xPos+TILE), int(obj.yPos), 1))
  302.                                                 || (jjLayers[4].maskedVLine(int(obj.xPos-TILE), int(obj.yPos), 1))) {
  303.                                         explode(obj, caster);
  304.                                 }
  305.                         }
  306.                         break;
  307.                 }
  308.         }
  309.         void onDraw(jjOBJ@ obj) {
  310.                 int angle = obj.direction >= 0 ? 0 : 540;
  311.                 jjDrawRotatedSprite(obj.xPos, obj.yPos, ANIM::AMMO, 14, frame, angle, 2, 2);
  312.                 if (frame < 7) frame++;
  313.                 else frame = 0;
  314.         }
  315.         void explode(jjOBJ@ obj, jjPLAYER@ caster) {
  316.                 acSpells::doAOE(caster, SPELL_FIREBALL, obj.xPos, obj.yPos);
  317.                 obj.delete();
  318.         }
  319.         bool onObjectHit(jjOBJ@ obj, jjOBJ@ bullet, jjPLAYER@ player, int force) {
  320.                 return false;
  321.         }
  322. }
  323.  
  324. class FireballExplosion : AreaOfEffect {
  325.         int frame;
  326.         FireballExplosion(float setXPos, float setYPos, float setRadius, int setDamage, uint setDuration, jjPLAYER@ setCaster) {
  327.                 super(setXPos, setYPos, setRadius, setDamage, setDuration, setCaster);
  328.                 frame = 0;
  329.         }
  330.         void animate() override {
  331.                 jjDrawResizedSprite(xPos, yPos, ANIM::AMMO, 81, frame, radius, radius, SPRITE::NORMAL, 0, 1);
  332.                 if (jjGameTicks % 7 == 0) {
  333.                         if (frame < 11) {
  334.                                 frame++;
  335.                         }
  336.                 }
  337.         }
  338.         void control(jjPLAYER@ play) override {
  339.                 float scaledRadius = acUtils::getScaledRadius(radius, ANIM::AMMO, frame, 81);
  340.                 float xDistance = play.xPos - xPos;
  341.                 float yDistance = play.yPos - yPos;
  342.                 if (xDistance*xDistance + yDistance*yDistance < scaledRadius*scaledRadius && acUtils::gameIsRunning()) {
  343.                         play.hurt(damage - players[play.playerID].magicResist, false, caster);
  344.                 }
  345.         }
  346. }
  347.  
  348. class Forgetfulness : Effect {
  349.         Forgetfulness(jjPLAYER@ setPlay, string setName, SPELL setEnumValue, SPELL setCounterEffect,
  350.                         uint setDuration, bool setIsLocal, bool setIsCurse) {
  351.                 super(setPlay, setName, setEnumValue, setCounterEffect, setDuration, setIsLocal, setIsCurse);
  352.         }
  353.         void affect() override {
  354.                 play.noFire = true;
  355.         }
  356. }
  357.  
  358. class Frenzy : Effect {
  359.         Frenzy(jjPLAYER@ setPlay, string setName, SPELL setEnumValue, SPELL setCounterEffect,
  360.                         uint setDuration) {
  361.                 super(setPlay, setName, setEnumValue, setCounterEffect, setDuration);
  362.         }
  363.         void affect() override {}
  364. }
  365.  
  366. class FrostRing : AreaOfEffect {
  367.         int frame;
  368.         FrostRing(float setXPos, float setYPos, float setRadius, int setDamage, uint setDuration, jjPLAYER@ setCaster) {
  369.                 super(setXPos, setYPos, setRadius, setDamage, setDuration, setCaster);
  370.                 frame = 0;
  371.         }
  372.         void animate() override {
  373.                 jjDrawResizedSprite(xPos, yPos, ANIM::AMMO, 82, frame, radius, radius, SPRITE::TINTED, 32, 1);
  374.                 if (jjGameTicks % 7 == 0) {
  375.                         if (frame < 11) {
  376.                                 frame++;
  377.                         }
  378.                 }
  379.         }
  380.         void control(jjPLAYER@ play) override {
  381.                 float scaledRadius = acUtils::getScaledRadius(radius, ANIM::AMMO, frame, 82);
  382.                 float xDistance = play.xPos - xPos;
  383.                 float yDistance = play.yPos - yPos;
  384.                 if (xDistance*xDistance + yDistance*yDistance < scaledRadius*scaledRadius && acUtils::gameIsRunning()
  385.                                 && play !is caster) {
  386.                         play.hurt(damage - players[play.playerID].magicResist, false, caster);
  387.                         play.freeze();
  388.                 }
  389.         }
  390. }
  391.  
  392. class GemMine {
  393.         int8 ownerID;
  394.         int captureElapsed, lockElapsed, yieldElapsed;
  395.         int captureBegin = 350;
  396.         int lockBegin = 2100;
  397.         int yieldBegin = 350;
  398.         array<int8> playersInRange;
  399.         GemMine() {
  400.                 ownerID = -1;
  401.                 captureElapsed = captureBegin;
  402.                 yieldElapsed = yieldBegin;
  403.                 lockElapsed = lockBegin;
  404.         }
  405.         void capture(int8 setOwnerID) {
  406.                 captureElapsed = captureBegin;
  407.                 lockElapsed = lockBegin;
  408.                 ownerID = setOwnerID;
  409.         }
  410.         void control(jjPLAYER@ localPlayer, Player@ asPlayer) {
  411.                 if (acUtils::gameIsRunning()) {
  412.                         if (lockElapsed > 0) {
  413.                                 lockElapsed--;
  414.                         } else {
  415.                                 for (int8 i = 0; i < 32; i++) {
  416.                                         jjPLAYER@ play = jjPlayers[i];
  417.                                         int index = playersInRange.find(play.playerID);
  418.                                         if (index < 0 && playerInRange(play)) {
  419.                                                 playersInRange.insertLast(play.playerID);
  420.                                         } else if (index >= 0 && !playerInRange(play)) {
  421.                                                 playersInRange.removeAt(index);
  422.                                         }
  423.                                 }
  424.                                 if (playerInRange(localPlayer) && !asPlayer.isDead
  425.                                                 && localPlayer.playerID != ownerID && playersInRange.length == 1) {
  426.                                         if (captureElapsed > 0) {
  427.                                                 if (captureElapsed == captureBegin) {
  428.                                                         jjAlert("Capturing gem mine in..." + captureElapsed / SECOND, false, STRING::MEDIUM);
  429.                                                 } else if (captureElapsed % SECOND == 0) {
  430.                                                         jjAlert("" + captureElapsed / SECOND, false, STRING::MEDIUM);
  431.                                                 }
  432.                                                 captureElapsed--;
  433.                                         } else {
  434.                                                 capture(localPlayer.playerID);
  435.                                                 acNetworking::sendCaptureGemMinePacket(localPlayer.playerID);
  436.                                         }
  437.                                 } else {
  438.                                         captureElapsed = captureBegin;
  439.                                 }
  440.                         }
  441.                         if (ownerID >= 0 && jjPlayers[ownerID].isLocal) {
  442.                                 yieldGem(jjPlayers[ownerID]);
  443.                         }
  444.                 }
  445.         }
  446.         void yieldGem(jjPLAYER@ play) {
  447.                 if (yieldElapsed > 0) {
  448.                         yieldElapsed--;
  449.                 } else {
  450.                         yieldElapsed = yieldBegin;
  451.                         jjAddObject(OBJECT::PURPLEGEM, play.xPos, play.yPos);
  452.                 }
  453.         }
  454.         void draw(jjCANVAS@ canvas) {
  455.                 uint8 color, _ = 0;
  456.                 string ownerName = "";
  457.                 if (ownerID >= 0) {
  458.                         jjPLAYER@ owner = jjPlayers[ownerID];
  459.                         owner.furGet(_, color, _, _);
  460.                         ownerName = owner.name;
  461.                 }
  462.                 jjTEXTAPPEARANCE centeredText();
  463.                 centeredText.align = STRING::CENTER;
  464.                 canvas.drawSprite(49*TILE, 84*TILE, ANIM::FLAG, 3, 0, 0, SPRITE::SINGLECOLOR, color);
  465.                 canvas.drawString(49*TILE, 85*TILE, "Owned by\n" + ownerName, STRING::SMALL, centeredText);
  466.                 if (acUtils::gameIsRunning() && lockElapsed > 0) {
  467.                         canvas.drawString(49*TILE, 85*TILE, "\n\nUnlocking in " + int(lockElapsed / SECOND), STRING::SMALL, centeredText);
  468.                 }
  469.         }
  470.         bool playerInRange(jjPLAYER@ play) {
  471.                 return play.xPos >= GEM_MINE_LEFT*TILE && play.xPos <= GEM_MINE_RIGHT*TILE
  472.                                 && play.yPos >= GEM_MINE_TOP*TILE && play.yPos <= GEM_MINE_BOTTOM*TILE;
  473.         }
  474. }
  475.  
  476. class Key {
  477.         int keyCode;
  478.         string key;
  479.         bool keyPressed;
  480.         Key() {}
  481.         Key(int setKeyCode, string setKey) {
  482.                 keyCode = setKeyCode;
  483.                 key = setKey;
  484.                 keyPressed = false;
  485.         }
  486. }
  487.  
  488. class IceBolt : jjBEHAVIORINTERFACE {
  489.         int frame;
  490.         IceBolt() {
  491.                 frame = 0;
  492.         }
  493.         void onBehave(jjOBJ@ obj) {
  494.                 switch(obj.state) {
  495.                         case STATE::START:
  496.                                 jjSample(obj.xPos, obj.yPos, SOUND::AMMO_ICEPU4);
  497.                                 obj.direction = jjPlayers[obj.creatorID].direction;
  498.                                 obj.xPos += obj.direction >= 0 ? 16 : -16;
  499.                                 obj.state = STATE::FLY;
  500.                                 obj.var[6] = 16;
  501.                                 obj.light = 100;
  502.                                 break;
  503.                         case STATE::FLY:
  504.                                 for (int i = 0; i < 32; i++) {
  505.                                         jjPLAYER@ play = jjPlayers[i];
  506.                                         jjPLAYER@ caster = jjPlayers[obj.creatorID];
  507.                                         Player@ asCaster = players[caster.playerID];
  508.                                         if (abs(obj.xPos - play.xPos) <= 32 && abs(obj.yPos - play.yPos) <= 32) {
  509.                                                 if (obj.doesCollide(play, true) && caster.isEnemy(play)
  510.                                                                 && acUtils::gameIsRunning()) {
  511.                                                         Spell@ spell = cast<Spell@>(spells["E"]);
  512.                                                         play.hurt(spell.baseDamage + asCaster.spellDamageBonus - players[play.playerID].magicResist,
  513.                                                                         false, caster);
  514.                                                         play.freeze();
  515.                                                 }
  516.                                         }
  517.                                 }
  518.                                 obj.xPos += obj.direction >= 0 ? 10 : -10;
  519.                                 if (obj.xPos < 0 || obj.xPos > jjLayerWidth[4] * TILE) {
  520.                                         obj.delete();
  521.                                 }
  522.                                 controlObjectCollision(obj);
  523.                                 break;
  524.                         case STATE::KILL:
  525.                                 obj.state = STATE::FLY;
  526.                                 break;
  527.                         case STATE::EXPLODE:
  528.                         default:
  529.                                 obj.state = STATE::FLY;
  530.                                 break;
  531.                 }
  532.         }
  533.         void onDraw(jjOBJ@ obj) {
  534.                 int angle = obj.direction >= 0 ? 0 : 540;
  535.                 jjDrawRotatedSprite(obj.xPos, obj.yPos, ANIM::AMMO, 11, frame, angle, 2, 2, SPRITE::TINTED, 35);
  536.                
  537.                 if (frame < 7) frame++;
  538.                 else frame = 0;
  539.         }
  540.         bool onObjectHit(jjOBJ@ obj, jjOBJ@ bullet, jjPLAYER@ player, int force) {
  541.                 return false;
  542.         }
  543. }
  544.  
  545. class Implosion : AreaOfEffect {
  546.         float scale;
  547.         int8[] targets;
  548.         bool finished;
  549.         Implosion(float setXPos, float setYPos, float setRadius, int setDamage, uint setDuration, jjPLAYER@ setCaster) {
  550.                 super(setXPos, setYPos, setRadius, setDamage, setDuration, setCaster);
  551.                 scale = 5;
  552.                 finished = false;
  553.                
  554.                 for (int i = 0; i < 32; i++) {
  555.                         jjPLAYER@ play = jjPlayers[i];
  556.                         float scaledRadius = acUtils::getScaledRadius(radius, ANIM::FLARE, 5);
  557.                         Spell@ spell = cast<Spell@>(spells["Q"]);
  558.                        
  559.                         if (acUtils::checkIfPlayerIsInRange(setCaster, play, scaledRadius) && acUtils::checkIfPlayerIsVulnerable(caster, play, spell)
  560.                                         && acUtils::gameIsRunning() && targets.find(play.playerID) < 0) {
  561.                                 if (debugModeOn) jjAlert("" + play.playerID);
  562.                                 targets.insertLast(play.playerID);
  563.                         }
  564.                 }
  565.         }
  566.         void animate() override {
  567.                 for (uint i = 0; i < targets.length; i++) {
  568.                         jjPLAYER@ play = jjPlayers[targets[i]];
  569.                         jjDrawRotatedSprite(play.xPos, play.yPos, ANIM::BOLLPLAT, 0, 0, 0, scale, scale, SPRITE::SINGLECOLOR, 1);
  570.                 }
  571.                 if (scale > 0) {
  572.                         scale -= 0.1;
  573.                 } else if (!finished) {
  574.                         finished = true;
  575.                         for (uint i = 0; i < targets.length; i++) {
  576.                                 jjPLAYER@ play = jjPlayers[targets[i]];
  577.                                
  578.                                 if (play.isLocal) {
  579.                                         play.hurt(damage - players[play.playerID].magicResist, true, caster);
  580.                                         jjSamplePriority(SOUND::SMALTREE_GROUND);
  581.                                 }
  582.                         }
  583.                 }
  584.         }
  585.         void control(jjPLAYER@ play) override {}
  586.         bool playerInRange(jjPLAYER@ play, bool vulnerable) {
  587.                 float scaledRadius = acUtils::getScaledRadius(radius, ANIM::FLARE, 5);
  588.                 float xDistance = play.xPos - xPos;
  589.                 float yDistance = play.yPos - yPos;
  590.                
  591.                 if (vulnerable) {
  592.                         return xDistance*xDistance + yDistance*yDistance < scaledRadius*scaledRadius && acUtils::gameIsRunning()
  593.                                 && acUtils::checkIfPlayerIsVulnerable(caster, play, cast<Spell@>(spells["Q"]));
  594.                 }
  595.                 return xDistance*xDistance + yDistance*yDistance < scaledRadius*scaledRadius;
  596.         }
  597. }
  598.  
  599. class MagicArrow : jjBEHAVIORINTERFACE {
  600.         void onBehave(jjOBJ@ obj) {
  601.                 switch(obj.state) {
  602.                         case STATE::START:
  603.                                 jjSample(obj.xPos, obj.yPos, SOUND::AMMO_BULFL1);
  604.                                 obj.direction = jjPlayers[obj.creatorID].direction;
  605.                                 obj.xPos += obj.direction >= 0 ? 16 : -16;
  606.                                 obj.state = STATE::FLY;
  607.                                 obj.var[6] = 16;
  608.                                 obj.light = 100;
  609.                                 break;
  610.                         case STATE::FLY:
  611.                         {
  612.                                 for (int i = 0; i < 32; i++) {
  613.                                         jjPLAYER@ play = jjPlayers[i];
  614.                                         jjPLAYER@ caster = jjPlayers[obj.creatorID];
  615.                                         Player@ asCaster = players[caster.playerID];
  616.                                         if (abs(obj.xPos - play.xPos) <= 32 && abs(obj.yPos - play.yPos) <= 32) {
  617.                                                 if (obj.doesCollide(play, true) && caster.isEnemy(play)
  618.                                                                 && acUtils::gameIsRunning()) {
  619.                                                         Spell@ spell = cast<Spell@>(spells["M"]);
  620.                                                         play.hurt(spell.baseDamage + asCaster.spellDamageBonus - players[play.playerID].magicResist,
  621.                                                         false, caster);
  622.                                                 }
  623.                                         }
  624.                                 }
  625.                                 obj.xPos += obj.direction >= 0 ? 10 : -10;
  626.                                 if (obj.xPos < 0 || obj.xPos > jjLayerWidth[4] * TILE) {
  627.                                         obj.delete();
  628.                                 }
  629.                                 controlObjectCollision(obj);
  630.                         }
  631.                         break;
  632.                         case STATE::KILL:
  633.                                 obj.state = STATE::FLY;
  634.                                 break;
  635.                         case STATE::EXPLODE:
  636.                         default:
  637.                                 obj.state = STATE::FLY;
  638.                                 break;
  639.                 }
  640.         }
  641.         void onDraw(jjOBJ@ obj) {
  642.                 int angle = obj.direction >= 0 ? 950 : 425;
  643.                 jjDrawRotatedSprite(obj.xPos, obj.yPos, ANIM::FLAG, 0, 0, angle, 1.5, 1.5, SPRITE::MENUPLAYER);
  644.                 obj.particlePixelExplosion(1);
  645.         }
  646.         bool onObjectHit(jjOBJ@ obj, jjOBJ@ bullet, jjPLAYER@ player, int force) {
  647.                 return true;
  648.         }
  649. }
  650.  
  651. class Player {
  652.         int mana;
  653.         int maxMana;
  654.         int manaRegenCounter;
  655.         int manaRegenCounterEnd = 140;
  656.         int manaRegenRate;
  657.         int cooldown;
  658.         int8 playerID;
  659.         int8 magicResist;
  660.         int8 spellDamageBonus;
  661.         uint32 originalFur;
  662.         uint8 currentHealth;
  663.         uint spellDurationBonus;
  664.         bool isDead;
  665.         bool isChanneling;
  666.         string selectedSpellKey;
  667.         array<SKILL> activeSkills;
  668.         array<Effect@> activeEffects;
  669.         Player() {
  670.                 isDead = false;
  671.                 isChanneling = false;
  672.                 selectedSpellKey = "";
  673.                 mana = STARTING_MANA;
  674.                 maxMana = DEFAULT_MAX_MANA;
  675.                 manaRegenCounter = 0;
  676.                 manaRegenRate = DEFAULT_MANA_REGEN_RATE;
  677.                 playerID = 0;
  678.                 cooldown = 0;
  679.                 magicResist = 0;
  680.                 spellDamageBonus = 0;
  681.                 spellDurationBonus = 0;
  682.         }
  683.         ~Player() {
  684.                 jjPLAYER@ play = jjPlayers[playerID];
  685.                 if (originalFur != 0 && !play.isZombie) {
  686.                         play.fur = originalFur;
  687.                 }
  688.         }
  689.         void addNewRandomSkill() {
  690.                 if (activeSkills.length < skills.length) {
  691.                         int index = 0;
  692.                         SKILL randomSkill = SKILL_MAX_MANA;
  693.                         while (index >= 0) {
  694.                                 uint random = jjRandom() % skills.length;
  695.                                 randomSkill = SKILL(random);
  696.                                 index = activeSkills.find(randomSkill);
  697.                         }
  698.                         Skill@ skill = skills[skills.find(Skill(randomSkill))];
  699.                         jjAlert("You learned " + skill.name + "! (||" + skill.description + "|||||)");
  700.                         acUtils::setSkillBonus(this, randomSkill, playerID);
  701.                         activeSkills.insertLast(randomSkill);
  702.                 }
  703.         }
  704.         bool isExistingEffectOrCounterEffect(Effect@ activeEffect, Effect@ newEffect) {
  705.                 return activeEffect.enumValue == newEffect.enumValue
  706.                                 || activeEffect.enumValue == newEffect.counterEffect;
  707.         }
  708.         void setNewActiveEffect(Effect@ newEffect) {
  709.                 int j = 0;
  710.                 int activeEffectsLength = activeEffects.length();
  711.                 for (int i = 0; i < activeEffectsLength; i++) {
  712.                         if (!isExistingEffectOrCounterEffect(activeEffects[i], newEffect)) {
  713.                                 @activeEffects[j] = activeEffects[i];
  714.                                 j++;
  715.                         }
  716.                 }
  717.                 activeEffects.removeRange(j, activeEffectsLength - j);
  718.                 activeEffects.insertLast(newEffect);
  719.         }
  720.         void setSelectedSpellKey(string newSelectedSpellKey) {
  721.                 selectedSpellKey = newSelectedSpellKey;
  722.         }
  723.         void setChanneledSpellKey(string newSelectedSpellKey) {
  724.                 selectedSpellKey = newSelectedSpellKey;
  725.                 isChanneling = true;
  726.         }
  727.         void unSetChanneledSpellKey() {
  728.                 selectedSpellKey = "";
  729.                 isChanneling = false;
  730.         }
  731.         void removeActiveEffect(uint index) {
  732.                 activeEffects.removeAt(index);
  733.         }
  734.         void removeAllActiveEffects() {
  735.                 activeEffects.removeRange(0, activeEffects.length);
  736.         }
  737.         void removeNegativeEffects() {
  738.                 int j = 0;
  739.                 int activeEffectsLength = activeEffects.length();
  740.                 for (int i = 0; i < activeEffectsLength; i++) {
  741.                         if (!activeEffects[i].isCurse) {
  742.                                 @activeEffects[j] = activeEffects[i];
  743.                                 j++;
  744.                         }
  745.                 }
  746.                 activeEffects.removeRange(j, activeEffectsLength - j);
  747.         }
  748.         void regenerateMana(jjPLAYER@ play) {
  749.                 if (manaRegenCounter < manaRegenCounterEnd) {
  750.                         manaRegenCounter += isInMagicWell(play) ? manaRegenRate * 2 : manaRegenRate;
  751.                 } else {
  752.                         manaRegenCounter = 0;
  753.                         if (mana < maxMana) {
  754.                                 mana++;
  755.                         }
  756.                 }
  757.         }
  758.         bool hasForgetfulness() {
  759.                 for (uint i = 0; i < activeEffects.length; i++) {
  760.                         if (activeEffects[i].enumValue == SPELL_FORGETFULNESS) {
  761.                                 return true;
  762.                         }
  763.                 }
  764.                 return false;
  765.         }
  766.         bool isInMagicWell(jjPLAYER@ play) {
  767.                 return play.xPos > MAGIC_WELL_LEFT * TILE && play.xPos < MAGIC_WELL_RIGHT * TILE
  768.                                 && play.yPos > MAGIC_WELL_TOP * TILE;
  769.         }
  770. }
  771.  
  772. class Precision : Effect {
  773.         Precision(jjPLAYER@ setPlay, string setName, SPELL setEnumValue, SPELL setCounterEffect,
  774.                         uint setDuration) {
  775.                 super(setPlay, setName, setEnumValue, setCounterEffect, setDuration);
  776.         }
  777.         void affect() override {}
  778. }
  779.  
  780. class Skill {
  781.         SKILL enumValue;
  782.         string name;
  783.         string description;
  784.         Skill(SKILL setEnumValue) {
  785.                 enumValue = setEnumValue;
  786.         }
  787.         Skill(SKILL setEnumValue, string setName, string setDescription) {
  788.                 enumValue = setEnumValue;
  789.                 name = setName;
  790.                 description = setDescription;
  791.         }
  792.         bool opEquals(Skill@ other) {
  793.                 return enumValue == other.enumValue;
  794.         }
  795. }
  796.  
  797. class Slow : Effect {
  798.         Slow(jjPLAYER@ setPlay, string setName, SPELL setEnumValue, SPELL setCounterEffect,
  799.                         uint setDuration, bool setIsLocal, bool setIsCurse) {
  800.                 super(setPlay, setName, setEnumValue, setCounterEffect, setDuration, setIsLocal, setIsCurse);
  801.         }
  802.         void affect() override {
  803.                 if (play.xSpeed > SLOW_SPEED) {
  804.                         play.xSpeed = SLOW_SPEED;
  805.                 } else if (play.xSpeed < -SLOW_SPEED) {
  806.                         play.xSpeed = -SLOW_SPEED;
  807.                 }
  808.         }
  809. }
  810.  
  811. class Spell {
  812.         string key;
  813.         string name;
  814.         SPELL enumValue;
  815.         SPELL counterSpell;
  816.         uint tier;
  817.         uint numpad;
  818.         int localCount;
  819.         int baseDamage;
  820.         uint baseDuration;
  821.         int baseManaCost;
  822.         float channelingTime;
  823.         float radius;
  824.         bool damagesAll;
  825.         string description;
  826.         Spell() {}
  827.         Spell(string setKey, string setName, SPELL setEnumValue, SPELL setCounterSpell, uint setTier,
  828.                         uint setNumpad, int setBaseDamage, uint setBaseDuration, int setBaseManaCost,
  829.                         float setChannelingTime, float setRadius, bool setDamagesSelf, string setDescription) {
  830.                 key = setKey;
  831.                 name = setName;
  832.                 enumValue = setEnumValue;
  833.                 counterSpell = setCounterSpell;
  834.                 tier = setTier;
  835.                 numpad = setNumpad;
  836.                 baseDamage = setBaseDamage;
  837.                 baseDuration = setBaseDuration;
  838.                 baseManaCost = setBaseManaCost;
  839.                 channelingTime = setChannelingTime;
  840.                 radius = setRadius;
  841.                 damagesAll = setDamagesSelf;
  842.                 description = setDescription;
  843.                
  844.                 localCount = 0;
  845.         }
  846.         int opCmp(const Spell@ otherSpell) {
  847.                 return numpad - otherSpell.numpad;
  848.         }
  849. }
  850.  
  851. class StoneSkin : Effect {
  852.         StoneSkin(jjPLAYER@ setPlay, string setName, SPELL setEnumValue, SPELL setCounterEffect,
  853.                         uint setDuration) {
  854.                 super(setPlay, setName, setEnumValue, setCounterEffect, setDuration);
  855.         }
  856.         void affect() override {}
  857. }
  858.  
  859. class VisualGem {
  860.         float x, y, scale;
  861.         int angle, color, frame;
  862.         VisualGem(float setX, float setY) {
  863.                 x = setX;
  864.                 y = setY;
  865.                 angle = jjRandom() % 1024;
  866.                 color = jjRandom() % 12;
  867.                 //scale = jjRandom() % 40 * 0.01 + 0.1;
  868.                 scale = 0.3;
  869.                 frame = jjRandom() % 8;
  870.         }
  871.         void draw(jjCANVAS@ canvas) {
  872.                 canvas.drawRotatedSprite(int(x*TILE), int(y*TILE), ANIM::PICKUPS, 34, frame, angle, scale, scale, SPRITE::GEM, color);
  873.         }
  874. }
  875.  
  876. class WallOfFire {
  877.         int damage;
  878.         int frame;
  879.         int channel;
  880.         int8 height;
  881.         float xOrigin;
  882.         float yOrigin;
  883.         uint elapsed;
  884.         uint duration;
  885.         jjPLAYER@ caster;
  886.         WallOfFire(float setXOrigin, float setYOrigin, int8 setHeight, int setDamage, uint setDuration, jjPLAYER@ setCaster) {
  887.                 xOrigin = setXOrigin;
  888.                 yOrigin = setYOrigin;
  889.                 height = setHeight;
  890.                 damage = setDamage;
  891.                 elapsed = duration = setDuration;
  892.                 @caster = setCaster;
  893.                 frame = 1;
  894.                 channel = 0;
  895.                 if (height > WALL_OF_FIRE_MAX_HEIGHT) {
  896.                         height = WALL_OF_FIRE_MAX_HEIGHT;
  897.                 }
  898.         }
  899. }
  900.  
  901. class Weakness : Effect {
  902.         Weakness(jjPLAYER@ setPlay, string setName, SPELL setEnumValue, SPELL setCounterEffect,
  903.                         uint setDuration, bool setIsLocal, bool setIsCurse) {
  904.                 super(setPlay, setName, setEnumValue, setCounterEffect, setDuration, setIsLocal, setIsCurse);
  905.         }
  906.         void affect() override {}
  907. }
  908.  
  909. void controlObjectCollision(jjOBJ@ obj) {
  910.         if (obj.state != STATE::EXPLODE) {
  911.                 uint yChunk = (uint(obj.yPos) + 64) >> 7;
  912.                 uint xChunk = (uint(obj.xPos) + 64) >> 7;
  913.                 for (uint y = 0; y < 2; y++) {
  914.                         for (uint x = 0; x < 2; x++) {
  915.                                 if (yChunk - y < chunks.length && xChunk - x < chunks[yChunk - y].length) {
  916.                                         array<jjOBJ@>@ objects = chunks[yChunk - y][xChunk - x].objects;
  917.                                         for (uint i = 0; i < objects.length; i++) {
  918.                                                 jjOBJ@ other = objects[i];
  919.                                                 if (other.playerHandling == HANDLING::PICKUP && obj.doesCollide(other, true)) {
  920.                                                         obj.objectHit(other, HANDLING::PICKUP);
  921.                                                 } else if (other.playerHandling == HANDLING::SPECIAL && obj.doesCollide(other, true)) {
  922.                                                         obj.objectHit(other, HANDLING::ENEMY);
  923.                                                 } else if (other.eventID == OBJECT::DESTRUCTSCENERY && obj.doesCollide(other, true)) {
  924.                                                         obj.objectHit(other, HANDLING::ENEMY);
  925.                                                         obj.delete();
  926.                                                 }
  927.                                         }
  928.                                 }
  929.                         }
  930.                 }
  931.         }
  932. }