| Name | Author | Game Mode | Rating | |||||
|---|---|---|---|---|---|---|---|---|
| NpcBunnies | Cranky | Mutator | N/A | |||||
namespace npcBunniesHooks
{
bool doneSetup = false;
bool requestedNpcServerSync = false;
bool requestedNpcSyncSetting = false;
bool receivedNpcSyncSetting = false;
void onLevelLoad()
{
npcBunniesCore::LoadNpcBunniesPreferences();
if(jjIsServer) npcBunniesCore::LoadNpcSyncSetting();
npcBunniesAnimations::LoadCharactersModAnimationIndexes();
//npcList.length + 1: because we have all characters and NpcAnim::HEAD
//the empty folders in charactersMod.j2a should theoretically leave ANIM::CUSTOM empty
//TODO: debug FurSetup_AdditionalCharacters to see if it's really like that
uint charCount = npcBunniesCore::npcList.length + 1;
npcBunniesAnimations::emptyCharSet.resize(charCount);
npcBunniesAnimations::animTypes.resize(charCount);
for(uint i=0; i<charCount; i++)
{
npcBunniesAnimations::FurSetup_AdditionalCharacters(i);
}
npcBunniesAccessories::NpcAccessories::Load();
npcBunniesParser::LoadNpcDialogueFile();
if(npcBunniesCore::LOAD_BIG_NPC_ANIMATIONS) npcBunniesAnimations::loadMenuAnims();
if(npcBunniesCore::LOAD_ALL_MONSTER_ANIMATIONS) npcBunniesAnimations::LoadNpcUiIconAnimSets();
npcBunniesMovement::readLevelMap();
doneSetup = false;
requestedNpcServerSync = false;
requestedNpcSyncSetting = jjIsServer;
receivedNpcSyncSetting = jjIsServer;
if(jjIsServer) sendNpcSyncSetting(0);
}
void onMain()
{
npcBunniesAccessories::NpcAccessories::OnMain();
npcBunniesEditorCore::NpcDesigner_OnMain();
if(!doneSetup)
{
doneSetup = true;
//importSettings(); - will import settings from the _npc.asdat file
}
if(!jjIsServer && !requestedNpcSyncSetting)
{
requestNpcSyncSetting();
requestedNpcSyncSetting = true;
}
if(receivedNpcSyncSetting && npcBunniesCore::ENABLE_NPC_SYNC && !jjIsServer && !requestedNpcServerSync)
{
requestNpcServerSync();
requestedNpcServerSync = true;
}
requestNpcBunniesAdminAccess();
npcBunniesCore::isChatOpen();
npcBunniesCore::ResetInactivePlayerQuestState();
ProcessNpcQuestDialogInput();
if(npcBunniesFighting::HasAnyNpcFightingEnabled()) npcBunniesFighting::UpdateNpcFightingPlayerFireState();
if(npcBunniesCore::ENABLE_NPC_MONSTER_QUESTS) npcBunniesQuests::UpdateTrackedDefeatObjects();
}
array<string> NpcDialogueWrappedMediumLines(const string &in text, int maxPixels)
{
array<string> lines;
if(maxPixels <= 0) return lines;
array<string> paragraphs = text.split("\n");
for(uint p = 0; p < paragraphs.length; p++)
{
if(paragraphs[p] == "")
{
lines.insertLast("");
continue;
}
array<string> words = paragraphs[p].split(" ");
string line = "";
for(uint i = 0; i < words.length; i++)
{
string remaining = words[i];
while(remaining != "" && jjGetStringWidth(remaining, STRING::MEDIUM, STRING::NORMAL) > maxPixels)
{
int take = int(remaining.length());
while(take > 1 && jjGetStringWidth(remaining.substr(0, take), STRING::MEDIUM, STRING::NORMAL) > maxPixels)
take--;
if(line != "")
{
lines.insertLast(line);
line = "";
}
lines.insertLast(remaining.substr(0, take));
remaining = remaining.substr(take);
}
if(remaining == "") continue;
string candidate = line == "" ? remaining : line + " " + remaining;
if(jjGetStringWidth(candidate, STRING::MEDIUM, STRING::NORMAL) <= maxPixels) line = candidate;
else
{
if(line != "") lines.insertLast(line);
line = remaining;
}
}
if(line != "") lines.insertLast(line);
}
if(lines.length == 0) lines.insertLast("");
return lines;
}
void DrawNpcDialogueScrollableText(jjCANVAS@ canvas, const string &in text, int x, int y, int w, int maxY)
{
array<string> lines = NpcDialogueWrappedMediumLines(text, w);
int textY = y;
int textMaxY = maxY;
int maxLineCount = int(lines.length);
int plainVisibleLines = (maxY - y) / 20;
if(plainVisibleLines < 1) plainVisibleLines = 1;
bool showScrollButtons = maxLineCount > plainVisibleLines;
if(showScrollButtons)
{
textY = y + 16;
textMaxY = maxY - 16;
}
int visibleLines = (textMaxY - textY) / 20;
if(visibleLines < 1) visibleLines = 1;
int maxScroll = int(lines.length) - visibleLines;
if(maxScroll < 0) maxScroll = 0;
if(npcBunniesCore::npcTalkScrollLine < 0) npcBunniesCore::npcTalkScrollLine = 0;
if(npcBunniesCore::npcTalkScrollLine > maxScroll) npcBunniesCore::npcTalkScrollLine = maxScroll;
if(showScrollButtons)
{
canvas.drawString(x + w / 2 - 12, y, "UP", STRING::SMALL, npcBunniesCore::npcTalkScrollLine > 0 ? STRING::NORMAL : STRING::PALSHIFT, npcBunniesCore::npcTalkScrollLine > 0 ? 0 : 24);
canvas.drawString(x + w / 2 - 20, maxY - 12, "DOWN", STRING::SMALL, npcBunniesCore::npcTalkScrollLine < maxScroll ? STRING::NORMAL : STRING::PALSHIFT, npcBunniesCore::npcTalkScrollLine < maxScroll ? 0 : 24);
}
int drawY = textY;
for(uint i = 0; i < lines.length; i++)
{
if(int(i) < npcBunniesCore::npcTalkScrollLine) continue;
if(drawY + 20 > textMaxY) break;
canvas.drawString(0x8000, drawY, lines[i], STRING::MEDIUM, STRING::NORMAL);
drawY += 20;
}
}
bool onDrawScore(jjPLAYER@ play, jjCANVAS@ canvas)
{
npcBunniesAccessories::NpcAccessories::OnDrawScore(play, canvas);
npcBunniesAnimations::DrawNpcCountsHud(play, canvas);
npcBunniesDialogue::DrawNpcQuestHud(canvas);
npcBunniesDialogue::DrawNpcQuestDialog(canvas, play);
if(npcBunniesMovement::convOn != -1)
{
bool DrawNpc = true;
npcBunniesCore::npcBunny npc = npcBunniesCore::npcBunnies[npcBunniesMovement::convOn];
int dialogueBoxW = (jjResolutionWidth - 200) * 3 / 2;
if(dialogueBoxW > jjResolutionWidth - 40) dialogueBoxW = jjResolutionWidth - 40;
int dialogueBoxH = 285;
if(dialogueBoxH > jjResolutionHeight - 40) dialogueBoxH = jjResolutionHeight - 40;
int dialogueBoxX = (jjResolutionWidth - dialogueBoxW) / 2;
int dialogueBoxY = (jjResolutionHeight - dialogueBoxH) / 2;
int dialogueBigSpriteRightOffsetX = 50;
int dialogueHeadRightOffsetX = 50;
int dialogueTextExtraWidthLeft = 100;
int dialoguePortraitRightX = dialogueBoxX + dialogueBoxW - 50 + dialogueBigSpriteRightOffsetX;
int dialoguePortraitTopY = dialogueBoxY + 36;
int dialogueSmallFaceX = dialogueBoxX + dialogueBoxW - 70 + dialogueHeadRightOffsetX;
int dialogueSmallFaceY = dialogueBoxY + 58;
canvas.drawRectangle(dialogueBoxX, dialogueBoxY, dialogueBoxW, dialogueBoxH, 0, SPRITE::SHADOW);
if(npc.action == npcBunniesCore::NpcAction::Menu)
{
//TO DO: call drawThisNpc(canvas);
npcBunniesDialogue::getMenuOptionControl(play);
if(npcBunniesMovement::convOn != -1)
{
npc = npcBunniesCore::npcBunnies[npcBunniesMovement::convOn];
jjTEXTAPPEARANCE ChosenAppearance(STRING::SPIN);
jjTEXTAPPEARANCE ConversationAppearance(STRING::NORMAL);
for(uint i=0; i<npcBunniesDialogue::npcMenuOptionsLength; i++)
{
int optionY = 225 + int(i) * 25;
canvas.drawString(0x8000, optionY, npcBunniesDialogue::GetNpcMenuLabel(npc, play, int(i)), STRING::MEDIUM, npcBunniesDialogue::chosenMenuOption == int(i) ? ChosenAppearance : ConversationAppearance);
}
canvas.drawString(0x8000, 237 + int(npcBunniesDialogue::npcMenuOptionsLength) * 25, npcBunniesDialogue::npcMenuOptions[0], STRING::MEDIUM, npcBunniesDialogue::chosenMenuOption == int(npcBunniesDialogue::npcMenuOptionsLength) ? ChosenAppearance : ConversationAppearance);
}
}
else if(npc.action == npcBunniesCore::NpcAction::Shop)
{
npcBunniesShop::DrawNpcShop(play, canvas, npcBunniesMovement::convOn);
}
else if(npc.action == npcBunniesCore::NpcAction::Guide && npc.dialogueIndex != -1)
{
jjTEXTAPPEARANCE ConversationAppearance(STRING::NORMAL);
string Line = npc.guidePendingLine;
uint CharacterCount = 0;
string stringToWrite = Line;
string leftOutLine = Line;
const int dialogueResolutionOffset = 300;
uint numberOfLines = uint(jjGetStringWidth(Line, STRING::MEDIUM, ConversationAppearance)/(jjResolutionWidth - dialogueResolutionOffset))+1;
for(uint i=0; i<numberOfLines; i++)
{
if(jjGetStringWidth(leftOutLine, STRING::MEDIUM, ConversationAppearance) > jjResolutionWidth - dialogueResolutionOffset)
{
array<string> lineParts = leftOutLine.split(" ");
CharacterCount = 0;
for(uint j=0; j<lineParts.length; j++)
{
if(jjGetStringWidth(leftOutLine.substr(0, CharacterCount+lineParts[j].length+1), STRING::MEDIUM, ConversationAppearance) < jjResolutionWidth - dialogueResolutionOffset)
CharacterCount+=lineParts[j].length+1;
else break;
}
stringToWrite = leftOutLine.substr(0, CharacterCount);
leftOutLine = leftOutLine.substr(CharacterCount, leftOutLine.length - CharacterCount + 1);
}
else stringToWrite = leftOutLine;
canvas.drawString(0x8000, 250+i*20, stringToWrite, STRING::MEDIUM, ConversationAppearance);
}
}
else if(npc.action == npcBunniesCore::NpcAction::Talk && npc.dialogueIndex != -1)
{
npcBunniesDialogue::DialogueSet@ portraitSet = npc.dialogueSets[npc.convIndex];
npcBunniesDialogue::DialogueLine@ portraitLine = portraitSet.dialogue[npc.dialogueIndex];
int frameIndex = portraitSet.bigFrame < 0 ? jjGameTicks / 10 : portraitSet.bigFrame;
uint animIndex = npcBunniesAnimations::animTypes.find(npc.type);
int personId = portraitLine.personId;
npcBunniesCore::npcBunny portraitNpc = npc;
if(portraitLine.isNpc == 0)
{
personId = portraitSet.speakerNpcIndex >= 0 ? portraitSet.speakerNpcIndex : npc.id;
if(0 <= personId && personId < int(npcBunniesCore::npcBunnies.length)) portraitNpc = npcBunniesCore::npcBunnies[personId];
else personId = npc.id;
animIndex = npcBunniesAnimations::animTypes.find(portraitNpc.type);
}
else if(0 <= personId && personId < 32 && portraitLine.isNpc == 1)
{
animIndex = 0;
if(jjPlayers[personId].isActive) animIndex = npcBunniesAnimations::charFind.find(jjPlayers[personId].charCurr);
//TO DO: you could also read if mario mutator is active and read which character the player is from the .asdat file
}
npcBunniesAnimations::DrawParams draw;
if(npcBunniesCore::LOAD_BIG_NPC_ANIMATIONS && animIndex <= npcBunniesCore::NpcAnim::LORI) //was 2 //TO DO: add big animations (or head animations) as chat icons for other characters
{
draw.add(
xPos: animIndex == npcBunniesCore::NpcAnim::JAZZ ? (dialoguePortraitRightX - 200) : dialoguePortraitRightX,
yPos: dialoguePortraitTopY,
animSet: ANIM::CUSTOM[npcBunniesAnimations::customMenu[animIndex]],
anim: 0,
frame: frameIndex,
sizeX: animIndex == npcBunniesCore::NpcAnim::JAZZ ? 1 : -1,
sizeY: 1,
spriteMode: SPRITE::PALSHIFT,
param: personId,
grav: 0
);
if(portraitLine.isNpc == 0)
{
array<int> params =
{
portraitNpc.fur.c2-24,
portraitNpc.fur.c3-32,
portraitNpc.fur.c4-40,
0,
npcBunniesCore::GetPrimaryFurShift(animIndex, portraitNpc.fur.c1),
};
//TODO: this for could go up to 4 and the DrawCharacterOnCanvas moved inside
// if the order was different in MenuAnimsMod.j2a
draw.param = params[4];
npcBunniesAnimations::DrawMergedCharacterLayerOnCanvas(canvas, draw, 5, 4);
for(int i=0; i<=3; i++)
{
draw.anim = 0; draw.param = params[i];
if(i == 3){ draw.spriteMode = SPRITE::NORMAL; }
npcBunniesAnimations::DrawMergedCharacterLayerOnCanvas(canvas, draw, 5, i);
}
}
else if(portraitLine.isNpc == 1)
{
draw.xPos = animIndex == npcBunniesCore::NpcAnim::JAZZ ? (dialoguePortraitRightX - 200) : dialoguePortraitRightX; draw.spriteMode = SPRITE::PLAYER; draw.sizeX = -draw.sizeX;
npcBunniesAnimations::DrawMergedCharacterLayerOnCanvas(canvas, draw, 5, 4);
}
}
else
{
int iconAnimSet = (portraitNpc.type == npcBunniesCore::NpcAnim::FROG || portraitNpc.type == npcBunniesCore::NpcAnim::BIRD) ? ANIM::FACES : ANIM::CUSTOM[npcBunniesAnimations::emptyCharSet[npcBunniesCore::NpcAnim::HEAD]]; //3 = frog or TO DO: add bird
int charIndex = (portraitNpc.type == npcBunniesCore::NpcAnim::FROG || portraitNpc.type == npcBunniesCore::NpcAnim::BIRD) ? portraitNpc.type : npcBunniesAnimations::animTypes.find(portraitNpc.type);
if(portraitLine.isNpc == 0 && (npcBunniesAnimations::IsNpcMonsterType(portraitNpc.type) || npcBunniesAnimations::IsNpcBossType(portraitNpc.type)))
{
npcBunniesAnimations::NpcUiAnimation monsterIcon;
if(npcMonsterAdjustments::GetWalkingUiAnimation(portraitNpc.type, monsterIcon))
{
canvas.drawResizedSprite(dialogueSmallFaceX + monsterIcon.offsetX, dialogueSmallFaceY + monsterIcon.offsetY, monsterIcon.animSet, monsterIcon.anim, npcBunniesAnimations::AnimatedNpcUiFrame(monsterIcon), -0.75, 0.75, monsterIcon.spriteMode, monsterIcon.param);
}
else
{
npcBunniesAnimations::GetNpcSimpleAnimation(portraitNpc.type, monsterIcon);
if(npcBunniesAnimations::CanDrawNpcSimpleAnimation(monsterIcon))
canvas.drawResizedSprite(dialogueSmallFaceX, dialogueSmallFaceY, monsterIcon.animSet, monsterIcon.anim, npcBunniesAnimations::NpcSimpleAnimationFrame(monsterIcon), -0.75, 0.75, monsterIcon.spriteMode, monsterIcon.param);
}
}
else if(portraitLine.isNpc == 0
&& 0 <= charIndex && charIndex < int(npcBunniesAnimations::emptyCharSet.length))
{
animIndex = iconAnimSet == ANIM::FACES ? npcBunniesAnimations::npcFacesDefault.find(charIndex) : npcBunniesAnimations::npcFaces.find(charIndex);
int animation = npcBunniesCore::NpcAnimations.find(RABBIT::STAND);
bool useSmallPlayerStand = !npcBunniesCore::LOAD_BIG_NPC_ANIMATIONS && charIndex >= npcBunniesCore::NpcAnim::JAZZ && charIndex <= npcBunniesCore::NpcAnim::LORI;
draw.add(
xPos: dialogueSmallFaceX-npcBunniesAnimations::adjustFaceX[charIndex],
yPos: dialogueSmallFaceY-npcBunniesAnimations::adjustFaceY[charIndex],
animSet: useSmallPlayerStand ? ANIM::CUSTOM[npcBunniesAnimations::emptyCharSet[charIndex]] : iconAnimSet,
anim: useSmallPlayerStand ? animation : animIndex,
frame: jjGameTicks/12,
sizeX: -1*npcBunniesAnimations::adjustSizeX[charIndex],
sizeY: 1*npcBunniesAnimations::adjustSizeY[charIndex],
spriteMode: SPRITE::PALSHIFT,
param: 0,
grav: 0
);
array<int> params =
{
portraitNpc.fur.c1-16,
portraitNpc.fur.c2-24,
portraitNpc.fur.c3-32,
portraitNpc.fur.c4-40,
0
};
if(useSmallPlayerStand)
{
if(charIndex == npcBunniesCore::NpcAnim::LORI)
{
array<int> loriParams = { npcBunniesCore::GetPrimaryFurShift(charIndex, portraitNpc.fur.c1), portraitNpc.fur.c4 - 40, 0, portraitNpc.fur.c4 - 40 };
for(int i = 0; i < 4; i++)
{
if(i == 2) continue;
if(i == 3 && npc.fur.c4 == 40) continue;
draw.param = loriParams[i];
npcBunniesAnimations::DrawMergedCharacterLayerOnCanvas(canvas, draw, 4, i);
}
draw.param = loriParams[2];
npcBunniesAnimations::DrawMergedCharacterLayerOnCanvas(canvas, draw, 4, 2);
}
else
{
array<int> bodyParams = { npcBunniesCore::GetPrimaryFurShift(charIndex, portraitNpc.fur.c1), portraitNpc.fur.c2 - 24, portraitNpc.fur.c3 - 32, portraitNpc.fur.c4 - 40, 0 };
for(int i = 0; i <= 4; i++)
{
draw.param = bodyParams[i];
npcBunniesAnimations::DrawMergedCharacterLayerOnCanvas(canvas, draw, 5, i);
}
}
}
else
{
if(charIndex == npcBunniesCore::NpcAnim::FROG)
{
draw.param = portraitNpc.fur.c1 - 80;
npcBunniesAnimations::DrawCharacterOnCanvas(canvas, draw);
draw.animSet = ANIM::CUSTOM[npcBunniesAnimations::emptyCharSet[npcBunniesCore::NpcAnim::HEAD]]; draw.anim = 4; draw.spriteMode = SPRITE::NORMAL; draw.param = 0;
npcBunniesAnimations::DrawCharacterOnCanvas(canvas, draw);
}
else if(charIndex == npcBunniesCore::NpcAnim::BIRD)
{
draw.param = portraitNpc.fur.c1 - 80;
npcBunniesAnimations::DrawCharacterOnCanvas(canvas, draw);
draw.animSet = ANIM::CUSTOM[npcBunniesAnimations::emptyCharSet[npcBunniesCore::NpcAnim::HEAD]]; draw.spriteMode = SPRITE::PALSHIFT;
int start = npcBunniesAnimations::npcFaces.find(charIndex);
for(int i=start; i<=start+2; i++)
{
draw.anim = i; draw.param = params[i - start + 1];
if(i == start+2) draw.param = 0;
npcBunniesAnimations::DrawCharacterOnCanvas(canvas, draw);
}
npcBunniesAnimations::DrawCharacterOnCanvas(canvas, draw);
}
else
{
for(int i=0; i<=3; i++)
{
if(npcBunniesAnimations::facePalshift[charIndex] == i+1)
{
draw.param = params[i];
npcBunniesAnimations::DrawCharacterOnCanvas(canvas, draw);
break;
}
}
}
if(iconAnimSet != ANIM::FACES && charIndex != npcBunniesCore::NpcAnim::MARIO && charIndex != npcBunniesCore::NpcAnim::DEVAN)
{
draw.anim = animIndex+1; draw.spriteMode = SPRITE::NORMAL;
npcBunniesAnimations::DrawCharacterOnCanvas(canvas, draw);
}
}
}
else if(portraitLine.isNpc == 1)
{
DrawNpc = false;
animIndex = npcBunniesAnimations::charFaces.find(jjPlayers[personId].charCurr);
draw.add(xPos: dialogueSmallFaceX, yPos: dialogueSmallFaceY, animSet: iconAnimSet, anim: animIndex, frame: jjGameTicks/12,
sizeX: 1, sizeY: 1, spriteMode: SPRITE::PLAYER, param: personId, grav: 0);
npcBunniesAnimations::DrawCharacterOnCanvas(canvas, draw);
}
}
//TO DO: change this depending on who is talking.
jjTEXTAPPEARANCE ConversationAppearance(STRING::NORMAL);
string Line = npc.dialogueOverrideLine != "" ? npc.dialogueOverrideLine : npc.dialogueSets[npc.convIndex].dialogue[npc.dialogueIndex].text;
npcBunniesDialogue::DialogueSet@ currentDialogueSet = npc.dialogueSets[npc.convIndex];
int textLeft = dialogueBoxX + 30;
int textWidth = dialogueBoxW - 210 + dialogueTextExtraWidthLeft;
if(textWidth < 160) textWidth = dialogueBoxW - 60;
int textY = dialogueBoxY + 30;
int textMaxY = dialogueBoxY + dialogueBoxH - 58;
if(currentDialogueSet.choicesAnswerMethod == npcBunniesCore::DialogueAction::Quest && (currentDialogueSet.quests.length > 0 || currentDialogueSet.rewards.length > 0))
{
textMaxY = dialogueBoxY + dialogueBoxH - 100;
if(npc.dialogueOverrideFinalQuestCompleted)
canvas.drawString(textLeft, dialogueBoxY + dialogueBoxH - 74, "Final quest already completed", STRING::SMALL, STRING::PALSHIFT, 24);
else
npcBunniesDialogue::DrawNpcQuestSetObjectives(canvas, currentDialogueSet, textLeft, dialogueBoxY + dialogueBoxH - 88, textWidth, 50, 20);
}
DrawNpcDialogueScrollableText(canvas, Line, textLeft, textY, textWidth, textMaxY);
if(npc.isLastLineInDialogueSet() && npc.dialogueSets[npc.convIndex].choicesAnswerMethod != 0)
{
//TO DO: display choices
if(npc.dialogueSets[npc.convIndex].choicesAnswerMethod == 1) //choices
{
for(uint i=0; i<npc.dialogueSets[npc.convIndex].choices.length; i++)
{
string choiceText = npc.dialogueSets[npc.convIndex].choices[i].choiceText;
canvas.drawString(0x8000 - (jjGetStringWidth(choiceText, STRING::SMALL, npcBunniesDialogue::highlightChoice == int(i) ? STRING::SPIN : STRING::NORMAL) - (jjGetStringWidth(choiceText, STRING::SMALL, npcBunniesDialogue::highlightChoice == int(i) ? STRING::SPIN : STRING::NORMAL) & ~1))/2, 350+i*20, choiceText, STRING::SMALL, npcBunniesDialogue::highlightChoice == int(i) ? STRING::SPIN : STRING::NORMAL);
//TO DO: highlightChoice global var - which choiceText is highlighted, incremented by up/down controls.
}
}
else if(npc.dialogueSets[npc.convIndex].choicesAnswerMethod == 2) //typed/text answers
{
string yourAnswer = "Type your answer here: ";
canvas.drawString(0x8000 - (jjGetStringWidth(yourAnswer, STRING::SMALL, STRING::NORMAL) - (jjGetStringWidth(yourAnswer, STRING::SMALL, STRING::NORMAL) & ~1))/2, 350, yourAnswer, STRING::SMALL, STRING::NORMAL);
canvas.drawString(0x8000 - (jjGetStringWidth(npcBunniesDialogue::answerText, STRING::SMALL, STRING::NORMAL) - (jjGetStringWidth(npcBunniesDialogue::answerText, STRING::SMALL, STRING::NORMAL) & ~1))/2, 375, npcBunniesDialogue::answerText, STRING::SMALL, STRING::NORMAL);
//TO DO: draw a square where you display what is being typed.
}
//result of a choice should be in 'npc.dialogueSet[npc.convIndex].goToSetAfterChoice'
}
}
//rewards were here before
}
npcBunniesEditorViews::NpcDesigner_Draw(play, canvas);
return false;
}
int NpcQuestPressedKey()
{
for(int key = 8; key <= 222; key++)
{
if(key == 0x01) continue;
if(jjKey[key]) return key;
}
return -1;
}
void AdvanceNpcQuestDialogPage(jjPLAYER@ play, int delta)
{
int groups = npcBunniesDialogue::CountNpcQuestGroups(play);
if(groups <= 0) { npcBunniesCore::npcQuestDialogOpen = false; npcBunniesCore::npcQuestDialogPage = 0; return; }
npcBunniesCore::npcQuestDialogPage += delta;
while(npcBunniesCore::npcQuestDialogPage < 0) npcBunniesCore::npcQuestDialogPage += groups;
while(npcBunniesCore::npcQuestDialogPage >= groups) npcBunniesCore::npcQuestDialogPage -= groups;
npcBunniesCore::npcQuestDialogScroll = 0;
npcBunniesDialogue::InvalidateNpcQuestDialogCache();
}
void ToggleNpcQuestDialog(jjPLAYER@ play)
{
if(!npcBunniesCore::npcQuestDialogOpen)
{
npcBunniesCore::npcQuestDialogOpen = true;
npcBunniesCore::npcQuestDialogPage = 0;
npcBunniesCore::npcQuestDialogScroll = 0;
npcBunniesDialogue::InvalidateNpcQuestDialogCache();
}
else
{
int groups = npcBunniesDialogue::CountNpcQuestGroups(play);
if(groups <= 0 || npcBunniesCore::npcQuestDialogPage + 1 >= groups) npcBunniesCore::npcQuestDialogOpen = false;
else npcBunniesCore::npcQuestDialogPage++;
npcBunniesDialogue::InvalidateNpcQuestDialogCache();
}
npcBunniesCore::npcQuestDialogCaptureKey = false;
npcBunniesCore::npcQuestDialogInputTick = jjGameTicks;
}
void ProcessNpcQuestDialogInput()
{
if(npcBunniesEditorCore::NpcDesignerOpen || npcBunniesCore::chatIsOpen || npcBunniesMovement::convOn != -1) return;
bool mouseDown = jjKey[0x01];
bool clicked = mouseDown && !npcBunniesCore::npcQuestMouseWasDown;
if(!mouseDown)
{
npcBunniesCore::npcQuestMouseWasDown = false;
}
else npcBunniesCore::npcQuestMouseWasDown = true;
if(npcBunniesCore::npcQuestDialogCaptureKey && !npcBunniesDialogue::NpcQuestHotkeyEnabled())
npcBunniesCore::npcQuestDialogCaptureKey = false;
if(npcBunniesCore::npcQuestDialogCaptureKey)
{
int key = NpcQuestPressedKey();
if(key > 0 && key != 0x1B && jjGameTicks > npcBunniesCore::npcQuestDialogInputTick + 10)
{
npcBunniesCore::NPC_QUEST_DIALOG_KEY = key;
npcBunniesCore::npcQuestDialogCaptureKey = false;
npcBunniesCore::npcQuestDialogInputTick = jjGameTicks;
npcBunniesCore::SaveNpcBunniesPreferences();
}
else if(jjKey[0x1B] && jjGameTicks > npcBunniesCore::npcQuestDialogInputTick + 10)
{
npcBunniesCore::npcQuestDialogCaptureKey = false;
npcBunniesCore::npcQuestDialogInputTick = jjGameTicks;
}
return;
}
bool questKeyPressed = npcBunniesDialogue::NpcQuestHotkeyEnabled() && jjKey[npcBunniesCore::NPC_QUEST_DIALOG_KEY] && jjGameTicks > npcBunniesCore::npcQuestDialogInputTick + 20;
if(!questKeyPressed && !clicked && !npcBunniesCore::npcQuestDialogOpen) return;
jjPLAYER@ questPlayer = npcBunniesDialogue::NpcQuestLocalPlayer();
if(questKeyPressed)
{
ToggleNpcQuestDialog(questPlayer);
return;
}
if(clicked && npcBunniesDialogue::NpcQuestHotkeyEnabled())
{
int hudX = npcBunniesCore::HUD_COUNTS_X;
int hudY = npcBunniesCore::HUD_COUNTS_Y + 32;
int hudW = npcBunniesDialogue::NpcQuestHudWidth();
int tx = jjMouseX;
int ty = jjMouseY + 8;
if(tx >= hudX && tx <= hudX + hudW && ty >= hudY && ty <= hudY + 14)
{
ToggleNpcQuestDialog(questPlayer);
return;
}
}
if(!npcBunniesCore::npcQuestDialogOpen) return;
int w = 315;
int textW = npcBunniesCore::npcQuestDialogShowInfo ? w - 20 - 62 : w - 20;
if(textW < 20) textW = w - 20;
int visibleHeight = npcBunniesCore::npcQuestDialogShowInfo ? 69 : 14;
npcBunniesDialogue::EnsureNpcQuestDialogCache(questPlayer, npcBunniesCore::npcQuestDialogPage, npcBunniesCore::npcQuestDialogShowInfo, textW, visibleHeight);
if(npcBunniesDialogue::cachedNpcQuestDialogGroups <= 0) npcBunniesCore::npcQuestDialogPage = 0;
else if(npcBunniesCore::npcQuestDialogPage >= npcBunniesDialogue::cachedNpcQuestDialogGroups)
{
npcBunniesCore::npcQuestDialogPage = npcBunniesDialogue::cachedNpcQuestDialogGroups - 1;
npcBunniesDialogue::EnsureNpcQuestDialogCache(questPlayer, npcBunniesCore::npcQuestDialogPage, npcBunniesCore::npcQuestDialogShowInfo, textW, visibleHeight, true);
}
else if(npcBunniesCore::npcQuestDialogPage < 0)
{
npcBunniesCore::npcQuestDialogPage = 0;
npcBunniesDialogue::EnsureNpcQuestDialogCache(questPlayer, npcBunniesCore::npcQuestDialogPage, npcBunniesCore::npcQuestDialogShowInfo, textW, visibleHeight, true);
}
int h = npcBunniesCore::npcQuestDialogShowInfo ? 180 : 58 + npcBunniesDialogue::cachedNpcQuestDialogBasicLines * 18;
if(h < 110) h = 110;
npcBunniesDialogue::EnsureNpcQuestDialogPosition(w, h);
npcBunniesCore::npcQuestDialogWindow.preferencesFile = "npcBunniesDialogueBox.asdat";
DbBegin(@npcBunniesCore::npcQuestDialogWindow, "npcQuestTracker", "Active quests", npcBunniesCore::npcQuestDialogX, npcBunniesCore::npcQuestDialogY, w, h);
npcBunniesCore::npcQuestDialogWindow.closeButton = true;
npcBunniesCore::npcQuestDialogWindow.draggable = true;
npcBunniesCore::npcQuestDialogWindow.titleBar = true;
npcBunniesCore::npcQuestDialogWindow.pad = 10;
npcBunniesCore::npcQuestDialogX = npcBunniesCore::npcQuestDialogWindow.x;
npcBunniesCore::npcQuestDialogY = npcBunniesCore::npcQuestDialogWindow.y;
DbAddControl(@npcBunniesCore::npcQuestDialogWindow, npcBunniesCore::npcQuestDialogX + w - 24, npcBunniesCore::npcQuestDialogY + 8, 18, 15, DB_ACTION_CLOSE, 0, 0, "X");
int textX = npcBunniesCore::npcQuestDialogX + 10;
int textY = npcBunniesCore::npcQuestDialogY + 33;
int controlsY = npcBunniesCore::npcQuestDialogY + h - 22;
if(npcBunniesCore::npcQuestDialogShowInfo)
{
int objectivesY = controlsY - 50;
int textMaxY = objectivesY - 6;
if(npcBunniesCore::npcQuestDialogScroll < 0) npcBunniesCore::npcQuestDialogScroll = 0;
if(npcBunniesCore::npcQuestDialogScroll > npcBunniesDialogue::cachedNpcQuestDialogMaxScroll) npcBunniesCore::npcQuestDialogScroll = npcBunniesDialogue::cachedNpcQuestDialogMaxScroll;
if(npcBunniesDialogue::cachedNpcQuestDialogMaxScroll > 0)
{
if(npcBunniesCore::npcQuestDialogScroll > 0) DbAddControl(@npcBunniesCore::npcQuestDialogWindow, textX + w - 20 - 46, textY, 42, 14, npcBunniesCore::DB_NPC_QUEST_SCROLL_UP, 0, -1, "UP");
if(npcBunniesCore::npcQuestDialogScroll < npcBunniesDialogue::cachedNpcQuestDialogMaxScroll) DbAddControl(@npcBunniesCore::npcQuestDialogWindow, textX + w - 20 - 58, textMaxY - 12, 54, 14, npcBunniesCore::DB_NPC_QUEST_SCROLL_DOWN, 0, 1, "DOWN");
}
}
else npcBunniesCore::npcQuestDialogScroll = 0;
if(npcBunniesDialogue::NpcQuestHotkeyEnabled())
DbAddControl(@npcBunniesCore::npcQuestDialogWindow, npcBunniesCore::npcQuestDialogX + 10, controlsY, 90, 14, npcBunniesCore::DB_NPC_QUEST_KEY, 0, 0, "Key");
DbAddControl(@npcBunniesCore::npcQuestDialogWindow, npcBunniesCore::npcQuestDialogX + 112, controlsY - 1, 16, 14, npcBunniesCore::DB_NPC_QUEST_PREV, 0, -1, "<");
DbAddControl(@npcBunniesCore::npcQuestDialogWindow, npcBunniesCore::npcQuestDialogX + 136, controlsY - 1, 16, 14, npcBunniesCore::DB_NPC_QUEST_NEXT, 0, 1, ">");
DbAddControl(@npcBunniesCore::npcQuestDialogWindow, npcBunniesCore::npcQuestDialogX + 164, controlsY, 130, 14, npcBunniesCore::DB_NPC_QUEST_INFO, 0, 0, "Show info");
if(jjKey[0x1B] && jjGameTicks > npcBunniesCore::npcQuestDialogInputTick + 20)
{
npcBunniesCore::npcQuestDialogOpen = false;
npcBunniesCore::npcQuestDialogInputTick = jjGameTicks;
return;
}
DbResult result;
if(DbProcessMouse(@npcBunniesCore::npcQuestDialogWindow, @result) || DbProcessKeyboard(@npcBunniesCore::npcQuestDialogWindow, questPlayer, @result, 10))
{
npcBunniesCore::npcQuestDialogX = npcBunniesCore::npcQuestDialogWindow.x;
npcBunniesCore::npcQuestDialogY = npcBunniesCore::npcQuestDialogWindow.y;
if(result.positionChanged) npcBunniesCore::SaveNpcBunniesPreferences();
if(result.closeRequested)
{
npcBunniesCore::npcQuestDialogOpen = false;
npcBunniesCore::npcQuestDialogInputTick = jjGameTicks;
}
else if(result.action == npcBunniesCore::DB_NPC_QUEST_PREV || result.action == npcBunniesCore::DB_NPC_QUEST_NEXT)
{
AdvanceNpcQuestDialogPage(questPlayer, result.b);
npcBunniesCore::npcQuestDialogInputTick = jjGameTicks;
}
else if(DbIsScrollResult(result, npcBunniesCore::DB_NPC_QUEST_SCROLL_UP, npcBunniesCore::DB_NPC_QUEST_SCROLL_DOWN))
{
npcBunniesCore::npcQuestDialogScroll = DbHandleScrollResult(result, npcBunniesCore::npcQuestDialogScroll, npcBunniesCore::DB_NPC_QUEST_SCROLL_UP, npcBunniesCore::DB_NPC_QUEST_SCROLL_DOWN);
npcBunniesCore::npcQuestDialogInputTick = jjGameTicks;
}
else if(result.action == npcBunniesCore::DB_NPC_QUEST_INFO)
{
npcBunniesCore::npcQuestDialogShowInfo = !npcBunniesCore::npcQuestDialogShowInfo;
npcBunniesCore::npcQuestDialogScroll = 0;
npcBunniesDialogue::InvalidateNpcQuestDialogCache();
npcBunniesCore::npcQuestDialogInputTick = jjGameTicks;
}
else if(result.action == npcBunniesCore::DB_NPC_QUEST_KEY && npcBunniesDialogue::NpcQuestHotkeyEnabled())
{
npcBunniesCore::npcQuestDialogCaptureKey = true;
npcBunniesCore::npcQuestDialogInputTick = jjGameTicks;
}
}
}
void onPlayerInput(jjPLAYER@ play)
{
npcBunniesEditorCore::NpcDesigner_OnPlayerInput(play);
if(npcBunniesEditorCore::NpcDesignerOpen && npcBunniesCore::LocalPlayerHasNpcBunniesAdminAccess()) { jjKeyChat = 5; return; }
//TODO: this is basically a method that changes the jjKeyChat so that you can use it when answering questions
//so maybe move this in a separate method (?)
if(npcBunniesMovement::convOn != -1)
{
if(npcBunniesCore::npcBunnies[npcBunniesMovement::convOn].isLastLineInDialogueSet() && npcBunniesCore::npcBunnies[npcBunniesMovement::convOn].dialogueSets[npcBunniesCore::npcBunnies[npcBunniesMovement::convOn].convIndex].choicesAnswerMethod == 2)
{
//play.keyLeft = play.keyRight = play.keyRun = play.keyFire = play.keySelect = play.keyJump = false;
jjKeyChat = 5;
}
else
{
jjKeyChat = 84;
}
}
else
{
jjKeyChat = 84;
}
if(jjGameTicks <= npcBunniesMovement::keyTicks[13] + 20) play.keySelect = false;
}
bool onLocalChat(string &in text, CHAT::Type type)
{
if(npcBunniesAccessories::NpcAccessories::OnLocalChat(text)) return true;
if(jjRegexMatch(text, "^!quests$", true)) { npcBunniesCore::npcQuestDialogOpen = true; npcBunniesCore::npcQuestDialogPage = 0; npcBunniesCore::npcQuestDialogCaptureKey = false; npcBunniesDialogue::InvalidateNpcQuestDialogCache(); return true; }
if(jjRegexMatch(text, "^!npcSync\\s+(ON|OFF)$", true))
{
array<string> parts = text.split(" ");
bool enabled = npcBunniesCore::ENABLE_NPC_SYNC;
string value = "";
for(uint i = 1; i < parts.length; i++)
{
if(parts[i] != "")
{
value = parts[i];
break;
}
}
if(value == "" || !npcBunniesCore::ParseNpcSyncSettingValue(value, enabled)) return true;
if(!LocalPlayerHasAnyAdminAccess())
{
jjAlert("NpcBunnies: only admins can change NPC sync.", false);
return true;
}
if(jjIsServer) SaveNpcSyncSettingCommand(enabled);
else
{
jjSTREAM packet;
packet.push("npcSyncSettingSet " + (enabled ? "1" : "0"));
jjSendPacket(packet);
jjAlert("NpcBunnies: requested NPC sync " + npcBunniesCore::NpcSyncSettingText(enabled) + ". It takes effect after level cycle.", false);
}
return true;
}
if(npcBunniesCore::LocalPlayerHasNpcBunniesAdminAccess() && jjRegexMatch(text, "^!respawnMonsters$", true))
{
if(jjIsServer)
{
int respawned = npcBunniesQuests::RespawnNpcFightingTargets(true);
jjAlert("NpcBunniesAdmin: respawned " + respawned + " monster/boss objects.", false);
}
else
{
npcBunniesQuests::RequestRespawnNpcFightingTargets();
jjAlert("NpcBunniesAdmin: requested monster respawn from the server.", false);
}
return true;
}
return npcBunniesEditorCore::NpcDesigner_OnLocalChat(text, type);
}
void exitConv()
{
npcBunniesMovement::convOn = -1;
}
void requestNpcBunniesAdminAccess()
{
if(jjIsServer || npcBunniesCore::npcBunniesAdminAccessGranted || npcBunniesCore::npcBunniesAdminAccessRequestTick + 70 > jjGameTicks) return;
npcBunniesCore::npcBunniesAdminAccessRequestTick = jjGameTicks;
jjSTREAM packet;
packet.push("npcAdminAccess");
jjSendPacket(packet);
}
int ClientIdToPlayerID(int clientID)
{
for(int i = 0; i < 32; i++)
{
if((jjPlayers[i].isActive || jjPlayers[i].isInGame) && jjPlayers[i].clientID == clientID) return i;
}
return -1;
}
bool PlayerHasAnyAdminAccess(jjPLAYER@ play)
{
if(play is null) return false;
return play.isAdmin || play.hasPrivilege("admin");
}
bool LocalPlayerHasAnyAdminAccess()
{
if(jjIsServer || jjIsAdmin) return true;
for(int i = 0; i < jjLocalPlayerCount; i++)
{
if(PlayerHasAnyAdminAccess(jjLocalPlayers[i])) return true;
}
return false;
}
bool ClientHasAnyAdminAccess(int clientID)
{
if(clientID == 0) return jjIsServer;
int playerID = ClientIdToPlayerID(clientID);
return playerID >= 0 && playerID < 32 && PlayerHasAnyAdminAccess(jjPlayers[playerID]);
}
void requestNpcSyncSetting()
{
jjSTREAM packet;
packet.push("npcSyncSettingRequest");
jjSendPacket(packet);
}
void sendNpcSyncSetting(int clientID)
{
if(!jjIsServer) return;
jjSTREAM packet;
packet.push("npcSyncSetting " + (npcBunniesCore::ENABLE_NPC_SYNC ? "1" : "0"));
if(clientID == 0) jjSendPacket(packet);
else jjSendPacket(packet, clientID);
}
void sendNpcSyncSettingSaved(int clientID, bool enabled, bool allowed)
{
if(!jjIsServer || clientID <= 0) return;
jjSTREAM packet;
packet.push("npcSyncSettingSaved " + (allowed ? "1" : "0") + " " + (enabled ? "1" : "0"));
jjSendPacket(packet, clientID);
}
void SaveNpcSyncSettingCommand(bool enabled)
{
if(!jjIsServer) return;
npcBunniesCore::SaveNpcSyncSetting(enabled);
jjAlert("NpcBunnies: NPC sync " + npcBunniesCore::NpcSyncSettingText(enabled) + " saved. It takes effect after level cycle.", false);
}
void sendNpcBunniesAdminAccess(int clientID)
{
if(!jjIsServer || clientID <= 0) return;
int playerID = int(npcBunniesMovement::clientToID(clientID));
bool allowed = playerID >= 0 && playerID < 32 && npcBunniesCore::HasNpcBunniesAdminPrivilege(jjPlayers[playerID]);
jjSTREAM packet;
packet.push("npcAdminAccess " + (allowed ? "1" : "0"));
jjSendPacket(packet, clientID);
}
void requestNpcServerSync()
{
if(!npcBunniesCore::ENABLE_NPC_SYNC) return;
jjSTREAM npcPacket;
npcPacket.push("npcAll");
jjSendPacket(npcPacket);
}
void applyNpcSync(int id, int playerFollowing, int followingTrainIndex, float xPos, float yPos, int direction, int hatFrame = -9999, int scarfFrame = -9999, int glassesFrame = -9999)
{
if(id < 0 || id >= int(npcBunniesCore::npcBunnies.length)) return;
npcBunniesMovement::EnsureNpcRuntimeArrays();
npcBunniesCore::npcBunnies[id].playerFollowing = playerFollowing;
npcBunniesCore::npcBunnies[id].followingTrainIndex = followingTrainIndex;
if(hatFrame != -9999) npcBunniesCore::npcBunnies[id].hatFrame = hatFrame;
if(scarfFrame != -9999) npcBunniesCore::npcBunnies[id].scarfFrame = scarfFrame;
if(glassesFrame != -9999) npcBunniesCore::npcBunnies[id].glassesFrame = glassesFrame;
jjOBJ@ obj = jjObjects[npcBunniesCore::npcBunnies[id].objectID];
obj.xPos = xPos;
obj.yPos = yPos;
obj.direction = direction;
npcBunniesMovement::previousNpcX[id] = xPos;
npcBunniesMovement::previousNpcY[id] = yPos;
}
void sendNpcSync(int id, int playerFollowing, int followingTrainIndex, int clientID = 0)
{
if(!npcBunniesCore::ENABLE_NPC_SYNC) return;
if(id < 0 || id >= int(npcBunniesCore::npcBunnies.length)) return;
jjOBJ@ obj = jjObjects[npcBunniesCore::npcBunnies[id].objectID];
jjSTREAM npcPacket;
npcPacket.push("npcSync " + id + " " + playerFollowing + " " + followingTrainIndex + " " + obj.xPos + " " + obj.yPos + " " + obj.direction + " " + npcBunniesCore::npcBunnies[id].hatFrame + " " + npcBunniesCore::npcBunnies[id].scarfFrame + " " + npcBunniesCore::npcBunnies[id].glassesFrame);
if(clientID == 0) jjSendPacket(npcPacket);
else jjSendPacket(npcPacket, clientID);
if(jjIsServer && clientID == 0)
{
npcBunniesMovement::EnsureNpcRuntimeArrays();
if(id < int(npcBunniesMovement::lastNpcSyncedX.length))
{
npcBunniesMovement::lastNpcSyncedX[id] = obj.xPos;
npcBunniesMovement::lastNpcSyncedY[id] = obj.yPos;
npcBunniesMovement::lastNpcPositionSyncTick[id] = jjGameTicks;
}
}
}
void sendAllNpcSync(int clientID)
{
if(!npcBunniesCore::ENABLE_NPC_SYNC) return;
if(clientID <= 0) return;
for(uint i = 0; i < npcBunniesCore::npcBunnies.length; i++)
{
sendNpcSync(int(i), npcBunniesCore::npcBunnies[i].playerFollowing, npcBunniesCore::npcBunnies[i].followingTrainIndex, clientID);
npcBunniesFighting::SendNpcFightingSync(int(i), clientID);
}
}
void MaybeSendNpcPositionSync(npcBunniesCore::npcBunny@ npc, jjOBJ@ obj, float oldX, float oldY)
{
if(!npcBunniesCore::ENABLE_NPC_SYNC || !npcBunniesCore::ENABLE_NPC_POSITION_SYNC) return;
if(!jjIsServer || npc is null) return;
if(npc.id < 0 || npc.id >= int(npcBunniesMovement::lastNpcPositionSyncTick.length)) return;
if(npc.playerFollowing < 0 && !npc.guideActive && npc.tpAnim < 0) return;
if(oldX == obj.xPos && oldY == obj.yPos) return;
if(npcBunniesMovement::lastNpcPositionSyncTick[npc.id] + npcBunniesCore::NPC_POSITION_SYNC_INTERVAL > jjGameTicks) return;
float dx = abs(obj.xPos - npcBunniesMovement::lastNpcSyncedX[npc.id]);
float dy = abs(obj.yPos - npcBunniesMovement::lastNpcSyncedY[npc.id]);
if(dx < npcBunniesCore::NPC_POSITION_SYNC_MIN_DISTANCE && dy < npcBunniesCore::NPC_POSITION_SYNC_MIN_DISTANCE) return;
npcBunniesMovement::lastNpcPositionSyncTick[npc.id] = jjGameTicks;
sendNpcSync(npc.id, npc.playerFollowing, npc.followingTrainIndex, 0);
}
void onReceive(jjSTREAM &in packet, int fromClientID)
{
string command;
packet.pop(command);
array<string> string_parts = command.split(" ");
if(command == "npcEditorBegin")
{
npcBunniesCore::npcDesignerSyncText = "";
npcBunniesCore::npcDesignerSyncAllowed = !jjIsServer || npcBunniesCore::HasNpcBunniesAdminPrivilege(jjPlayers[int(npcBunniesMovement::clientToID(fromClientID))]);
return;
}
if(command == "npcEditorChunk")
{
string chunk;
packet.pop(chunk);
if(npcBunniesCore::npcDesignerSyncAllowed) npcBunniesCore::npcDesignerSyncText += npcBunniesEditorSerialization::NdePacketDecode(chunk);
return;
}
if(command == "npcEditorEnd")
{
if(npcBunniesCore::npcDesignerSyncAllowed && npcBunniesCore::npcDesignerSyncText != "")
{
npcBunniesEditorSerialization::NdeApplyNpcDesignerState(npcBunniesCore::npcDesignerSyncText, jjIsServer);
if(jjIsServer) npcBunniesEditorSerialization::NdeBroadcastNpcDesignerState(npcBunniesCore::npcDesignerSyncText, -fromClientID);
}
npcBunniesCore::npcDesignerSyncText = "";
npcBunniesCore::npcDesignerSyncAllowed = false;
return;
}
if(command == "npcAdminAccess" && jjIsServer)
{
sendNpcBunniesAdminAccess(fromClientID);
return;
}
if(string_parts.length >= 2 && string_parts[0] == "npcAdminAccess" && !jjIsServer)
{
npcBunniesCore::npcBunniesAdminAccessGranted = parseInt(string_parts[1]) != 0;
return;
}
if(command == "npcSyncSettingRequest" && jjIsServer)
{
sendNpcSyncSetting(fromClientID);
return;
}
if(string_parts.length >= 2 && string_parts[0] == "npcSyncSetting")
{
npcBunniesCore::ENABLE_NPC_SYNC = parseInt(string_parts[1]) != 0;
receivedNpcSyncSetting = true;
return;
}
if(string_parts.length >= 2 && string_parts[0] == "npcSyncSettingSet" && jjIsServer)
{
bool enabled = parseInt(string_parts[1]) != 0;
bool allowed = ClientHasAnyAdminAccess(fromClientID);
if(allowed) npcBunniesCore::SaveNpcSyncSetting(enabled);
sendNpcSyncSettingSaved(fromClientID, enabled, allowed);
return;
}
if(string_parts.length >= 3 && string_parts[0] == "npcSyncSettingSaved" && !jjIsServer)
{
bool allowed = parseInt(string_parts[1]) != 0;
bool enabled = parseInt(string_parts[2]) != 0;
if(allowed) jjAlert("NpcBunnies: NPC sync " + npcBunniesCore::NpcSyncSettingText(enabled) + " saved. It takes effect after level cycle.", false);
else jjAlert("NpcBunnies: server denied NPC sync setting change.", false);
return;
}
if(!npcBunniesCore::ENABLE_NPC_SYNC && (command == "npcAll" || (string_parts.length >= 1 && (string_parts[0] == "npcSync" || string_parts[0] == "npcFight")))) return;
if(command == "npcAll" && jjIsServer)
{
sendAllNpcSync(fromClientID);
return;
}
if(command == "npcRespawnMonsters" && jjIsServer)
{
int senderID = int(npcBunniesMovement::clientToID(fromClientID));
if(fromClientID == 0 || (senderID >= 0 && senderID < 32 && npcBunniesCore::HasNpcBunniesAdminPrivilege(jjPlayers[senderID]))) npcBunniesQuests::RespawnNpcFightingTargets(true);
return;
}
if(string_parts.length >= 4 && string_parts[0] == "npcRespawnMonsterAt" && !jjIsServer)
{
int eventID = parseInt(string_parts[1]);
int xTile = parseInt(string_parts[2]);
int yTile = parseInt(string_parts[3]);
npcBunniesQuests::RespawnNpcFightingTargetAt(eventID, xTile, yTile);
return;
}
if(string_parts.length >= 2 && string_parts[0] == "npcReturn" && jjIsServer)
{
int id = parseInt(string_parts[1]);
npcBunniesDialogue::ReturnNpcToOrigin(id);
return;
}
if(string_parts.length >= 3 && string_parts[0] == "npcFight")
{
int id = parseInt(string_parts[1]);
bool enabled = parseInt(string_parts[2]) != 0;
if(jjIsServer && fromClientID != 0)
{
int senderID = int(npcBunniesMovement::clientToID(fromClientID));
if(id >= 0 && id < int(npcBunniesCore::npcBunnies.length) && npcBunniesCore::npcBunnies[id].playerFollowing == senderID) npcBunniesFighting::ApplyNpcFightingSync(id, enabled);
npcBunniesFighting::SendNpcFightingSync(id, 0);
}
else npcBunniesFighting::ApplyNpcFightingSync(id, enabled);
return;
}
if(string_parts.length < 7 || string_parts[0] != "npcSync") return;
int id = parseInt(string_parts[1]);
int playerFollowing = parseInt(string_parts[2]);
int followingTrainIndex = parseInt(string_parts[3]);
float xPos = parseFloat(string_parts[4]);
float yPos = parseFloat(string_parts[5]);
int direction = parseInt(string_parts[6]);
int hatFrame = string_parts.length >= 10 ? parseInt(string_parts[7]) : -9999;
int scarfFrame = string_parts.length >= 10 ? parseInt(string_parts[8]) : -9999;
int glassesFrame = string_parts.length >= 10 ? parseInt(string_parts[9]) : -9999;
if(id < 0 || id >= int(npcBunniesCore::npcBunnies.length)) return;
if(jjIsServer && fromClientID != 0)
{
int senderID = int(npcBunniesMovement::clientToID(fromClientID));
if(playerFollowing == -1)
{
if(npcBunniesCore::npcBunnies[id].playerFollowing == senderID)
{
npcBunniesMovement::recalculateFollowingTrainIndexes(npcBunniesCore::npcBunnies[id].followingTrainIndex, senderID);
npcBunniesCore::npcBunnies[id].playerFollowing = -1;
npcBunniesCore::npcBunnies[id].followingTrainIndex = -1;
}
}
else if(playerFollowing == senderID && npcBunniesCore::npcBunnies[id].playerFollowing == -1)
{
int nextIndex = npcBunniesMovement::getFollowingTrainIndex(senderID);
if(npcBunniesCore::NPC_FOLLOW_LIMIT == -1 || nextIndex <= npcBunniesCore::NPC_FOLLOW_LIMIT)
{
npcBunniesCore::npcBunnies[id].playerFollowing = senderID;
npcBunniesCore::npcBunnies[id].followingTrainIndex = nextIndex;
}
}
sendNpcSync(id, npcBunniesCore::npcBunnies[id].playerFollowing, npcBunniesCore::npcBunnies[id].followingTrainIndex, 0);
return;
}
applyNpcSync(id, playerFollowing, followingTrainIndex, xPos, yPos, direction, hatFrame, scarfFrame, glassesFrame);
}
void onObjectHit(jjOBJ@ obj, jjOBJ@ bullet, jjPLAYER@ player, int force)
{
if(!npcBunniesCore::ENABLE_NPC_MONSTER_QUESTS) return;
if(player is null || obj is null) return;
if(!npcBunniesQuests::IsTrackedDefeatEventID(obj.eventID)) return;
npcBunniesQuests::EnsureTrackedDefeatObjectArrays();
int id = obj.objectID;
if(id >= 0 && id < int(npcBunniesQuests::trackedDefeatObjectLastHitPlayer.length))
{
npcBunniesQuests::trackedDefeatObjectWasActive[id] = true;
npcBunniesQuests::trackedDefeatObjectEventID[id] = obj.eventID;
npcBunniesQuests::trackedDefeatObjectLastHitPlayer[id] = player.nameUnformatted;
if(force >= obj.energy && !npcBunniesQuests::trackedDefeatObjectCounted[id])
{
npcBunniesQuests::IncrementDefeatedObjectCountForName(player.nameUnformatted, obj.eventID);
npcBunniesQuests::IncrementActiveQuestDefeatProgressForName(player.nameUnformatted, obj.eventID);
npcBunniesQuests::trackedDefeatObjectCounted[id] = true;
}
}
}
}
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.