| Name | Author | Game Mode | Rating | |||||
|---|---|---|---|---|---|---|---|---|
| NpcBunnies | Cranky | Mutator | N/A | |||||
namespace npcBunniesEditorSerialization
{
const int NDE_SYNC_CHUNK_SIZE = 96;
void NdeSendNpcDesignerStateToServer(const string &in text)
{
NdeSendNpcDesignerState(text, 0);
}
void NdeBroadcastNpcDesignerState(const string &in text, int clientID)
{
if(!jjIsServer) return;
NdeSendNpcDesignerState(text, clientID);
}
void NdeSendNpcDesignerState(const string &in text, int clientID)
{
string encoded = NdePacketEncode(text);
jjSTREAM begin;
begin.push("npcEditorBegin");
if(clientID == 0) jjSendPacket(begin);
else jjSendPacket(begin, clientID);
for(int i = 0; i < int(encoded.length); i += NDE_SYNC_CHUNK_SIZE)
{
jjSTREAM chunkPacket;
chunkPacket.push("npcEditorChunk");
chunkPacket.push(encoded.substr(i, npcBunniesEditorCore::NdeMin(NDE_SYNC_CHUNK_SIZE, int(encoded.length) - i)));
if(clientID == 0) jjSendPacket(chunkPacket);
else jjSendPacket(chunkPacket, clientID);
}
jjSTREAM end;
end.push("npcEditorEnd");
if(clientID == 0) jjSendPacket(end);
else jjSendPacket(end, clientID);
}
string NdeHexDigit(int value)
{
return value < 10 ? "" + value : (value == 10 ? "A" : value == 11 ? "B" : value == 12 ? "C" : value == 13 ? "D" : value == 14 ? "E" : "F");
}
int NdeHexValue(uint8 c)
{
if(c >= 48 && c <= 57) return c - 48;
if(c >= 65 && c <= 70) return c - 55;
if(c >= 97 && c <= 102) return c - 87;
return 0;
}
string NdePacketEncode(const string &in text)
{
string encoded = "";
for(uint i = 0; i < text.length; i++)
{
uint8 c = text[i];
encoded += NdeHexDigit(c >> 4);
encoded += NdeHexDigit(c & 15);
}
return encoded;
}
string NdePacketDecode(const string &in encoded)
{
string text = "";
for(uint i = 0; i + 1 < encoded.length; i += 2)
{
uint8 c = uint8((NdeHexValue(encoded[i]) << 4) | NdeHexValue(encoded[i + 1]));
text += " ";
text[text.length - 1] = c;
}
return text;
}
void NdeClearNpcRuntimeState()
{
for(uint i = 0; i < npcBunniesCore::npcBunnies.length; i++)
{
if(npcBunniesCore::npcBunnies[i].objectID >= 0) jjObjects[npcBunniesCore::npcBunnies[i].objectID].delete();
}
npcBunniesCore::npcBunnies.resize(0);
npcBunniesCore::npcObjectIds.deleteAll();
npcBunniesMovement::npcFollowWaypoints.resize(0);
npcBunniesMovement::previousNpcX.resize(0);
npcBunniesMovement::previousNpcY.resize(0);
npcBunniesMovement::lastNpcPositionSyncTick.resize(0);
npcBunniesMovement::lastNpcSyncedX.resize(0);
npcBunniesMovement::lastNpcSyncedY.resize(0);
}
void NdeApplyNpcDesignerState(const string &in text, bool saveFile)
{
NdeClearNpcRuntimeState();
array<string> tokens = npcBunniesParser::TokenizeNpcJson(npcBunniesParser::StripNpcJsonComments(text));
array<int> pos = {0};
npcBunniesParser::ParseNpcJsonRoot(tokens, pos);
npcBunniesMovement::EnsureNpcRuntimeArrays();
if(saveFile) NdeSaveNpcFileText(text);
}
/// <summary>
/// Builds the NPC data filename for the current level.
/// </summary>
string NdeNpcFileName()
{
string baseName = jjLevelFileName;
int dot = int(baseName.findLast("."));
if(dot > 0) baseName = baseName.substr(0, dot);
return baseName + "_npc.asdat";
}
/// <summary>
/// Creates a one-time backup of the current level NPC data file.
/// </summary>
void NdeBackupNpcFile()
{
if(npcBunniesEditorCore::NpcDesignerBackedUp) return;
npcBunniesEditorCore::NpcDesignerBackedUp = true;
string filename = NdeNpcFileName();
jjSTREAM load(filename);
if(load.isEmpty()) return;
jjSTREAM save;
string line;
while(load.getLine(line)) save.write(line + "\n");
string baseName = jjLevelFileName;
int dot = int(baseName.findLast("."));
if(dot > 0) baseName = baseName.substr(0, dot);
save.save(baseName + "_npc - BACKUP.asdat");
}
/// <summary>
/// Builds the current NPC data file text.
/// </summary>
string NdeBuildNpcFileText()
{
string text = NdeCollectNpcFileComments();
text += "{\n";
text += "\t\"LoadBigAnimations\": " + (npcBunniesCore::LOAD_BIG_NPC_ANIMATIONS ? "true" : "false") + ",\n";
text += "\t\"LoadAllMonsterAnimations\": " + (npcBunniesCore::LOAD_ALL_MONSTER_ANIMATIONS ? "true" : "false") + ",\n";
text += npcBunniesEditorPreview::NdeAdditionalMonsterAnimationsJson();
text += " \"NPCS\": [\n";
for(uint i = 0; i < npcBunniesCore::npcBunnies.length; i++)
{
text += NdeNpcJson(npcBunniesCore::npcBunnies[i], i + 1 == npcBunniesCore::npcBunnies.length);
}
text += " ]\n";
text += "}\n";
return text;
}
/// <summary>
/// Writes the current NPC data to the level NPC file.
/// </summary>
void NdeSaveNpcFile()
{
if(!npcBunniesCore::LocalPlayerHasNpcBunniesAdminAccess()) return;
string text = NdeBuildNpcFileText();
if(jjIsServer)
{
NdeSaveNpcFileText(text);
NdeBroadcastNpcDesignerState(text, 0);
}
else
{
NdeSendNpcDesignerStateToServer(text);
}
}
void NdeSaveNpcFileText(const string &in text)
{
NdeBackupNpcFile();
jjSTREAM save;
save.write(text);
save.save(NdeNpcFileName());
}
string NdeAccessoryJsonValue(int frame)
{
return frame < 0 ? "No" : "" + frame;
}
/// <summary>
/// Writes npc data to the NPC file stream.
/// </summary>
string NdeNpcJson(npcBunniesCore::npcBunny &in npc, bool last)
{
string text = " {\n";
text += " \"NPC\": \"" + NdeJson(npc.name) + "\",\n";
text += " \"Type\": \"" + npcBunniesEditorPreview::NdeNpcTypeFileName(npc) + "\",\n";
text += " \"Vars\": {\n";
text += " \"PosX\": " + npc.xTile + ",\n";
text += " \"PosY\": " + npc.yTile + ",\n";
text += " \"Direction\": " + npc.direction + ",\n";
text += " \"Size\": \"" + npcBunniesEditorActions::NdeNpcSizeName(npc.sizeMode) + "\",\n";
text += " \"Speed\": " + npcBunniesEditorActions::NdeNpcSpeedName(npc.moveSpeed) + ",\n";
text += " \"FurColor1\": " + npc.fur.c1 + ",\n";
text += " \"FurColor2\": " + npc.fur.c2 + ",\n";
text += " \"FurColor3\": " + npc.fur.c3 + ",\n";
text += " \"FurColor4\": " + npc.fur.c4 + ",\n";
text += " \"Hat\": \"" + NdeAccessoryJsonValue(npc.hatFrame) + "\",\n";
text += " \"Scarf\": \"" + NdeAccessoryJsonValue(npc.scarfFrame) + "\",\n";
text += " \"Glasses\": \"" + NdeAccessoryJsonValue(npc.glassesFrame) + "\",\n";
text += " \"Moveable\": " + (npc.moveable ? "true" : "false") + ",\n";
text += " \"Pushable\": " + (npc.pushable ? "true" : "false") + ",\n";
text += " \"Fighting\": { \"Enabled\": " + (npc.fightingAvailable ? "true" : "false") + ", \"Style\": \"" + npcBunniesFighting::NpcFightingStyleName(npc.fightingStyle) + "\" },\n";
text += " \"TraceMode\": \"" + npcBunniesCore::NpcTraceModeName(npc.traceMode) + "\",\n";
text += " \"TraceColor\": " + npc.traceColor + "\n";
text += " },\n";
if(npc.dialogueSets.length > 0) text += NdeDialogueSetsJson(npc);
if(npc.shopItems.length > 0) text += NdeShopJson(npc);
if(npc.guideEnabled || npc.guidePaths.length > 0) text += NdeGuideJson(npc);
text += " \"GuideEnabled\": " + (npc.guideEnabled ? "true" : "false") + "\n";
text += last ? " }\n" : " },\n";
return text;
}
string NdeDialogueSetsJson(npcBunniesCore::npcBunny &in npc)
{
string text = " \"DialogueSets\": [\n";
for(uint i = 0; i < npc.dialogueSets.length; i++)
{
npcBunniesDialogue::DialogueSet@ set = npc.dialogueSets[i];
text += " {\n";
text += " \"Type\": \"" + npcBunniesEditorPreview::NdeDialogueTypeName(set.choicesAnswerMethod) + "\",\n";
text += " \"Speaker\": " + (set.speakerNpcIndex < 0 ? "\"Default\"" : "" + (set.speakerNpcIndex + 1)) + ",\n";
text += " \"GoToSet\": " + set.goToSet + ",\n";
if(set.choicesAnswerMethod == npcBunniesCore::DialogueAction::Normal)
{
text += " \"AutoContinue\": \"" + npcBunniesEditorActions::NdeDialogueAutoContinueName(npc, set) + "\",\n";
text += " \"AutoContinueSet\": " + set.autoContinueSet + ",\n";
}
text += " \"Frame\": " + set.bigFrame + ",\n";
text += " \"QuestInProgressLine\": \"" + NdeJson(set.questInProgressLine) + "\",\n";
text += " \"QuestFinishedLine\": \"" + NdeJson(set.questCompletedLine) + "\",\n";
if(set.choicesAnswerMethod == npcBunniesCore::DialogueAction::Quest) text += " \"RespawnMonsters\": " + (set.respawnMonsters ? "true" : "false") + ",\n";
text += " \"DialogueLines\": [\n";
for(uint j = 0; j < set.dialogue.length; j++) text += " { \"Text\": \"" + NdeJson(set.dialogue[j].text) + "\" }" + (j + 1 == set.dialogue.length ? "\n" : ",\n");
text += " ]";
if(set.choices.length > 0 && (set.choicesAnswerMethod == npcBunniesCore::DialogueAction::Answer || set.choicesAnswerMethod == npcBunniesCore::DialogueAction::Choice))
{
text += ",\n \"" + (set.choicesAnswerMethod == npcBunniesCore::DialogueAction::Answer ? "TextAnswers" : "Choices") + "\": [\n";
for(uint j = 0; j < set.choices.length; j++) text += " { \"Text\": \"" + NdeJson(set.choices[j].choiceText) + "\", \"GoToSet\": " + set.choices[j].goToSet + " }" + (j + 1 == set.choices.length ? "\n" : ",\n");
text += " ]";
}
if(set.quests.length > 0) text += NdeQuestsJson(set);
if(set.rewards.length > 0) text += NdeRewardsJson(set);
text += "\n }" + (i + 1 == npc.dialogueSets.length ? "\n" : ",\n");
}
text += " ],\n";
return text;
}
string NdeQuestsJson(npcBunniesDialogue::DialogueSet@ set)
{
string text = ",\n \"Quests\": [\n";
for(uint i = 0; i < set.quests.length; i++) text += " " + NdeQuestJson(set.quests[i]) + (i + 1 == set.quests.length ? "\n" : ",\n");
text += " ]";
return text;
}
string NdeRewardsJson(npcBunniesDialogue::DialogueSet@ set)
{
string text = ",\n \"Rewards\": [\n";
for(uint i = 0; i < set.rewards.length; i++) text += " " + NdeRewardJson(set.rewards[i]) + (i + 1 == set.rewards.length ? "\n" : ",\n");
text += " ]";
return text;
}
string NdeShopJson(npcBunniesCore::npcBunny &in npc)
{
string text = " \"Shop\": {\n \"Columns\": " + npc.shopColumns + ",\n \"Items\": [\n";
for(uint i = 0; i < npc.shopItems.length; i++)
{
npcBunniesShop::NpcShopItem@ item = npc.shopItems[i];
text += " { \"Name\": \"" + NdeJson(item.name) + "\", \"Item\": " + NdeRewardJson(item.item) + ", \"PriceCoins\": " + item.priceCoins + ", \"PriceGems\": " + item.priceGems + ", \"GemType\": \"Red\", \"Stock\": \"" + (item.stock < 0 ? "inf" : "" + item.stock) + "\" }" + (i + 1 == npc.shopItems.length ? "\n" : ",\n");
}
text += " ]\n },\n";
return text;
}
string NdeGuideJson(npcBunniesCore::npcBunny &in npc)
{
string text = " \"Guide\": {\n \"Enabled\": " + (npc.guideEnabled ? "true" : "false") + ",\n \"Paths\": [\n";
for(uint i = 0; i < npc.guidePaths.length; i++)
{
text += " { \"Type\": \"" + (npc.guidePaths[i].isTeleport ? "Teleport" : "Path") + "\", \"Line\": \"" + NdeJson(npc.guidePaths[i].line) + "\"";
if(npc.guidePaths[i].isTeleport)
text += ", \"X\": " + npc.guidePaths[i].teleportX + ", \"Y\": " + npc.guidePaths[i].teleportY + ", \"Direction\": " + npc.guidePaths[i].teleportDirection;
else
{
text += ", \"Path\": [";
for(uint j = 0; j < npc.guidePaths[i].steps.length; j++) text += "[" + npc.guidePaths[i].steps[j].x + ", " + npc.guidePaths[i].steps[j].y + "]" + (j + 1 == npc.guidePaths[i].steps.length ? "" : ", ");
text += "]";
}
text += " }" + (i + 1 == npc.guidePaths.length ? "\n" : ",\n");
}
text += " ],\n";
text += " \"EndLine\": \"" + NdeJson(npc.guideEndLine) + "\",\n";
text += " \"AlreadyAtDestinationLine\": \"" + NdeJson(npc.guideAlreadyAtDestinationLine) + "\"\n";
text += " },\n";
return text;
}
void NdeWriteNpc(jjSTREAM &inout save, npcBunniesCore::npcBunny &in npc, bool last)
{
save.write("\t\t{\n");
save.write("\t\t\t\"NPC\": \"" + NdeJson(npc.name) + "\",\n");
save.write("\t\t\t\"Type\": \"" + npcBunniesEditorPreview::NdeNpcTypeFileName(npc) + "\",\n");
save.write("\t\t\t\"Vars\": {\n");
save.write("\t\t\t\t\"PosX\": " + npc.xTile + ",\n");
save.write("\t\t\t\t\"PosY\": " + npc.yTile + ",\n");
save.write("\t\t\t\t\"Direction\": " + npc.direction + ",\n");
save.write("\t\t\t\t\"Size\": \"" + npcBunniesEditorActions::NdeNpcSizeName(npc.sizeMode) + "\",\n");
save.write("\t\t\t\t\"Speed\": " + npcBunniesEditorActions::NdeNpcSpeedName(npc.moveSpeed) + ",\n");
save.write("\t\t\t\t\"FurColor1\": " + npc.fur.c1 + ",\n");
save.write("\t\t\t\t\"FurColor2\": " + npc.fur.c2 + ",\n");
save.write("\t\t\t\t\"FurColor3\": " + npc.fur.c3 + ",\n");
save.write("\t\t\t\t\"FurColor4\": " + npc.fur.c4 + ",\n");
save.write("\t\t\t\t\"Hat\": \"" + NdeAccessoryJsonValue(npc.hatFrame) + "\",\n");
save.write("\t\t\t\t\"Scarf\": \"" + NdeAccessoryJsonValue(npc.scarfFrame) + "\",\n");
save.write("\t\t\t\t\"Glasses\": \"" + NdeAccessoryJsonValue(npc.glassesFrame) + "\",\n");
save.write("\t\t\t\t\"Moveable\": " + (npc.moveable ? "true" : "false") + ",\n");
save.write("\t\t\t\t\"Pushable\": " + (npc.pushable ? "true" : "false") + ",\n");
save.write("\t\t\t\t\"Fighting\": { \"Enabled\": " + (npc.fightingAvailable ? "true" : "false") + ", \"Style\": \"" + npcBunniesFighting::NpcFightingStyleName(npc.fightingStyle) + "\" },\n");
save.write("\t\t\t\t\"TraceMode\": \"" + npcBunniesCore::NpcTraceModeName(npc.traceMode) + "\",\n");
save.write("\t\t\t\t\"TraceColor\": " + npc.traceColor + "\n");
save.write("\t\t\t},\n");
if(npc.dialogueSets.length > 0) NdeWriteDialogueSets(save, npc);
if(npc.shopItems.length > 0) NdeWriteShop(save, npc);
if(npc.guideEnabled || npc.guidePaths.length > 0) NdeWriteGuide(save, npc);
save.write("\t\t\t\"GuideEnabled\": " + (npc.guideEnabled ? "true" : "false") + "\n");
save.write(last ? "\t\t}\n" : "\t\t},\n");
}
/// <summary>
/// Writes dialogue sets data to the NPC file stream.
/// </summary>
void NdeWriteDialogueSets(jjSTREAM &inout save, npcBunniesCore::npcBunny &in npc)
{
save.write("\t\t\t\"DialogueSets\": [\n");
for(uint i = 0; i < npc.dialogueSets.length; i++)
{
npcBunniesDialogue::DialogueSet@ set = npc.dialogueSets[i];
save.write("\t\t\t\t{\n");
save.write("\t\t\t\t\t\"Type\": \"" + npcBunniesEditorPreview::NdeDialogueTypeName(set.choicesAnswerMethod) + "\",\n");
save.write("\t\t\t\t\t\"Speaker\": " + (set.speakerNpcIndex < 0 ? "\"Default\"" : "" + (set.speakerNpcIndex + 1)) + ",\n");
save.write("\t\t\t\t\t\"GoToSet\": " + set.goToSet + ",\n");
if(set.choicesAnswerMethod == npcBunniesCore::DialogueAction::Normal)
{
save.write("\t\t\t\t\t\"AutoContinue\": \"" + npcBunniesEditorActions::NdeDialogueAutoContinueName(npc, set) + "\",\n");
save.write("\t\t\t\t\t\"AutoContinueSet\": " + set.autoContinueSet + ",\n");
}
save.write("\t\t\t\t\t\"Frame\": " + set.bigFrame + ",\n");
save.write("\t\t\t\t\t\"QuestInProgressLine\": \"" + NdeJson(set.questInProgressLine) + "\",\n");
save.write("\t\t\t\t\t\"QuestFinishedLine\": \"" + NdeJson(set.questCompletedLine) + "\",\n");
if(set.choicesAnswerMethod == npcBunniesCore::DialogueAction::Quest) save.write("\t\t\t\t\t\"RespawnMonsters\": " + (set.respawnMonsters ? "true" : "false") + ",\n");
save.write("\t\t\t\t\t\"DialogueLines\": [\n");
for(uint j = 0; j < set.dialogue.length; j++)
save.write("\t\t\t\t\t\t{ \"Text\": \"" + NdeJson(set.dialogue[j].text) + "\" }" + (j + 1 == set.dialogue.length ? "\n" : ",\n"));
save.write("\t\t\t\t\t]");
if(set.choices.length > 0 && (set.choicesAnswerMethod == npcBunniesCore::DialogueAction::Answer || set.choicesAnswerMethod == npcBunniesCore::DialogueAction::Choice))
{
save.write(",\n\t\t\t\t\t\"" + (set.choicesAnswerMethod == npcBunniesCore::DialogueAction::Answer ? "TextAnswers" : "Choices") + "\": [\n");
for(uint j = 0; j < set.choices.length; j++)
save.write("\t\t\t\t\t\t{ \"Text\": \"" + NdeJson(set.choices[j].choiceText) + "\", \"GoToSet\": " + set.choices[j].goToSet + " }" + (j + 1 == set.choices.length ? "\n" : ",\n"));
save.write("\t\t\t\t\t]");
}
if(set.quests.length > 0) NdeWriteQuests(save, set);
if(set.rewards.length > 0) NdeWriteRewards(save, set);
save.write("\n\t\t\t\t}" + (i + 1 == npc.dialogueSets.length ? "\n" : ",\n"));
}
save.write("\t\t\t],\n");
}
/// <summary>
/// Writes quests data to the NPC file stream.
/// </summary>
void NdeWriteQuests(jjSTREAM &inout save, npcBunniesDialogue::DialogueSet@ set)
{
save.write(",\n\t\t\t\t\t\"Quests\": [\n");
for(uint i = 0; i < set.quests.length; i++)
save.write("\t\t\t\t\t\t" + NdeQuestJson(set.quests[i]) + (i + 1 == set.quests.length ? "\n" : ",\n"));
save.write("\t\t\t\t\t]");
}
/// <summary>
/// Writes rewards data to the NPC file stream.
/// </summary>
void NdeWriteRewards(jjSTREAM &inout save, npcBunniesDialogue::DialogueSet@ set)
{
save.write(",\n\t\t\t\t\t\"Rewards\": [\n");
for(uint i = 0; i < set.rewards.length; i++)
save.write("\t\t\t\t\t\t" + NdeRewardJson(set.rewards[i]) + (i + 1 == set.rewards.length ? "\n" : ",\n"));
save.write("\t\t\t\t\t]");
}
/// <summary>
/// Writes shop data to the NPC file stream.
/// </summary>
void NdeWriteShop(jjSTREAM &inout save, npcBunniesCore::npcBunny &in npc)
{
save.write("\t\t\t\"Shop\": {\n\t\t\t\t\"Columns\": " + npc.shopColumns + ",\n\t\t\t\t\"Items\": [\n");
for(uint i = 0; i < npc.shopItems.length; i++)
{
npcBunniesShop::NpcShopItem@ item = npc.shopItems[i];
save.write("\t\t\t\t\t{ \"Name\": \"" + NdeJson(item.name) + "\", \"Item\": " + NdeRewardJson(item.item) + ", \"PriceCoins\": " + item.priceCoins + ", \"PriceGems\": " + item.priceGems + ", \"GemType\": \"Red\", \"Stock\": \"" + (item.stock < 0 ? "inf" : "" + item.stock) + "\" }" + (i + 1 == npc.shopItems.length ? "\n" : ",\n"));
}
save.write("\t\t\t\t]\n\t\t\t},\n");
}
/// <summary>
/// Writes guide data to the NPC file stream.
/// </summary>
void NdeWriteGuide(jjSTREAM &inout save, npcBunniesCore::npcBunny &in npc)
{
save.write("\t\t\t\"Guide\": {\n\t\t\t\t\"Enabled\": " + (npc.guideEnabled ? "true" : "false") + ",\n\t\t\t\t\"Paths\": [\n");
for(uint i = 0; i < npc.guidePaths.length; i++)
{
save.write("\t\t\t\t\t{ \"Type\": \"" + (npc.guidePaths[i].isTeleport ? "Teleport" : "Path") + "\", \"Line\": \"" + NdeJson(npc.guidePaths[i].line) + "\"");
if(npc.guidePaths[i].isTeleport)
save.write(", \"X\": " + npc.guidePaths[i].teleportX + ", \"Y\": " + npc.guidePaths[i].teleportY + ", \"Direction\": " + npc.guidePaths[i].teleportDirection);
else
{
save.write(", \"Path\": [");
for(uint j = 0; j < npc.guidePaths[i].steps.length; j++)
save.write("[" + npc.guidePaths[i].steps[j].x + ", " + npc.guidePaths[i].steps[j].y + "]" + (j + 1 == npc.guidePaths[i].steps.length ? "" : ", "));
save.write("]");
}
save.write(" }" + (i + 1 == npc.guidePaths.length ? "\n" : ",\n"));
}
save.write("\t\t\t\t],\n");
save.write("\t\t\t\t\"EndLine\": \"" + NdeJson(npc.guideEndLine) + "\",\n");
save.write("\t\t\t\t\"AlreadyAtDestinationLine\": \"" + NdeJson(npc.guideAlreadyAtDestinationLine) + "\"\n");
save.write("\t\t\t},\n");
}
/// <summary>
/// Collects existing line and block comments so saving does not discard manual notes.
/// </summary>
string NdeCollectNpcFileComments()
{
jjSTREAM load(NdeNpcFileName());
if(load.isEmpty()) return "";
string comments = "";
string line;
bool inBlock = false;
bool inPreservedBlock = false;
while(load.getLine(line))
{
if(line.findFirst("NPC Designer preserved comments from previous _npc.asdat") >= 0) { inPreservedBlock = true; continue; }
if(inPreservedBlock)
{
if(line.findFirst("NPC Designer end preserved comments") >= 0) inPreservedBlock = false;
else comments += line + "\n";
continue;
}
if(inBlock)
{
int blockEnd = line.findFirst("*/");
if(blockEnd >= 0)
{
comments += line.substr(0, blockEnd + 2) + "\n";
inBlock = false;
}
else comments += line + "\n";
continue;
}
int blockAt = line.findFirst("/*");
int lineAt = line.findFirst("//");
if(blockAt >= 0 && (lineAt < 0 || blockAt < lineAt))
{
int blockEnd = line.findFirst("*/");
if(blockEnd >= blockAt) comments += line.substr(blockAt, blockEnd - blockAt + 2) + "\n";
else
{
comments += line.substr(blockAt) + "\n";
inBlock = true;
}
}
else if(lineAt >= 0)
{
comments += line.substr(lineAt) + "\n";
}
}
if(comments == "") return "";
return "// NPC Designer preserved comments from previous _npc.asdat:\n" + comments + "// NPC Designer end preserved comments\n";
}
/// <summary>
/// Serializes a reward item as parser-friendly JSON text.
/// </summary>
string NdeRewardJson(npcBunniesQuests::NpcReward@ reward)
{
string json = "{ \"RewardType\": \"" + NdeRewardTypeJsonName(reward) + "\", \"Amount\": " + reward.amount;
if(reward.rewardType == npcBunniesQuests::RewardType::RewardGems) json += ", \"GemType\": \"" + npcBunniesEditorPreview::NdeGemName(reward.gemType) + "\"";
if(reward.rewardType == npcBunniesQuests::RewardType::RewardAmmo || reward.rewardType == npcBunniesQuests::RewardType::RewardPowerup) json += ", \"Weapon\": \"" + npcBunniesShop::GetWeaponDisplayName(reward.weaponType) + "\"";
if(reward.rewardType == npcBunniesQuests::RewardType::RewardTeleport) json += ", \"X\": " + int(reward.x) + ", \"Y\": " + int(reward.y);
if(reward.rewardType == npcBunniesQuests::RewardType::RewardTrigger) json += ", \"Trigger\": " + reward.triggerId + ", \"TriggerState\": " + (reward.triggerState ? "true" : "false");
if(reward.rewardType == npcBunniesQuests::RewardType::RewardGuidePath) json += ", \"GuidePath\": " + (reward.guidePathIndex + 1);
json += " }";
return json;
}
/// <summary>
/// Serializes a quest item as parser-friendly JSON text.
/// </summary>
string NdeQuestJson(npcBunniesQuests::NpcQuest@ quest)
{
string json = "{ \"QuestType\": \"" + NdeQuestTypeJsonName(quest) + "\", \"Amount\": " + quest.amount;
if(quest.questType == npcBunniesQuests::QuestType::Gems) json += ", \"GemType\": \"" + npcBunniesEditorPreview::NdeGemName(quest.gemType) + "\"";
if(quest.questType == npcBunniesQuests::QuestType::DefeatMonster) json += ", \"Monster\": \"" + npcBunniesEditorPreview::NdeDefeatMonsterName(quest.objectType) + "\", \"EventID\": " + quest.objectType;
if(quest.questType == npcBunniesQuests::QuestType::DefeatBoss) json += ", \"Boss\": \"" + npcBunniesEditorPreview::NdeDefeatBossName(quest.objectType) + "\", \"EventID\": " + quest.objectType;
json += " }";
return json;
}
/// <summary>
/// Returns the canonical JSON QuestType value for a quest.
/// </summary>
string NdeQuestTypeJsonName(npcBunniesQuests::NpcQuest@ quest)
{
if(quest.questType == npcBunniesQuests::QuestType::Gems) return "Gems";
if(quest.questType == npcBunniesQuests::QuestType::DefeatMonster) return "DefeatMonster";
if(quest.questType == npcBunniesQuests::QuestType::DefeatBoss) return "DefeatBoss";
return "Coins";
}
/// <summary>
/// Returns the canonical JSON RewardType value for a reward.
/// </summary>
string NdeRewardTypeJsonName(npcBunniesQuests::NpcReward@ reward)
{
if(reward.rewardType == npcBunniesQuests::RewardType::RewardGems) return "Gems";
if(reward.rewardType == npcBunniesQuests::RewardType::RewardAmmo) return "Ammo";
if(reward.rewardType == npcBunniesQuests::RewardType::RewardPowerup) return "Powerup";
if(reward.rewardType == npcBunniesQuests::RewardType::RewardTeleport) return "Teleport";
if(reward.rewardType == npcBunniesQuests::RewardType::RewardMoveable) return "Moveable";
if(reward.rewardType == npcBunniesQuests::RewardType::RewardTrigger) return "Trigger";
if(reward.rewardType == npcBunniesQuests::RewardType::RewardFastFire) return "FastFire";
if(reward.rewardType == npcBunniesQuests::RewardType::RewardGuidePath) return "GuidePath";
return "Coins";
}
/// <summary>
/// Escapes a string for JSON output.
/// </summary>
string NdeJson(const string &in text)
{
string escaped = "";
for(uint i = 0; i < text.length; i++)
{
uint8 c = text[i];
if(c == 34) escaped += "\\\"";
else if(c == 92) escaped += "\\\\";
else { escaped += " "; escaped[escaped.length - 1] = c; }
}
return escaped;
}
//----------------------------------------------------------------------------------------------------
// NPC DESIGNER HELP REGION
// Edit npcHelp.asc freely; NdeDrawHelp will wrap long lines and long words to the current screen.
}
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.