Name | Author | Game Mode | Rating | |||||
---|---|---|---|---|---|---|---|---|
Anniversary Bash 19 Levels | Jazz2Online | Multiple | N/A |
#pragma require "SEroller.asc"
#pragma require "SEminimirv.asc"
#pragma require "SEse.j2t"
#include "SEroller.asc"
#include "SEminimirv.asc"
funcdef int LanternFrameGetter(int, int, int);
enum CustomAnimation {
animLantern,
animGarland,
animRoller,
animMiniMirv
}
class Catenary {
private double xt, yt, a;
Catenary(double s, double x1, double y1, double x2, double y2) {
double h = x2 - x1;
double v = y1 - y2;
double g = sqrt(sqrt(s * s - v * v) / h - 1.0);
double b = 0.204124145232 / g;
double p = 0.0;
while (abs(p - b) > 1e-5) {
p = b;
double c = b * 2.0;
double r = 1.0 / c;
double t = sinh(r);
double d = c * t - 1.0;
b -= d * (1.0 - sqrt(d) / g) / (r * cosh(r) - t);
}
a = b * h;
double e = 2.718281828459 ** (1.0 / b);
double m = e - 1.0;
double ve = v * e;
double am = a * m;
double ame = am * e;
double shift = a * log((ve + sqrt(am * ame + ve * ve)) / ame);
xt = x1 - shift;
yt = y1 + a * cosh(shift / a);
}
double getX() const {
return xt;
}
double getY() const {
return yt;
}
double getA() const {
return a;
}
double getLength(double x1, double x2) const {
return a * (sinh((x2 - xt) / a) - sinh((x1 - xt) / a));
}
double getStep(double x, double s) const {
double z = s / a + sinh((x - xt) / a);
return xt + log(z + sqrt(z * z + 1)) * a;
}
double opCall(double x) const {
return yt - a * cosh((x - xt) / a);
}
}
class Lantern {
private uint8 palshift;
private int x, y, type;
private jjANIMATION@ anim;
Lantern(double xPos, double yPos, uint sprite, int color) {
const array<uint8> palshifts = {24, 8, 24, 72, 0};
x = int(xPos - 15.0);
y = int(yPos + 1.5);
@anim = jjAnimations[sprite];
type = color % palshifts.length();
palshift = palshifts[type];
}
void draw(jjCANVAS@ canvas, int order, int row, LanternFrameGetter@ getFrame) const {
canvas.drawSpriteFromCurFrame(x, y, anim + getFrame(type, order, row), 0, SPRITE::PALSHIFT, palshift);
}
}
class Garland {
private Catenary@ arc;
private double left, right, top, bottom;
private uint sprite;
private array<Lantern@> lanterns;
Garland(int x1, int y1, int x2, int y2, double lengthMultiplier, double lanternSpacing, uint lanternSprite) {
double xl = x1 << 5 | 16;
double xr = x2 << 5 | 15;
double yl = y1 << 5 | 16;
double yr = y2 << 5 | 16;
double width = xr - xl;
double height = yr - yl;
double length = sqrt(width * width + height * height) * lengthMultiplier;
@arc = Catenary(length, xl, yl, xr, yr);
left = xl + 8.0;
right = xr - 8.0;
top = yl < yr ? yl : yr;
double x = arc.getX();
bottom = x < left ? left : x > right ? right : arc(x);
int segments = int(length / lanternSpacing);
double segment = length / segments;
double lanternX = xl;
for (int i = 1; i < segments; i++) {
lanternX = arc.getStep(lanternX, segment);
int m = i - 1;
if (segments - i - 1 < m)
m = segments - i - 1;
lanterns.insertLast(Lantern(lanternX, arc(lanternX), lanternSprite + getLanternSprite(i, segments, lanternX), m % 9));
}
}
private int getLanternSprite(int id, int count, double x) const {
if (count & 1 != 0 && x > jjLayers[4].width << 4)
id = ~id;
return id & 1;
}
void draw(jjCANVAS@ canvas, int order, int row, LanternFrameGetter@ getFrame) const {
if (sprite != 0) {
canvas.drawSpriteFromCurFrame(int(left), int(top), sprite);
for (int i = lanterns.length(); i-- != 0;) {
lanterns[i].draw(canvas, order, row, getFrame);
}
}
}
void generateSprite(uint frame, uint8 color) {
int64 l = int64(left + 0.5);
int64 r = int64(right + 0.5);
int64 t = int64(top + 0.5);
int64 b = int64(bottom + 0.5);
int width = r - l + 1;
int height = b - t + 1;
jjPIXELMAP image(width, height);
int prev = -1;
for (int i = 0; i < width; i++) {
int cur = int64(arc(l + i) + 0.5) - t;
image[i, cur] = color;
if (i != 0) {
int step = cur - prev;
while (step > 1) {
step--;
image[i - 1, cur - step] = color;
}
while (step < -1) {
step++;
image[i, cur - step] = color;
}
}
prev = cur;
}
if (image.save(jjAnimFrames[frame]))
sprite = frame;
}
}
class Decorator {
private array<Garland@> garlands;
private uint lanternSprite;
void draw(jjCANVAS@ canvas, int order, int row) const {
LanternFrameGetter@ getFrame = lanternPatternDigitalRitual;
string music = lowercase(jjMusicFileName);
if (music == "yukatan.it")
@getFrame = lanternPatternYukatan;
else if (music == "digital ritual.mo3")
@getFrame = lanternPatternDigitalRitual;
for (int i = garlands.length(); i-- != 0;) {
garlands[i].draw(canvas, order, row, getFrame);
}
}
void createLanternSprites() {
const array<int> tiles = {566, 570};
const int frameCount = 6;
lanternSprite = jjAnimSets[ANIM::CUSTOM[animLantern]].allocate(array<uint>(2, frameCount));
for (int i = 0; i < 2; i++) {
jjPIXELMAP src(tiles[i]), dest;
int dark = 255, bright = 0;
for (int j = 0; j < 32; j++) {
for (int k = 0; k < 32; k++) {
uint8 pixel = src[k, j];
if (pixel != 0) {
int light = jjPalette.color[pixel].getLight();
if (light > bright)
bright = light;
if (light < dark)
dark = light;
}
}
}
int range = bright - dark + 1;
uint anim = jjAnimations[lanternSprite + i];
for (int l = 0; l < frameCount; l++) {
for (int j = 0; j < 32; j++) {
for (int k = 0; k < 32; k++) {
uint8 pixel = src[k, j];
if (pixel != 0)
dest[k, j] = 7 - (jjPalette.color[pixel].getLight() - dark) * (l + (9 - frameCount)) / range | 16;
}
}
dest.save(jjAnimFrames[anim + l]);
}
}
}
void hangGarlands() {
array<int> x(64, -1), y(64, -1);
for (int i = 0; i < jjLayers[4].width; i++) {
for (int j = 0; j < jjLayers[4].height; j++) {
if (jjEventGet(i, j) == AREA::PATH) {
int id = jjParameterGet(i, j, 0, 6);
if (x[id] != -1 && x[id] != i)
garlands.insertLast(Garland(x[id], y[id], i, j, 1.05, 50.0, lanternSprite));
x[id] = i;
y[id] = j;
jjEventSet(i, j, 0);
}
}
}
int count = garlands.length();
jjANIMSET@ animSet = jjAnimSets[ANIM::CUSTOM[animGarland]];
animSet.allocate(array<uint> = {count});
jjANIMATION@ anim = jjAnimations[animSet];
for (int i = 0; i < count; i++) {
garlands[i].generateSprite(anim + i, 127);
}
}
}
Decorator decorator;
se::DefaultAmmoDisplayHook ammoDisplayHook;
se::DefaultNetworkHook networkHook;
string lowercase(string &in s) {
for (int i = s.length(); i-- != 0;) {
if (s[i] > 64 && s[i] < 91)
s[i] ^= 32;
}
return s;
}
int lanternPatternDigitalRitual(int type, int order, int row) {
switch (type) {
case 0:
if (order >= 14 && order < 30 || order >= 32 && order < 36 || order >= 50 && order < 66) {
if (row < 4)
return 5 - row;
if (row >= 4 && row < 9)
return 9 - row;
if (row >= 16 && row < 21)
return 21 - row;
if (order & 1 != 0) {
if (order == 25 || order == 35 || order == 61 || order == 65) {
if (row >= 32 && row < 36)
return 37 - row;
if (row >= 36 && row < 41)
return 41 - row;
if (row >= 48 && row < 53)
return 53 - row;
} else if (order < 29 || order >= 50) {
if (row >= 48 && row < 52)
return 53 - row;
if (row >= 52 && row < 57)
return 57 - row;
}
} else if (order >= 22 && order < 28 || order == 34 || order >= 58) {
if (row >= 40 && row < 45)
return 45 - row;
if (row >= 48 && row < 53)
return 53 - row;
}
} else if (order == 66 && row < 5) {
return 5 - row;
}
break;
case 1:
if (order >= 14 && order < 30 || order >= 32 && order < 36 || order >= 50 && order < 66) {
if (row & ~3 != 20 && row & ~3 != 32 && (row < 52 || order != 25 && order != 29 && order != 35 && order != 61 && order != 65))
return 5 - (row & 3);
} else if (order == 30 || order == 31) {
if (row < 16)
return 5 - (row & 3);
} else if (order >= 36 && order < 40) {
if (row < 5)
return 5 - row;
if (row >= 8 && row < 13)
return 13 - row;
if (row >= 24 && row < 29)
return 29 - row;
if (row >= 28 && row < 33)
return 33 - row;
if (row >= 52)
return 5 - (row & 3);
} else if (order >= 40 && order < 48) {
if (row < 5)
return 5 - row;
if (row >= 8 && row < 13)
return 13 - row;
if (row >= 28 && row < 33)
return 33 - row;
if (row >= 32 && row < 37)
return 37 - row;
if (row >= 40 && row < 45)
return 45 - row;
if (order & 1 != 0 && row >= 56 || order == 47 && row >= 44)
return 5 - (row & 3);
} else if (order >= 66) {
return 0;
} else if (row < 32) {
if ((row >> 2) % 3 != 2)
return 5 - (row & 3);
} else if (order == 3 || order == 7 || order == 9 || order == 11 || order == 13 || order == 49) {
if (row >= 48 && row & ~3 != 52)
return 5 - (row & 3);
} else if (order == 5) {
if (row >= 48 && row < 53)
return 53 - row;
} else if (order == 10 || order == 12) {
if (row >= 36 && row < 41)
return 41 - row;
if (row >= 44 && row < 48)
return 49 - row;
if (row >= 48 && row < 53)
return 53 - row;
if (row >= 56 && row < 61)
return 61 - row;
} else if (order == 48) {
if (row & ~3 != 32 && row & ~3 != 56)
return 5 - (row & 3);
}
break;
case 2:
if (order >= 14 && order < 30 || order >= 32 && order < 36 || order >= 50 && order < 66) {
if (row >= 8 && row < 13)
return 13 - row;
if (row >= 12 && row < 17)
return 17 - row;
if (order & 1 != 0) {
if (order == 25 || order == 35 || order == 61 || order == 65) {
if (row >= 24 && row < 28)
return 29 - row;
if (row >= 28 && row < 33)
return 33 - row;
if (row >= 40 && row < 45)
return 45 - row;
} else if (order < 29 || order >= 50) {
if (row >= 36 && row < 40)
return 41 - row;
if (row >= 40 && row < 44)
return 45 - row;
if (row >= 44 && row < 49)
return 49 - row;
if (row >= 56 && row < 60)
return 61 - row;
if (row >= 60 && row < 65)
return 65 - row;
}
} else if (order >= 22 && order < 28 || order == 34 || order >= 58) {
if (row >= 36 && row < 41)
return 41 - row;
if (row >= 44 && row < 49)
return 49 - row;
if (row >= 52 && row < 57)
return 57 - row;
}
} else if (order == 66 && row < 5) {
return 5 - row;
}
break;
case 3:
if (order >= 2 && order < 14 || order == 48 || order == 49) {
if (order >= 6) {
if (row >= 20 && row < 25)
return 25 - row;
if (row >= 56 && row < 60)
return 61 - row;
}
if (row >= 44 && row < 48)
return 49 - row;
if (row >= 48 && row < 53)
return 53 - row;
if (order >= 5 && row >= 60 && row < 65)
return 65 - row;
} else if (order >= 18 && order < 36) {
if (order >= 26) {
if (row >= 48 && row < 50)
return 53 - row;
if (row >= 50 && row < 54)
return 55 - row;
if (row >= 54 && row < 59)
return 59 - row;
if (row >= 60 && row < 65)
return 65 - row;
}
if (order >= 22 && row >= 20 && row < 25)
return 25 - row;
if (row >= 44 && row < 49)
return 49 - row;
} else if (order >= 40 && order < 48) {
if (row >= 16 && row < 21)
return 21 - row;
if (row >= 32 && row < 37)
return 37 - row;
if (row >= 44 && row < 49)
return 49 - row;
if (row >= 60 && row < 65)
return 65 - row;
} else if (order >= 54 && order != 57 && order < 66) {
if (row >= 44 && row < 48)
return 49 - row;
}
break;
case 4:
if (order == 1 || order == 4) {
if (row >= 22 && row < 32)
return (row - 20) >> 1;
if (row >= 32 && row < 42)
return (43 - row) >> 1;
if (row >= 44 && row < 50)
return (51 - row) >> 1;
if (row >= 52 && row < 60)
return (61 - row) >> 1;
} else if (order == 5 || order == 13 || order == 49) {
if (row >= 48 && row < 53)
return 53 - row;
} else if (order == 32) {
if (row < 4)
return 4 - row;
} else if (order == 66) {
if (row >= 14 && row < 22)
return (25 - row) >> 1;
if (row >= 22 && row < 32)
return (33 - row) >> 1;
}
break;
}
return 0;
}
int lanternPatternYukatan(int type, int order, int row) {
switch (type) {
case 0:
if (order < 26 || order >= 34) {
if (row >= 4 && row < 9)
return 9 - row;
if (row >= 12 && row < 17)
return 17 - row;
if (row >= 24 && row < 29)
return 29 - row;
if (row >= 32 && row < 37)
return 37 - row;
if (row >= 40 && row < 45)
return 45 - row;
}
break;
case 1:
if (order < 52) {
int beat;
if (order < 4)
beat = row & 15;
else if (order == 5 && row >= 52)
beat = row - 52;
else if (order == 13 && row >= 36)
beat = row & 3 + (row == 40 || row == 52 ? 4 : 0);
else if (order == 33 && row >= 38)
beat = row >= 46 && row < 54 ? row - 46 : row >= 62 ? row - 62 : row & 1;
else if (order >= 34 && order < 46)
beat = row + 4 & 7;
else
beat = row & 7;
return beat < 5 ? 5 - beat : 0;
}
break;
case 2:
if (order < 26 || order >= 34) {
if (row >= 20 && row < 25)
return 25 - row;
if (row >= 28 && row < 33)
return 33 - row;
if (row >= 48 && row < 53)
return 53 - row;
if (row >= 56 && row < 61)
return 61 - row;
}
break;
case 3:
if (order >= 6 && order < 42) {
if (row >= 32 && row < 37)
return 37 - row;
if (order >= 7 && order < 20 || order == 25) {
int power = order < 14 ? 4 : 2;
if (row < power)
return power - row;
if (row >= 8 && row < 8 + power)
return 8 + power - row;
}
}
break;
case 4:
if (order & 3 == 1 && row >= 36 && order != 1 && order != 25) {
int beat = row >= 52 ? row - 52 : row + 4 & 7;
return beat < 5 ? 5 - beat : 0;
}
if (order & 3 == 2 && row >= 4 && row < 20 && (order < 34 || order >= 46)) {
int beat = row + 4 & 7;
return beat < 5 ? 5 - beat : 0;
}
break;
}
return 0;
}
void removeSpritePaletteReferences() {
array<int> mapping(256);
for (int i = 1; i < 96; i++) {
jjPALCOLOR color = jjPalette.color[i];
int best = 0x40000;
for (int j = 96; j < 256; j++) {
jjPALCOLOR match = jjPalette.color[j];
int red = int(match.red) - color.red;
int green = int(match.green) - color.green;
int blue = int(match.blue) - color.blue;
int dist = red * red + green * green + blue * blue;
if (dist < best) {
best = dist;
mapping[i] = j;
}
}
}
for (int i = 96; i < 256; i++) {
mapping[i] = i;
}
for (uint i = 1; i < jjTileCount; i++) {
jjPIXELMAP tile(i);
for (int j = 0; j < 32; j++) {
for (int k = 0; k < 32; k++) {
tile[k, j] = mapping[tile[k, j]];
}
}
tile.save(i, true);
}
}
void setLevelPalette() {
jjPAL@ pal = jjPalette;
pal.reset();
for (int i = 96; i < 256; i++) {
pal.color[i].red = pal.color[i].red * 2 / 3;
pal.color[i].green = pal.color[i].green * 2 / 3;
if (pal.color[i].red / 2 > pal.color[i].blue)
pal.color[i].blue = pal.color[i].red / 2;
if (pal.color[i].green / 2 > pal.color[i].blue)
pal.color[i].blue = pal.color[i].green / 2;
}
/*pal.color[96] = jjPALCOLOR(191, 213, 237);
pal.color[120] = jjPALCOLOR(155, 177, 203);
pal.color[123] = jjPALCOLOR(47, 96, 150);
pal.color[126] = jjPALCOLOR(37, 75, 121);
pal.color[138] = jjPALCOLOR(26, 54, 91);
pal.color[144] = jjPALCOLOR(18, 38, 68);
pal.color[167] = jjPALCOLOR(15, 33, 61);
pal.color[200] = jjPALCOLOR(12, 27, 53);
pal.color[206] = jjPALCOLOR(7, 16, 42);
pal.color[219] = jjPALCOLOR(5, 11, 38);
pal.color[242] = jjPALCOLOR(4, 19, 50);*/
pal.color[96] = jjPALCOLOR(228, 226, 244);
pal.color[120] = jjPALCOLOR(170, 165, 219);
pal.color[123] = jjPALCOLOR(132, 126, 204);
pal.color[126] = jjPALCOLOR(109, 102, 194);
pal.color[138] = jjPALCOLOR(74, 66, 167);
pal.color[144] = jjPALCOLOR(88, 80, 185);
pal.color[167] = jjPALCOLOR(65, 57, 147);
pal.color[200] = jjPALCOLOR(40, 35, 91);
pal.color[206] = jjPALCOLOR(54, 47, 121);
pal.color[219] = jjPALCOLOR(26, 23, 59);
pal.color[242] = jjPALCOLOR(17, 15, 38);
pal.apply();
}
void adjustMask() {
const array<int> emptyMasks = {85, 86, 87, 107, 430, 431, 432, 454, 567, 569};
jjMASKMAP mask;
for (int i = emptyMasks.length(); i-- != 0;) {
mask.save(emptyMasks[i], true);
}
jjMASKMAP(21).save(380, true);
jjMASKMAP(23).save(382, true);
}
void loadBackground() {
int start = jjTileCount;
const string originalTileset = jjTilesetFileName;
if (jjTilesFromTileset("SEse.j2t", 10, 570)) {
jjLAYER@ layer = jjLayers[8];
layer.generateSettableTileArea();
for (int i = 0; i < layer.height; i++) {
for (int j = 0; j < layer.width; j++) {
int tileID = start + i * 10 + j % 10 + j / 10 * 190;
layer.tileSet(j, i, tileID);
// JJ2+ bug workaround
jjTileType[tileID] = 0;
}
}
}
// JJ2+ bug workaround
jjTilesFromTileset(originalTileset, 0, 0);
}
bool onDrawAmmo(jjPLAYER@ player, jjCANVAS@ canvas) {
return ammoDisplayHook.draw(player, canvas);
}
void onDrawLayer4(jjPLAYER@, jjCANVAS@ canvas) {
decorator.draw(canvas, jjGetModOrder(), jjGetModRow());
}
void onLevelLoad() {
decorator.createLanternSprites();
removeSpritePaletteReferences();
setLevelPalette();
adjustMask();
loadBackground();
decorator.hangGarlands();
jjLayers[2].spriteMode = SPRITE::TRANSLUCENT;
se::roller.loadAnims(jjAnimSets[ANIM::CUSTOM[animRoller]]);
se::roller.loadSamples(array<SOUND::Sample> = {SOUND::INTRO_BLOW});
se::roller.setAsWeapon(3, ammoDisplayHook, networkHook);
se::miniMirv.loadAnims(jjAnimSets[ANIM::CUSTOM[animMiniMirv]]);
se::miniMirv.loadSamples(array<SOUND::Sample> = {SOUND::INTRO_BOEM1, SOUND::INTRO_BOEM2});
se::miniMirv.setAsWeapon(9, ammoDisplayHook, networkHook);
}
void onLevelReload() {
setLevelPalette();
}
void onReceive(jjSTREAM &in packet, int clientID) {
networkHook.process(packet, clientID);
}
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.