| Name | Author | Game Mode | Rating | |||||
|---|---|---|---|---|---|---|---|---|
| A Pleasant Little Planet | Lark | Capture the flag | N/A | |||||
const bool MLLESetupSuccessful = MLLE::Setup(); ///@MLLE-Generated
#include "MLLE-Include-1.8.asc" ///@MLLE-Generated
#pragma require "JJ1 Tubelectric NOKA.j2t" ///@MLLE-Generated
#pragma require "olckeengrounds.j2l" ///@MLLE-Generated
#pragma require "jj2_silence.wav"
#pragma require "keen_pants.wav"
const int LOCAL_PLAYER_SLOTS = 4;
const int WEAPON_COUNT = 9;
const int SUGAR_RUSH_DURATION = 15 * 70;
const int SUGAR_RUSH_HIT_PENALTY = 5 * 70;
const int SUGAR_RUSH_HIT_COOLDOWN = 70;
const string SUGAR_RUSH_MUSIC = "keen_pants.wav";
array<bool> sugarRushActive(LOCAL_PLAYER_SLOTS, false);
array<int> sugarRushTicksRemaining(LOCAL_PLAYER_SLOTS, 0);
array<int> sugarRushHitCooldown(LOCAL_PLAYER_SLOTS, 0);
array<int> protectedHealth(LOCAL_PLAYER_SLOTS, 0);
array<bool> infiniteAmmo(LOCAL_PLAYER_SLOTS * WEAPON_COUNT, false);
array<int> savedAmmo(LOCAL_PLAYER_SLOTS * WEAPON_COUNT, 0);
bool sugarRushMusicActive = false;
string musicBeforeSugarRush;
bool supportedGameMode() {
return jjGameMode == GAME::CTF || jjGameMode == GAME::SP;
}
bool supportedCharacter(const jjPLAYER@ player) {
return player.charCurr == CHAR::JAZZ || player.charCurr == CHAR::SPAZ;
}
int ammoIndex(int localPlayerID, int weapon) {
return localPlayerID * WEAPON_COUNT + weapon - 1;
}
void silenceNativeSugarRushJingle() {
jjSampleLoad(SOUND::RUSH_RUSH, "jj2_silence.wav");
}
void startSugarRushMusic() {
if (sugarRushMusicActive) {
return;
}
musicBeforeSugarRush = jjMusicFileName;
sugarRushMusicActive = jjMusicLoad(SUGAR_RUSH_MUSIC, true, true);
if (!sugarRushMusicActive) {
musicBeforeSugarRush = "";
}
}
void restoreLevelMusic(bool force = false) {
if (!sugarRushMusicActive) {
return;
}
if (!force) {
for (int playerID = 0; playerID < LOCAL_PLAYER_SLOTS; ++playerID) {
if (sugarRushActive[playerID]) {
return;
}
}
}
if (musicBeforeSugarRush.length() > 0) {
jjMusicLoad(musicBeforeSugarRush, true);
} else {
jjMusicStop();
}
sugarRushMusicActive = false;
musicBeforeSugarRush = "";
}
void resetSugarRushState() {
for (int playerID = 0; playerID < LOCAL_PLAYER_SLOTS; ++playerID) {
sugarRushActive[playerID] = false;
sugarRushTicksRemaining[playerID] = 0;
sugarRushHitCooldown[playerID] = 0;
protectedHealth[playerID] = 0;
for (int weapon = 1; weapon <= WEAPON_COUNT; ++weapon) {
const int index = ammoIndex(playerID, weapon);
infiniteAmmo[index] = false;
savedAmmo[index] = 0;
}
}
}
void beginSugarRush(jjPLAYER@ player) {
const int playerID = player.localPlayerID;
player.startSugarRush(SUGAR_RUSH_DURATION);
sugarRushActive[playerID] = true;
sugarRushTicksRemaining[playerID] = SUGAR_RUSH_DURATION;
sugarRushHitCooldown[playerID] = 0;
protectedHealth[playerID] = player.health;
startSugarRushMusic();
for (int weapon = 1; weapon <= WEAPON_COUNT; ++weapon) {
const int index = ammoIndex(playerID, weapon);
const int ammo = player.ammo[weapon];
infiniteAmmo[index] = ammo > 0;
savedAmmo[index] = ammo;
}
}
void shortenSugarRushAfterHit(jjPLAYER@ player) {
const int playerID = player.localPlayerID;
if (sugarRushHitCooldown[playerID] > 0) {
return;
}
sugarRushHitCooldown[playerID] = SUGAR_RUSH_HIT_COOLDOWN;
sugarRushTicksRemaining[playerID] -= SUGAR_RUSH_HIT_PENALTY;
jjSamplePriority(SOUND::P2_CRUNCH);
// One tick lets JJ2 finish the rush normally when a hit uses up its time.
if (sugarRushTicksRemaining[playerID] < 1) {
sugarRushTicksRemaining[playerID] = 1;
}
player.startSugarRush(sugarRushTicksRemaining[playerID]);
}
bool isHostileProjectile(const jjOBJ@ object, const jjPLAYER@ victim) {
if (object.playerHandling == HANDLING::ENEMYBULLET) {
return true;
}
if (object.playerHandling != HANDLING::PLAYERBULLET ||
object.creatorType != CREATOR::PLAYER || object.creatorID >= 32 ||
object.creatorID == victim.playerID) {
return false;
}
const jjPLAYER@ attacker = jjPlayers[object.creatorID];
return attacker.isActive && attacker.isEnemy(victim);
}
bool touchingHostileProjectile(jjPLAYER@ player) {
for (int objectID = 1; objectID < jjObjectCount; ++objectID) {
const jjOBJ@ object = jjObjects[objectID];
if (!object.isActive || object.state == STATE::KILL) {
continue;
}
if (isHostileProjectile(object, player) && player.doesCollide(object, true)) {
return true;
}
}
return false;
}
void maintainSugarRush(jjPLAYER@ player) {
const int playerID = player.localPlayerID;
player.blink = 70;
for (int weapon = 1; weapon <= WEAPON_COUNT; ++weapon) {
const int index = ammoIndex(playerID, weapon);
const int ammo = player.ammo[weapon];
// A gun collected during the rush also becomes infinite.
if (!infiniteAmmo[index] && ammo > 0) {
infiniteAmmo[index] = true;
savedAmmo[index] = ammo;
}
if (infiniteAmmo[index]) {
// Keep ammo pickups, but undo ammo spent by firing.
if (ammo > savedAmmo[index]) {
savedAmmo[index] = ammo;
} else if (ammo < savedAmmo[index]) {
player.ammo[weapon] = savedAmmo[index];
}
}
}
}
void endSugarRush(jjPLAYER@ player) {
const int playerID = player.localPlayerID;
for (int weapon = 1; weapon <= WEAPON_COUNT; ++weapon) {
const int index = ammoIndex(playerID, weapon);
if (infiniteAmmo[index]) {
player.ammo[weapon] = savedAmmo[index];
}
infiniteAmmo[index] = false;
savedAmmo[index] = 0;
}
player.blink = 0;
sugarRushActive[playerID] = false;
sugarRushTicksRemaining[playerID] = 0;
sugarRushHitCooldown[playerID] = 0;
protectedHealth[playerID] = 0;
restoreLevelMusic();
}
void onLevelLoad() {
resetSugarRushState();
jjObjectPresets[OBJECT::CANDY].determineCurFrame();
silenceNativeSugarRushJingle();
}
void onLevelBegin() {
jjSugarRushAllowed = true;
// Reload after JJ2 has initialized its animation-set sounds.
silenceNativeSugarRushJingle();
}
void onLevelReload() {
restoreLevelMusic(true);
resetSugarRushState();
silenceNativeSugarRushJingle();
}
void onPlayer(jjPLAYER@ player) {
if (player is null || !player.isLocal) {
return;
}
const int playerID = player.localPlayerID;
if (playerID < 0 || playerID >= LOCAL_PLAYER_SLOTS || !sugarRushActive[playerID]) {
return;
}
if (sugarRushTicksRemaining[playerID] > 0) {
--sugarRushTicksRemaining[playerID];
}
if (sugarRushHitCooldown[playerID] > 0) {
--sugarRushHitCooldown[playerID];
}
// Restore any damage that bypasses blink, while still charging five seconds.
if (player.health < protectedHealth[playerID]) {
player.health = protectedHealth[playerID];
shortenSugarRushAfterHit(player);
} else if (player.health > protectedHealth[playerID]) {
protectedHealth[playerID] = player.health;
}
// Blink prevents ordinary damage, so detect the projectile that would hit.
if (sugarRushHitCooldown[playerID] == 0 && touchingHostileProjectile(player)) {
shortenSugarRushAfterHit(player);
}
}
bool onDrawAmmo(jjPLAYER@ player, jjCANVAS@ canvas) {
if (player !is null && supportedGameMode() && supportedCharacter(player)) {
const int right = player.subscreenX + jjSubscreenWidth - 8;
const int aboveAmmo = player.subscreenY + jjSubscreenHeight - 52;
const string foodMeter = "x" + formatInt(player.food % 100, "1") + "/100";
jjTEXTAPPEARANCE foodTextStyle(STRING::RIGHTALIGN);
const int textWidth = jjGetStringWidth(foodMeter, STRING::MEDIUM, foodTextStyle);
canvas.drawSpriteFromCurFrame(
right - textWidth - 12,
aboveAmmo + 2,
jjObjectPresets[OBJECT::CANDY].curFrame
);
canvas.drawString(right, aboveAmmo, foodMeter, STRING::MEDIUM, STRING::RIGHTALIGN);
}
// Keep JJ2's normal weapon and ammo indicator.
return false;
}
void onPlayerDraw(jjPLAYERDRAW& draw) {
jjPLAYER@ player = draw.player;
if (player is null || !player.isLocal) {
return;
}
const int playerID = player.localPlayerID;
if (playerID < 0 || playerID >= LOCAL_PLAYER_SLOTS) {
return;
}
if (!supportedGameMode() || !supportedCharacter(player)) {
if (sugarRushActive[playerID]) {
endSugarRush(player);
}
return;
}
// draw.sugarRush is JJ2+'s exposed native Sugar Rush state. It is false
// while frozen, so preserve an already active rush until the player thaws.
const bool rushing = draw.sugarRush || (sugarRushActive[playerID] && player.frozen > 0);
if (rushing) {
if (!sugarRushActive[playerID]) {
beginSugarRush(player);
}
maintainSugarRush(player);
} else if (sugarRushActive[playerID]) {
endSugarRush(player);
}
}
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.