Name | Author | Game Mode | Rating | |||||
---|---|---|---|---|---|---|---|---|
Anniversary Bash 20 Levels | Jazz2Online | Multiple | N/A |
#pragma require "SEroller.asc"
#pragma require "SEminimirv.asc"
#pragma require "SEse.dat"
#pragma require "SEse.j2a"
#pragma require "SEse.j2t"
#include "SEroller.asc"
#include "SEminimirv.asc"
funcdef int LanternFrameGetter(int, int, int);
enum CustomAnimation {
animBanner,
animLantern,
animGarland,
animGrass,
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 Banner {
private int x, y, frame;
private float angle, speed;
private WindPollingPoint@ wind;
Banner(int xPos, int yPos, int anim, WindPollingPoint@ windPoint) {
x = xPos;
y = yPos;
frame = jjAnimations[anim];
angle = speed = 0.f;
@wind = windPoint;
}
void draw(jjCANVAS@ canvas) {
canvas.drawRotatedSpriteFromCurFrame(x, y, frame, int(angle));
}
void process() {
float xWind, yWind;
wind.poll(xWind, yWind);
yWind += 3.f;
float xPoint = jjSin(int(angle)), yPoint = jjCos(int(angle));
float torque = (yPoint * xWind - xPoint * yWind) * 0.2f;
speed += torque;
speed *= 0.99f;
angle += speed;
}
}
class Lantern {
private uint8 palshift;
private int x, y, type;
private float angle, speed, dist;
private jjANIMATION@ anim, lightAnim;
private WindPollingPoint@ wind;
Lantern(double xPos, double yPos, uint sprite, uint lightSprite, float lightDistance, int color, WindPollingPoint@ windPoint) {
const array<uint8> palshifts = {24, 8, 24, 72, 0};
x = int(xPos);
y = int(yPos + 1.5);
angle = speed = 0.f;
@anim = jjAnimations[sprite];
@lightAnim = jjAnimations[lightSprite];
type = color % palshifts.length();
palshift = palshifts[type];
@wind = windPoint;
dist = lightDistance;
}
void draw(jjCANVAS@ canvas, int order, int row, LanternFrameGetter@ getFrame) const {
int frame = getFrame(type, order, row);
int intAngle = int(angle);
if (!jjLowDetail && jjColorDepth != 8 && frame != 0) {
int s = int(jjSin(intAngle) * dist);
int c = int(jjCos(intAngle) * dist);
canvas.drawSpriteFromCurFrame(x + s, y + c, lightAnim + frame, 0, SPRITE::ALPHAMAP, palshift + 16);
}
canvas.drawRotatedSpriteFromCurFrame(x, y, anim + frame, intAngle, 1.f, 1.f, SPRITE::PALSHIFT, palshift);
}
void process() {
float xWind, yWind;
wind.poll(xWind, yWind);
yWind += 0.5f;
float xPoint = jjSin(int(angle)), yPoint = jjCos(int(angle));
float torque = (yPoint * xWind - xPoint * yWind) * 0.8f;
speed += torque;
speed *= 0.98f;
angle += speed;
angle %= 1024.f;
}
}
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, WindManager@ wind) {
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;
double lanternY = arc(lanternX);
int anim = getLanternSprite(i, segments, lanternX);
float dist = 14.f + anim * 4;
lanterns.insertLast(Lantern(lanternX, lanternY, lanternSprite + anim, lanternSprite + 2, dist, m % 9, wind.getPollingPoint(lanternX, lanternY)));
}
}
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(const jjPLAYER@ player, jjCANVAS@ canvas, int order, int row, LanternFrameGetter@ getFrame) const {
const double tolerance = 48.0;
if (sprite != 0 && player.cameraX < right + tolerance && player.cameraY < bottom + tolerance && player.cameraX + jjSubscreenWidth > left - tolerance && player.cameraY + jjSubscreenHeight > top - tolerance) {
canvas.drawSpriteFromCurFrame(int(left), int(top), sprite, 0, SPRITE::ALPHAMAP, 127);
for (int i = lanterns.length(); i-- != 0;) {
lanterns[i].draw(canvas, order, row, getFrame);
}
}
}
void process() {
for (int i = lanterns.length(); i-- != 0;) {
lanterns[i].process();
}
}
void generateSprite(uint frame) {
const int iterationCount = 20;
const double iterationCountD = iterationCount;
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 + 2;
jjPIXELMAP image(width, height);
array<double> opacity(height);
double verticalShift = 0.5 - t;
for (int i = 0; i < width; i++) {
int highest = height, lowest = 0;
for (int j = 0; j < iterationCount; j++) {
double value = arc(l + i + j / iterationCountD) + verticalShift;
int cur = int64(value);
double frac = value % 1.0;
opacity[cur] += 1.0 - frac;
opacity[cur + 1] += frac;
if (cur < highest)
highest = cur;
if (cur > lowest)
lowest = cur;
}
lowest += 2;
double mul = (lowest - highest) * (127.5 / iterationCountD);
for (int j = highest; j < lowest; j++) {
int color = int(opacity[j] * mul);
image[i, j] = color < 256 ? color : 255;
opacity[j] = 0.0;
}
}
if (image.save(jjAnimFrames[frame]))
sprite = frame;
}
}
class Grass {
private int x, y, angle;
private uint frame;
private WindPollingPoint@ wind;
Grass(int xPos, int yPos, uint sprite, int a, WindPollingPoint@ windPoint) {
x = xPos;
y = yPos;
frame = sprite;
angle = a;
@wind = windPoint;
}
void draw(jjCANVAS@ canvas, uint frameShift) const {
float xWind, yWind;
wind.poll(xWind, yWind);
int mod = int(abs(xWind) * 4.f);
canvas.drawRotatedSpriteFromCurFrame(x, y, frame + frameShift, angle - int(xWind * 128.f) + jjRandom() % (mod * 2 + 1) - mod);
}
}
class GrassChunk {
private array<Grass@> grass;
void draw(jjCANVAS@ canvas, uint frameShift) const {
for (int i = grass.length(); i-- != 0;) {
grass[i].draw(canvas, frameShift);
}
}
void insert(int x, int y, uint sprite, int angle, const WindManager@ windManager) {
grass.insertLast(Grass(x, y, sprite, angle, windManager.getPollingPoint(x, y)));
}
void shuffle(jjRNG& random) {
for (uint i = grass.length(); i > 1;) {
uint j = random() % i;
i--;
Grass@ temp = grass[i];
@grass[i] = grass[j];
@grass[j] = temp;
}
}
}
class WindChunk {
float xPrev, yPrev, x, y, xNext, yNext;
}
class WindDataPoint {
private const WindChunk@ chunk;
private float proximity;
WindDataPoint(const WindChunk@ src, float srcProximity) {
@chunk = src;
proximity = srcProximity;
}
float getX() const {
return chunk.x * proximity;
}
float getXPrev() const {
return chunk.xPrev * proximity;
}
float getY() const {
return chunk.y * proximity;
}
float getYPrev() const {
return chunk.yPrev * proximity;
}
}
class WindPollingPoint {
private array<const WindDataPoint@> dataPoints;
private const WindManager@ manager;
private uint periodNumber;
private float xPrev, x, yPrev, y;
WindPollingPoint(const WindManager@ windManager, const array<const WindDataPoint@> &in points) {
dataPoints = points;
@manager = windManager;
periodNumber = ~1;
}
private void cachePoll(float &out xOut, float &out yOut) const {
float progress = manager.getProgress();
xOut = xPrev + (x - xPrev) * progress;
yOut = yPrev + (y - yPrev) * progress;
}
void poll(float &out xOut, float &out yOut) {
uint curPeriod = manager.getPeriodNumber();
if (curPeriod != periodNumber) {
if (curPeriod == periodNumber + 1) {
xPrev = x;
yPrev = y;
x = y = 0.f;
for (int i = dataPoints.length(); i-- != 0;) {
const WindDataPoint@ point = dataPoints[i];
x += point.getX();
y += point.getY();
}
} else {
xPrev = x = yPrev = y = 0.f;
for (int i = dataPoints.length(); i-- != 0;) {
const WindDataPoint@ point = dataPoints[i];
xPrev += point.getXPrev();
x += point.getX();
yPrev += point.getYPrev();
y += point.getY();
}
}
periodNumber = curPeriod;
}
cachePoll(xOut, yOut);
}
}
class WindManager {
private jjRNG random;
private array<array<WindChunk>> winds;
private uint chunkSize, period, periodNumber, counter;
private int chunkRows, chunkColumns, chunkCount;
private float chunkSizeInPixels, persistence, volatility, influence, explosiveness, progress, sqrtHalf = sqrt(0.5f);
private double randMul = 2.0 ** -63;
WindManager(uint64 seed, uint windChunkSize, uint windPeriod, float windPersistence, float windVolatility, float windInfluence, float windExplosiveness) {
random.seed(seed);
chunkSize = windChunkSize;
chunkSizeInPixels = chunkSize << 5;
period = windPeriod;
counter = periodNumber = 0;
progress = 0.f;
persistence = windPersistence;
volatility = windVolatility;
influence = windInfluence;
explosiveness = windExplosiveness;
winds.resize(jjLayers[4].height / chunkSize + 2);
for (int i = winds.length(); i-- != 0;) {
winds[i].resize(jjLayers[4].width / chunkSize + 2);
}
chunkRows = winds.length();
chunkColumns = winds[0].length();
chunkCount = chunkRows * chunkColumns;
}
void process() {
int left = counter * chunkCount / period;
counter++;
int right = counter * chunkCount / period;
for (int i = left; i < right; i++) {
int y = i % chunkRows;
int x = i / chunkRows;
WindChunk@ chunk = winds[y][x];
chunk.xNext = chunk.x * persistence + float(int64(random()) * randMul) * volatility;
chunk.yNext = chunk.y * persistence + float(int64(random()) * randMul) * volatility;
for (int j = -1; j <= 1; j++) {
int yOther = y + j;
if (yOther >= 0 && yOther < chunkRows) {
for (int k = -1; k <= 1; k++) {
if (j != 0 || k != 0) {
int xOther = x + k;
if (xOther >= 0 && xOther < chunkColumns) {
const WindChunk@ other = winds[yOther][xOther];
float dot = other.x * k + other.y * j;
dot *= dot > 0.f ? explosiveness : influence;
if (j != 0 && k != 0)
dot *= sqrtHalf;
chunk.xNext -= other.x * dot;
chunk.yNext -= other.y * dot;
}
}
}
}
}
float sum = chunk.xNext * chunk.xNext + chunk.yNext * chunk.yNext;
if (sum > 1.f) {
sum = sqrt(sum);
chunk.xNext /= sum;
chunk.yNext /= sum;
}
}
if (counter == period) {
counter = 0;
periodNumber++;
for (int i = chunkRows; i-- != 0;) {
for (int j = chunkColumns; j-- != 0;) {
WindChunk@ chunk = winds[i][j];
chunk.xPrev = chunk.x;
chunk.yPrev = chunk.y;
chunk.x = chunk.xNext;
chunk.y = chunk.yNext;
}
}
}
progress = float(counter) / float(period);
}
WindPollingPoint@ getPollingPoint(float x, float y) const {
float leftF = x / chunkSizeInPixels + 0.5f;
float topF = y / chunkSizeInPixels + 0.5f;
int left = int(leftF);
int top = int(topF);
array<const WindDataPoint@> dataPoints(4);
for (int i = 0; i < 2; i++) {
int row = top + i;
float rowDist = 1.f - abs(row - topF);
for (int j = 0; j < 2; j++) {
int col = left + j;
float colDist = 1.f - abs(col - leftF);
@dataPoints[i << 1 | j] = WindDataPoint(winds[row][col], rowDist * colDist);
}
}
return WindPollingPoint(this, dataPoints);
}
float getProgress() const {
return progress;
}
uint getPeriodNumber() const {
return periodNumber;
}
}
class Decorator {
private array<Banner@> banners;
private array<Garland@> garlands;
private uint lanternSprite;
private array<array<GrassChunk>> grassChunks;
private WindManager@ windManager;
void draw(const jjPLAYER@ player, jjCANVAS@ canvas, int order, int row) const {
{
int left = (int(player.cameraX) - 32) >>> 8;
int top = (int(player.cameraY) - 32) >>> 8;
int right = (int(player.cameraX) + jjSubscreenWidth + 32) >>> 8;
int bottom = (int(player.cameraY) + jjSubscreenHeight + 32) >>> 8;
if (left < 0)
left = 0;
if (top < 0)
top = 0;
if (right >= int(grassChunks[0].length()))
right = grassChunks[0].length() - 1;
if (bottom >= int(grassChunks.length()))
bottom = grassChunks.length() - 1;
for (int k = jjLowDetail ? 1 : 2; k-- != 0;) {
for (int i = top; i <= bottom; i++) {
for (int j = left; j <= right; j++) {
grassChunks[i][j].draw(canvas, k);
}
}
}
}
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(player, canvas, order, row, getFrame);
}
}
void drawForeground(jjCANVAS@ canvas) const {
for (int i = banners.length(); i-- != 0;) {
banners[i].draw(canvas);
}
}
void createLanternSprites() {
const array<int> tiles = {566, 570};
const int frameCount = 6;
lanternSprite = jjAnimSets[ANIM::CUSTOM[animLantern]].allocate(array<uint>(3, 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]);
jjAnimFrames[anim + l].hotSpotX = -15;
jjAnimFrames[anim + l].hotSpotY = 0;
}
}
{
const int size = 40;
const float halfSizeF = (size - 1) * 0.5f;
jjPIXELMAP src(size, size), dest(size, size);
for (int j = 0; j < size; j++) {
float dy = (halfSizeF - j) / halfSizeF;
float dy2 = dy * dy;
for (int k = 0; k < size; k++) {
float dx = (halfSizeF - k) / halfSizeF;
float dx2 = dx * dx;
float value = 1.f - sqrt(dx2 + dy2);
if (value > 0.f) {
src[k, j] = int(value * 255.f);
}
}
}
uint anim = jjAnimations[lanternSprite + 2];
for (int l = 0; l < frameCount; l++) {
for (int j = 0; j < size; j++) {
for (int k = 0; k < size; k++) {
dest[k, j] = src[k, j] * l / frameCount;
}
}
dest.save(jjAnimFrames[anim + l]);
jjAnimFrames[anim + l].hotSpotX = 1 - size / 2;
jjAnimFrames[anim + l].hotSpotY = -size / 2;
}
}
}
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, windManager));
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);
}
}
void loadGrassSprites() {
jjAnimSets[ANIM::CUSTOM[animGrass]].load(0, "SEse.j2a");
}
void plantGrass() {
const uint totalGrassAnims = 3;
int height = ((jjLayers[4].height - 1) >> 3) + 1;
int width = ((jjLayers[4].width - 1) >> 3) + 1;
grassChunks.resize(height);
for (int i = height; i-- != 0;) {
grassChunks[i].resize(width);
}
array<uint16> grassTiles = {
1, 2, 3, 6, 7, 16, 17, 19, 26, 27, 28, 29, 30, 36, 37, 38, 39,
70, 71, 74, 79, 94, 105, 106, 110, 111, 114, 138, 141, 143,
360, 362, 365, 366, 385, 386, 387, 388, 482, 489, 510, 517
};
uint8 grassColor = jjPIXELMAP(2)[16, 2];
uint animSet = jjAnimSets[ANIM::CUSTOM[animGrass]];
jjRNG random;
for (int i = 0; i < jjLayers[4].height; i++) {
int top = i << 5;
for (int j = 0; j < jjLayers[4].width; j++) {
uint16 tile = jjLayers[4].tileGet(j, i);
if (grassTiles.find(tile) >= 0 && (i == 0 || jjLayers[3].tileGet(j, i) == 0)) {
int left = j << 5;
jjPIXELMAP src(tile);
for (int k = j % 3; k < 32; k += 3) {
for (int l = 0; l < 32; l++) {
if (src[k, l] == grassColor) {
int x = left + k;
int y = top + l;
int anim = random() % totalGrassAnims;
grassChunks[y >>> 8][x >>> 8].insert(x, y, jjAnimations[animSet + anim], random() % 65 - 32, windManager);
break;
}
}
}
}
}
}
for (int i = height; i-- != 0;) {
for (int j = width; j-- != 0;) {
grassChunks[i][j].shuffle(random);
}
}
}
void createBannerSprites() {
const array<int> tiles = {0, 10, 10, 20};
jjANIMSET@ animSet = jjAnimSets[ANIM::CUSTOM[animBanner]];
animSet.allocate(array<uint> = {1, 1});
for (int i = 2; i-- != 0;) {
jjPIXELMAP dest(32, 128);
for (int j = 4; j-- != 0;) {
jjPIXELMAP src(538 + i + tiles[j]);
int offset = j << 5;
for (int k = 32; k-- != 0;) {
for (int l = 32; l-- != 0;) {
dest[l, k | offset] = src[l, k];
}
}
}
jjANIMFRAME@ frame = jjAnimFrames[jjAnimations[animSet + i]];
dest.save(frame);
frame.hotSpotX = -16;
frame.hotSpotY = -2;
}
}
void hangBanners() {
uint animSet = jjAnimSets[ANIM::CUSTOM[animBanner]];
jjLAYER@ layer = jjLayers[3];
for (int i = layer.height; i-- != 0;) {
for (int j = layer.width; j-- != 0;) {
int tile = layer.tileGet(j, i);
if (tile | 1 == 539) {
layer.generateSettableTileArea(j, i, 1, 4);
for (int k = 4; k-- != 0;) {
layer.tileSet(j, i + k, 0);
}
int x = j << 5 | 16, y = i << 5 | 5;
banners.insertLast(Banner(x, y, animSet + (tile & 1), windManager.getPollingPoint(x, y)));
}
}
}
}
void prepareWind(uint64 seed, uint chunkSize, uint period, float persistence, float volatility, float influence, float explosiveness) {
@windManager = WindManager(seed, chunkSize, period, persistence, volatility, influence, explosiveness);
}
void processWind() const {
windManager.process();
for (int i = garlands.length(); i-- != 0;) {
garlands[i].process();
}
for (int i = banners.length(); i-- != 0;) {
banners[i].process();
}
}
}
Decorator decorator;
se::DefaultWeaponHook weaponHook;
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 fillWaterPools() {
const array<int> poolFloorTiles = {37, 2, 38};
const jjLAYER@ foreground = jjLayers[3];
int width = foreground.width;
int height = foreground.height;
jjLAYER waterLayer(width, height);
waterLayer.xSpeed = foreground.xSpeed;
waterLayer.ySpeed = foreground.ySpeed;
waterLayer.spriteMode = SPRITE::TRANSLUCENT;
int minYForNextGenerate = 0;
for (int i = 2; i < height; i++) {
for (int j = 0; j < width; j++) {
int tile = foreground.tileGet(j, i);
if (tile != 0) {
int index = poolFloorTiles.find(tile);
if (index >= 0) {
if (i >= minYForNextGenerate) {
waterLayer.generateSettableTileArea(j, i - 2, width - j, 2);
minYForNextGenerate = i + 2;
}
waterLayer.tileSet(j, i - 2, 243 + index);
waterLayer.tileSet(j, i - 1, 263 + index);
}
}
}
}
array<jjLAYER@>@ order = jjLayerOrderGet();
order.insertAt(order.findByRef(foreground), waterLayer);
jjLayerOrderSet(order);
}
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 < 245; 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(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 addColumnShadow() {
const array<int> tiles = {606, 758};
for (int k = tiles.length(); k-- != 0;) {
jjPIXELMAP tile(tiles[k]);
for (int i = 32; i-- != 0;) {
int y = 41 - i;
if (y > 0) {
for (int j = 32; j-- != 0;) {
int x = (14 - j) << 2;
int d = x * x + y * y;
if (tile[j, i] != 0 && d > 1040) {
float alpha = (52.f - sqrt(d)) * 0.05f;
if (alpha < 0.25f)
alpha = 0.25f;
jjPALCOLOR color = jjPalette.color[tile[j, i]];
color.red = int(color.red * alpha);
color.green = int(color.green * alpha) >> 1;
color.blue = int(color.blue * alpha);
uint8 index = jjPalette.findNearestColor(color);
if (index == 246)
index = 47;
else if (index | 1 == 91)
index = 112;
tile[j, i] = index;
}
}
}
}
tile.save(tiles[k]);
}
}
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);
}
}
}
}
void loadCaveLighting() {
jjSTREAM data("SEse.dat");
uint16 tileCount;
if (data.pop(tileCount) && jjTilesFromTileset("SEse.j2t", 580, tileCount)) {
uint16 left, top, width, height;
if (data.pop(left) && data.pop(top) && data.pop(width) && data.pop(height)) {
if (data.getSize() == width * height * 2) {
jjLAYER@ layer = jjLayers[5];
layer.generateSettableTileArea(left, top, width, height);
for (uint i = 0; i < height; i++) {
for (uint j = 0; j < width; j++) {
uint16 tileID;
data.pop(tileID);
if (tileID != 0)
layer.tileSet(left + j, top + i, tileID);
}
}
}
}
}
}
bool onDrawAmmo(jjPLAYER@ player, jjCANVAS@ canvas) {
return weaponHook.drawAmmo(player, canvas);
}
void onDrawLayer3(jjPLAYER@, jjCANVAS@ canvas) {
decorator.drawForeground(canvas);
}
void onDrawLayer4(jjPLAYER@ player, jjCANVAS@ canvas) {
decorator.draw(player, canvas, jjGetModOrder(), jjGetModRow());
}
void onLevelLoad() {
//beginLevelLoad();
decorator.prepareWind(jjUnixTimeMs(), 10, 20, 0.8f, 0.2f, 0.25f, 0.05f);
decorator.createLanternSprites();
fillWaterPools();
removeSpritePaletteReferences();
setLevelPalette();
adjustMask();
addColumnShadow();
loadBackground();
loadCaveLighting();
//generateCaveLighting();
decorator.createBannerSprites();
decorator.hangBanners();
decorator.hangGarlands();
decorator.loadGrassSprites();
decorator.plantGrass();
se::roller.loadAnims(jjAnimSets[ANIM::CUSTOM[animRoller]]);
se::roller.loadSamples(array<SOUND::Sample> = {SOUND::INTRO_BLOW});
se::roller.setAsWeapon(3, weaponHook);
se::miniMirv.loadAnims(jjAnimSets[ANIM::CUSTOM[animMiniMirv]]);
se::miniMirv.loadSamples(array<SOUND::Sample> = {SOUND::INTRO_BOEM1, SOUND::INTRO_BOEM2});
se::miniMirv.setAsWeapon(9, weaponHook);
//endLevelLoad();
}
void onLevelReload() {
setLevelPalette();
}
void onMain() {
decorator.processWind();
}
void onPlayer(jjPLAYER@ player) {
if (player.yPos < 712.f)
player.yPos = 712.f;
}
void onReceive(jjSTREAM &in packet, int clientID) {
weaponHook.processPacket(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.