Name | Author | Game Mode | Rating | |||||
---|---|---|---|---|---|---|---|---|
Anniversary Bash 25 CTF | Jazz2Online | Capture the flag | N/A | |||||
Anniversary Bash 22 levels | Jazz2Online | Multiple | N/A | |||||
Anniversary Bash 21 Levels | Jazz2Online | Multiple | N/A | |||||
Cleanse | Seren | Capture the flag | 8.7 |
#pragma require "Buildsit.j2t"
#pragma require "cleanSE.j2a"
#pragma require "cleanSE.pal"
#pragma require "SEenergyblast.asc"
#include "SEenergyblast.asc"
class float2 {
float x, y;
float2() {}
float2(const float2& other) {
this = other;
}
float2(float x, float y) {
this.x = x;
this.y = y;
}
float& opIndex(uint index) {
return index == 0 ? x : y;
}
const float& opIndex(uint index) const {
return index == 0 ? x : y;
}
float2& opAddAssign(const float2& rhs) {
x += rhs.x;
y += rhs.y;
return this;
}
float2& opSubAssign(const float2& rhs) {
x -= rhs.x;
y -= rhs.y;
return this;
}
float2& opMulAssign(float rhs) {
x *= rhs;
y *= rhs;
return this;
}
float2& opDivAssign(float rhs) {
x /= rhs;
y /= rhs;
return this;
}
bool opEquals(const float2& rhs) const {
return x == rhs.x && y == rhs.y;
}
float2 opNeg() const {
return float2(-x, -y);
}
float2 opAdd(const float2& rhs) const {
return float2(x + rhs.x, y + rhs.y);
}
float2 opSub(const float2& rhs) const {
return float2(x - rhs.x, y - rhs.y);
}
float2 opMul(float rhs) const {
return float2(x * rhs, y * rhs);
}
float2 opMul_r(float lhs) const {
return float2(lhs * x, lhs * y);
}
float2 opDiv(float rhs) const {
return float2(x / rhs, y / rhs);
}
}
float dot(const float2& lhs, const float2& rhs) {
return lhs.x * rhs.x + lhs.y * rhs.y;
}
float abs(const float2& vec) {
return ::sqrt(dot(vec, vec));
}
class BuzzSaw : jjBEHAVIORINTERFACE {
void onBehave(jjOBJ@ obj) override {
if (obj.state == STATE::START) {
obj.state = STATE::TURN;
obj.special = 3 + 2 * jjParameterGet(int(obj.xOrg) >>> 5, int(obj.yOrg) >>> 5, 0, 1);
bool prev = false;
for (int i = 0; i <= 1024; i += 2) {
float sine = jjSin(i);
float cosine = jjCos(i);
float x = floor(obj.xPos + sine * 56.f + 0.5f);
float y = floor(obj.yPos + cosine * 56.f + 0.5f);
bool cur = jjMaskedPixel(int(x), int(y));
if (prev && !cur) {
obj.state = STATE::ROTATE;
obj.xAcc = x;
obj.yAcc = y;
obj.xSpeed = cosine * 4.f;
obj.ySpeed = -sine * 4.f;
break;
}
prev = cur;
}
}
bool makeParticles = false;
for (int i = 0; i < jjLocalPlayerCount; i++) {
const jjPLAYER@ player = jjLocalPlayers[i];
float camRight = player.cameraX + jjSubscreenWidth;
float camBottom = player.cameraY + jjSubscreenHeight;
if (player.cameraX <= obj.xPos + 128.f && camRight >= obj.xPos - 128.f && camBottom >= obj.yPos - 64.f) {
makeParticles = obj.state == STATE::ROTATE;
if (player.cameraY <= obj.yPos + 64.f) {
const auto@ anim = jjAnimations[obj.animSpeed];
int frame = anim + jjGameTicks % anim.frameCount;
jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, frame, 0, SPRITE::NORMAL, 0, obj.special, 4, player.playerID);
}
}
}
if (makeParticles) {
uint rand = jjRandom();
if (rand & 3 == 0) {
rand >>= 2;
float xSpeed = obj.xSpeed + ((rand & 255) - 127.5f) * 4e-3f;
rand >>= 8;
float ySpeed = obj.ySpeed + ((rand & 255) - 127.5f) * 4e-3f;
rand >>= 8;
const auto@ anim = jjAnimations[obj.killAnim];
int frame = anim + rand % anim.frameCount;
addParticle(obj.xAcc, obj.yAcc, xSpeed, ySpeed, frame);
}
}
}
bool onObjectHit(jjOBJ@ obj, jjOBJ@ bullet, jjPLAYER@ player, int) {
if (bullet is null) {
int invincibility = player.invincibility;
player.invincibility = 0;
player.hurt(2);
player.invincibility = invincibility;
}
return true;
}
}
class Particle {
bool active;
float xPos;
float yPos;
float xSpeed;
float ySpeed;
int frame;
int age;
void process() {
if (active) {
ySpeed += 0.125f;
xPos += xSpeed;
yPos += ySpeed;
age++;
if (age == 4) {
age = 0;
active = !jjMaskedPixel(int(xPos), int(yPos));
}
}
}
void draw(jjCANVAS@ canvas) {
if (active)
canvas.drawSpriteFromCurFrame(int(xPos), int(yPos), frame, 0, SPRITE::SINGLECOLOR, 0);
}
}
void addParticle(float xPos, float yPos, float xSpeed, float ySpeed, int frame) {
auto@ part = particles[jjRandom() & particlesMask];
part.active = true;
part.xPos = xPos;
part.yPos = yPos;
part.xSpeed = xSpeed;
part.ySpeed = ySpeed;
part.frame = frame;
part.age = 0;
}
array<uint8>@ makeTintedMapping(bool strong) {
array<uint8> mapping(256);
for (int i = 0; i < 256; i++) {
mapping[i] = i;
}
for (int i = 96; i < 112; i++) {
mapping[i] = strong ? (i << 1) - 16 : (i >> 1) + 112;
}
for (int i = 112; i < 120; i++) {
mapping[i] = strong ? (i << 2) - 272 : i + 48;
}
for (int i = 128; i < 144; i++) {
mapping[i] = strong ? (i << 1) - 80 : i + 96;
}
for (int i = 143; i < 148; i++) {
mapping[i] = strong ? 207 : 125;
}
for (int i = 148; i < 154; i++) {
mapping[i] = strong ? (i << 1) - 99 : i - 28;
}
for (int i = 170; i < 172; i++) {
mapping[i] = i - 44;
}
for (int i = 172; i < 177; i++) {
mapping[i] = (i >> 1) + 82;
}
return mapping;
}
array<uint8>@ makeSingleColorMapping(uint8 color) {
array<uint8> result(256, color);
result[0] = 0;
return result;
}
void shiftLayer(jjLAYER@ layer, int shift) {
layer.generateSettableTileArea();
for (int i = 0; i < layer.height; i++) {
for (int j = 0; j < layer.widthReal; j++) {
int tile = layer.tileGet(j, i);
if (tile != 0)
layer.tileSet(j, i, tile + shift);
}
}
}
const float2@ findNearestBuzzSaw(const float2& pos, float maxDistance) {
const auto@ layer = jjLayers[4];
int left = (int(pos.x - maxDistance) + 15) >>> 5;
if (left < 0)
left = 0;
int right = (int(pos.x + maxDistance) + 16) >>> 5;
if (right > layer.width)
right = layer.width;
int top = (int(pos.y - maxDistance) + 15) >>> 5;
if (top < 0)
top = 0;
int bottom = (int(pos.y + maxDistance) + 16) >>> 5;
if (bottom > layer.height)
bottom = layer.height;
const float2@ result;
for (int i = top; i < bottom; i++) {
for (int j = left; j < right; j++) {
if (jjEventGet(j, i) == OBJECT::SPIKEBOLL3D) {
float2 other(j << 5 | 16, i << 5 | 16);
float distance = abs(other - pos);
if (distance < maxDistance) {
@result = other;
maxDistance = distance;
}
}
}
}
return result;
}
const uint particlesMask = 511;
const float flameX = 119 << 5 | 16;
const float flameY = 99 << 5 | 23;
se::DefaultWeaponHook weaponHook(69);
array<Particle> particles(particlesMask + 1);
array<float2> pickupsToDelete;
jjPAL palette = jjPalette;
bool onDrawAmmo(jjPLAYER@ player, jjCANVAS@ canvas) {
return weaponHook.drawAmmo(player, canvas);
}
void onDrawLayer4(jjPLAYER@, jjCANVAS@ canvas) {
for (int i = particles.length(); i-- != 0;) {
particles[i].draw(canvas);
}
}
void onMain() {
weaponHook.processMain();
const auto@ buzzSawFrame = jjAnimFrames[jjAnimations[jjAnimSets[ANIM::CUSTOM[0]]]];
for (int i = 1; i < jjObjectCount; i++) {
jjOBJ@ obj = jjObjects[i];
if (obj.isActive) {
if (obj.playerHandling == HANDLING::PICKUP) {
float2 org(obj.xOrg, obj.yOrg);
float2 pos(obj.xPos, obj.yPos);
const float2@ buzzSaw = findNearestBuzzSaw(pos, 80.f);
if (buzzSaw !is null && jjAnimFrames[obj.curFrame].doesCollide(int(pos.x), int(pos.y), obj.direction, buzzSawFrame, int(buzzSaw.x), int(buzzSaw.y), 0, true)) {
obj.particlePixelExplosion(0);
if (obj.counterEnd++ >= 16) {
if (obj.creatorType != CREATOR::OBJECT || jjObjects[obj.creatorID].eventID == OBJECT::GENERATOR) {
jjSTREAM packet;
packet.push(uint8(0));
packet.push(float(obj.xOrg));
packet.push(float(obj.yOrg));
jjSendPacket(packet);
}
obj.delete();
} else if (pickupsToDelete.find(org) >= 0) {
obj.delete();
}
} else if (pickupsToDelete.find(org) >= 0) {
obj.behavior = BEHAVIOR::EXPLOSION2;
}
} else if (obj.eventID == OBJECT::LASERSHIELD) {
jjDrawSprite(flameX, flameY, ANIM::PLUS_SCENERY, 1, jjGameTicks / 7, 0, SPRITE::PALSHIFT, 32);
}
}
}
pickupsToDelete.resize(0);
for (int i = particles.length(); i-- != 0;) {
particles[i].process();
}
}
void onPlayer(jjPLAYER@ player) {
if (player.shieldTime > 0 && player.blink == -210)
player.shieldTime -= 700;
weaponHook.processPlayer(player);
}
void onPlayerInput(jjPLAYER@ player) {
weaponHook.processPlayerInput(player);
}
void onReceive(jjSTREAM &in packet, int clientID) {
if (!weaponHook.processPacket(packet, clientID)) {
jjSTREAM copy = packet;
float2 pos;
if (packet.discard(1) && packet.pop(pos.x) && packet.pop(pos.y)) {
pickupsToDelete.insertLast(pos);
if (jjIsServer)
jjSendPacket(copy, -clientID);
}
}
}
void onLevelLoad() {
jjDelayGeneratedCrateOrigins = true;
jjUseLayer8Speeds = true;
jjTexturedBGTexture = TEXTURE::WINDSTORMFORTRESS;
const uint tileCount = jjTileCount;
if (jjTilesFromTileset(jjTilesetFileName, 0, tileCount, makeTintedMapping(false)))
shiftLayer(jjLayers[6], tileCount);
if (jjTilesFromTileset(jjTilesetFileName, 0, tileCount, makeTintedMapping(true)))
shiftLayer(jjLayers[7], tileCount * 2);
if (jjTilesFromTileset("Buildsit.j2t", 20, 60, makeSingleColorMapping(207))) {
shiftLayer(jjLayers[1], tileCount * 3);
shiftLayer(jjLayers[2], tileCount * 3 + 39);
}
for (uint i = tileCount * 3 + 60; i-- != tileCount * 2;) {
jjTileType[i] = 1;
}
auto@ order = jjLayerOrderGet();
for (int i = 0; i < 2; i++) {
order.insertAt(6, order[0]);
order.removeAt(0);
}
jjLayerOrderSet(order);
jjAnimSets[ANIM::PLUS_SCENERY].load();
jjAnimSets[ANIM::CUSTOM[0]].load(0, "cleanSE.j2a");
if (palette.load("cleanSE.pal"))
palette.apply();
auto@ saw = jjObjectPresets[OBJECT::SPIKEBOLL3D];
saw.behavior = BuzzSaw();
saw.bulletHandling = HANDLING::IGNOREBULLET;
saw.scriptedCollisions = true;
saw.curAnim = jjAnimSets[ANIM::CUSTOM[0]];
saw.animSpeed = saw.curAnim + 1;
saw.killAnim = saw.curAnim + 2;
saw.determineCurFrame();
se::energyBlast.loadAnims(jjAnimSets[ANIM::CUSTOM[1]]);
se::energyBlast.loadSamples(array<SOUND::Sample> = {SOUND::INTRO_BLOW});
se::energyBlast.setAsWeapon(3, weaponHook);
}
void onLevelReload() {
palette.apply();
}
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.