| Name | Author | Game Mode | Rating | |||||
|---|---|---|---|---|---|---|---|---|
| NpcBunnies | Cranky | Mutator | N/A | |||||
namespace npcBunniesEditorActions
{
void NdeProcessClick(jjPLAYER@ play)
{
bool mouseDown = jjKey[0x01];
if(!mouseDown) { npcBunniesEditorCore::NpcDesignerMouseWasDown = false; return; }
if(npcBunniesEditorCore::NpcDesignerMouseWasDown) return;
npcBunniesEditorCore::NpcDesignerMouseWasDown = true;
if(jjGameTicks < npcBunniesEditorCore::NpcDesignerIgnoreInputUntilTick) return;
if(jjGameTicks - npcBunniesEditorCore::NpcDesignerLastClick < 20) return;
npcBunniesEditorCore::NpcDesignerLastClick = jjGameTicks;
int tx = jjMouseX, ty = jjMouseY + 8;
int hit = DbHitControl(@npcBunniesEditorCore::NpcDesignerWindow, tx, ty);
if(hit >= 0 && hit < int(npcBunniesEditorCore::NpcDesignerButtons.length))
{
npcBunniesEditorCore::NpcDesignerFocus = hit;
npcBunniesEditorCore::NpcDesignerWindow.focusIndex = hit;
NdeActivateButton(npcBunniesEditorCore::NpcDesignerButtons[hit]);
return;
}
if(npcBunniesEditorCore::NpcDesignerView == npcBunniesEditorCore::NDE_CREATE && npcBunniesEditorCore::NpcDesignerSelectedType >= 0 && !npcBunniesEditorCore::NdeInsidePanel(tx, ty))
NdePlaceNewNpcAtMouse(play, jjMouseX, jjMouseY);
else if(npcBunniesEditorCore::NpcDesignerMovingNpc && NdeValidNpc() && !npcBunniesEditorCore::NdeInsidePanel(tx, ty))
NdeMoveSelectedNpcToMouse(play, jjMouseX, jjMouseY);
else if(npcBunniesEditorCore::NpcDesignerEditingTeleport && NdeValidNpc() && !npcBunniesEditorCore::NdeInsidePanel(tx, ty))
NdeSetTeleportEditFromMouse(play, jjMouseX, jjMouseY);
}
/// <summary>
/// Keeps the keyboard focus index inside the current button list.
/// </summary>
void NdeNormalizeFocus()
{
if(npcBunniesEditorCore::NpcDesignerButtons.length == 0) { npcBunniesEditorCore::NpcDesignerFocus = 0; return; }
npcBunniesEditorCore::NpcDesignerWindow.focusIndex = npcBunniesEditorCore::NpcDesignerFocus;
DbNormalizeFocus(@npcBunniesEditorCore::NpcDesignerWindow);
npcBunniesEditorCore::NpcDesignerFocus = npcBunniesEditorCore::NpcDesignerWindow.focusIndex;
}
/// <summary>
/// Activates the currently focused editor button.
/// </summary>
void NdeActivateFocus()
{
if(npcBunniesEditorCore::NpcDesignerButtons.length == 0) return;
NdeNormalizeFocus();
NdeActivateButton(npcBunniesEditorCore::NpcDesignerButtons[npcBunniesEditorCore::NpcDesignerFocus]);
}
/// <summary>
/// Global editor state for focused button action.
/// </summary>
int NdeFocusedButtonAction()
{
if(npcBunniesEditorCore::NpcDesignerFocus < 0 || npcBunniesEditorCore::NpcDesignerFocus >= int(npcBunniesEditorCore::NpcDesignerButtons.length)) return -1;
return npcBunniesEditorCore::NpcDesignerButtons[npcBunniesEditorCore::NpcDesignerFocus].action;
}
/// <summary>
/// Dispatches a clicked or focused editor button to its behavior.
/// </summary>
void NdeActivateButton(npcBunniesEditorCore::NpcDesignerButton@ b)
{
if(b is null) return;
if(b.action != npcBunniesEditorCore::NDE_ACT_FIELD) NdeEndTextEdit();
if(b.action == npcBunniesEditorCore::NDE_ACT_TOGGLE_PLAYER_MOVEMENT) { npcBunniesEditorCore::NpcDesignerAllowPlayerMovement = !npcBunniesEditorCore::NpcDesignerAllowPlayerMovement; return; }
if(b.action == npcBunniesEditorCore::NDE_ACT_HELP) { npcBunniesEditorCore::NpcDesignerView = npcBunniesEditorCore::NDE_HELP; npcBunniesEditorCore::NpcDesignerScroll = 0; return; }
if(b.action == npcBunniesEditorCore::NDE_ACT_TOGGLE_BIG_ANIMS) { npcBunniesCore::LOAD_BIG_NPC_ANIMATIONS = !npcBunniesCore::LOAD_BIG_NPC_ANIMATIONS; if(npcBunniesCore::LOAD_BIG_NPC_ANIMATIONS) npcBunniesAnimations::loadMenuAnims(); npcBunniesEditorSerialization::NdeSaveNpcFile(); return; }
if(b.action == npcBunniesEditorCore::NDE_ACT_TOGGLE_ALL_MONSTER_ANIMS) { npcBunniesCore::LOAD_ALL_MONSTER_ANIMATIONS = !npcBunniesCore::LOAD_ALL_MONSTER_ANIMATIONS; npcBunniesCore::NPC_ALL_MONSTER_ANIMS_PENDING_RESTART = npcBunniesCore::LOAD_ALL_MONSTER_ANIMATIONS; if(npcBunniesCore::LOAD_ALL_MONSTER_ANIMATIONS) jjAlert("NPC Designer: all monster/boss animations will fully apply after level restart.", false); npcBunniesEditorSerialization::NdeSaveNpcFile(); return; }
if(b.action == npcBunniesEditorCore::NDE_ACT_LOAD_ANIMS) { npcBunniesEditorCore::NpcDesignerView = npcBunniesEditorCore::NDE_LOAD_ANIMS; return; }
if(b.action == npcBunniesEditorCore::NDE_ACT_LOAD_ANIM_PREV || b.action == npcBunniesEditorCore::NDE_ACT_LOAD_ANIM_NEXT) { npcBunniesEditorPreview::NdeCycleLoadAnimSelection(b.a, b.b); return; }
if(b.action == npcBunniesEditorCore::NDE_ACT_LOAD_ANIM_LOAD) { npcBunniesEditorPreview::NdeSetAdditionalSimpleAnim(npcBunniesEditorPreview::NdeSelectedLoadAnimType(b.a), true); npcBunniesEditorSerialization::NdeSaveNpcFile(); return; }
if(b.action == npcBunniesEditorCore::NDE_ACT_LOAD_ANIM_UNLOAD) { npcBunniesEditorPreview::NdeSetAdditionalSimpleAnim(npcBunniesEditorPreview::NdeSelectedLoadAnimType(b.a), false); npcBunniesEditorSerialization::NdeSaveNpcFile(); return; }
if(b.action == npcBunniesEditorCore::NDE_ACT_EXIT) { NdeFlushTextEdit(); NdeCancelGuideEntryEdit(); npcBunniesEditorCore::NpcDesignerOpen = false; npcBunniesEditorCore::NpcDesignerHelpOnly = false; npcBunniesEditorCore::NpcDesignerMovingNpc = false; return; }
if(b.action == npcBunniesEditorCore::NDE_ACT_BACK_MAIN)
{
if(npcBunniesEditorCore::NpcDesignerHelpOnly) { NdeFlushTextEdit(); NdeCancelGuideEntryEdit(); npcBunniesEditorCore::NpcDesignerOpen = false; npcBunniesEditorCore::NpcDesignerHelpOnly = false; npcBunniesEditorCore::NpcDesignerMovingNpc = false; return; }
if(b.a == 1) npcBunniesEditorCore::NpcDesignerView = npcBunniesEditorCore::NDE_EDIT;
else if(b.a == 2) { NdeCancelGuideEntryEdit(); npcBunniesEditorCore::NpcDesignerView = npcBunniesEditorCore::NDE_GUIDE; npcBunniesEditorSerialization::NdeSaveNpcFile(); }
else if(b.a == 3) npcBunniesEditorCore::NpcDesignerView = npcBunniesEditorCore::NDE_DIALOGUE;
else npcBunniesEditorCore::NpcDesignerView = npcBunniesEditorCore::NDE_MAIN;
return;
}
if(b.action == npcBunniesEditorCore::NDE_ACT_MAIN_CREATE) { npcBunniesEditorCore::NpcDesignerView = npcBunniesEditorCore::NDE_CREATE; npcBunniesEditorCore::NpcDesignerScroll = 0; npcBunniesEditorCore::NpcDesignerSelectedType = -1; npcBunniesEditorCore::NpcDesignerDeletePick = false; return; }
if(b.action == npcBunniesEditorCore::NDE_ACT_MAIN_EDIT) { npcBunniesEditorCore::NpcDesignerView = npcBunniesEditorCore::NDE_EDIT_PICK; npcBunniesEditorCore::NpcDesignerScroll = 0; npcBunniesEditorCore::NpcDesignerDeletePick = false; return; }
if(b.action == npcBunniesEditorCore::NDE_ACT_MAIN_DELETE) { npcBunniesEditorCore::NpcDesignerView = npcBunniesEditorCore::NDE_EDIT_PICK; npcBunniesEditorCore::NpcDesignerScroll = 0; npcBunniesEditorCore::NpcDesignerDeletePick = true; return; }
if(b.action == npcBunniesEditorCore::NDE_ACT_MAIN_UNDO_DELETE) { NdeUndoDelete(); return; }
if(b.action == npcBunniesEditorCore::NDE_ACT_SCROLL_UP) { npcBunniesEditorCore::NpcDesignerScroll--; return; }
if(b.action == npcBunniesEditorCore::NDE_ACT_SCROLL_DOWN) { npcBunniesEditorCore::NpcDesignerScroll++; return; }
if(b.action == npcBunniesEditorCore::NDE_ACT_CREATE_TYPE)
{
if(npcBunniesEditorCore::NpcDesignerSelectedType == b.a) return;
npcBunniesEditorCore::NpcDesignerSelectedType = b.a;
return;
}
if(b.action == npcBunniesEditorCore::NDE_ACT_PICK_EDIT_NPC)
{
npcBunniesEditorCore::NpcDesignerSelectedNpc = b.a;
if(npcBunniesEditorCore::NpcDesignerDeletePick)
{
NdeDeleteNpc(b.a);
npcBunniesEditorCore::NpcDesignerDeletePick = false;
npcBunniesEditorCore::NpcDesignerView = npcBunniesEditorCore::NDE_MAIN;
return;
}
if(npcBunniesEditorCore::NpcDesignerView == npcBunniesEditorCore::NDE_EDIT_PICK) npcBunniesEditorCore::NpcDesignerView = npcBunniesEditorCore::NDE_EDIT;
return;
}
if(b.action == npcBunniesEditorCore::NDE_ACT_FIELD)
{
if(npcBunniesEditorCore::NpcDesignerEditingField != b.a) NdeFlushTextEdit();
npcBunniesEditorCore::NpcDesignerEditingField = -1;
npcBunniesEditorCore::NpcDesignerEditingValueField = -1;
npcBunniesEditorCore::NpcDesignerTextScrollField = b.a;
npcBunniesEditorCore::NpcDesignerTextScrollLine = 0;
npcBunniesEditorCore::NpcDesignerTextScrollManual = false;
if(NdeValidNpc())
{
npcBunniesCore::npcBunny editNpc = npcBunniesCore::npcBunnies[npcBunniesEditorCore::NpcDesignerSelectedNpc];
npcBunniesEditorCore::NpcDesignerEditingValue = NdeGetFieldValue(editNpc, b.a);
npcBunniesEditorCore::NpcDesignerEditingValueField = b.a;
npcBunniesEditorCore::NpcDesignerEditingCursor = int(npcBunniesEditorCore::NpcDesignerEditingValue.length);
}
npcBunniesEditorCore::NpcDesignerEditingField = b.a;
return;
}
if(!NdeValidNpc()) return;
npcBunniesCore::npcBunny npc = npcBunniesCore::npcBunnies[npcBunniesEditorCore::NpcDesignerSelectedNpc];
if(b.action == npcBunniesEditorCore::NDE_ACT_TOGGLE_POSITION) { npcBunniesEditorCore::NpcDesignerMovingNpc = !npcBunniesEditorCore::NpcDesignerMovingNpc; return; }
if(b.action == npcBunniesEditorCore::NDE_ACT_CHAR_CATEGORY) { npcBunniesEditorPreview::NdeCycleNpcTypeCategory(npc); return; }
if(b.action == npcBunniesEditorCore::NDE_ACT_CHAR_PREV || b.action == npcBunniesEditorCore::NDE_ACT_CHAR_NEXT) { npcBunniesEditorPreview::NdeCycleNpcCharacter(npc, b.b); return; }
if(b.action == npcBunniesEditorCore::NDE_ACT_NPC_ACCESSORY) { NdeCycleNpcAccessory(npc, b.a, b.b); return; }
if(b.action == npcBunniesEditorCore::NDE_ACT_NPC_SIZE) { NdeCycleNpcSize(npc, b.b); return; }
if(b.action == npcBunniesEditorCore::NDE_ACT_NPC_SPEED) { NdeCycleNpcSpeed(npc, b.b); return; }
if(b.action == npcBunniesEditorCore::NDE_ACT_TOGGLE_PUSHABLE) { npc.pushable = !npc.pushable; npcBunniesCore::npcBunnies[npcBunniesEditorCore::NpcDesignerSelectedNpc] = npc; npcBunniesEditorSerialization::NdeSaveNpcFile(); return; }
if(b.action == npcBunniesEditorCore::NDE_ACT_TELEPORT_DIRECTION && npcBunniesEditorCore::NpcDesignerPathIndex >= 0 && npcBunniesEditorCore::NpcDesignerPathIndex < int(npc.guidePaths.length))
{
npc.guidePaths[npcBunniesEditorCore::NpcDesignerPathIndex].teleportDirection = npc.guidePaths[npcBunniesEditorCore::NpcDesignerPathIndex].teleportDirection < 0 ? 1 : -1;
npcBunniesCore::npcBunnies[npcBunniesEditorCore::NpcDesignerSelectedNpc] = npc;
npcBunniesEditorSerialization::NdeSaveNpcFile();
return;
}
if(b.action == npcBunniesEditorCore::NDE_ACT_DIRECTION)
{
npc.direction = npc.direction < 0 ? 1 : -1;
if(npc.objectID >= 0) jjObjects[npc.objectID].direction = npc.direction;
npcBunniesCore::npcBunnies[npcBunniesEditorCore::NpcDesignerSelectedNpc] = npc; npcBunniesEditorSerialization::NdeSaveNpcFile(); return;
}
if(b.action == npcBunniesEditorCore::NDE_ACT_TOGGLE_FOLLOW) { npc.moveable = !npc.moveable; npcBunniesCore::npcBunnies[npcBunniesEditorCore::NpcDesignerSelectedNpc] = npc; npcBunniesEditorSerialization::NdeSaveNpcFile(); return; }
if(b.action == npcBunniesEditorCore::NDE_ACT_TOGGLE_FIGHTING) { npc.fightingAvailable = !npc.fightingAvailable; npc.fightingEnabled = npc.fightingAvailable; npc.fightingTargetObjectID = -1; npcBunniesCore::npcBunnies[npcBunniesEditorCore::NpcDesignerSelectedNpc] = npc; npcBunniesEditorSerialization::NdeSaveNpcFile(); return; }
if(b.action == npcBunniesEditorCore::NDE_ACT_FIGHTING_STYLE) { npc.fightingStyle = npc.fightingStyle == npcBunniesCore::NpcFightingStyle::Complex ? npcBunniesCore::NpcFightingStyle::Simple : npcBunniesCore::NpcFightingStyle::Complex; npcBunniesCore::npcBunnies[npcBunniesEditorCore::NpcDesignerSelectedNpc] = npc; npcBunniesEditorSerialization::NdeSaveNpcFile(); return; }
if(b.action == npcBunniesEditorCore::NDE_ACT_EDIT_DIALOGUE) { npcBunniesEditorCore::NpcDesignerView = npcBunniesEditorCore::NDE_DIALOGUE; npcBunniesEditorCore::NpcDesignerDialogueSet = 0; return; }
if(b.action == npcBunniesEditorCore::NDE_ACT_EDIT_SHOP) { npcBunniesEditorCore::NpcDesignerView = npcBunniesEditorCore::NDE_SHOP; return; }
if(b.action == npcBunniesEditorCore::NDE_ACT_TOGGLE_GUIDE) { npc.guideEnabled = !npc.guideEnabled; npcBunniesCore::npcBunnies[npcBunniesEditorCore::NpcDesignerSelectedNpc] = npc; npcBunniesEditorSerialization::NdeSaveNpcFile(); return; }
if(b.action == npcBunniesEditorCore::NDE_ACT_EDIT_GUIDE) { npcBunniesEditorCore::NpcDesignerView = npcBunniesEditorCore::NDE_GUIDE; npcBunniesEditorCore::NpcDesignerScroll = 0; return; }
if(b.action == npcBunniesEditorCore::NDE_ACT_NPC_TRACE_MODE)
{
npc.traceMode++;
if(npc.traceMode > npcBunniesCore::NPC_TRACE_CONST) npc.traceMode = npcBunniesCore::NPC_TRACE_OFF;
npc.traceMode = npcBunniesCore::ClampNpcTraceMode(npc.traceMode);
if(npc.traceMode == npcBunniesCore::NPC_TRACE_OFF) npcBunniesMovement::ClearNpcTraceFrames(npc.id);
npcBunniesCore::npcBunnies[npcBunniesEditorCore::NpcDesignerSelectedNpc] = npc;
npcBunniesEditorSerialization::NdeSaveNpcFile();
return;
}
if(b.action == npcBunniesEditorCore::NDE_ACT_NPC_TRACE_COLOR)
{
int index = npcBunniesCore::NPC_TRACE_COLOR_VALUES.find(npc.traceColor);
if(index < 0) index = 0;
else index = (index + 1) % int(npcBunniesCore::NPC_TRACE_COLOR_VALUES.length);
npc.traceColor = npcBunniesCore::NPC_TRACE_COLOR_VALUES[index];
npcBunniesCore::npcBunnies[npcBunniesEditorCore::NpcDesignerSelectedNpc] = npc;
npcBunniesEditorSerialization::NdeSaveNpcFile();
return;
}
if(b.action == npcBunniesEditorCore::NDE_ACT_PATH_EDIT_START)
{
NdeEndTextEdit();
if(NdeValidNpc() && npcBunniesEditorCore::NpcDesignerPathIndex >= 0 && npcBunniesEditorCore::NpcDesignerPathIndex < int(npcBunniesCore::npcBunnies[npcBunniesEditorCore::NpcDesignerSelectedNpc].guidePaths.length) && npcBunniesCore::npcBunnies[npcBunniesEditorCore::NpcDesignerSelectedNpc].guidePaths[npcBunniesEditorCore::NpcDesignerPathIndex].isTeleport)
NdeStartTeleportEdit(false);
else
{
npcBunniesEditorCore::NpcDesignerRecordingPath = true;
int tx, ty;
NdeGuidePathEnd(npcBunniesCore::npcBunnies[npcBunniesEditorCore::NpcDesignerSelectedNpc], npcBunniesEditorCore::NpcDesignerPathIndex, tx, ty);
NdeCenterEditorCameraOnTile(tx, ty);
}
return;
}
if(b.action == npcBunniesEditorCore::NDE_ACT_PATH_EDIT_STOP) { NdeCancelGuideEntryEdit(); npcBunniesEditorSerialization::NdeSaveNpcFile(); return; }
NdeActivateDialogueShopGuide(b, npc);
}
/// <summary>
/// Dispatches button actions shared by dialogue, shop, guide, quest, and reward views.
/// </summary>
void NdeActivateDialogueShopGuide(npcBunniesEditorCore::NpcDesignerButton@ b, npcBunniesCore::npcBunny npc)
{
if(b.action == npcBunniesEditorCore::NDE_ACT_DIALOGUE_PREV) { npcBunniesEditorCore::NpcDesignerDialogueSet--; if(npcBunniesEditorCore::NpcDesignerDialogueSet < 0) npcBunniesEditorCore::NpcDesignerDialogueSet = int(npc.dialogueSets.length) - 1; return; }
if(b.action == npcBunniesEditorCore::NDE_ACT_DIALOGUE_NEXT) { npcBunniesEditorCore::NpcDesignerDialogueSet++; if(npcBunniesEditorCore::NpcDesignerDialogueSet >= int(npc.dialogueSets.length)) npcBunniesEditorCore::NpcDesignerDialogueSet = 0; return; }
if(b.action == npcBunniesEditorCore::NDE_ACT_DIALOGUE_ADD)
{
npc.dialogueSets.insertLast(@npcBunniesDialogue::DialogueSet(array<npcBunniesDialogue::DialogueLine@>(), array<npcBunniesDialogue::Choice@>(), npcBunniesCore::DialogueAction::Normal));
npcBunniesEditorCore::NpcDesignerDialogueSet = int(npc.dialogueSets.length) - 1;
npcBunniesCore::npcBunnies[npcBunniesEditorCore::NpcDesignerSelectedNpc] = npc; npcBunniesEditorSerialization::NdeSaveNpcFile(); return;
}
if(b.action == npcBunniesEditorCore::NDE_ACT_DIALOGUE_DELETE && npc.dialogueSets.length > 1 && npcBunniesEditorCore::NpcDesignerDialogueSet >= 0 && npcBunniesEditorCore::NpcDesignerDialogueSet < int(npc.dialogueSets.length))
{
npc.dialogueSets.removeAt(npcBunniesEditorCore::NpcDesignerDialogueSet);
if(npcBunniesEditorCore::NpcDesignerDialogueSet >= int(npc.dialogueSets.length)) npcBunniesEditorCore::NpcDesignerDialogueSet = int(npc.dialogueSets.length) - 1;
npcBunniesCore::npcBunnies[npcBunniesEditorCore::NpcDesignerSelectedNpc] = npc; npcBunniesEditorSerialization::NdeSaveNpcFile(); return;
}
if(npcBunniesEditorCore::NpcDesignerDialogueSet >= 0 && npcBunniesEditorCore::NpcDesignerDialogueSet < int(npc.dialogueSets.length))
{
npcBunniesDialogue::DialogueSet@ set = npc.dialogueSets[npcBunniesEditorCore::NpcDesignerDialogueSet];
if(b.action == npcBunniesEditorCore::NDE_ACT_DIALOGUE_TYPE)
{
set.choicesAnswerMethod++;
if(set.choicesAnswerMethod > npcBunniesCore::DialogueAction::Quest) set.choicesAnswerMethod = npcBunniesCore::DialogueAction::Normal;
NdeEnsureDialogueShape(set); npcBunniesCore::npcBunnies[npcBunniesEditorCore::NpcDesignerSelectedNpc] = npc; npcBunniesEditorSerialization::NdeSaveNpcFile(); return;
}
if(b.action == npcBunniesEditorCore::NDE_ACT_DIALOGUE_SPEAKER)
{
NdeCycleDialogueSpeaker(set, b.b);
npcBunniesCore::npcBunnies[npcBunniesEditorCore::NpcDesignerSelectedNpc] = npc; npcBunniesEditorSerialization::NdeSaveNpcFile(); return;
}
if(b.action == npcBunniesEditorCore::NDE_ACT_DIALOGUE_AUTO_CONTINUE)
{
NdeCycleDialogueAutoContinue(npc, set, b.b);
npcBunniesCore::npcBunnies[npcBunniesEditorCore::NpcDesignerSelectedNpc] = npc; npcBunniesEditorSerialization::NdeSaveNpcFile(); return;
}
if(b.action == npcBunniesEditorCore::NDE_ACT_DIALOGUE_GOTO)
{
NdeCycleDialogueGoTo(npc, set, b.b);
npcBunniesCore::npcBunnies[npcBunniesEditorCore::NpcDesignerSelectedNpc] = npc; npcBunniesEditorSerialization::NdeSaveNpcFile(); return;
}
if(b.action == npcBunniesEditorCore::NDE_ACT_QUEST_RESPAWN_MONSTERS)
{
set.respawnMonsters = !set.respawnMonsters;
npcBunniesCore::npcBunnies[npcBunniesEditorCore::NpcDesignerSelectedNpc] = npc; npcBunniesEditorSerialization::NdeSaveNpcFile(); return;
}
if(b.action == npcBunniesEditorCore::NDE_ACT_DIALOGUE_FRAME_PREV || b.action == npcBunniesEditorCore::NDE_ACT_DIALOGUE_FRAME_NEXT)
{
npcBunniesCore::npcBunny speakerNpc = NdeDialogueSpeakerNpc(npc, set);
npcBunniesEditorPreview::NdeCycleDialogueBigFrame(speakerNpc, set, b.b);
npcBunniesCore::npcBunnies[npcBunniesEditorCore::NpcDesignerSelectedNpc] = npc; npcBunniesEditorSerialization::NdeSaveNpcFile(); return;
}
if(b.action == npcBunniesEditorCore::NDE_ACT_CHOICE_GOTO)
{
NdeEnsureChoice(set, b.a);
set.choices[b.a].goToSet++;
if(set.choices[b.a].goToSet >= int(npc.dialogueSets.length)) set.choices[b.a].goToSet = -1;
npcBunniesCore::npcBunnies[npcBunniesEditorCore::NpcDesignerSelectedNpc] = npc; npcBunniesEditorSerialization::NdeSaveNpcFile(); return;
}
if(b.action == npcBunniesEditorCore::NDE_ACT_EDIT_QUEST) { npcBunniesEditorCore::NpcDesignerView = npcBunniesEditorCore::NDE_QUEST; return; }
if(b.action == npcBunniesEditorCore::NDE_ACT_EDIT_REWARD) { npcBunniesEditorCore::NpcDesignerView = npcBunniesEditorCore::NDE_REWARD; return; }
}
if(b.action == npcBunniesEditorCore::NDE_ACT_SHOP_ADD) { npc.shopItems.insertLast(NdeNewShopItem()); npcBunniesCore::npcBunnies[npcBunniesEditorCore::NpcDesignerSelectedNpc] = npc; npcBunniesEditorSerialization::NdeSaveNpcFile(); return; }
if(b.action == npcBunniesEditorCore::NDE_ACT_SHOP_REWARD_PREV || b.action == npcBunniesEditorCore::NDE_ACT_SHOP_REWARD_NEXT) { NdeCycleShopReward(npc, b.a, b.b); return; }
if(b.action == npcBunniesEditorCore::NDE_ACT_SHOP_AMOUNT) { NdeChangeShopAmount(npc, b.a, b.b); return; }
if(b.action == npcBunniesEditorCore::NDE_ACT_SHOP_COINS) { NdeChangeShopCost(npc, b.a, true, b.b); return; }
if(b.action == npcBunniesEditorCore::NDE_ACT_SHOP_GEMS) { NdeChangeShopCost(npc, b.a, false, b.b); return; }
if(b.action == npcBunniesEditorCore::NDE_ACT_SHOP_STOCK) { NdeChangeShopStock(npc, b.a, b.b); return; }
if(b.action == npcBunniesEditorCore::NDE_ACT_SHOP_DELETE && b.a >= 0 && b.a < int(npc.shopItems.length)) { npc.shopItems.removeAt(b.a); npcBunniesCore::npcBunnies[npcBunniesEditorCore::NpcDesignerSelectedNpc] = npc; npcBunniesEditorSerialization::NdeSaveNpcFile(); return; }
if(b.action == npcBunniesEditorCore::NDE_ACT_GUIDE_ADD) { npcBunniesCore::GuidePath@ path = @npcBunniesCore::GuidePath(); npc.guidePaths.insertLast(path); npc.guideEnabled = true; npcBunniesCore::npcBunnies[npcBunniesEditorCore::NpcDesignerSelectedNpc] = npc; npcBunniesEditorSerialization::NdeSaveNpcFile(); return; }
if(b.action == npcBunniesEditorCore::NDE_ACT_GUIDE_ADD_TELEPORT) { NdeAddGuideTeleport(npc); return; }
if(b.action == npcBunniesEditorCore::NDE_ACT_GUIDE_EDIT && b.a >= 0 && b.a < int(npc.guidePaths.length)) { npcBunniesEditorCore::NpcDesignerPathIndex = b.a; npcBunniesEditorCore::NpcDesignerView = npcBunniesEditorCore::NDE_PATH_RECORD; npcBunniesEditorCore::NpcDesignerRecordingPath = false; npcBunniesEditorCore::NpcDesignerEditingTeleport = false; return; }
if(b.action == npcBunniesEditorCore::NDE_ACT_GUIDE_DELETE && b.a >= 0 && b.a < int(npc.guidePaths.length)) { NdeRemoveGuidePathRewardReferences(npc, b.a); npc.guidePaths.removeAt(b.a); npcBunniesCore::npcBunnies[npcBunniesEditorCore::NpcDesignerSelectedNpc] = npc; npcBunniesEditorSerialization::NdeSaveNpcFile(); return; }
if(b.action == npcBunniesEditorCore::NDE_ACT_EDIT_PATH_REWARD && b.a >= 0 && b.a < int(npc.dialogueSets.length)) { NdeCancelGuideEntryEdit(); npcBunniesEditorCore::NpcDesignerDialogueSet = b.a; npcBunniesEditorCore::NpcDesignerView = npcBunniesEditorCore::NDE_REWARD; npcBunniesEditorCore::NpcDesignerScroll = npcBunniesEditorCore::NdeMax(0, b.b); return; }
if(b.action == npcBunniesEditorCore::NDE_ACT_QUEST_ADD) { NdeCurrentSet().quests.insertLast(@npcBunniesQuests::NpcQuest()); npcBunniesEditorSerialization::NdeSaveNpcFile(); return; }
if(b.action == npcBunniesEditorCore::NDE_ACT_REWARD_ADD) { NdeCurrentSet().rewards.insertLast(@npcBunniesQuests::NpcReward()); npcBunniesEditorSerialization::NdeSaveNpcFile(); return; }
if(b.action == npcBunniesEditorCore::NDE_ACT_QUEST_TYPE) { NdeCycleQuest(b.a, b.b); return; }
if(b.action == npcBunniesEditorCore::NDE_ACT_REWARD_TYPE) { NdeCycleReward(b.a, b.b); return; }
if(b.action == npcBunniesEditorCore::NDE_ACT_QUEST_AMOUNT && b.a >= 0 && b.a < int(NdeCurrentSet().quests.length)) { NdeCurrentSet().quests[b.a].amount = npcBunniesEditorCore::NdeMax(0, NdeCurrentSet().quests[b.a].amount + (b.b < 0 ? -1 : 1)); npcBunniesEditorSerialization::NdeSaveNpcFile(); return; }
if(b.action == npcBunniesEditorCore::NDE_ACT_REWARD_AMOUNT) { NdeChangeRewardAmount(b.a, b.b); return; }
if(b.action == npcBunniesEditorCore::NDE_ACT_QUEST_DELETE && b.a >= 0 && b.a < int(NdeCurrentSet().quests.length)) { NdeCurrentSet().quests.removeAt(b.a); npcBunniesEditorSerialization::NdeSaveNpcFile(); return; }
if(b.action == npcBunniesEditorCore::NDE_ACT_REWARD_DELETE && b.a >= 0 && b.a < int(NdeCurrentSet().rewards.length)) { NdeCurrentSet().rewards.removeAt(b.a); npcBunniesEditorSerialization::NdeSaveNpcFile(); return; }
}
/// <summary>
/// Global editor state for valid npc.
/// </summary>
bool NdeValidNpc()
{
return npcBunniesEditorCore::NpcDesignerSelectedNpc >= 0 && npcBunniesEditorCore::NpcDesignerSelectedNpc < int(npcBunniesCore::npcBunnies.length);
}
/// <summary>
/// Returns the currently selected dialogue set.
/// </summary>
npcBunniesDialogue::DialogueSet@ NdeCurrentSet()
{
return npcBunniesCore::npcBunnies[npcBunniesEditorCore::NpcDesignerSelectedNpc].dialogueSets[npcBunniesEditorCore::NpcDesignerDialogueSet];
}
/// <summary>
/// Ensures required nested dialogue data exists before editing it.
/// </summary>
void NdeEnsureDialogueShape(npcBunniesDialogue::DialogueSet@ set)
{
if(set.dialogue.length == 0) set.dialogue.insertLast(@npcBunniesDialogue::DialogueLine(0, 0, ""));
if(set.choicesAnswerMethod == npcBunniesCore::DialogueAction::Answer) NdeEnsureChoice(set, 0);
if(set.choicesAnswerMethod == npcBunniesCore::DialogueAction::Choice) for(int i = 0; i < 4; i++) NdeEnsureChoice(set, i);
}
/// <summary>
/// Ensures required nested dialogue data exists before editing it.
/// </summary>
void NdeEnsureChoice(npcBunniesDialogue::DialogueSet@ set, int index)
{
while(int(set.choices.length) <= index) set.choices.insertLast(@npcBunniesDialogue::Choice("", -1));
}
/// <summary>
/// Applies keyboard input to the active editor text field.
/// </summary>
bool NdeEditingFieldIsNumeric()
{
return (npcBunniesEditorCore::NpcDesignerEditingField >= 1 && npcBunniesEditorCore::NpcDesignerEditingField <= 6)
|| NdeAccessoryCategoryFromField(npcBunniesEditorCore::NpcDesignerEditingField) >= 0;
}
bool NdeEditingFieldIsDecimal()
{
return npcBunniesEditorCore::NpcDesignerEditingField == 5 || npcBunniesEditorCore::NpcDesignerEditingField == 6;
}
bool NdeEditingFieldAllowsNewline()
{
return npcBunniesEditorCore::NpcDesignerEditingField == 10
|| npcBunniesEditorCore::NpcDesignerEditingField == 20
|| (npcBunniesEditorCore::NpcDesignerEditingField >= 30 && npcBunniesEditorCore::NpcDesignerEditingField < 40)
|| (npcBunniesEditorCore::NpcDesignerEditingField >= npcBunniesEditorCore::NDE_FIELD_GUIDE_PATH_BASE && npcBunniesEditorCore::NpcDesignerEditingField < npcBunniesEditorCore::NDE_FIELD_GUIDE_END_LINE)
|| npcBunniesEditorCore::NpcDesignerEditingField == npcBunniesEditorCore::NDE_FIELD_GUIDE_END_LINE
|| npcBunniesEditorCore::NpcDesignerEditingField == npcBunniesEditorCore::NDE_FIELD_GUIDE_ALREADY_LINE
|| npcBunniesEditorCore::NpcDesignerEditingField == npcBunniesEditorCore::NDE_FIELD_QUEST_IN_PROGRESS_LINE
|| npcBunniesEditorCore::NpcDesignerEditingField == npcBunniesEditorCore::NDE_FIELD_QUEST_FINISHED_LINE;
}
bool NdeShiftKeyDown()
{
return jjKey[16] || jjKey[160] || jjKey[161];
}
void NdeSaveActiveFieldValue(npcBunniesCore::npcBunny npc, const string &in value)
{
npcBunniesEditorCore::NpcDesignerEditingValue = value;
npcBunniesEditorCore::NpcDesignerEditingValueField = npcBunniesEditorCore::NpcDesignerEditingField;
npcBunniesEditorCore::NpcDesignerEditingCursor = DbClampTextCursor(value, npcBunniesEditorCore::NpcDesignerEditingCursor);
npcBunniesEditorViews::NdeKeepActiveTextFieldCursorViewport(value, npcBunniesEditorViews::NdeActiveTextFieldInnerWidth(npcBunniesEditorCore::NpcDesignerEditingField), npcBunniesEditorViews::NdeActiveTextFieldInnerHeight(npcBunniesEditorCore::NpcDesignerEditingField));
if(NdeEditingFieldIsDecimal() && value.length > 0 && value[value.length - 1] == 46) return;
NdeSetFieldValue(npc, npcBunniesEditorCore::NpcDesignerEditingField, value);
npcBunniesCore::npcBunnies[npcBunniesEditorCore::NpcDesignerSelectedNpc] = npc;
npcBunniesEditorCore::NpcDesignerTextDirty = true;
npcBunniesEditorCore::NpcDesignerTextDirtyTick = jjGameTicks;
}
void NdeFlushTextEdit()
{
if(!npcBunniesEditorCore::NpcDesignerTextDirty) return;
npcBunniesEditorCore::NpcDesignerTextDirty = false;
npcBunniesEditorSerialization::NdeSaveNpcFile();
}
void NdeEndTextEdit()
{
NdeFlushTextEdit();
npcBunniesEditorCore::NpcDesignerEditingField = -1;
npcBunniesEditorCore::NpcDesignerEditingValueField = -1;
npcBunniesEditorCore::NpcDesignerEditingCursor = 0;
}
void NdeInsertIntoActiveField(const string &in insert)
{
if(!NdeValidNpc() || npcBunniesEditorCore::NpcDesignerEditingField < 0 || insert == "") return;
if(insert == "\n" && (NdeEditingFieldIsNumeric() || !NdeEditingFieldAllowsNewline())) return;
npcBunniesCore::npcBunny npc = npcBunniesCore::npcBunnies[npcBunniesEditorCore::NpcDesignerSelectedNpc];
string value = NdeGetFieldValue(npc, npcBunniesEditorCore::NpcDesignerEditingField);
npcBunniesEditorCore::NpcDesignerEditingCursor = DbClampTextCursor(value, npcBunniesEditorCore::NpcDesignerEditingCursor);
value = DbInsertTextAtCursor(value, npcBunniesEditorCore::NpcDesignerEditingCursor, insert);
npcBunniesEditorCore::NpcDesignerEditingCursor += int(insert.length);
NdeSaveActiveFieldValue(npc, value);
}
void NdeMoveEditingCursor(int delta)
{
if(!NdeValidNpc() || npcBunniesEditorCore::NpcDesignerEditingField < 0) return;
npcBunniesCore::npcBunny npc = npcBunniesCore::npcBunnies[npcBunniesEditorCore::NpcDesignerSelectedNpc];
string value = NdeGetFieldValue(npc, npcBunniesEditorCore::NpcDesignerEditingField);
npcBunniesEditorCore::NpcDesignerEditingCursor = DbMoveTextCursor(value, npcBunniesEditorCore::NpcDesignerEditingCursor, delta);
npcBunniesEditorViews::NdeKeepActiveTextFieldCursorViewport(value, npcBunniesEditorViews::NdeActiveTextFieldInnerWidth(npcBunniesEditorCore::NpcDesignerEditingField), npcBunniesEditorViews::NdeActiveTextFieldInnerHeight(npcBunniesEditorCore::NpcDesignerEditingField));
}
void NdeMoveEditingCursorVertical(int delta)
{
if(!NdeValidNpc() || npcBunniesEditorCore::NpcDesignerEditingField < 0) return;
npcBunniesCore::npcBunny npc = npcBunniesCore::npcBunnies[npcBunniesEditorCore::NpcDesignerSelectedNpc];
string value = NdeGetFieldValue(npc, npcBunniesEditorCore::NpcDesignerEditingField);
npcBunniesEditorCore::NpcDesignerEditingCursor = DbMoveTextCursorVertical(value, npcBunniesEditorViews::NdeActiveTextFieldInnerWidth(npcBunniesEditorCore::NpcDesignerEditingField), npcBunniesEditorCore::NpcDesignerEditingCursor, delta);
npcBunniesEditorViews::NdeKeepActiveTextFieldCursorViewport(value, npcBunniesEditorViews::NdeActiveTextFieldInnerWidth(npcBunniesEditorCore::NpcDesignerEditingField), npcBunniesEditorViews::NdeActiveTextFieldInnerHeight(npcBunniesEditorCore::NpcDesignerEditingField));
}
void NdeTypeIntoField()
{
if(!NdeValidNpc()) return;
if(jjKey[20])
{
if(!npcBunniesEditorCore::NpcDesignerCapsKeyDown) npcBunniesEditorCore::NpcDesignerCapsLock = !npcBunniesEditorCore::NpcDesignerCapsLock;
npcBunniesEditorCore::NpcDesignerCapsKeyDown = true;
}
else npcBunniesEditorCore::NpcDesignerCapsKeyDown = false;
int key = -1;
if((npcBunniesEditorCore::NpcDesignerEditingField == 5 || npcBunniesEditorCore::NpcDesignerEditingField == 6) && (jjKey[0xBE])) key = 46;
if(key < 0 && jjKey[32]) key = 32;
for(int i = 48; key < 0 && i <= 57; i++)
if(jjKey[i]) key = i;
for(int i = 65; key < 0 && i <= 90; i++)
if(jjKey[i]) key = i;
if(NdeFocusedButtonAction() != npcBunniesEditorCore::NDE_ACT_FIELD && key == 32) key = -1;
if(key < 0)
{
if(jjKey[186]) key = 186;
else if(jjKey[187]) key = 187;
else if(jjKey[189]) key = 189;
else if(jjKey[192]) key = 192;
else if(jjKey[219]) key = 219;
else if(jjKey[220]) key = 220;
else if(jjKey[221]) key = 221;
else if(jjKey[222]) key = 222;
else if(jjKey[191]) key = 191;
else if(jjKey[188]) key = 188;
else if(jjKey[190]) key = 190;
else if(jjKey[110]) key = 110;
}
if(key < 0 && jjKey[8]) key = 8;
if(key < 0)
{
npcBunniesEditorCore::NpcDesignerLastTextKey = -1;
return;
}
if(key == npcBunniesEditorCore::NpcDesignerLastTextKey && (key != 8 || jjGameTicks - npcBunniesEditorCore::NpcDesignerLastTextTick < 18)) return;
if(jjGameTicks - npcBunniesEditorCore::NpcDesignerLastTextTick < 8) return;
npcBunniesEditorCore::NpcDesignerLastTextKey = key;
npcBunniesEditorCore::NpcDesignerLastTextTick = jjGameTicks;
npcBunniesCore::npcBunny npc = npcBunniesCore::npcBunnies[npcBunniesEditorCore::NpcDesignerSelectedNpc];
string value = NdeGetFieldValue(npc, npcBunniesEditorCore::NpcDesignerEditingField);
npcBunniesEditorCore::NpcDesignerEditingCursor = DbClampTextCursor(value, npcBunniesEditorCore::NpcDesignerEditingCursor);
if(key == 8)
{
if(npcBunniesEditorCore::NpcDesignerEditingCursor > 0)
{
value = DbBackspaceTextAtCursor(value, npcBunniesEditorCore::NpcDesignerEditingCursor);
npcBunniesEditorCore::NpcDesignerEditingCursor--;
}
}
else
{
bool numeric = NdeEditingFieldIsNumeric();
bool decimalField = NdeEditingFieldIsDecimal();
string special = NdeSpecialCharacterFromKey(key);
bool decimalPoint = decimalField && value.findFirst(".") < 0 && (special == "." || key == 46 || key == 58 || key == 110 || key == 190);
int dotAt = value.findFirst(".");
bool decimalLimitReached = decimalField && dotAt >= 0 && key >= 48 && key <= 57 && npcBunniesEditorCore::NpcDesignerEditingCursor > dotAt && int(value.length) - dotAt - 1 >= 3;
if(!decimalLimitReached && (!numeric || (key >= 48 && key <= 57) || decimalPoint))
{
string insert = " ";
if(!numeric && special != "") insert[0] = special[0];
else if(key >= 48 && key <= 57) insert[0] += key - 32;
else if(decimalPoint) insert[0] = 46;
else if(key >= 65 && key <= 90)
{
bool uppercase = npcBunniesEditorCore::NpcDesignerCapsLock || NdeShiftKeyDown();
insert[0] += uppercase ? key - 32 : key;
}
else insert[0] = 32;
value = DbInsertTextAtCursor(value, npcBunniesEditorCore::NpcDesignerEditingCursor, insert);
npcBunniesEditorCore::NpcDesignerEditingCursor += int(insert.length);
}
}
NdeSaveActiveFieldValue(npc, value);
}
/// <summary>
/// Maps supported keyboard keys to allowed special characters for text fields.
/// </summary>
string NdeSpecialCharacterFromKey(int key)
{
string value = "";
bool shifted = NdeShiftKeyDown();
if(key == 49 && shifted) value = "!";
else if(key == 50 && shifted) value = "@";
else if(key == 51 && shifted) value = "#";
else if(key == 52 && shifted) value = "$";
else if(key == 53 && shifted) value = "%";
else if(key == 54 && shifted) value = "^";
else if(key == 55 && shifted) value = "&";
else if(key == 56 && shifted) value = "*";
else if(key == 57 && shifted) value = "(";
else if(key == 48 && shifted) value = ")";
else if(key == 186) value = shifted ? ":" : ";";
else if(key == 187) value = shifted ? "+" : "=";
else if(key == 189) value = shifted ? "_" : "-";
else if(key == 191) value = shifted ? "?" : "/";
else if(key == 188) value = shifted ? "<" : ",";
else if(key == 190 || key == 110 || key == 58 || key == 46) value = shifted && key == 190 ? ">" : ".";
else if(key == 219) value = shifted ? "{" : "[";
else if(key == 221) value = shifted ? "}" : "]";
else if(key == 220) value = shifted ? "|" : "\\";
else if(key == 222) value = shifted ? "\"" : "'";
else if(key == 192) value = shifted ? "~" : "`";
if(value != "" && npcBunniesEditorCore::ALLOWED_SPECIAL_CHARACTERS.findFirst(value) >= 0) return value;
return "";
}
/// <summary>
/// Reads the current value for an editor text field identifier.
/// </summary>
string NdeGetFieldValue(npcBunniesCore::npcBunny &inout npc, int field)
{
if(field == npcBunniesEditorCore::NpcDesignerEditingField && field == npcBunniesEditorCore::NpcDesignerEditingValueField) return npcBunniesEditorCore::NpcDesignerEditingValue;
if(field == 0) return npc.name;
if(field == 1) return "" + npc.fur.c1;
if(field == 2) return "" + npc.fur.c2;
if(field == 3) return "" + npc.fur.c3;
if(field == 4) return "" + npc.fur.c4;
if(field == 5) return "" + npc.xTile;
if(field == 6) return "" + npc.yTile;
if(field == 10 && npcBunniesEditorCore::NpcDesignerDialogueSet < int(npc.dialogueSets.length))
{
NdeEnsureDialogueShape(npc.dialogueSets[npcBunniesEditorCore::NpcDesignerDialogueSet]);
return npc.dialogueSets[npcBunniesEditorCore::NpcDesignerDialogueSet].dialogue[0].text;
}
if(field >= 20 && field < 40 && npcBunniesEditorCore::NpcDesignerDialogueSet < int(npc.dialogueSets.length))
{
int index = field == 20 ? 0 : field - 30;
NdeEnsureChoice(npc.dialogueSets[npcBunniesEditorCore::NpcDesignerDialogueSet], index);
return npc.dialogueSets[npcBunniesEditorCore::NpcDesignerDialogueSet].choices[index].choiceText;
}
if(field >= npcBunniesEditorCore::NDE_FIELD_GUIDE_PATH_BASE && field < npcBunniesEditorCore::NDE_FIELD_GUIDE_END_LINE)
{
int index = field - npcBunniesEditorCore::NDE_FIELD_GUIDE_PATH_BASE;
if(index >= 0 && index < int(npc.guidePaths.length)) return npc.guidePaths[index].line;
}
if(field == npcBunniesEditorCore::NDE_FIELD_GUIDE_END_LINE) return npc.guideEndLine;
if(field == npcBunniesEditorCore::NDE_FIELD_GUIDE_ALREADY_LINE) return npc.guideAlreadyAtDestinationLine;
if(field == npcBunniesEditorCore::NDE_FIELD_QUEST_IN_PROGRESS_LINE && npcBunniesEditorCore::NpcDesignerDialogueSet < int(npc.dialogueSets.length)) return npc.dialogueSets[npcBunniesEditorCore::NpcDesignerDialogueSet].questInProgressLine;
if(field == npcBunniesEditorCore::NDE_FIELD_QUEST_FINISHED_LINE && npcBunniesEditorCore::NpcDesignerDialogueSet < int(npc.dialogueSets.length)) return npc.dialogueSets[npcBunniesEditorCore::NpcDesignerDialogueSet].questCompletedLine;
if(field == npcBunniesEditorCore::NDE_FIELD_NPC_TRACE_COLOR) return "" + npc.traceColor;
int accessoryCategory = NdeAccessoryCategoryFromField(field);
if(accessoryCategory >= 0)
{
int frame = NdeNpcAccessoryFrame(npc, accessoryCategory);
return frame < 0 ? "" : "" + frame;
}
return "";
}
/// <summary>
/// Writes a new value for an editor text field identifier.
/// </summary>
void NdeSetFieldValue(npcBunniesCore::npcBunny &inout npc, int field, const string &in value)
{
if(field == 0) npc.name = value;
else if(field == 1) npc.fur.c1 = parseInt(value);
else if(field == 2) npc.fur.c2 = parseInt(value);
else if(field == 3) npc.fur.c3 = parseInt(value);
else if(field == 4) npc.fur.c4 = parseInt(value);
else if(field == 5) { npc.xTile = npcBunniesEditorCore::NdeRoundTileFloat(npcBunniesEditorCore::NdeClampFloat(parseFloat(value), 0, jjLayerWidth[4] - 1)); NdeApplyNpcTilePosition(npc); }
else if(field == 6) { npc.yTile = npcBunniesEditorCore::NdeRoundTileFloat(npcBunniesEditorCore::NdeClampFloat(parseFloat(value), 0, jjLayerHeight[4] - 1)); NdeApplyNpcTilePosition(npc); }
else if(field == 10 && npcBunniesEditorCore::NpcDesignerDialogueSet < int(npc.dialogueSets.length))
{
NdeEnsureDialogueShape(npc.dialogueSets[npcBunniesEditorCore::NpcDesignerDialogueSet]);
npc.dialogueSets[npcBunniesEditorCore::NpcDesignerDialogueSet].dialogue[0].text = value;
}
else if(field >= 20 && field < 40 && npcBunniesEditorCore::NpcDesignerDialogueSet < int(npc.dialogueSets.length))
{
int index = field == 20 ? 0 : field - 30;
NdeEnsureChoice(npc.dialogueSets[npcBunniesEditorCore::NpcDesignerDialogueSet], index);
npc.dialogueSets[npcBunniesEditorCore::NpcDesignerDialogueSet].choices[index].choiceText = value;
}
else if(field >= npcBunniesEditorCore::NDE_FIELD_GUIDE_PATH_BASE && field < npcBunniesEditorCore::NDE_FIELD_GUIDE_END_LINE)
{
int index = field - npcBunniesEditorCore::NDE_FIELD_GUIDE_PATH_BASE;
if(index >= 0 && index < int(npc.guidePaths.length)) npc.guidePaths[index].line = value;
}
else if(field == npcBunniesEditorCore::NDE_FIELD_GUIDE_END_LINE) npc.guideEndLine = value;
else if(field == npcBunniesEditorCore::NDE_FIELD_GUIDE_ALREADY_LINE) npc.guideAlreadyAtDestinationLine = value;
else if(field == npcBunniesEditorCore::NDE_FIELD_QUEST_IN_PROGRESS_LINE && npcBunniesEditorCore::NpcDesignerDialogueSet < int(npc.dialogueSets.length)) npc.dialogueSets[npcBunniesEditorCore::NpcDesignerDialogueSet].questInProgressLine = value;
else if(field == npcBunniesEditorCore::NDE_FIELD_QUEST_FINISHED_LINE && npcBunniesEditorCore::NpcDesignerDialogueSet < int(npc.dialogueSets.length)) npc.dialogueSets[npcBunniesEditorCore::NpcDesignerDialogueSet].questCompletedLine = value;
else if(field == npcBunniesEditorCore::NDE_FIELD_NPC_TRACE_COLOR) npc.traceColor = npcBunniesCore::ClampNpcTraceColor(parseInt(value));
else
{
int accessoryCategory = NdeAccessoryCategoryFromField(field);
if(accessoryCategory >= 0) NdeSetNpcAccessoryFrame(npc, accessoryCategory, value == "" ? -1 : parseInt(value));
}
}
int NdeAccessoryCategoryFromField(int field)
{
if(field == npcBunniesEditorCore::NDE_FIELD_NPC_HAT) return npcBunniesAccessories::NpcAccessories::CATEGORY_HAT;
if(field == npcBunniesEditorCore::NDE_FIELD_NPC_SCARF) return npcBunniesAccessories::NpcAccessories::CATEGORY_SCARF;
if(field == npcBunniesEditorCore::NDE_FIELD_NPC_GLASSES) return npcBunniesAccessories::NpcAccessories::CATEGORY_GLASSES;
return -1;
}
int NdeNpcAccessoryFrame(npcBunniesCore::npcBunny &in npc, int category)
{
if(category == npcBunniesAccessories::NpcAccessories::CATEGORY_HAT) return npc.hatFrame;
if(category == npcBunniesAccessories::NpcAccessories::CATEGORY_SCARF) return npc.scarfFrame;
if(category == npcBunniesAccessories::NpcAccessories::CATEGORY_GLASSES) return npc.glassesFrame;
return -1;
}
void NdeSetNpcAccessoryFrame(npcBunniesCore::npcBunny &inout npc, int category, int frame)
{
if(category == npcBunniesAccessories::NpcAccessories::CATEGORY_HAT && !npcBunniesAccessories::NpcAccessories::CanNpcUseHat(npc.type))
{
npc.hatFrame = -1;
return;
}
int count = npcBunniesAccessories::NpcAccessories::FrameCount(category);
if(count <= 0) frame = -1;
else if(frame < 0) frame = -1;
else if(frame >= count) frame = count - 1;
if(category == npcBunniesAccessories::NpcAccessories::CATEGORY_HAT) npc.hatFrame = frame;
else if(category == npcBunniesAccessories::NpcAccessories::CATEGORY_SCARF) npc.scarfFrame = frame;
else if(category == npcBunniesAccessories::NpcAccessories::CATEGORY_GLASSES) npc.glassesFrame = frame;
}
void NdeCycleNpcAccessory(npcBunniesCore::npcBunny npc, int category, int delta)
{
if(category == npcBunniesAccessories::NpcAccessories::CATEGORY_HAT && !npcBunniesAccessories::NpcAccessories::CanNpcUseHat(npc.type)) return;
int count = npcBunniesAccessories::NpcAccessories::FrameCount(category);
if(count <= 0) return;
int frame = NdeNpcAccessoryFrame(npc, category) + delta;
if(frame < -1) frame = count - 1;
if(frame >= count) frame = -1;
NdeSetNpcAccessoryFrame(npc, category, frame);
npcBunniesCore::npcBunnies[npcBunniesEditorCore::NpcDesignerSelectedNpc] = npc;
npcBunniesEditorSerialization::NdeSaveNpcFile();
}
string NdeNpcSizeName(int sizeMode)
{
if(sizeMode <= 0) return "Tiny";
if(sizeMode >= 2) return "Giant";
return "Normal";
}
void NdeCycleNpcSize(npcBunniesCore::npcBunny npc, int delta)
{
npc.sizeMode += delta;
if(npc.sizeMode < 0) npc.sizeMode = 2;
if(npc.sizeMode > 2) npc.sizeMode = 0;
npcBunniesCore::npcBunnies[npcBunniesEditorCore::NpcDesignerSelectedNpc] = npc;
npcBunniesEditorSerialization::NdeSaveNpcFile();
}
float NdeClampNpcSpeed(float speed)
{
if(speed < 0.1) return 0.1;
if(speed > 5.0) return 5.0;
return speed;
}
string NdeNpcSpeedName(float speed)
{
int tenths = int(NdeClampNpcSpeed(speed) * 10 + 0.5);
return "" + (tenths / 10) + "." + (tenths % 10);
}
void NdeCycleNpcSpeed(npcBunniesCore::npcBunny npc, int delta)
{
int tenths = int(NdeClampNpcSpeed(npc.moveSpeed) * 10 + 0.5) + delta;
if(tenths < 1) tenths = 1;
if(tenths > 50) tenths = 50;
npc.moveSpeed = float(tenths) / 10.0;
npcBunniesCore::npcBunnies[npcBunniesEditorCore::NpcDesignerSelectedNpc] = npc;
npcBunniesEditorSerialization::NdeSaveNpcFile();
}
npcBunniesCore::npcBunny NdeDialogueSpeakerNpc(npcBunniesCore::npcBunny &in fallbackNpc, npcBunniesDialogue::DialogueSet@ set)
{
if(set !is null && set.speakerNpcIndex >= 0 && set.speakerNpcIndex < int(npcBunniesCore::npcBunnies.length)) return npcBunniesCore::npcBunnies[set.speakerNpcIndex];
return fallbackNpc;
}
string NdeDialogueSpeakerName(npcBunniesCore::npcBunny &in fallbackNpc, npcBunniesDialogue::DialogueSet@ set)
{
if(set is null || set.speakerNpcIndex < 0) return "default";
if(set.speakerNpcIndex >= int(npcBunniesCore::npcBunnies.length)) return "default";
string name = npcBunniesCore::npcBunnies[set.speakerNpcIndex].name;
if(name == "") name = "NPC " + (set.speakerNpcIndex + 1);
return name;
}
void NdeCycleDialogueSpeaker(npcBunniesDialogue::DialogueSet@ set, int delta)
{
if(set is null || npcBunniesCore::npcBunnies.length == 0) return;
array<int> choices;
choices.insertLast(-1);
for(uint i = 0; i < npcBunniesCore::npcBunnies.length; i++)
if(int(i) != npcBunniesEditorCore::NpcDesignerSelectedNpc) choices.insertLast(int(i));
int current = choices.find(set.speakerNpcIndex);
if(current < 0) current = 0;
current += delta < 0 ? -1 : 1;
if(current < 0) current = int(choices.length) - 1;
if(current >= int(choices.length)) current = 0;
set.speakerNpcIndex = choices[current];
npcBunniesEditorPreview::NdeClampDialogueBigFrame(NdeDialogueSpeakerNpc(npcBunniesCore::npcBunnies[npcBunniesEditorCore::NpcDesignerSelectedNpc], set), set);
}
string NdeDialogueAutoContinueName(npcBunniesCore::npcBunny &in npc, npcBunniesDialogue::DialogueSet@ set)
{
if(set is null || set.autoContinueSet == -2) return "-";
if(set.autoContinueSet == -1) return "next set";
if(set.autoContinueSet >= 0 && set.autoContinueSet < int(npc.dialogueSets.length)) return "set " + (set.autoContinueSet + 1);
return "-";
}
string NdeDialogueGoToName(npcBunniesCore::npcBunny &in npc, npcBunniesDialogue::DialogueSet@ set)
{
if(set is null || set.goToSet == -1) return "next";
if(set.goToSet == -2) return "stay";
if(set.goToSet >= 0 && set.goToSet < int(npc.dialogueSets.length)) return "set " + (set.goToSet + 1);
return "next";
}
void NdeBuildDialogueGoToChoices(npcBunniesCore::npcBunny &in npc, array<int> &out choices)
{
choices.resize(0);
choices.insertLast(-1);
choices.insertLast(-2);
for(uint i = 0; i < npc.dialogueSets.length; i++) choices.insertLast(int(i));
}
void NdeCycleDialogueGoTo(npcBunniesCore::npcBunny &in npc, npcBunniesDialogue::DialogueSet@ set, int delta)
{
if(set is null) return;
array<int> choices;
NdeBuildDialogueGoToChoices(npc, choices);
int current = choices.find(set.goToSet);
if(current < 0) current = 0;
current += delta < 0 ? -1 : 1;
if(current < 0) current = int(choices.length) - 1;
if(current >= int(choices.length)) current = 0;
set.goToSet = choices[current];
}
void NdeBuildDialogueAutoContinueChoices(npcBunniesCore::npcBunny &in npc, array<int> &out choices)
{
choices.resize(0);
choices.insertLast(-2);
if(!NdeDialogueAutoContinueWouldLoop(npc, npcBunniesEditorCore::NpcDesignerDialogueSet, -1)) choices.insertLast(-1);
for(uint i = 0; i < npc.dialogueSets.length; i++)
{
if(int(i) == npcBunniesEditorCore::NpcDesignerDialogueSet) continue;
if(NdeDialogueAutoContinueWouldLoop(npc, npcBunniesEditorCore::NpcDesignerDialogueSet, int(i))) continue;
choices.insertLast(int(i));
}
}
int NdeDialogueAutoContinueTarget(npcBunniesCore::npcBunny &in npc, int sourceSet, int autoContinueSet)
{
if(autoContinueSet == -2) return -1;
if(autoContinueSet == -1) return sourceSet + 1;
return autoContinueSet;
}
bool NdeDialogueAutoContinueWouldLoop(npcBunniesCore::npcBunny &in npc, int sourceSet, int candidate)
{
int target = NdeDialogueAutoContinueTarget(npc, sourceSet, candidate);
for(int guard = 0; guard < int(npc.dialogueSets.length) + 1; guard++)
{
if(target < 0 || target >= int(npc.dialogueSets.length)) return false;
if(target == sourceSet) return true;
npcBunniesDialogue::DialogueSet@ set = npc.dialogueSets[target];
if(set is null || set.choicesAnswerMethod != npcBunniesCore::DialogueAction::Normal || set.autoContinueSet == -2) return false;
target = NdeDialogueAutoContinueTarget(npc, target, set.autoContinueSet);
}
return true;
}
void NdeCycleDialogueAutoContinue(npcBunniesCore::npcBunny &in npc, npcBunniesDialogue::DialogueSet@ set, int delta)
{
if(set is null) return;
array<int> choices;
NdeBuildDialogueAutoContinueChoices(npc, choices);
int current = choices.find(set.autoContinueSet);
if(current < 0) current = 0;
current += delta < 0 ? -1 : 1;
if(current < 0) current = int(choices.length) - 1;
if(current >= int(choices.length)) current = 0;
set.autoContinueSet = choices[current];
}
string NdeNpcAccessoryDisplay(npcBunniesCore::npcBunny &in npc, int category)
{
int frame = NdeNpcAccessoryFrame(npc, category);
if(frame < 0)
{
if(category == npcBunniesAccessories::NpcAccessories::CATEGORY_HAT) return "No Hat";
if(category == npcBunniesAccessories::NpcAccessories::CATEGORY_SCARF) return "No scarf";
return "No glasses";
}
return "" + frame;
}
/// <summary>
/// Creates the selected NPC type at the mouse position in the level.
/// </summary>
void NdePlaceNewNpcAtMouse(jjPLAYER@ play, int mouseX, int mouseY)
{
npcBunniesCore::npcBunny npc; npcBunniesParser::SetNpcDefaults(npc);
npc.name = npcBunniesEditorCore::NpcDesignerNpcNames[npcBunniesEditorCore::NpcDesignerSelectedType];
npc.type = npcBunniesAnimations::getTypeFromTypeName(npc.name);
NdeSetNpcTileFromMouse(npc, play, mouseX, mouseY);
npcBunniesParser::AddParsedNpc(npc);
npcBunniesEditorCore::NpcDesignerSelectedNpc = int(npcBunniesCore::npcBunnies.length) - 1;
npcBunniesEditorCore::NpcDesignerView = npcBunniesEditorCore::NDE_EDIT;
npcBunniesEditorCore::NpcDesignerSelectedType = -1;
npcBunniesEditorSerialization::NdeSaveNpcFile();
}
/// <summary>
/// Moves the selected NPC to the mouse position in the level.
/// </summary>
void NdeMoveSelectedNpcToMouse(jjPLAYER@ play, int mouseX, int mouseY)
{
npcBunniesCore::npcBunny npc = npcBunniesCore::npcBunnies[npcBunniesEditorCore::NpcDesignerSelectedNpc];
NdeSetNpcTileFromMouse(npc, play, mouseX, mouseY);
NdeApplyNpcTilePosition(npc);
npcBunniesCore::npcBunnies[npcBunniesEditorCore::NpcDesignerSelectedNpc] = npc;
npcBunniesEditorCore::NpcDesignerMovingNpc = false;
npcBunniesEditorSerialization::NdeSaveNpcFile();
}
/// <summary>
/// Converts the mouse screen position to tile coordinates and stores them on an NPC.
/// </summary>
void NdeSetNpcTileFromMouse(npcBunniesCore::npcBunny &inout npc, jjPLAYER@ play, int mouseX, int mouseY)
{
float worldX = play.cameraX + mouseX;
float worldY = play.cameraY + mouseY;
npc.xTile = npcBunniesEditorCore::NdeClamp(int(worldX / 32.0 + npcBunniesEditorCore::NDE_MOUSE_PLACE_TILE_OFFSET_X), 0, jjLayerWidth[4] - 1);
npc.yTile = npcBunniesEditorCore::NdeClamp(int(worldY / 32.0 + npcBunniesEditorCore::NDE_MOUSE_PLACE_TILE_OFFSET_Y), 0, jjLayerHeight[4] - 1);
}
void NdeSetTeleportEditFromMouse(jjPLAYER@ play, int mouseX, int mouseY)
{
npcBunniesEditorCore::NpcDesignerTeleportEditX = int((play.cameraX + mouseX) / 32);
npcBunniesEditorCore::NpcDesignerTeleportEditY = int((play.cameraY + mouseY) / 32);
NdeCenterEditorCameraOnTile(npcBunniesEditorCore::NpcDesignerTeleportEditX, npcBunniesEditorCore::NpcDesignerTeleportEditY);
}
/// <summary>
/// Applies an NPC's tile coordinates to its live object and follow/fall target positions.
/// </summary>
void NdeApplyNpcTilePosition(npcBunniesCore::npcBunny &inout npc)
{
if(npc.objectID >= 0)
{
jjObjects[npc.objectID].xPos = npc.xTile * 32;
jjObjects[npc.objectID].yPos = npc.yTile * 32;
jjObjects[npc.objectID].direction = npc.direction;
}
npc.fallToX = npc.xTile * 32;
npc.fallToY = npc.yTile * 32;
}
/// <summary>
/// Restores the most recently deleted NPC.
/// </summary>
void NdeUndoDelete()
{
if(!npcBunniesEditorCore::NpcDesignerHasDeletedNpc) return;
npcBunniesParser::AddParsedNpc(npcBunniesEditorCore::NpcDesignerDeletedNpc);
npcBunniesEditorCore::NpcDesignerHasDeletedNpc = false;
npcBunniesEditorCore::NpcDesignerSelectedNpc = int(npcBunniesCore::npcBunnies.length) - 1;
npcBunniesEditorCore::NpcDesignerView = npcBunniesEditorCore::NDE_EDIT;
npcBunniesEditorSerialization::NdeSaveNpcFile();
}
/// <summary>
/// Deletes an NPC and stores it for undo.
/// </summary>
void NdeDeleteNpc(int index)
{
if(index < 0 || index >= int(npcBunniesCore::npcBunnies.length)) return;
npcBunniesEditorCore::NpcDesignerDeletedNpc = npcBunniesCore::npcBunnies[index];
npcBunniesEditorCore::NpcDesignerHasDeletedNpc = true;
if(npcBunniesCore::npcBunnies[index].objectID >= 0) jjObjects[npcBunniesCore::npcBunnies[index].objectID].delete();
npcBunniesCore::npcBunnies.removeAt(index);
for(uint i = 0; i < npcBunniesCore::npcBunnies.length; i++) npcBunniesCore::npcBunnies[i].id = int(i);
npcBunniesEditorCore::NpcDesignerSelectedNpc = -1;
npcBunniesEditorSerialization::NdeSaveNpcFile();
}
/// <summary>
/// Creates a default shop item.
/// </summary>
npcBunniesShop::NpcShopItem@ NdeNewShopItem()
{
npcBunniesShop::NpcShopItem@ item = @npcBunniesShop::NpcShopItem();
item.priceCoins = 0;
item.priceGems = 0;
item.stock = -1;
NdeApplyShopReward(item.item, 0);
item.name = NdeShopRewardName(item.item);
return item;
}
/// <summary>
/// Cycles the selected shop reward value.
/// </summary>
void NdeCycleShopReward(npcBunniesCore::npcBunny npc, int index, int delta)
{
if(index < 0 || index >= int(npc.shopItems.length)) return;
npcBunniesQuests::NpcReward@ reward = npc.shopItems[index].item;
int itemIndex = NdeFindShopRewardIndex(reward);
itemIndex += delta;
while(itemIndex < 0) itemIndex += int(npcBunniesEditorCore::NpcDesignerShopRewardTypes.length);
while(itemIndex >= int(npcBunniesEditorCore::NpcDesignerShopRewardTypes.length)) itemIndex -= int(npcBunniesEditorCore::NpcDesignerShopRewardTypes.length);
NdeApplyShopReward(reward, itemIndex);
npc.shopItems[index].name = NdeShopRewardName(reward);
npcBunniesCore::npcBunnies[npcBunniesEditorCore::NpcDesignerSelectedNpc] = npc;
npcBunniesEditorSerialization::NdeSaveNpcFile();
}
/// <summary>
/// Global editor state for find shop reward index.
/// </summary>
int NdeFindShopRewardIndex(npcBunniesQuests::NpcReward@ reward)
{
if(reward is null) return 0;
for(uint i = 0; i < npcBunniesEditorCore::NpcDesignerShopRewardTypes.length; i++)
{
if(reward.rewardType == npcBunniesEditorCore::NpcDesignerShopRewardTypes[i] && (reward.rewardType == npcBunniesQuests::RewardType::RewardFastFire || reward.weaponType == npcBunniesEditorCore::NpcDesignerShopWeaponTypes[i]))
return int(i);
}
return 0;
}
/// <summary>
/// Applies the selected editor list value to shop reward data.
/// </summary>
void NdeApplyShopReward(npcBunniesQuests::NpcReward@ reward, int index)
{
if(reward is null) return;
index = npcBunniesEditorCore::NdeClamp(index, 0, int(npcBunniesEditorCore::NpcDesignerShopRewardTypes.length) - 1);
reward.rewardType = npcBunniesEditorCore::NpcDesignerShopRewardTypes[index];
reward.weaponType = npcBunniesEditorCore::NpcDesignerShopWeaponTypes[index];
reward.amount = reward.rewardType == npcBunniesQuests::RewardType::RewardFastFire ? 5 : (reward.rewardType == npcBunniesQuests::RewardType::RewardAmmo ? 5 : 1);
}
/// <summary>
/// Returns the display name for a shop reward item.
/// </summary>
string NdeShopRewardName(npcBunniesQuests::NpcReward@ reward)
{
if(reward is null) return "Item";
if(reward.rewardType == npcBunniesQuests::RewardType::RewardAmmo) return npcBunniesShop::GetWeaponDisplayName(reward.weaponType) + " ammo";
if(reward.rewardType == npcBunniesQuests::RewardType::RewardPowerup) return npcBunniesShop::GetWeaponDisplayName(reward.weaponType) + " powerup";
if(reward.rewardType == npcBunniesQuests::RewardType::RewardFastFire) return "Fastfire";
return "Item";
}
/// <summary>
/// Changes the selected shop cost value and saves the NPC file.
/// </summary>
void NdeChangeShopCost(npcBunniesCore::npcBunny npc, int index, bool coins, int delta)
{
if(index < 0 || index >= int(npc.shopItems.length)) return;
if(coins) npc.shopItems[index].priceCoins = npcBunniesEditorCore::NdeMax(0, npc.shopItems[index].priceCoins + delta);
else npc.shopItems[index].priceGems = npcBunniesEditorCore::NdeMax(0, npc.shopItems[index].priceGems + delta);
npcBunniesCore::npcBunnies[npcBunniesEditorCore::NpcDesignerSelectedNpc] = npc;
npcBunniesEditorSerialization::NdeSaveNpcFile();
}
/// <summary>
/// Changes the selected shop amount value and saves the NPC file.
/// </summary>
void NdeChangeShopAmount(npcBunniesCore::npcBunny npc, int index, int delta)
{
if(index < 0 || index >= int(npc.shopItems.length)) return;
npc.shopItems[index].item.amount = npcBunniesEditorCore::NdeMax(1, npc.shopItems[index].item.amount + delta);
npcBunniesCore::npcBunnies[npcBunniesEditorCore::NpcDesignerSelectedNpc] = npc;
npcBunniesEditorSerialization::NdeSaveNpcFile();
}
/// <summary>
/// Changes the selected shop stock value and saves the NPC file.
/// </summary>
void NdeChangeShopStock(npcBunniesCore::npcBunny npc, int index, int delta)
{
if(index < 0 || index >= int(npc.shopItems.length)) return;
if(npc.shopItems[index].stock < 0) npc.shopItems[index].stock = delta > 0 ? 1 : -1;
else
{
npc.shopItems[index].stock += delta;
if(npc.shopItems[index].stock < 1) npc.shopItems[index].stock = -1;
}
npcBunniesCore::npcBunnies[npcBunniesEditorCore::NpcDesignerSelectedNpc] = npc;
npcBunniesEditorSerialization::NdeSaveNpcFile();
}
/// <summary>
/// Cycles the selected quest value.
/// </summary>
void NdeCycleQuest(int index, int delta)
{
npcBunniesDialogue::DialogueSet@ set = NdeCurrentSet();
if(index < 0 || index >= int(set.quests.length)) return;
int itemIndex = NdeFindQuestIndex(set.quests[index]) + delta;
while(itemIndex < 0) itemIndex += int(npcBunniesEditorCore::NpcDesignerQuestTypes.length);
while(itemIndex >= int(npcBunniesEditorCore::NpcDesignerQuestTypes.length)) itemIndex -= int(npcBunniesEditorCore::NpcDesignerQuestTypes.length);
NdeApplyQuestItem(set.quests[index], itemIndex);
npcBunniesEditorSerialization::NdeSaveNpcFile();
}
/// <summary>
/// Global editor state for find quest index.
/// </summary>
int NdeFindQuestIndex(npcBunniesQuests::NpcQuest@ quest)
{
if(quest is null) return 0;
for(uint i = 0; i < npcBunniesEditorCore::NpcDesignerQuestTypes.length; i++)
{
if(quest.questType != npcBunniesEditorCore::NpcDesignerQuestTypes[i]) continue;
if(quest.questType == npcBunniesQuests::QuestType::Gems && quest.gemType != npcBunniesEditorCore::NpcDesignerQuestSubTypes[i]) continue;
if((quest.questType == npcBunniesQuests::QuestType::DefeatMonster || quest.questType == npcBunniesQuests::QuestType::DefeatBoss) && quest.objectType != npcBunniesEditorCore::NpcDesignerQuestSubTypes[i]) continue;
return int(i);
}
return 0;
}
/// <summary>
/// Applies the selected editor list value to quest item data.
/// </summary>
void NdeApplyQuestItem(npcBunniesQuests::NpcQuest@ quest, int index)
{
if(quest is null) return;
index = npcBunniesEditorCore::NdeClamp(index, 0, int(npcBunniesEditorCore::NpcDesignerQuestTypes.length) - 1);
quest.questType = npcBunniesEditorCore::NpcDesignerQuestTypes[index];
if(quest.questType == npcBunniesQuests::QuestType::Gems) quest.gemType = npcBunniesEditorPreview::NdeGemFromInt(npcBunniesEditorCore::NpcDesignerQuestSubTypes[index]);
else if(quest.questType == npcBunniesQuests::QuestType::DefeatMonster || quest.questType == npcBunniesQuests::QuestType::DefeatBoss) quest.objectType = npcBunniesEditorCore::NpcDesignerQuestSubTypes[index];
}
/// <summary>
/// Cycles the selected reward value.
/// </summary>
void NdeCycleReward(int index, int delta)
{
npcBunniesDialogue::DialogueSet@ set = NdeCurrentSet();
if(index < 0 || index >= int(set.rewards.length)) return;
array<int> rewardTypes;
array<int> rewardSubTypes;
NdeBuildRewardChoices(npcBunniesEditorCore::NpcDesignerDialogueSet, index, rewardTypes, rewardSubTypes);
if(rewardTypes.length == 0) return;
int itemIndex = NdeFindRewardIndex(set.rewards[index], rewardTypes, rewardSubTypes) + delta;
while(itemIndex < 0) itemIndex += int(rewardTypes.length);
while(itemIndex >= int(rewardTypes.length)) itemIndex -= int(rewardTypes.length);
NdeApplyRewardItem(set.rewards[index], itemIndex, rewardTypes, rewardSubTypes);
npcBunniesEditorSerialization::NdeSaveNpcFile();
}
/// <summary>
/// Builds the reward type list, including guide paths that are not already claimed by another reward.
/// </summary>
void NdeBuildRewardChoices(int currentSetIndex, int currentRewardIndex, array<int> &out rewardTypes, array<int> &out rewardSubTypes)
{
rewardTypes.resize(0);
rewardSubTypes.resize(0);
for(uint i = 0; i < npcBunniesEditorCore::NpcDesignerRewardTypes.length; i++)
{
rewardTypes.insertLast(npcBunniesEditorCore::NpcDesignerRewardTypes[i]);
rewardSubTypes.insertLast(npcBunniesEditorCore::NpcDesignerRewardSubTypes[i]);
}
if(!NdeValidNpc()) return;
npcBunniesCore::npcBunny npc = npcBunniesCore::npcBunnies[npcBunniesEditorCore::NpcDesignerSelectedNpc];
if(!npc.guideEnabled || npc.guidePaths.length == 0) return;
for(uint path = 0; path < npc.guidePaths.length; path++)
{
if(!NdeGuidePathRewardUsedByOther(npc, int(path), currentSetIndex, currentRewardIndex))
{
rewardTypes.insertLast(npcBunniesQuests::RewardType::RewardGuidePath);
rewardSubTypes.insertLast(int(path));
}
}
}
bool NdeGuidePathRewardUsedByOther(npcBunniesCore::npcBunny &in npc, int pathIndex, int currentSetIndex, int currentRewardIndex)
{
for(uint s = 0; s < npc.dialogueSets.length; s++)
{
npcBunniesDialogue::DialogueSet@ set = npc.dialogueSets[s];
for(uint r = 0; r < set.rewards.length; r++)
{
if(int(s) == currentSetIndex && int(r) == currentRewardIndex) continue;
npcBunniesQuests::NpcReward@ reward = set.rewards[r];
if(reward !is null && reward.rewardType == npcBunniesQuests::RewardType::RewardGuidePath && reward.guidePathIndex == pathIndex) return true;
}
}
return false;
}
string NdeGuideEntryRewardName(int index)
{
if(!NdeValidNpc() || index < 0 || index >= int(npcBunniesCore::npcBunnies[npcBunniesEditorCore::NpcDesignerSelectedNpc].guidePaths.length)) return "Guide entry";
return NdeGuideEntryDisplayName(npcBunniesCore::npcBunnies[npcBunniesEditorCore::NpcDesignerSelectedNpc], index);
}
string NdeGuideEntryDisplayName(npcBunniesCore::npcBunny &in npc, int index)
{
if(index < 0 || index >= int(npc.guidePaths.length)) return "Guide entry";
bool teleport = npc.guidePaths[index].isTeleport;
int number = 0;
for(int i = 0; i <= index && i < int(npc.guidePaths.length); i++)
{
if(npc.guidePaths[i].isTeleport == teleport) number++;
}
return (teleport ? "Teleport " : "Path ") + number;
}
void NdeRemoveGuidePathRewardReferences(npcBunniesCore::npcBunny &inout npc, int removedPathIndex)
{
for(uint s = 0; s < npc.dialogueSets.length; s++)
{
npcBunniesDialogue::DialogueSet@ set = npc.dialogueSets[s];
for(uint r = 0; r < set.rewards.length; r++)
{
npcBunniesQuests::NpcReward@ reward = set.rewards[r];
if(reward is null || reward.rewardType != npcBunniesQuests::RewardType::RewardGuidePath) continue;
if(reward.guidePathIndex == removedPathIndex)
{
reward.rewardType = npcBunniesQuests::RewardType::RewardCoins;
reward.amount = 1;
reward.guidePathIndex = -1;
}
else if(reward.guidePathIndex > removedPathIndex)
reward.guidePathIndex--;
}
}
}
/// <summary>
/// Global editor state for find reward index.
/// </summary>
int NdeFindRewardIndex(npcBunniesQuests::NpcReward@ reward, array<int> &in rewardTypes, array<int> &in rewardSubTypes)
{
if(reward is null) return 0;
for(uint i = 0; i < rewardTypes.length; i++)
{
if(reward.rewardType != rewardTypes[i]) continue;
if(reward.rewardType == npcBunniesQuests::RewardType::RewardGems && reward.gemType != rewardSubTypes[i]) continue;
if((reward.rewardType == npcBunniesQuests::RewardType::RewardAmmo || reward.rewardType == npcBunniesQuests::RewardType::RewardPowerup) && reward.weaponType != rewardSubTypes[i]) continue;
if(reward.rewardType == npcBunniesQuests::RewardType::RewardGuidePath && reward.guidePathIndex != rewardSubTypes[i]) continue;
return int(i);
}
return 0;
}
/// <summary>
/// Applies the selected editor list value to reward item data.
/// </summary>
void NdeApplyRewardItem(npcBunniesQuests::NpcReward@ reward, int index, array<int> &in rewardTypes, array<int> &in rewardSubTypes)
{
if(reward is null) return;
index = npcBunniesEditorCore::NdeClamp(index, 0, int(rewardTypes.length) - 1);
reward.rewardType = rewardTypes[index];
if(reward.rewardType == npcBunniesQuests::RewardType::RewardGems) reward.gemType = npcBunniesEditorPreview::NdeGemFromInt(rewardSubTypes[index]);
else if(reward.rewardType == npcBunniesQuests::RewardType::RewardAmmo || reward.rewardType == npcBunniesQuests::RewardType::RewardPowerup) reward.weaponType = rewardSubTypes[index];
else if(reward.rewardType == npcBunniesQuests::RewardType::RewardGuidePath) reward.guidePathIndex = rewardSubTypes[index];
if(reward.rewardType == npcBunniesQuests::RewardType::RewardTeleport) { reward.amount = 1; reward.x = int(reward.x); reward.y = int(reward.y); }
else if(reward.rewardType == npcBunniesQuests::RewardType::RewardTrigger) { reward.amount = 1; if(reward.triggerId < 0) reward.triggerId = 0; }
else if(reward.rewardType == npcBunniesQuests::RewardType::RewardPowerup || reward.rewardType == npcBunniesQuests::RewardType::RewardMoveable || reward.rewardType == npcBunniesQuests::RewardType::RewardGuidePath) reward.amount = 1;
else if(reward.amount <= 0) reward.amount = reward.rewardType == npcBunniesQuests::RewardType::RewardFastFire ? 5 : 1;
}
/// <summary>
/// Changes the selected reward amount value and saves the NPC file.
/// </summary>
void NdeChangeRewardAmount(int index, int encodedDelta)
{
npcBunniesDialogue::DialogueSet@ set = NdeCurrentSet();
if(index < 0 || index >= int(set.rewards.length)) return;
npcBunniesQuests::NpcReward@ reward = set.rewards[index];
int delta = encodedDelta < 0 ? -1 : 1;
int part = npcBunniesEditorCore::NdeAbs(encodedDelta) / 10;
if(reward.rewardType == npcBunniesQuests::RewardType::RewardTeleport)
{
if(part == 1) reward.x = npcBunniesEditorCore::NdeMax(0, int(reward.x) + delta);
else if(part == 2) reward.y = npcBunniesEditorCore::NdeMax(0, int(reward.y) + delta);
else reward.amount = npcBunniesEditorCore::NdeMax(0, reward.amount + delta);
}
else if(reward.rewardType == npcBunniesQuests::RewardType::RewardTrigger)
{
reward.triggerId = npcBunniesEditorCore::NdeMax(0, (reward.triggerId < 0 ? 0 : reward.triggerId) + delta);
reward.amount = 1;
}
else if(reward.rewardType == npcBunniesQuests::RewardType::RewardGuidePath)
{
reward.amount = 1;
}
else reward.amount = npcBunniesEditorCore::NdeMax(0, reward.amount + delta);
npcBunniesEditorSerialization::NdeSaveNpcFile();
}
/// <summary>
/// Records arrow-key guide path steps while path editing is active.
/// </summary>
void NdeRecordPathKeys()
{
if(!NdeValidNpc() || npcBunniesEditorCore::NpcDesignerPathIndex < 0 || npcBunniesEditorCore::NpcDesignerPathIndex >= int(npcBunniesCore::npcBunnies[npcBunniesEditorCore::NpcDesignerSelectedNpc].guidePaths.length)) return;
if(jjGameTicks - npcBunniesEditorCore::NpcDesignerLastKey < 8) return;
if(npcBunniesEditorCore::NpcDesignerEditingTeleport)
{
NdeRecordTeleportKeys();
return;
}
if(jjKey[0x0D] || jjKey[0x1B])
{
npcBunniesEditorCore::NpcDesignerLastKey = jjGameTicks;
npcBunniesEditorCore::NpcDesignerRecordingPath = false;
NdeUnfreezeEditorCamera();
npcBunniesEditorSerialization::NdeSaveNpcFile();
return;
}
int dx = 0, dy = 0;
if(jjKey[0x25]) dx = -1;
else if(jjKey[0x27]) dx = 1;
else if(jjKey[0x26]) dy = -1;
else if(jjKey[0x28]) dy = 1;
if(dx == 0 && dy == 0) return;
npcBunniesEditorCore::NpcDesignerLastKey = jjGameTicks;
npcBunniesCore::GuidePath@ path = npcBunniesCore::npcBunnies[npcBunniesEditorCore::NpcDesignerSelectedNpc].guidePaths[npcBunniesEditorCore::NpcDesignerPathIndex];
int startX, startY;
NdeGuidePathStart(npcBunniesCore::npcBunnies[npcBunniesEditorCore::NpcDesignerSelectedNpc], npcBunniesEditorCore::NpcDesignerPathIndex, startX, startY);
int currentX = startX, currentY = startY;
for(uint i = 0; i < path.steps.length; i++)
{
currentX += path.steps[i].x;
currentY += path.steps[i].y;
}
int nextX = currentX + dx, nextY = currentY + dy;
if(nextX == startX && nextY == startY)
{
path.steps.resize(0);
NdeCenterEditorCameraOnTile(startX, startY);
npcBunniesEditorSerialization::NdeSaveNpcFile();
return;
}
int testX = startX, testY = startY;
for(uint i = 0; i < path.steps.length; i++)
{
testX += path.steps[i].x;
testY += path.steps[i].y;
if(nextX == testX && nextY == testY)
{
path.steps.resize(i + 1);
NdeCenterEditorCameraOnTile(nextX, nextY);
npcBunniesEditorSerialization::NdeSaveNpcFile();
return;
}
}
path.steps.insertLast(@npcBunniesCore::GuideStep(dx, dy));
NdeGuidePathEnd(npcBunniesCore::npcBunnies[npcBunniesEditorCore::NpcDesignerSelectedNpc], npcBunniesEditorCore::NpcDesignerPathIndex, nextX, nextY);
NdeCenterEditorCameraOnTile(nextX, nextY);
npcBunniesEditorSerialization::NdeSaveNpcFile();
}
void NdeRecordTeleportKeys()
{
if(jjKey[0x1B])
{
npcBunniesEditorCore::NpcDesignerLastKey = jjGameTicks;
NdeCancelGuideEntryEdit();
npcBunniesEditorSerialization::NdeSaveNpcFile();
return;
}
if(jjKey[0x0D] || npcBunniesEditorCore::NpcDesignerTeleportFirePressed || (jjLocalPlayerCount > 0 && jjLocalPlayers[0].keyFire))
{
npcBunniesEditorCore::NpcDesignerLastKey = jjGameTicks;
npcBunniesEditorCore::NpcDesignerTeleportFirePressed = false;
NdeCommitTeleportEdit();
return;
}
int dx = 0, dy = 0;
if(jjKey[0x25]) dx = -1;
else if(jjKey[0x27]) dx = 1;
else if(jjKey[0x26]) dy = -1;
else if(jjKey[0x28]) dy = 1;
if(dx == 0 && dy == 0) return;
npcBunniesEditorCore::NpcDesignerLastKey = jjGameTicks;
npcBunniesEditorCore::NpcDesignerTeleportEditX += dx;
npcBunniesEditorCore::NpcDesignerTeleportEditY += dy;
NdeCenterEditorCameraOnTile(npcBunniesEditorCore::NpcDesignerTeleportEditX, npcBunniesEditorCore::NpcDesignerTeleportEditY);
}
/// <summary>
/// Draws the recorded path portion of the NPC Designer UI.
/// </summary>
void NdeDrawRecordedPath(jjCANVAS@ canvas)
{
if(npcBunniesEditorCore::NpcDesignerView != npcBunniesEditorCore::NDE_PATH_RECORD || !NdeValidNpc() || npcBunniesEditorCore::NpcDesignerPathIndex < 0 || npcBunniesEditorCore::NpcDesignerPathIndex >= int(npcBunniesCore::npcBunnies[npcBunniesEditorCore::NpcDesignerSelectedNpc].guidePaths.length)) return;
npcBunniesCore::npcBunny npc = npcBunniesCore::npcBunnies[npcBunniesEditorCore::NpcDesignerSelectedNpc];
if(npc.guidePaths[npcBunniesEditorCore::NpcDesignerPathIndex].isTeleport)
{
int tx = npcBunniesEditorCore::NpcDesignerEditingTeleport ? npcBunniesEditorCore::NpcDesignerTeleportEditX : npc.guidePaths[npcBunniesEditorCore::NpcDesignerPathIndex].teleportX;
int ty = npcBunniesEditorCore::NpcDesignerEditingTeleport ? npcBunniesEditorCore::NpcDesignerTeleportEditY : npc.guidePaths[npcBunniesEditorCore::NpcDesignerPathIndex].teleportY;
int screenX, screenY;
NdeWorldToScreen(tx * 32, ty * 32, screenX, screenY);
canvas.drawRectangle(screenX, screenY, 32, 32, 80, SPRITE::TRANSLUCENT);
return;
}
int tx, ty;
NdeGuidePathStart(npc, npcBunniesEditorCore::NpcDesignerPathIndex, tx, ty);
for(uint i = 0; i < npc.guidePaths[npcBunniesEditorCore::NpcDesignerPathIndex].steps.length; i++)
{
tx += npc.guidePaths[npcBunniesEditorCore::NpcDesignerPathIndex].steps[i].x;
ty += npc.guidePaths[npcBunniesEditorCore::NpcDesignerPathIndex].steps[i].y;
int screenX, screenY;
NdeWorldToScreen(tx * 32 + 12, ty * 32 + 12, screenX, screenY);
canvas.drawRectangle(screenX, screenY, 8, 8, 24, SPRITE::NORMAL);
}
}
/// <summary>
/// Calculates the tile where a guide path starts.
/// </summary>
void NdeGuidePathStart(npcBunniesCore::npcBunny &in npc, int pathIndex, int &out tx, int &out ty)
{
tx = int(npc.xTile);
ty = int(npc.yTile);
for(int path = 0; path < pathIndex && path < int(npc.guidePaths.length); path++)
{
if(npc.guidePaths[path].isTeleport)
{
tx = npc.guidePaths[path].teleportX;
ty = npc.guidePaths[path].teleportY;
continue;
}
for(uint step = 0; step < npc.guidePaths[path].steps.length; step++)
{
tx += npc.guidePaths[path].steps[step].x;
ty += npc.guidePaths[path].steps[step].y;
}
}
}
void NdeGuidePathEnd(npcBunniesCore::npcBunny &in npc, int pathIndex, int &out tx, int &out ty)
{
NdeGuidePathStart(npc, pathIndex, tx, ty);
if(pathIndex < 0 || pathIndex >= int(npc.guidePaths.length)) return;
if(npc.guidePaths[pathIndex].isTeleport)
{
tx = npc.guidePaths[pathIndex].teleportX;
ty = npc.guidePaths[pathIndex].teleportY;
return;
}
for(uint step = 0; step < npc.guidePaths[pathIndex].steps.length; step++)
{
tx += npc.guidePaths[pathIndex].steps[step].x;
ty += npc.guidePaths[pathIndex].steps[step].y;
}
}
void NdeAddGuideTeleport(npcBunniesCore::npcBunny &inout npc)
{
npcBunniesCore::GuidePath@ teleport = @npcBunniesCore::GuidePath();
teleport.isTeleport = true;
int tx, ty;
NdeGuidePathEnd(npc, int(npc.guidePaths.length) - 1, tx, ty);
teleport.teleportX = tx;
teleport.teleportY = ty;
npc.guidePaths.insertLast(teleport);
npc.guideEnabled = true;
npcBunniesCore::npcBunnies[npcBunniesEditorCore::NpcDesignerSelectedNpc] = npc;
npcBunniesEditorCore::NpcDesignerPathIndex = int(npc.guidePaths.length) - 1;
npcBunniesEditorCore::NpcDesignerView = npcBunniesEditorCore::NDE_PATH_RECORD;
NdeStartTeleportEdit(true);
npcBunniesEditorSerialization::NdeSaveNpcFile();
}
void NdeStartTeleportEdit(bool isNew)
{
if(!NdeValidNpc() || npcBunniesEditorCore::NpcDesignerPathIndex < 0 || npcBunniesEditorCore::NpcDesignerPathIndex >= int(npcBunniesCore::npcBunnies[npcBunniesEditorCore::NpcDesignerSelectedNpc].guidePaths.length)) return;
npcBunniesCore::GuidePath@ entry = npcBunniesCore::npcBunnies[npcBunniesEditorCore::NpcDesignerSelectedNpc].guidePaths[npcBunniesEditorCore::NpcDesignerPathIndex];
if(entry is null || !entry.isTeleport) return;
npcBunniesEditorCore::NpcDesignerRecordingPath = false;
npcBunniesEditorCore::NpcDesignerEditingTeleport = true;
npcBunniesEditorCore::NpcDesignerTeleportIsNew = isNew;
npcBunniesEditorCore::NpcDesignerTeleportFirePressed = false;
npcBunniesEditorCore::NpcDesignerTeleportEditX = entry.teleportX;
npcBunniesEditorCore::NpcDesignerTeleportEditY = entry.teleportY;
npcBunniesEditorCore::NpcDesignerTeleportOriginalX = entry.teleportX;
npcBunniesEditorCore::NpcDesignerTeleportOriginalY = entry.teleportY;
NdeCenterEditorCameraOnTile(npcBunniesEditorCore::NpcDesignerTeleportEditX, npcBunniesEditorCore::NpcDesignerTeleportEditY);
}
void NdeCommitTeleportEdit()
{
if(!NdeValidNpc() || npcBunniesEditorCore::NpcDesignerPathIndex < 0 || npcBunniesEditorCore::NpcDesignerPathIndex >= int(npcBunniesCore::npcBunnies[npcBunniesEditorCore::NpcDesignerSelectedNpc].guidePaths.length)) return;
npcBunniesCore::GuidePath@ entry = npcBunniesCore::npcBunnies[npcBunniesEditorCore::NpcDesignerSelectedNpc].guidePaths[npcBunniesEditorCore::NpcDesignerPathIndex];
if(entry is null || !entry.isTeleport) return;
entry.teleportX = npcBunniesEditorCore::NpcDesignerTeleportEditX;
entry.teleportY = npcBunniesEditorCore::NpcDesignerTeleportEditY;
npcBunniesEditorCore::NpcDesignerEditingTeleport = false;
npcBunniesEditorCore::NpcDesignerTeleportIsNew = false;
npcBunniesEditorCore::NpcDesignerTeleportFirePressed = false;
NdeUnfreezeEditorCamera();
npcBunniesEditorSerialization::NdeSaveNpcFile();
}
void NdeCancelGuideEntryEdit()
{
npcBunniesEditorCore::NpcDesignerRecordingPath = false;
if(npcBunniesEditorCore::NpcDesignerEditingTeleport)
{
if(npcBunniesEditorCore::NpcDesignerTeleportIsNew && NdeValidNpc() && npcBunniesEditorCore::NpcDesignerPathIndex >= 0 && npcBunniesEditorCore::NpcDesignerPathIndex < int(npcBunniesCore::npcBunnies[npcBunniesEditorCore::NpcDesignerSelectedNpc].guidePaths.length))
{
npcBunniesCore::npcBunny npc = npcBunniesCore::npcBunnies[npcBunniesEditorCore::NpcDesignerSelectedNpc];
NdeRemoveGuidePathRewardReferences(npc, npcBunniesEditorCore::NpcDesignerPathIndex);
npc.guidePaths.removeAt(npcBunniesEditorCore::NpcDesignerPathIndex);
npcBunniesCore::npcBunnies[npcBunniesEditorCore::NpcDesignerSelectedNpc] = npc;
npcBunniesEditorCore::NpcDesignerPathIndex = -1;
}
else if(NdeValidNpc() && npcBunniesEditorCore::NpcDesignerPathIndex >= 0 && npcBunniesEditorCore::NpcDesignerPathIndex < int(npcBunniesCore::npcBunnies[npcBunniesEditorCore::NpcDesignerSelectedNpc].guidePaths.length))
{
npcBunniesCore::GuidePath@ entry = npcBunniesCore::npcBunnies[npcBunniesEditorCore::NpcDesignerSelectedNpc].guidePaths[npcBunniesEditorCore::NpcDesignerPathIndex];
if(entry !is null && entry.isTeleport)
{
entry.teleportX = npcBunniesEditorCore::NpcDesignerTeleportOriginalX;
entry.teleportY = npcBunniesEditorCore::NpcDesignerTeleportOriginalY;
}
}
}
npcBunniesEditorCore::NpcDesignerEditingTeleport = false;
npcBunniesEditorCore::NpcDesignerTeleportIsNew = false;
npcBunniesEditorCore::NpcDesignerTeleportFirePressed = false;
NdeUnfreezeEditorCamera();
}
void NdeCenterEditorCameraOnTile(int tx, int ty)
{
if(jjLocalPlayerCount <= 0) return;
jjLocalPlayers[0].cameraFreeze(tx * 32 + 16, ty * 32 + 16, true, true);
}
void NdeUnfreezeEditorCamera()
{
if(jjLocalPlayerCount <= 0) return;
jjLocalPlayers[0].cameraUnfreeze();
}
/// <summary>
/// Converts world pixel coordinates to local screen coordinates.
/// </summary>
void NdeWorldToScreen(int worldX, int worldY, int &out screenX, int &out screenY)
{
jjPLAYER@ play = jjLocalPlayers[0];
screenX = int(worldX - play.cameraX);
screenY = int(worldY - play.cameraY);
}
/// <summary>
/// Draws the npc preview portion of the NPC Designer UI.
/// </summary>
}
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.