Name | Author | Game Mode | Rating | |||||
---|---|---|---|---|---|---|---|---|
Academy | Superjazz | Battle | N/A |
#include "academy_drawing.asc"
#include "academy_drawing_general.asc"
#include "academy_entities.asc"
#include "academy_init.asc"
#include "academy_networking.asc"
#include "academy_spells.asc"
#include "academy_statics.asc"
#include "academy_utils.asc"
#include "sevk.asc"
#include "SEweapon.asc"
#pragma require "academy_drawing.asc"
#pragma require "academy_drawing_general.asc"
#pragma require "academy_entities.asc"
#pragma require "academy_init.asc"
#pragma require "academy_networking.asc"
#pragma require "academy_spells.asc"
#pragma require "academy_statics.asc"
#pragma require "academy_utils.asc"
#pragma require "sevk.asc"
#pragma require "SEweapon.asc"
/* Academy Engine, written by Superjazz of XLM */
bool demoModeOn = false;
bool debugModeOn = false;
bool channelingStarted = false;
bool delayFreeScroll = false;
bool isDuelLevel = false;
bool fullFeatures = true;
// A player can have chat open while the level cycles, there probably is no fix for this
bool inChatMode = false;
bool infoOpen = false;
bool hotkeyInUse = false;
bool recovering = false;
bool showResources = true;
bool skillsOpen = false;
bool spellBookOpen = false;
int channelingElapsed = 0;
int chatReleaseDelay = 0;
int freeScrollDelay = 10;
int hintBoxY = 0;
int hotkeyCycleSpells = 67; //112 = F1
int hotkeyInfo = 73;
int hotkeyKeyMenu = 75;
int hotkeyResources = 82;
int hotkeySkills = 76;
int hotkeySpellbook = 83;
int infoScrollY = 0;
int keyMenuState = 1;
int numpadElapsed = 0;
int selectionHotkey = -1;
int recoverElapsed = 0;
int secondarySkillCost = SECONDARY_SKILL_BASE_COST;
uint infoBoxHintPage = 0;
dictionary spells = {};
array<string> keyBindings;
array<string> ownSpells;
array<string> learnableSpells;
array<uint> numpadBuffer;
array<uint> numpadKeys = {96, 97, 98, 99, 100, 101, 102, 103, 104, 105};
array<array<Chunk>> chunks;
array<Key> keys(256);
array<Player> players(32);
array<AreaOfEffect@> activeAreasOfEffect;
array<array<ChainLightningTarget@>> chainLightningTargetGroups;
array<GemMine@> gemMines;
array<MagicMirrorAnimation@> magicMirrorAnimations;
array<WallOfFire@> activeWallsOfFire;
namespace acEngine {
void controlActiveAreasOfEffect(jjPLAYER@ play) {
int j = 0;
int activeAreasOfEffectLength = activeAreasOfEffect.length();
for (int i = 0; i < activeAreasOfEffectLength; i++) {
AreaOfEffect@ aoe = activeAreasOfEffect[i];
aoe.animate();
aoe.control(play);
if (aoe.elapsed > 0) {
aoe.elapsed--;
@activeAreasOfEffect[j] = aoe;
j++;
}
}
activeAreasOfEffect.removeRange(j, activeAreasOfEffectLength - j);
}
void controlActiveEffects(jjPLAYER@ play, Player@ asPlayer) {
if (asPlayer.activeEffects.length() >= 1) {
for (uint i = 0; i < asPlayer.activeEffects.length(); i++) {
jjTEXTAPPEARANCE centeredText();
centeredText.align = STRING::CENTER;
Effect@ effect = asPlayer.activeEffects[i];
jjDrawString(play.xPos, play.yPos + EFFECT_INITIAL_HEIGHT + i * ROW_SPACING,
effect.name + " " + int(effect.elapsed / SECOND), STRING::SMALL, centeredText, 0,
SPRITE::PALSHIFT, 0, 1);
if (acUtils::gameIsRunning()) {
if (effect.elapsed > 0) {
effect.elapsed--;
if ((play.isLocal && effect.isLocal) || (jjIsServer && !effect.isLocal)) {
effect.affect();
}
} else {
asPlayer.removeActiveEffect(i);
acSpells::unDoEffect(play.playerID);
}
}
}
}
}
void controlActiveWallsOfFire(jjPLAYER@ play) {
int j = 0;
int activeWallsOfFireLength = activeWallsOfFire.length();
for (int i = 0; i < activeWallsOfFireLength; i++) {
WallOfFire@ wallOfFire = activeWallsOfFire[i];
acDrawing::drawWallOfFire(wallOfFire);
if (acUtils::gameIsRunning()) {
float adjustedXOrigin = wallOfFire.xOrigin - TILE;
float adjustedYOrigin = wallOfFire.yOrigin + TILE;
float adjustedWidth = (WALL_OF_FIRE_WIDTH + 1) * TILE;
float adjustedHeight = (wallOfFire.height + 2) * TILE;
array<float> hitBox = {adjustedXOrigin, adjustedYOrigin,
adjustedXOrigin+adjustedWidth, adjustedYOrigin-adjustedHeight};
if (play.xPos >= hitBox[0] && play.xPos <= hitBox[2] &&
play.yPos <= hitBox[1] && play.yPos >= hitBox[3]) {
play.hurt(wallOfFire.damage - players[play.playerID].magicResist, false, wallOfFire.caster);
}
if (wallOfFire.elapsed > 0) {
wallOfFire.elapsed--;
}
}
if (wallOfFire.elapsed > 0) {
@activeWallsOfFire[j] = wallOfFire;
j++;
}
}
activeWallsOfFire.removeRange(j, activeWallsOfFireLength - j);
}
void controlChat(int clientID, string &in received, CHAT::Type type) {
if (jjIsServer) {
array<jjPLAYER@> players = jjPlayersWithClientID(clientID);
if (players.length() >= 1 && (players[0].isLocal || players[0].isAdmin) && type == CHAT::NORMAL) {
if (jjRegexMatch(received.substr(0, 9), "!demomode.*", true)) {
string choice = received.substr(9);
if (jjRegexMatch(choice, "\\s*on\\s*", true)) {
demoModeOn = true;
ownSpells = acInit::loadSpells();
acNetworking::sendDemoModeData(0);
jjAlert("Demo mode has been turned on!", true);
} else if (jjRegexMatch(choice, "\\s*off\\s*", true)) {
demoModeOn = false;
acUtils::disableDemoMode();
acNetworking::sendDemoModeData(0);
jjAlert("Demo mode has been turned off!", true);
}
}
}
}
}
void controlCycleSpellsInput(Player@ asPlayer) {
if (jjKey[hotkeyCycleSpells] && !keys[hotkeyCycleSpells].keyPressed && ownSpells.length() > 0) {
int index = acSpells::getSpellIndexByKey(asPlayer.selectedSpellKey);
if (index == -1) {
Spell@ spell = cast<Spell@>(spells[ownSpells[0]]);
asPlayer.setSelectedSpellKey(spell.key);
} else if (index >= 0) {
if (index >= int(ownSpells.length()) - 1) {
asPlayer.unSetChanneledSpellKey();
} else {
while (index < int(ownSpells.length() - 2) && ownSpells[index] == ownSpells[index+1]) {
index++;
if (debugModeOn) {
jjAlert("ownSpells[index]: " + ownSpells[index]);
jjAlert("ownSpells[index+1]: " + ownSpells[index+1]);
jjAlert("index: " + index);
}
}
Spell@ nextSpell = cast<Spell@>(spells[ownSpells[index+1]]);
asPlayer.setSelectedSpellKey(nextSpell.key);
}
}
acUtils::closeOtherBoxes(BOX_SPELL_BOOK, null);
}
}
void controlElevatorTileInput(jjPLAYER@ play) {
int elevatorTileX = isDuelLevel ? ELEVATOR_TILE_X_DUEL : ELEVATOR_TILE_X;
int elevatorTileY = isDuelLevel ? ELEVATOR_TILE_Y_DUEL : ELEVATOR_TILE_Y;
if (selectionHotkey < 0 && play.keyUp
&& int(play.xPos/TILE) == elevatorTileX && int(play.yPos/TILE) == elevatorTileY) {
play.warpToID(0, true);
}
}
void controlChatMode() {
if (jjKey[HOTKEY_CHAT] && !inChatMode) {
inChatMode = true;
} else if (inChatMode &&
(jjKey[getKeyCodeByName("Enter")]
|| jjKey[getKeyCodeByName("Esc")])) {
chatReleaseDelay = CHAT_RELEASE_DELAY;
}
if (chatReleaseDelay > 0) chatReleaseDelay--;
}
void controlInfoMenuInput(jjPLAYER@ play, Player@ asPlayer) {
if (selectionHotkey < 0 && !infoOpen && !keys[hotkeyInfo].keyPressed && jjKey[hotkeyInfo]) {
infoOpen = true;
acUtils::closeOtherBoxes(BOX_INFO, asPlayer);
} else if (selectionHotkey < 0 && infoOpen && jjKey[1] && !keys[1].keyPressed
&& acUtils::mouseIsInSelection(jjSubscreenWidth - 256, hintBoxY + HINT_BOX_HEIGHT - 16, 256, 16)) {
if (infoBoxHintPage < infoBoxHints.length() - 1) infoBoxHintPage++;
else infoBoxHintPage = 0;
} else if (infoOpen && (play.keyFire || (!keys[hotkeyInfo].keyPressed && jjKey[hotkeyInfo]))) {
infoOpen = false;
}
}
void controlInfoScrolling(jjPLAYER@ play) {
if (jjMouseX >= 0 && jjMouseY >= 0 && jjMouseX <= jjSubscreenWidth && jjMouseY <= jjSubscreenHeight) {
if ((jjMouseY <= SPELL_BOXES_INIT_Y + 32 || play.keyUp) && infoScrollY < 0) {
infoScrollY += INFO_SCROLL_SPEED;
} else if ((jjMouseY >= jjSubscreenHeight / 10 * 9 || play.keyDown) && acUtils::infoViewIsAboveBottom()) {
infoScrollY -= INFO_SCROLL_SPEED;
}
}
}
void controlKeyMenuInput(Player@ asPlayer) {
if (selectionHotkey < 0 && keyMenuState == 2 && ((jjKey[hotkeyKeyMenu] && !keys[hotkeyKeyMenu].keyPressed) ||
(jjKey[1] && acUtils::mouseIsOutsideKeyMenu()))) {
keyMenuState = 0;
selectionHotkey = -1;
} else if (keyMenuState == 2 && jjKey[1] && acUtils::mouseIsWithinKeyMenuChoices()) {
jjTEXTAPPEARANCE centeredText;
centeredText.align = STRING::CENTER;
for (uint i = 0; i < keyBindings.length(); i++) {
string keyBinding = keyBindings[i];
int stringWidth = jjGetStringWidth(keyBinding, STRING::SMALL, centeredText);
if (jjMouseX < jjSubscreenWidth / 2 + stringWidth / 2 && jjMouseX > jjSubscreenWidth / 2 - stringWidth / 2
&& jjMouseY >= jjSubscreenHeight / 4 + 102 + i*16 && jjMouseY < jjSubscreenHeight / 4 + 102 + (i+1)*16) {
selectionHotkey = int(i);
}
}
} else if (selectionHotkey < 0 && keyMenuState == 1 && jjKey[hotkeyKeyMenu] && !keys[hotkeyKeyMenu].keyPressed) {
hotkeyInUse = false;
keyMenuState = 2;
acUtils::closeOtherBoxes(BOX_KEY_MENU, asPlayer);
} else if (selectionHotkey < 0 && jjKey[hotkeyKeyMenu] && !keys[hotkeyKeyMenu].keyPressed) {
hotkeyInUse = false;
keyMenuState = 1;
}
}
void controlMain() {
for (uint y = 0; y < chunks.length(); y++) {
for (uint x = 0; x < chunks[y].length(); x++) {
chunks[y][x].clearObjects();
}
}
for (int i = 0; i < jjObjectCount; i++) {
jjOBJ@ obj = jjObjects[i];
uint yChunk = uint(obj.yPos) >> 7;
uint xChunk = uint(obj.xPos) >> 7;
if (obj.isActive && yChunk < chunks.length() && xChunk < chunks[yChunk].length()) {
chunks[yChunk][xChunk].addObject(obj);
}
}
for (uint i = 0; i < players.length(); i++) {
jjPLAYER@ play = jjPlayers[i];
Player@ asPlayer = players[i];
controlActiveEffects(play, asPlayer);
if (asPlayer.isChanneling) {
acDrawing::drawAreaOfEffect(play, cast<Spell@>(spells[asPlayer.selectedSpellKey]).radius);
if (asPlayer.selectedSpellKey == "L") {
acDrawing::drawChainLightningTargets(play);
} else if (asPlayer.selectedSpellKey == "I") {
acDrawing::drawWallOfFireTarget(play);
}
}
}
if (numpadBuffer.length() > 0) {
if (numpadElapsed > 0) {
numpadElapsed--;
} else {
numpadBuffer.removeRange(0, numpadBuffer.length());
}
}
}
void controlNumpadKey(uint index, uint i, Player@ asPlayer) {
if (!keys[index].keyPressed && jjKey[index]) {
if (i == 0 && numpadBuffer.length() < 1) {
asPlayer.unSetChanneledSpellKey();
} else {
if (numpadBuffer.length() >= 1) {
array<uint> buffer = {numpadBuffer[0], i};
int numpadIndex = acUtils::parseNumpadIndex(buffer);
string spellKey = acUtils::getSpellKeyByNumpadIndex(uint(numpadIndex));
if (numpadBuffer.length() >= 2 || (numpadIndex >= 1 && ownSpells.find(spellKey) < 0)) {
numpadBuffer.removeRange(0, numpadBuffer.length());
}
}
numpadElapsed = NUMPAD_BUFFER_TIME;
numpadBuffer.insertLast(i);
int numpadIndex = acUtils::parseNumpadIndex(numpadBuffer);
if (debugModeOn) jjAlert("numpadIndex: " + numpadIndex);
if (numpadIndex >= 1) {
string spellKey = acUtils::getSpellKeyByNumpadIndex(uint(numpadIndex));
if (ownSpells.find(spellKey) >= 0) {
channelingElapsed = 0;
asPlayer.setSelectedSpellKey(spellKey);
} else {
numpadBuffer.removeRange(0, numpadBuffer.length());
}
acUtils::closeOtherBoxes(BOX_SPELL_BOOK, null);
} else {
asPlayer.unSetChanneledSpellKey();
}
}
}
}
void controlNumpadKeys(Player@ asPlayer) {
for (uint i = 0; i < numpadKeys.length(); i++) {
uint index = numpadKeys[i];
controlNumpadKey(index, i, asPlayer);
}
}
void controlPlayer(jjPLAYER@ play) {
Player@ asPlayer = players[play.playerID];
play.noFire = (players[play.playerID].selectedSpellKey != "" ||
players[play.playerID].hasEffect(SPELL_FORGETFULNESS));
if (recovering) {
if (recoverElapsed < 35) {
play.noFire = true;
recoverElapsed++;
} else {
recovering = false;
recoverElapsed = 0;
}
}
if (acUtils::gameIsRunning()) {
asPlayer.regenerateMana(play);
if (asPlayer.cooldown > 0) {
asPlayer.cooldown--;
}
}
acDrawing::drawBulletPointer(play, asPlayer);
controlActiveWallsOfFire(play);
controlActiveAreasOfEffect(play);
controlPlayerAssets(play, asPlayer);
acDrawing::drawChainLightnings();
acDrawing::drawMagicMirrorAnimations();
for (uint i = 0; i < gemMines.length(); i++) {
gemMines[i].control(play, asPlayer);
}
if ((play.health <= 0 || !play.isInGame) && !asPlayer.isDead) {
asPlayer.isDead = true;
acNetworking::sendPlayerDeadPacket(play.playerID);
if (jjIsServer) {
doDeadPlayer(play.playerID);
}
} else if (play.health > 0) {
asPlayer.isDead = false;
}
if (infoOpen) controlInfoScrolling(play);
}
void controlPlayerAssets(jjPLAYER@ play, Player@ asPlayer) {
string firstLearnableSpellKey = acUtils::getFirstLearnableSpell();
if (firstLearnableSpellKey.length() > 0) {
Spell@ learnableSpell = cast<Spell@>(spells[firstLearnableSpellKey]);
int spellPrice = acUtils::getSpellPriceByTier(learnableSpell.tier);
if (play.coins >= spellPrice && play.testForCoins(spellPrice)) {
ownSpells.insertLast(learnableSpell.key);
ownSpells = acUtils::sortSpellKeys(ownSpells);
if (jjIsServer) {
acUtils::alertOfLearnedSpell(play.nameUnformatted, learnableSpell.name);
} else {
acNetworking::sendSpellLearnedPacket(play.playerID, firstLearnableSpellKey);
}
string nextLearnableSpellKey = acUtils::getFirstLearnableSpell();
if (nextLearnableSpellKey.length() > 0) {
Spell@ nextLearnableSpell = cast<Spell@>(spells[nextLearnableSpellKey]);
spellPrice = acUtils::getSpellPriceByTier(nextLearnableSpell.tier);
jjAlert("To learn the next spell ("
+ nextLearnableSpell.name + "), gather "+ spellPrice + " coins.");
} else {
jjAlert("You have learned all the spells! Use your full powers upon thy enemies!");
}
}
}
if (asPlayer.activeSkills.length() < skills.length()
&& play.gems[GEM::PURPLE] >= secondarySkillCost
&& play.testForGems(secondarySkillCost, GEM::PURPLE)) {
secondarySkillCost += SECONDARY_SKILL_EXTRA_COST;
asPlayer.addNewRandomSkill(play.nameUnformatted);
if (asPlayer.activeSkills.length() < skills.length()) {
jjAlert("To learn the next secondary skill (by random), gather " + secondarySkillCost + " purple gems.");
} else {
jjAlert("You have learned all the secondary skills!");
}
}
}
void controlPlayerInput(jjPLAYER@ play) {
Player@ asPlayer = players[play.playerID];
// controlChatMode has to be the first one
controlChatMode();
if (chatReleaseDelay <= 0) {
controlElevatorTileInput(play);
controlKeyMenuInput(asPlayer);
controlResourceTabInput();
controlCycleSpellsInput(asPlayer);
controlInfoMenuInput(play, asPlayer);
controlNumpadKeys(asPlayer);
controlSelectionOfHotkeys();
controlSkillsMenuInput(asPlayer);
controlSpellbookInput(asPlayer);
controlSpellCastingInput(play, asPlayer);
}
// controlPressedKeys has to be the last one
controlPressedKeys();
}
void controlPressedKeys() {
for (uint i = 0; i < 256; i++) {
if (@keys[i] != null) {
keys[i].keyPressed = jjKey[i];
}
}
}
void controlResourceTabInput() {
if (!showResources && jjKey[hotkeyResources] && !keys[hotkeyResources].keyPressed) {
showResources = true;
} else if (jjKey[hotkeyResources] && !keys[hotkeyResources].keyPressed) {
showResources = false;
}
}
void controlSelectionOfHotkeys() {
if (selectionHotkey >= 0 && !jjKey[1]) {
for (uint i = 0; i < 256; i++) {
if (jjKey[i]) {
acUtils::setHotkey(selectionHotkey, i);
selectionHotkey = -1;
break;
}
}
}
}
void controlSkillsMenuInput(Player@ asPlayer) {
if (selectionHotkey < 0 && !skillsOpen && !keys[hotkeySkills].keyPressed && jjKey[hotkeySkills]) {
skillsOpen = true;
acUtils::closeOtherBoxes(BOX_SKILLS, asPlayer);
} else if (skillsOpen && !keys[hotkeySkills].keyPressed && jjKey[hotkeySkills]) {
skillsOpen = false;
}
}
void controlSpellbookInput(Player@ asPlayer) {
if (selectionHotkey < 0 && !spellBookOpen && !keys[hotkeySpellbook].keyPressed && jjKey[hotkeySpellbook]) {
spellBookOpen = true;
acUtils::closeOtherBoxes(BOX_SPELL_BOOK, asPlayer);
} else if (spellBookOpen && !keys[hotkeySpellbook].keyPressed && jjKey[hotkeySpellbook]) {
spellBookOpen = false;
}
}
void controlSpellCastingInput(jjPLAYER@ play, Player@ asPlayer) {
if (asPlayer.selectedSpellKey == "I" && activeWallsOfFire.length() >= MAX_ACTIVE_WALLS_OF_FIRE) {
//Cannot cast
} else if (play.isInGame && asPlayer.selectedSpellKey != "" && play.keyFire
&& acSpells::canCastSpell(players[play.playerID], cast<Spell@>(spells[asPlayer.selectedSpellKey]))) {
spellBookOpen = false;
if (!channelingStarted) {
channelingStarted = true;
asPlayer.setChanneledSpellKey(asPlayer.selectedSpellKey);
acNetworking::sendChannelingStartedPacket(play.playerID, cast<Spell@>(spells[asPlayer.selectedSpellKey]).enumValue);
}
acSpells::channel(play);
} else if (asPlayer.selectedSpellKey != "" && !play.keyFire) {
if (channelingStarted) {
channelingStarted = false;
asPlayer.isChanneling = false;
if (!demoModeOn) {
Spell@ spell = cast<Spell@>(spells[asPlayer.selectedSpellKey]);
asPlayer.cooldown = spell.tier * SECOND;
}
acNetworking::sendChannelingStoppedPacket(play.playerID);
if (jjIsServer) {
asPlayer.unSetChanneledSpellKey();
}
}
channelingElapsed = 0.f;
}
}
void doDeadPlayer(int8 playerID) {
Player@ asPlayer = players[playerID];
asPlayer.isChanneling = false;
if (asPlayer.activeEffects.length() == 0) {
asPlayer.originalFur = jjPlayers[playerID].fur; //Make sure the original fur is saved
} else {
asPlayer.removeAllActiveEffects();
}
if (jjIsServer) acSpells::changePlayerFur(SPELL_UNDO, playerID);
}
bool doDrawAmmo(jjPLAYER@ play, jjCANVAS@ canvas) {
return acDrawing::drawHUDObjects(play, canvas);
}
void doDrawLayer4(jjPLAYER@ play, jjCANVAS@ canvas) {
acDrawing::drawLayer4(play, canvas);
}
void doGameStop() {
players[jjLocalPlayers[0].playerID].cooldown = 0;
}
void doReceive(jjSTREAM &in packet, int clientID) {
acNetworking::doReceive(packet, clientID);
}
void initialize(bool setFullFeatures = true, bool setDuelLevel = false) {
fullFeatures = setFullFeatures;
isDuelLevel = setDuelLevel;
spells = fullFeatures ? acSpells::fullSpells : acSpells::normalSpells;
acInit::loadPlayerIDs();
acInit::loadChunks();
acInit::loadKeys();
acInit::loadSprites();
acInit::loadBullets();
ownSpells = demoModeOn ? acInit::loadSpells() : acInit::loadSpells(LOWEST_TIER, HIGHEST_STARTING_TIER);
learnableSpells = acInit::loadSpells(LOWEST_LEARNABLE_TIER, HIGHEST_TIER);
acInit::loadOthers();
}
void initializeLevel() {
jjSugarRushAllowed = true;
if (!isDuelLevel) {
gemMines.insertLast(GemMine(0, "Eastern", GEM_MINE_EAST_TOP, GEM_MINE_EAST_BOTTOM,
GEM_MINE_EAST_LEFT, GEM_MINE_EAST_RIGHT));
gemMines.insertLast(GemMine(1, "Western", GEM_MINE_WEST_TOP, GEM_MINE_WEST_BOTTOM,
GEM_MINE_WEST_LEFT, GEM_MINE_WEST_RIGHT));
}
if (!jjIsServer) acNetworking::sendReqSyncDataPacket();
}
}
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.