Name | Author | Game Mode | Rating | |||||
---|---|---|---|---|---|---|---|---|
Anniversary Bash 16 Levels | Jazz2Online | Multiple | N/A |
namespace palshift {
const array<uint8> ammoCount = {208, 232, 240, 216};
const uint8 asmdParticle = 221;
const uint8 barrel = 64;
const uint8 barrelDurability = 232;
const uint8 flak = 32;
const array<uint8> rocketShard = {200, 248};
}
namespace tileset {
const uint healthBar = 230;
const uint ventShaft = 19;
const uint weaponLarge = 140;
const uint weaponSmall = 200;
const uint weaponSmallInactive = 205;
}
namespace trigger {
enum option {disableSD, friendlyFire, damageAmplifier}
}
namespace weapon {
const uint8 count = 6;
const int dispersionPistolRegenerationTime = 80;
const int displayTime = 40;
const array<int> fireInterval = {0, 25, 5, 40, 70, 40, 1, 0, 0, 0};
const uint8 first = 1;
enum flags {maxDamage = 7, allowSD = 8}
const array<string> name = {"0", "Dispersion Pistol", "Machine Gun", "ASMD", "Rocket Launcher", "Flak Cannon", "Flamethrower", "7", "8", "9"};
const array<int> pickupMultiplier = {0, 1, 20, 4, 1, 1, 50, 0, 0, 0};
}
const int blinkingTime = 40;
interface Itile {
void step();
}
class TlocalPlayer {
int dispersionPistolRegenerationTimer, fireIntervalTimer, weaponChangeTimer;
bool keyFirePrevious;
uint8 weaponLastShot, weaponPrevious;
TlocalPlayer() {
dispersionPistolRegenerationTimer = fireIntervalTimer = weaponChangeTimer = 0;
weaponPrevious = weaponLastShot = WEAPON::BLASTER;
keyFirePrevious = false;
}
}
class Tsprite {
int x, y, direction;
uint frame;
SPRITE::Mode mode;
uint8 param;
void draw(jjCANVAS@ canvas) const {
canvas.drawSpriteFromCurFrame(x, y, frame, direction, mode, param);
}
}
class TventShaft : Itile {
private int x, y;
private float xHigh, xLow, yHigh, yLow;
TventShaft(int X, int Y) {
x = X * 32 + 12;
y = Y * 32 + 12;
xHigh = x + 328;
yHigh = y + 248;
xLow = x - 320;
yLow = y - 240;
}
void step() {
if (jjGameTicks & 1 == 0) {
const uint random = jjRandom();
if (random & 1 == 0) {
for (int i = 0; i < jjLocalPlayerCount; i++) {
const jjPLAYER@ player = jjLocalPlayers[i];
if (player.cameraX < xHigh && player.cameraY <= yHigh && player.cameraX + jjSubscreenWidth > xLow && player.cameraY + jjSubscreenHeight > yLow) {
jjPARTICLE@ part = jjAddParticle(PARTICLE::SMOKE);
if (part !is null) {
part.xPos = x + (random >> 1 & 7);
part.yPos = y + (random >> 4 & 7);
part.ySpeed = (random >> 7 & 3) / 4.f - 1.5f;
}
return;
}
}
}
}
}
}
bool admin = false;
array<TlocalPlayer> localPlayer(jjLocalPlayerCount);
array<Itile@> specialTile;
bool onDrawAmmo(jjPLAYER@ player, jjCANVAS@ canvas) {
if (jjSubscreenWidth >= 640 && jjSubscreenHeight >= 384) {
draw::weaponIconBarVertical(canvas, jjSubscreenWidth - 88, jjSubscreenHeight / 2, player);
} else if (jjSubscreenWidth >= 640) {
draw::weaponIconBarHorizontal(canvas, jjSubscreenWidth / 2, jjSubscreenHeight - 40, player);
} else if (jjSubscreenWidth >= 512 || jjSubscreenHeight >= 400) {
uint8 weapon = player.currWeapon;
draw::largeWeaponIcon(canvas, jjSubscreenWidth - 176, jjSubscreenHeight - 80, weapon - 1);
draw::ammoCounter(canvas, jjSubscreenWidth - 176, jjSubscreenHeight - 24, player.ammo[weapon], jjWeapons[weapon].maximum, true);
if (localPlayer[player.localPlayerID].weaponChangeTimer > 0)
draw::weaponName(canvas, jjSubscreenWidth - 16, jjSubscreenHeight - 72, weapon);
} else {
uint8 weapon = player.currWeapon;
draw::smallWeaponIcon(canvas, jjSubscreenWidth - 88, jjSubscreenHeight - 40, weapon - 1, true);
draw::ammoCounter(canvas, jjSubscreenWidth - 88, jjSubscreenHeight - 8, player.ammo[weapon], jjWeapons[weapon].maximum, true);
}
return true;
}
bool onDrawHealth(jjPLAYER@ player, jjCANVAS@ canvas) {
if (jjSubscreenWidth >= 512 || jjSubscreenHeight >= 400) {
draw::healthBar(canvas, 16, text::getScoreHUDHeight(jjGameMode), player.health, jjMaxHealth, player.blink > 0 && jjGameTicks >> 2 & 1 == 0);
return true;
}
return false;
}
void onDrawLayer3(jjPLAYER@, jjCANVAS@ canvas) {
sprite::drawQueue(canvas, 2);
}
void onDrawLayer4(jjPLAYER@, jjCANVAS@ canvas) {
for (int i = 0; i < jjObjectCount; i++) {
jjOBJ@ obj = jjObjects[i];
if (!obj.isActive || obj.eventID != OBJECT::GENERATOR)
continue;
if (obj.direction == 0)
obj.direction = obj.var[3] != OBJECT::TNT && (obj.xPos / 32 % 2 > 1 ^^ obj.yPos / 32 % 2 > 1) ? -1 : 1;
bool draw = obj.var[0] <= 0;
if (!draw) {
jjOBJ@ item = jjObjects[obj.var[0]];
draw = !item.isActive || item.creatorType != CREATOR::LEVEL || item.creatorID != uint(obj.objectID);
}
if (draw)
canvas.drawSpriteFromCurFrame(obj.xPos, obj.yPos, jjObjectPresets[obj.var[3]].curFrame, obj.direction, SPRITE::SINGLECOLOR, 79);
}
sprite::drawQueue(canvas, 3);
}
bool onDrawLives(jjPLAYER@ player, jjCANVAS@ canvas) {
if ((jjGameMode == GAME::COOP || jjGameMode == GAME::SP) && (jjSubscreenWidth >= 512 || jjSubscreenHeight >= 400)) {
string text = formatInt(player.lives, "");
canvas.drawString(43 - text::getSmallFontIntWidth(text) / 2, text::getScoreHUDHeight(jjGameMode) + 33, text);
return true;
}
return false;
}
bool onDrawScore(jjPLAYER@, jjCANVAS@) {
return true;
}
void onLevelLoad() {
initialize::objectPresets();
initialize::weaponProfiles();
initialize::specialTiles();
initialize::palette();
jjTexturedBGTexture = TEXTURE::WTF;
}
void onLevelReload() {
initialize::palette();
}
void onMain() {
for (int i = 0; i < 32; i++) {
jjPLAYER@ player = jjPlayers[i];
if (player.blink < -blinkingTime)
player.blink = -blinkingTime;
}
for (uint i = 0; i < specialTile.length(); i++) {
specialTile[i].step();
}
if ((jjIsServer || jjIsAdmin) && !admin) {
jjAlert("/trigger " + formatInt(trigger::disableSD, "") + " " + (jjTriggers[trigger::disableSD] ? "off - enable" : "on - disable") + " Self-Destruction");
jjAlert("/trigger " + formatInt(trigger::friendlyFire, "") + " " + (jjTriggers[trigger::friendlyFire] ? "off - disable" : "on - enable") + " Friendly Fire");
jjAlert("/trigger " + formatInt(trigger::damageAmplifier, "") + " " + (jjTriggers[trigger::damageAmplifier] ? "off - disable" : "on - enable") + " Damage Amplifier");
admin = true;
}
}
void onObjectHit(jjOBJ@ obj, jjOBJ@ other, jjPLAYER@ player, int) {
if (obj is null)
return;
if (other !is null) {
if (obj.eventID == OBJECT::TNT)
collision::barrelExplode(obj, other);
return;
}
if (player !is null) {
switch (obj.eventID) {
case OBJECT::BOUNCERAMMO3:
collision::ammoPickup(player, obj, WEAPON::BOUNCER);
break;
case OBJECT::ICEAMMO3:
collision::ammoPickup(player, obj, WEAPON::ICE);
break;
case OBJECT::SEEKERAMMO3:
collision::ammoPickup(player, obj, WEAPON::SEEKER);
break;
case OBJECT::RFAMMO3:
collision::ammoPickup(player, obj, WEAPON::RF);
break;
case OBJECT::TOASTERAMMO3:
collision::ammoPickup(player, obj, WEAPON::TOASTER);
break;
case OBJECT::BLASTERBULLET:
case OBJECT::BOUNCERBULLET:
case OBJECT::ICEBULLET:
case OBJECT::SEEKERBULLET:
case OBJECT::RFBULLET:
case OBJECT::TOASTERBULLET:
collision::projectile(player, obj);
break;
}
}
}
void onPlayer(jjPLAYER@ player) {
sprite::clearQueues();
player.frozen = 0;
player.fastfire = weapon::fireInterval[player.currWeapon];
TlocalPlayer@ thisPlayer = localPlayer[player.localPlayerID];
if (--thisPlayer.dispersionPistolRegenerationTimer < 0) {
if (player.ammo[WEAPON::BLASTER] < jjWeapons[WEAPON::BLASTER].maximum) {
player.ammo[WEAPON::BLASTER] = player.ammo[WEAPON::BLASTER] + weapon::pickupMultiplier[WEAPON::BLASTER];
thisPlayer.dispersionPistolRegenerationTimer = weapon::dispersionPistolRegenerationTime;
} else {
thisPlayer.dispersionPistolRegenerationTimer = 0;
}
}
thisPlayer.weaponChangeTimer--;
if (player.currWeapon != thisPlayer.weaponPrevious) {
thisPlayer.weaponChangeTimer = weapon::displayTime;
thisPlayer.weaponPrevious = player.currWeapon;
}
thisPlayer.fireIntervalTimer++;
}
void onPlayerInput(jjPLAYER@ player) {
TlocalPlayer@ thisPlayer = localPlayer[player.localPlayerID];
if (!thisPlayer.keyFirePrevious && player.keyFire) {
if (thisPlayer.fireIntervalTimer < player.fastfire)
player.keyFire = false;
else
thisPlayer.fireIntervalTimer = 0;
}
thisPlayer.keyFirePrevious = player.keyFire;
}
namespace behavior {
void asmdBeam(jjOBJ@ obj) {
switch (obj.state) {
case STATE::START:
obj.var[5] = jjGameTicks;
break;
case STATE::ACTION:
obj.state = STATE::EXPLODE;
break;
}
obj.behave(BEHAVIOR::BULLET, false);
if (obj.state != STATE::EXPLODE) {
obj.xPos = obj.xPos + obj.xSpeed / 2;
obj.yPos = obj.yPos + obj.ySpeed / 2;
if (obj.counter % 2 == 0) {
explosion::simpleExplosion(obj.xPos, obj.yPos, ANIM::AMMO, 71);
if (obj.counter % 10 == 0)
explosion::asmdRing(obj.xPos, obj.yPos, obj.xSpeed, obj.ySpeed, 30.f / obj.counter, 10.f / obj.counter);
}
} else if (obj.age++ == 0 && jjMaskedPixel(obj.xPos, obj.yPos)){
explosion::asmdRing(obj.xPos, obj.yPos, obj.xSpeed, obj.ySpeed, 6, 2);
explosion::harmfulExplosion(obj.xPos, obj.yPos, 32, 2, obj.creatorType == CREATOR::PLAYER ? jjPlayers[obj.creatorID] : null);
}
}
void barrel(jjOBJ@ obj) {
switch (obj.state) {
case STATE::START:
obj.putOnGround();
obj.state = STATE::IDLE;
case STATE::IDLE:
sprite::add(obj.xPos, obj.yPos, obj.curFrame, 0, SPRITE::PALSHIFT, palshift::barrel);
for (int i = 1; i < jjObjectCount; i++) {
jjOBJ@ bull = jjObjects[i];
if (bull.isActive
&& bull.playerHandling == HANDLING::SPECIAL
&& bull.animSpeed > 0
&& bull.state != STATE::START
&& bull.state != STATE::EXPLODE
&& bull.xPos > obj.xPos - 11
&& bull.xPos < obj.xPos + 15
&& bull.yPos > obj.yPos - 19
&& bull.yPos < obj.yPos + 15)
collision::barrel(obj, bull);
}
if (obj.energy < jjObjectPresets[obj.eventID].energy) {
string text = formatInt(obj.energy, "");
jjDrawString(obj.xPos - text::getSmallFontIntWidth(text) / 2, obj.yPos + 16, text, STRING::SMALL, STRING::PALSHIFT, palshift::barrelDurability);
}
break;
case STATE::EXTRA:
{
int ID = jjAddObject(OBJECT::BULLET, obj.xPos, obj.yPos, obj.special, CREATOR::PLAYER, behavior::delete);
if (ID != 0) {
jjOBJ@ bull = jjObjects[ID];
bull.curFrame = obj.curFrame;
bull.freeze = 1;
bull.playerHandling = HANDLING::PLAYERBULLET;
bull.state = STATE::IDLE;
}
}
break;
case STATE::ACTION:
explosion::barrelExplosion(obj.xPos, obj.yPos, obj.special >= 0 ? jjPlayers[obj.special] : null);
obj.delete();
break;
}
}
void bullet(jjOBJ@ obj) {
if (obj.state == STATE::ACTION)
obj.state = STATE::EXPLODE;
obj.behave(BEHAVIOR::BULLET, false);
sprite::add(obj.xPos, obj.yPos, obj.curFrame, obj.xSpeed);
}
void delete(jjOBJ@ obj) {
if (++obj.age > 3)
obj.delete();
}
void explosion(jjOBJ@ obj) {
obj.behave(BEHAVIOR::EXPLOSION, false);
sprite::add(obj.xPos, obj.yPos, obj.curFrame, obj.direction, SPRITE::RESIZED, obj.age, 3);
}
void flak(jjOBJ@ obj) {
switch (obj.state) {
case STATE::START:
obj.var[5] = 0;
obj.determineCurAnim(ANIM::PICKUPS, 44 + (jjRandom() & 3));
obj.determineCurFrame();
obj.state = STATE::DELAYEDSTART;
break;
case STATE::DELAYEDSTART:
if (obj.special > 0) {
if (obj.var[7] * obj.xSpeed > 0)
obj.xSpeed = obj.xSpeed + float(obj.var[7]) / 0x10000;
const float angle = atan2(obj.ySpeed, obj.xSpeed);
const float speed = sqrt(obj.xSpeed * obj.xSpeed + obj.ySpeed * obj.ySpeed);
for (int i = -obj.special; i <= obj.special; i++) {
int ID = jjAddObject(OBJECT::RFBULLET, obj.xPos, obj.yPos, obj.creatorID, obj.creatorType);
if (ID != 0) {
float newAngle = angle + i * 0.05f;
float newSpeed = speed + (i * i & 18);
jjOBJ@ bull = jjObjects[ID];
bull.counterEnd = obj.counterEnd + (i * i & 5);
bull.direction = obj.direction;
bull.special = 0;
bull.xAcc = -obj.xAcc * cos(newAngle);
bull.xSpeed = newSpeed * cos(newAngle);
bull.yAcc = obj.yAcc;
bull.ySpeed = newSpeed * sin(newAngle);
}
}
obj.delete();
return;
}
obj.state = STATE::FLY;
break;
case STATE::ACTION:
obj.state = STATE::EXPLODE;
case STATE::EXPLODE:
explosion::simpleExplosion(obj.xPos, obj.yPos, ANIM::PICKUPS, 4);
obj.delete();
return;
}
uint8 damage = jjObjectPresets[obj.eventID].doesHurt * (obj.counterEnd - obj.counter) / obj.counterEnd + 1;
if (damage > weapon::maxDamage)
damage = weapon::maxDamage;
obj.doesHurt = damage | obj.var[5];
if (uint(++obj.counter) > obj.counterEnd)
obj.state = STATE::EXPLODE;
obj.xSpeed = obj.xSpeed + obj.xAcc;
obj.ySpeed = obj.ySpeed + obj.yAcc;
float x = obj.xPos;
obj.xPos = obj.xPos + obj.xSpeed;
if (obj.counter > 5)
obj.var[5] = weapon::allowSD;
if (jjMaskedPixel(obj.xPos, obj.yPos)) {
if (jjRandom() & 3 == 0)
jjSample(obj.xPos, obj.yPos, SOUND::COMMON_CUP);
obj.counter += 5;
obj.xPos = x;
obj.xSpeed = -obj.xSpeed;
obj.xAcc = -obj.xAcc;
}
float y = obj.yPos;
obj.yPos = obj.yPos + obj.ySpeed;
if (jjMaskedPixel(obj.xPos, obj.yPos)) {
obj.counter += 5;
obj.yPos = y;
obj.ySpeed = -obj.ySpeed;
}
if (jjGameTicks % 7 == 0) {
obj.frameID++;
obj.determineCurFrame();
}
sprite::add(obj.xPos, obj.yPos, obj.curFrame, obj.xSpeed, SPRITE::PALSHIFT, palshift::flak);
}
void flame(jjOBJ@ obj) {
obj.xSpeed = obj.xSpeed + obj.xAcc;
obj.ySpeed = obj.ySpeed + obj.yAcc;
float xPrev = obj.xPos;
float yPrev = obj.yPos;
obj.xPos = obj.xPos + obj.xSpeed;
obj.yPos = obj.yPos + obj.ySpeed;
if (jjRandom() & 15 == 0) {
jjPARTICLE@ part = jjAddParticle(PARTICLE::SMOKE);
if (part !is null) {
part.xPos = obj.xPos;
part.yPos = obj.yPos;
}
}
switch (obj.state) {
case STATE::START:
if (jjGameTicks & 6 == 0)
jjSample(obj.xPos, obj.yPos, SOUND::BILSBOSS_FIRE);
obj.xSpeed = obj.xSpeed * 4 + float(obj.var[7]) / 0x20000;
obj.ySpeed = obj.ySpeed * 4;
obj.age = abs(obj.xSpeed) > abs(obj.ySpeed) ? 1 : 0;
obj.state = STATE::FIRE;
case STATE::FIRE:
{
bool masked = jjMaskedPixel(obj.xPos, obj.yPos);
if (masked || (obj.age == 1 ? abs(obj.xSpeed) < 1 : abs(obj.ySpeed) < 1)) {
obj.xAcc = obj.yAcc = 0;
obj.doesHurt = (obj.doesHurt & weapon::maxDamage) / 2 | weapon::allowSD;
if (masked) {
obj.xPos = xPrev;
obj.yPos = yPrev;
obj.xSpeed = obj.ySpeed = 0;
}
obj.state = STATE::FALL;
}
}
break;
case STATE::FALL:
if (obj.yAcc < 0.125f)
obj.yAcc = obj.yAcc + 0.005f;
if (jjMaskedPixel(obj.xPos, obj.yPos)) {
obj.xAcc = obj.xSpeed = obj.yAcc = obj.ySpeed = 0;
if (uint(++obj.counter) > obj.counterEnd)
obj.state = STATE::EXPLODE;
}
break;
case STATE::ACTION:
obj.state = STATE::EXPLODE;
case STATE::EXPLODE:
explosion::simpleExplosion(obj.xPos, obj.yPos, ANIM::AMMO, 55);
obj.delete();
return;
}
if (jjGameTicks % 7 == 0) {
obj.frameID++;
obj.determineCurFrame();
}
sprite::add(obj.xPos, obj.yPos, obj.curFrame, obj.xSpeed);
}
void pickup(jjOBJ@ obj) {
switch (obj.state) {
case STATE::ACTION:
jjSample(obj.xPos, obj.yPos, SOUND::COMMON_PICKUPW1);
explosion::simpleExplosion(obj.xPos, obj.yPos, ANIM::PICKUPS, 86);
obj.delete();
return;
case STATE::FLOATFALL:
obj.state = STATE::FLOAT;
break;
}
obj.behave(BEHAVIOR::PICKUP);
}
void rocket(jjOBJ@ obj) {
switch (obj.state) {
case STATE::START:
obj.state = STATE::ROCKETFLY;
break;
case STATE::ACTION:
obj.state = STATE::EXPLODE;
case STATE::EXPLODE:
explosion::rocketExplosion(obj.xPos, obj.yPos, obj.creatorType == CREATOR::PLAYER ? jjPlayers[obj.creatorID] : null);
obj.delete();
return;
}
obj.xSpeed = obj.xSpeed + obj.xAcc;
obj.ySpeed = obj.ySpeed + obj.yAcc;
obj.xPos = obj.xPos + obj.xSpeed;
obj.yPos = obj.yPos + obj.ySpeed;
if (jjMaskedPixel(obj.xPos, obj.yPos))
obj.state = STATE::EXPLODE;
if (jjGameTicks % 7 == 0) {
obj.frameID++;
obj.determineCurFrame();
}
sprite::add(obj.xPos, obj.yPos, obj.curFrame, obj.xSpeed);
}
void shard(jjOBJ@ obj) {
if (obj.age == 0)
obj.age = (4 << (jjRandom() & 3)) - 1;
obj.behave(BEHAVIOR::SHARD, false);
obj.determineCurFrame();
sprite::add(obj.xPos, obj.yPos, obj.curFrame, obj.direction, SPRITE::PALSHIFT, obj.special);
uint random = jjRandom();
if (random & obj.age == 0) {
jjPARTICLE@ part = jjAddParticle(PARTICLE::FIRE);
if (part !is null) {
part.xPos = obj.xPos;
part.yPos = obj.yPos;
}
}
}
void shortLivedParticle(jjOBJ@ obj) {
if (--obj.counter < 0) {
obj.delete();
return;
}
obj.xSpeed = obj.xSpeed + obj.xAcc;
obj.ySpeed = obj.ySpeed + obj.yAcc;
obj.xPos = obj.xPos + obj.xSpeed;
obj.yPos = obj.yPos + obj.ySpeed;
if (jjGameTicks % 7 == 0 || obj.state == STATE::START) {
obj.frameID++;
obj.determineCurFrame();
}
sprite::add(obj.xPos, obj.yPos, obj.curFrame, 0, SPRITE::Mode(obj.special), obj.var[0]);
}
}
namespace collision {
void ammoPickup(jjPLAYER@ player, jjOBJ@ obj, WEAPON::Weapon type) {
if (player.ammo[type] >= jjWeapons[type].maximum)
return;
if (player.ammo[type] == 0 && !player.keyFire && (uint8(type) > player.currWeapon || type == WEAPON::SEEKER))
player.currWeapon = type;
player.ammo[type] = player.ammo[type] + weapon::pickupMultiplier[type];
if (player.ammo[type] > jjWeapons[type].maximum)
player.ammo[type] = jjWeapons[type].maximum;
obj.state = STATE::ACTION;
}
void barrel(jjOBJ@ obj, jjOBJ@ bull) {
if (obj.state == STATE::ACTION || obj.state == STATE::EXTRA)
return;
if (bull.eventID == OBJECT::ICEBULLET) {
if (obj.var[5] == bull.var[5])
return;
obj.var[5] = bull.var[5];
} else {
bull.state = STATE::EXPLODE;
}
if (obj.energy > bull.animSpeed)
obj.energy -= bull.animSpeed;
else
obj.state = STATE::EXTRA;
if (bull.creatorType == CREATOR::PLAYER)
obj.special = bull.creatorID;
}
void barrelExplode(jjOBJ@ obj, jjOBJ@ bull) {
obj.state = STATE::ACTION;
bull.delete();
}
bool hurtPlayer(jjPLAYER@ player, jjPLAYER@ attacker, uint8 damage) {
if (player.blink != 0 || player.health <= 0)
return false;
if (attacker !is null) {
if (attacker.blink != 0)
return false;
if (player is attacker) {
if (damage & weapon::allowSD == 0 || jjTriggers[trigger::disableSD])
return false;
} else if (jjGameMode == GAME::CTF && player.teamRed == attacker.teamRed && !jjTriggers[trigger::friendlyFire]) {
return false;
}
}
if (jjTriggers[trigger::damageAmplifier] && damage & weapon::maxDamage < weapon::maxDamage)
damage++;
if (player.hurt(damage & weapon::maxDamage, true, attacker)) {
player.blink = 60;
return true;
}
return false;
}
void projectile(jjPLAYER@ player, jjOBJ@ bull) {
jjPLAYER@ creator;
if (bull.creatorType == CREATOR::PLAYER)
@creator = jjPlayers[bull.creatorID];
if (hurtPlayer(player, creator, bull.doesHurt))
bull.state = STATE::ACTION;
}
}
namespace draw {
void ammoCounter(jjCANVAS@ canvas, int x, int y, int value, int max, bool active) {
int color = 0;
if (active) {
if (2 * value <= max) {
if (4 * value <= max) {
if (value <= 0)
color = 3;
else
color = 2;
} else {
color = 1;
}
}
}
STRING::Mode mode = active ? STRING::PALSHIFT : STRING::DARK;
string text = formatInt(value, "") + "/" + formatInt(max, "");
canvas.drawString(x, y, text, STRING::SMALL, mode, palshift::ammoCount[color]);
}
void healthBar(jjCANVAS@ canvas, int x, int y, int value, int max, bool red) {
const uint backgroundTile = tileset::healthBar + 12;
const int backgroundX = x + 32;
const uint insideTile = tileset::healthBar + 5;
const int insideX = x + 46;
const int insideY = y + 8;
const int length = 105 * value / max;
const bool low = value <= max / 2;
const TILE::Quadrant quadrantLeft = low ? TILE::BOTTOMLEFT : TILE::TOPLEFT;
const TILE::Quadrant quadrantRight = low ? TILE::BOTTOMRIGHT : TILE::TOPRIGHT;
if (value < max) {
for (int i = 0; i < 4; i++) {
canvas.drawTile(backgroundX + i * 32, y, backgroundTile + i);
}
}
for (int i = 0; i < length; i++) {
uint random = jjRandom();
canvas.drawTile(insideX + i, insideY, insideTile + (random & 1), random & 2 != 0 ? quadrantLeft : quadrantRight);
}
if (value < max)
canvas.drawTile(insideX + length, insideY, insideTile + 11, jjRandom() & 1 != 0 ? quadrantLeft : quadrantRight);
if (length >= 9) {
canvas.drawTile(x + 48, y, insideTile + 4, quadrantLeft);
if (length >= 50) {
canvas.drawTile(x + 80, y, insideTile + 4, quadrantRight);
if (length >= 82)
canvas.drawTile(x + 112, y, insideTile + 4, quadrantRight);
}
}
for (int i = 0; i < 5; i++) {
canvas.drawTile(x + i * 32, y, tileset::healthBar + i);
}
for (int i = 0; i < 2; i++) {
canvas.drawTile(x + i * 32, y + 32, tileset::healthBar + 10 + i);
}
if (!red) {
const uint heartTile = tileset::healthBar + (low ? 17 : 7);
for (int i = 0; i < 2; i++) {
canvas.drawTile(x + i * 32, y + 16, heartTile + i);
}
}
}
void largeWeaponIcon(jjCANVAS@ canvas, int x, int y, uint8 type) {
uint modifier = tileset::weaponLarge + (type + (type & ~1)) * 5;
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 5; j++) {
canvas.drawTile(x + j * 32, y + i * 32, modifier + i * 10 + j);
}
}
}
void smallWeaponIcon(jjCANVAS@ canvas, int x, int y, uint8 type, bool active) {
uint modifier = (active ? tileset::weaponSmall : tileset::weaponSmallInactive) + type * 5;
if (type & 1 == 0) {
canvas.drawTile(x, y, modifier);
canvas.drawTile(x + 32, y, modifier + 1);
canvas.drawTile(x + 64, y, modifier + 2, TILE::TOPLEFT);
canvas.drawTile(x + 64, y + 16, modifier + 2, TILE::BOTTOMLEFT);
} else {
canvas.drawTile(x, y, modifier - 3, TILE::TOPRIGHT);
canvas.drawTile(x, y + 16, modifier - 3, TILE::BOTTOMRIGHT);
canvas.drawTile(x + 16, y, modifier - 2);
canvas.drawTile(x + 48, y, modifier - 1);
}
}
void weaponIconBarHorizontal(jjCANVAS@ canvas, int x, int y, const jjPLAYER@ player) {
const bool largeDisplay = localPlayer[player.localPlayerID].weaponChangeTimer > 0;
x -= weapon::count * 40 + (largeDisplay ? 40 : 0);
int x2 = x + (largeDisplay ? 80 : 0);
const uint8 currentWeaponID = player.currWeapon - weapon::first;
for (uint i = 0; i < weapon::count; i++) {
uint8 weapon = i + 1;
bool hasAmmoOrIsCurrent = player.ammo[weapon] > 0 || i == currentWeaponID;
if (i == currentWeaponID && largeDisplay) {
largeWeaponIcon(canvas, x + i * 80, y - 32, i);
ammoCounter(canvas, x + i * 80, y + 24, player.ammo[weapon], jjWeapons[weapon].maximum, true);
weaponName(canvas, x + i * 80 + 160, y - 24, weapon);
} else {
int xUsed = i < currentWeaponID ? x : x2;
smallWeaponIcon(canvas, xUsed + i * 80, y - 16, i, hasAmmoOrIsCurrent);
if (hasAmmoOrIsCurrent)
ammoCounter(canvas, xUsed + i * 80, y + 16, player.ammo[weapon], jjWeapons[weapon].maximum, i == currentWeaponID);
}
}
}
void weaponIconBarVertical(jjCANVAS@ canvas, int x, int y, const jjPLAYER@ player) {
const bool largeDisplay = localPlayer[player.localPlayerID].weaponChangeTimer > 0;
y -= weapon::count * 24 + (largeDisplay ? 16 : 0);
const int x2 = x - 32;
const int y2 = y + (largeDisplay ? 32 : 0);
const uint8 currentWeaponID = player.currWeapon - weapon::first;
for (uint i = 0; i < weapon::count; i++) {
uint8 weapon = i + 1;
bool hasAmmoOrIsCurrent = player.ammo[weapon] > 0 || i == currentWeaponID;
if (i == currentWeaponID && largeDisplay) {
largeWeaponIcon(canvas, x - 80, y + i * 48 + 8, i);
ammoCounter(canvas, x - 80, y + i * 48 + 72, player.ammo[weapon], jjWeapons[weapon].maximum, true);
weaponName(canvas, x + 80, y + i * 48 + 8, weapon);
} else {
int yUsed = i < currentWeaponID ? y : y2;
int xUsed = i != currentWeaponID ? x : x2;
smallWeaponIcon(canvas, xUsed, yUsed + i * 48, i, hasAmmoOrIsCurrent);
if (hasAmmoOrIsCurrent)
ammoCounter(canvas, xUsed, yUsed + i * 48 + 32, player.ammo[weapon], jjWeapons[weapon].maximum, i == currentWeaponID);
}
}
}
void weaponName(jjCANVAS@ canvas, int x, int y, uint8 type) {
canvas.drawString(x - text::getSmallFontIntWidth(weapon::name[type]), y, weapon::name[type]);
}
}
namespace explosion {
void asmdRing(float x, float y, float xSpeed, float ySpeed, float semiMajor, float semiMinor) {
const int shift = semiMajor > 2 ? 5 : 6;
const int count = 1 << (10 - shift);
const float multiplier = 6.2831853 / count;
const float hypot = sqrt(xSpeed * xSpeed + ySpeed * ySpeed);
const float axisSin = ySpeed / hypot;
const float axisCos = xSpeed / hypot;
const float majorSin = semiMajor * axisSin;
const float minorSin = semiMinor * axisSin;
const float majorCos = semiMajor * axisCos;
const float minorCos = semiMinor * axisCos;
for (int i = 0; i < count; i++) {
const int angle = i << shift;
const float angleSin = jjSin(angle);
const float angleCos = jjCos(angle);
const float xSpeedUlt = minorCos * angleCos - majorSin * angleSin;
const float ySpeedUlt = minorSin * angleCos + majorCos * angleSin;
particle(x, y, xSpeedUlt, ySpeedUlt, 0, 0, 35, ANIM::AMMO, 9, SPRITE::PALSHIFT, palshift::asmdParticle);
}
}
void barrelExplosion(float x, float y, jjPLAYER@ creator) {
harmfulExplosion(x, y, 128, 4, creator);
jjSample(x, y, SOUND::COMMON_GLASS2);
for (int i = 0; i < 20; i++) {
int ID = jjAddObject(OBJECT::SHARD, x, y);
if (ID != 0) {
jjOBJ@ obj = jjObjects[ID];
obj.behavior = behavior::shard;
obj.special = palshift::barrel;
uint random = jjRandom();
int speed = 8 + (random >> 2 & 7);
uint angle = random >> 5;
obj.xSpeed = jjSin(angle) * speed;
obj.ySpeed = jjCos(angle) * speed;
obj.determineCurAnim(ANIM::PICKUPS, 6 + (random & 3));
}
}
simpleExplosion(x, y, ANIM::AMMO, 5, 128);
for (int i = 0; i < 3; i++) {
uint angle = jjRandom();
simpleExplosion(x + jjSin(angle) * 64, y + jjCos(angle) * 64, ANIM::AMMO, 3, 64);
angle >>= 10;
simpleExplosion(x + jjSin(angle) * 96, y + jjCos(angle) * 96, ANIM::AMMO, 3, 48);
angle >>= 10;
simpleExplosion(x + jjSin(angle) * 96, y + jjCos(angle) * 96, ANIM::AMMO, 3, 32);
}
uint16 creatorID = creator !is null ? creator.playerID : 0;
CREATOR::Type creatorType = creator !is null ? CREATOR::PLAYER : CREATOR::LEVEL;
for (int i = 0; i < 5; i++) {
int ID = jjAddObject(OBJECT::TOASTERBULLET, x, y, creatorID, creatorType);
if (ID != 0) {
uint angle = (i - 2) << 6;
jjOBJ@ obj = jjObjects[ID];
obj.doesHurt = (obj.doesHurt & weapon::maxDamage) / 2 | weapon::allowSD;
obj.state = STATE::FALL;
obj.xAcc = obj.yAcc = 0;
obj.xSpeed = jjSin(angle) * 2;
obj.ySpeed = -jjCos(angle) * 2;
}
}
}
void harmfulExplosion(float x, float y, float radius, uint damage, jjPLAYER@ creator) {
damage &= weapon::maxDamage;
for (int i = 0; i < jjLocalPlayerCount; i++) {
jjPLAYER@ player = jjLocalPlayers[i];
if (player.xPos < x - radius || player.xPos > x + radius || player.yPos < y - radius || player.yPos > y + radius)
continue;
float dx = player.xPos - x;
float dy = player.yPos - y;
float distance = sqrt(dx * dx + dy * dy);
if (distance > radius)
continue;
uint value = uint(damage * (radius - distance) / radius) + 1;
if (value > damage)
value = damage;
collision::hurtPlayer(player, creator, value | weapon::allowSD);
}
}
void particle(float x, float y, float xSpeed, float ySpeed, float xAcc, float yAcc, int counter, ANIM::Set setID, uint8 animation, SPRITE::Mode mode = SPRITE::NORMAL, uint8 param = 0) {
int ID = jjAddObject(OBJECT::EXPLOSION, x, y);
if (ID != 0) {
jjOBJ@ obj = jjObjects[ID];
obj.behavior = behavior::shortLivedParticle;
obj.xSpeed = xSpeed;
obj.ySpeed = ySpeed;
obj.xAcc = xAcc;
obj.yAcc = yAcc;
obj.counter = counter;
obj.determineCurAnim(setID, animation);
obj.special = mode;
obj.var[0] = param;
}
}
void rocketExplosion(float x, float y, jjPLAYER@ creator) {
harmfulExplosion(x, y, 160, 5, creator);
jjSample(x, y, SOUND::COMMON_DAMPED1);
for (int i = 0; i < 20; i++) {
int ID = jjAddObject(OBJECT::SHARD, x, y);
if (ID != 0) {
jjOBJ@ obj = jjObjects[ID];
obj.behavior = behavior::shard;
uint random = jjRandom();
obj.special = palshift::rocketShard[random >> 2 & 1];
int speed = 7 + (random >> 3 & 3);
uint angle = random >> 5;
obj.xSpeed = jjSin(angle) * speed;
obj.ySpeed = jjCos(angle) * speed;
obj.determineCurAnim(ANIM::PICKUPS, 6 + (random & 3));
}
}
simpleExplosion(x, y, ANIM::AMMO, 5, 160);
for (int i = 0; i < 2; i++) {
uint angle = jjRandom();
simpleExplosion(x + jjSin(angle) * 64, y + jjCos(angle) * 64, ANIM::AMMO, 5, 96);
angle >>= 10;
simpleExplosion(x + jjSin(angle) * 96, y + jjCos(angle) * 96, ANIM::AMMO, 5, 64);
angle >>= 10;
simpleExplosion(x + jjSin(angle) * 128, y + jjCos(angle) * 128, ANIM::AMMO, 5, 48);
}
}
void simpleExplosion(float x, float y, ANIM::Set setID, uint8 animation, uint8 scale = 32) {
int ID = jjAddObject(OBJECT::EXPLOSION, x, y);
if (ID != 0) {
jjOBJ@ obj = jjObjects[ID];
obj.determineCurAnim(setID, animation);
obj.age = scale;
}
}
}
namespace initialize {
void objectPresets() {
jjOBJ@ preset;
@preset = jjObjectPresets[OBJECT::BLASTERBULLET];
preset.animSpeed = 4;
preset.behavior = behavior::bullet;
preset.bulletHandling = HANDLING::IGNOREBULLET;
preset.counterEnd = 50;
preset.doesHurt = 1;
preset.freeze = 0;
preset.isFreezable = false;
preset.killAnim = preset.determineCurAnim(ANIM::AMMO, 82, false);
preset.lightType = LIGHT::POINT2;
preset.playerHandling = HANDLING::SPECIAL;
preset.scriptedCollisions = true;
preset.special = preset.determineCurAnim(ANIM::AMMO, 32, false);
preset.xAcc = preset.yAcc = preset.ySpeed = 0;
preset.xSpeed = 7;
preset.determineCurAnim(ANIM::AMMO, 30);
preset.determineCurFrame();
@preset = jjObjectPresets[OBJECT::BOUNCERBULLET];
preset.animSpeed = 6;
preset.behavior = behavior::bullet;
preset.bulletHandling = HANDLING::IGNOREBULLET;
preset.counterEnd = 100;
preset.doesHurt = 1;
preset.freeze = 0;
preset.isFreezable = false;
preset.killAnim = preset.determineCurAnim(ANIM::AMMO, 10, false);
preset.lightType = LIGHT::NONE;
preset.playerHandling = HANDLING::SPECIAL;
preset.scriptedCollisions = true;
preset.special = preset.determineCurAnim(ANIM::AMMO, 22, false);
preset.xAcc = preset.yAcc = preset.ySpeed = 0;
preset.xSpeed = 10;
preset.determineCurAnim(ANIM::AMMO, 20);
preset.determineCurFrame();
@preset = jjObjectPresets[OBJECT::ICEBULLET];
preset.animSpeed = 25;
preset.behavior = behavior::asmdBeam;
preset.bulletHandling = HANDLING::IGNOREBULLET;
preset.counterEnd = 40;
preset.doesHurt = 2;
preset.freeze = 0;
preset.isFreezable = false;
preset.killAnim = preset.determineCurAnim(ANIM::AMMO, 71, false);
preset.light = 4;
preset.lightType = LIGHT::RING2;
preset.playerHandling = HANDLING::SPECIAL;
preset.scriptedCollisions = true;
preset.special = preset.determineCurAnim(ANIM::AMMO, 65, false);
preset.xAcc = preset.yAcc = preset.ySpeed = 0;
preset.xSpeed = 10;
preset.determineCurAnim(ANIM::AMMO, 73);
preset.determineCurFrame();
@preset = jjObjectPresets[OBJECT::SEEKERBULLET];
preset.animSpeed = 1000;
preset.behavior = behavior::rocket;
preset.bulletHandling = HANDLING::IGNOREBULLET;
preset.doesHurt = 7;
preset.freeze = 0;
preset.isFreezable = false;
preset.lightType = LIGHT::NONE;
preset.playerHandling = HANDLING::SPECIAL;
preset.scriptedCollisions = true;
preset.xAcc = preset.yAcc = preset.ySpeed = 0;
preset.xSpeed = 10;
@preset = jjObjectPresets[OBJECT::RFBULLET];
preset.animSpeed = 10;
preset.behavior = behavior::flak;
preset.bulletHandling = HANDLING::IGNOREBULLET;
preset.counterEnd = 35;
preset.doesHurt = 6;
preset.freeze = 0;
preset.isFreezable = false;
preset.lightType = LIGHT::NONE;
preset.playerHandling = HANDLING::SPECIAL;
preset.scriptedCollisions = true;
preset.special = 3;
preset.xAcc = -0.1f;
preset.yAcc = 0.1f;
preset.xSpeed = 15;
preset.ySpeed = 0;
@preset = jjObjectPresets[OBJECT::TOASTERBULLET];
preset.animSpeed = 5;
preset.behavior = behavior::flame;
preset.bulletHandling = HANDLING::IGNOREBULLET;
preset.counterEnd = 40;
preset.doesHurt = 2;
preset.freeze = 0;
preset.isFreezable = false;
preset.lightType = LIGHT::NONE;
preset.playerHandling = HANDLING::SPECIAL;
preset.scriptedCollisions = true;
preset.xAcc = -0.2f;
preset.xSpeed = 3;
preset.yAcc = preset.ySpeed = 0;
preset.determineCurAnim(ANIM::AMMO, 13);
@preset = jjObjectPresets[OBJECT::TNT];
preset.behavior = behavior::barrel;
preset.bulletHandling = HANDLING::DETECTBULLET;
preset.energy = 100;
preset.freeze = 0;
preset.isFreezable = false;
preset.lightType = LIGHT::NONE;
preset.playerHandling = HANDLING::SPECIAL;
preset.scriptedCollisions = true;
preset.special = -1;
preset.determineCurAnim(ANIM::PICKUPS, 3);
preset.determineCurFrame();
@preset = jjObjectPresets[OBJECT::BOUNCERAMMO3];
preset.behavior = behavior::pickup;
preset.scriptedCollisions = true;
preset.determineCurAnim(ANIM::HATTER, 1);
preset.determineCurFrame();
@preset = jjObjectPresets[OBJECT::ICEAMMO3];
preset.behavior = behavior::pickup;
preset.scriptedCollisions = true;
preset.determineCurAnim(ANIM::AMMO, 69);
preset.determineCurFrame();
@preset = jjObjectPresets[OBJECT::SEEKERAMMO3];
preset.behavior = behavior::pickup;
preset.scriptedCollisions = true;
preset.determineCurAnim(ANIM::SONCSHIP, 0);
preset.determineCurFrame();
@preset = jjObjectPresets[OBJECT::RFAMMO3];
preset.behavior = behavior::pickup;
preset.scriptedCollisions = true;
preset.determineCurAnim(ANIM::LIZARD, 1);
preset.determineCurFrame();
@preset = jjObjectPresets[OBJECT::TOASTERAMMO3];
preset.behavior = behavior::pickup;
preset.scriptedCollisions = true;
preset.determineCurAnim(ANIM::BILSBOSS, 3);
preset.determineCurFrame();
@preset = jjObjectPresets[OBJECT::CARROT];
preset.behavior = behavior::pickup;
@preset = jjObjectPresets[OBJECT::EXPLOSION];
preset.behavior = behavior::explosion;
}
void palette() {
jjPalette.fill(255, 255, 0, 104, 1);
jjPalette.fill(192, 192, 0, 117, 1);
jjPalette.gradient(200, 200, 0, 0, 0, 0, 123, 5);
jjPalette.gradient(255, 255, 0, 192, 0, 0, 144, 5);
jjPalette.gradient(192, 0, 0, 0, 0, 0, 148, 4);
jjPalette.gradient(128, 192, 192, 32, 48, 48);
jjPalette.apply();
}
void specialTiles() {
for (int i = 0; i < jjLayerHeight[4]; i++) {
for (int j = 0; j < jjLayerWidth[4]; j++) {
switch (jjTileGet(4, j, i)) {
case tileset::ventShaft:
specialTile.insertLast(TventShaft(j, i));
break;
}
}
}
}
void weaponProfiles() {
jjWEAPON@ weapon;
@weapon = jjWeapons[WEAPON::BLASTER];
weapon.infinite = false;
weapon.maximum = 24;
weapon.multiplier = 1;
weapon.replenishes = true;
weapon.style = WEAPON::NORMAL;
@weapon = jjWeapons[WEAPON::BOUNCER];
weapon.infinite = false;
weapon.maximum = 120;
weapon.multiplier = 1;
weapon.replenishes = false;
weapon.style = WEAPON::NORMAL;
@weapon = jjWeapons[WEAPON::ICE];
weapon.infinite = false;
weapon.maximum = 40;
weapon.multiplier = 1;
weapon.replenishes = false;
weapon.style = WEAPON::NORMAL;
@weapon = jjWeapons[WEAPON::SEEKER];
weapon.infinite = false;
weapon.maximum = 4;
weapon.multiplier = 1;
weapon.replenishes = false;
weapon.style = WEAPON::NORMAL;
@weapon = jjWeapons[WEAPON::RF];
weapon.infinite = false;
weapon.maximum = 16;
weapon.multiplier = 1;
weapon.replenishes = false;
weapon.style = WEAPON::NORMAL;
@weapon = jjWeapons[WEAPON::TOASTER];
weapon.infinite = false;
weapon.maximum = 300;
weapon.multiplier = 1;
weapon.replenishes = false;
weapon.style = WEAPON::NORMAL;
@weapon = jjWeapons[WEAPON::TNT];
weapon.infinite = false;
weapon.maximum = 0;
@weapon = jjWeapons[WEAPON::GUN8];
weapon.infinite = false;
weapon.maximum = 0;
@weapon = jjWeapons[WEAPON::GUN9];
weapon.infinite = false;
weapon.maximum = 0;
}
}
namespace sprite {
array<array<Tsprite>> spriteQueue(8);
void add(float x, float y, uint frame, int direction = 0, SPRITE::Mode mode = SPRITE::NORMAL, uint8 param = 0, uint8 layer = 4) {
Tsprite sprite;
sprite.x = floor(x);
sprite.y = floor(y);
sprite.frame = frame;
sprite.direction = direction;
sprite.mode = mode;
sprite.param = param;
spriteQueue[layer - 1].insertLast(sprite);
}
void clearQueues() {
for (uint i = 0; i < spriteQueue.length(); i++) {
spriteQueue[i].resize(0);
}
}
void drawQueue(jjCANVAS@ canvas, uint8 layer) {
for (uint i = 0; i < spriteQueue[layer].length(); i++) {
spriteQueue[layer][i].draw(canvas);
}
}
}
namespace text {
const array<int> smallFontCharacterWidth = {8, 6, 7, 15, 10, 12, 0, 4, 9, 9, 10, 13, 6, 10, 5, 11, 10, 8 /*actually 6*/, 11, 10, 11, 10, 9, 8, 9, 8, 6, 7, 9, 11, 9, 10, 14, 11, 11, 10, 12, 12, 9, 11, 9, 10, 11, 12, 10, 12, 12, 11, 10, 11, 12, 9, 11, 11, 9, 12, 10, 9, 11, 8, 11, 8, 13, 10, 6, 10, 9, 9, 9, 11, 9, 10, 10, 6, 9, 10, 6, 11, 10, 9, 10, 10, 10, 10, 9, 10, 9, 12, 9, 8, 9, 0, 0, 0, 0, 0};
int getScoreHUDHeight(GAME::Mode mode) {
switch (jjGameMode) {
case GAME::BATTLE:
case GAME::TREASURE:
return 36;
case GAME::CTF:
case GAME::RACE:
return 72;
case GAME::COOP:
case GAME::SP:
return 16;
}
return 0;
}
int getSmallFontIntWidth(string text) {
int width = 0;
for (uint i = 0; i < text.length(); i++) {
if(text[i] - 32 < smallFontCharacterWidth.length())
width += smallFontCharacterWidth[text[i] - 32] + 1;
else
width++;
}
return width;
}
}
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.