| Name | Author | Game Mode | Rating | |||||
|---|---|---|---|---|---|---|---|---|
| NpcBunnies | Cranky | Mutator | N/A | |||||
const string DB_PREFERENCES_FILE = "DialogueBoxPreferences.asdat";
const int DB_ACTION_NONE = 0;
const int DB_ACTION_CLOSE = -1;
const int DB_ACTION_TAB = -2;
const int DB_ACTION_SCROLL_UP = -3;
const int DB_ACTION_SCROLL_DOWN = -4;
const int DB_FOCUS_OFFSET_Y = -10;
class DbResult
{
bool activated = false;
bool closeRequested = false;
bool tabChanged = false;
bool positionChanged = false;
int action = DB_ACTION_NONE;
int a = 0;
int b = 0;
int tab = 0;
}
class DbControl
{
int x;
int y;
int w;
int h;
int action;
int a;
int b;
string text;
bool enabled = true;
}
class DbWindow
{
string id;
string title;
string preferencesFile = "DialogueBoxPreferences.asdat";
int x;
int y;
int w;
int h;
int pad = 10;
int titleHeight = 18;
int focusIndex = 0;
int activeTab = 0;
int lastClickTick = -9999;
int lastKeyTick = -9999;
bool mouseWasDown = false;
bool dragging = false;
int dragOffsetX = 0;
int dragOffsetY = 0;
bool closeButton = true;
bool draggable = false;
bool titleBar = true;
int backgroundColor = 32;
int titleColor = 72;
int focusColor = 15;
array<DbControl> controls;
}
array<string> dbPreferenceIds;
array<int> dbPreferenceX;
array<int> dbPreferenceY;
bool dbPreferencesLoaded = false;
string dbPreferencesLoadedFile = "";
int DbPreferenceIndex(const string &in id)
{
for(uint i = 0; i < dbPreferenceIds.length; i++)
if(dbPreferenceIds[i] == id) return int(i);
return -1;
}
void DbLoadPreferences(const string &in filename)
{
if(dbPreferencesLoaded && dbPreferencesLoadedFile == filename) return;
dbPreferencesLoaded = true;
dbPreferencesLoadedFile = filename;
dbPreferenceIds.resize(0);
dbPreferenceX.resize(0);
dbPreferenceY.resize(0);
jjSTREAM load(filename);
string line;
while(load.getLine(line))
{
array<string> parts = line.split(" ");
if(parts.length >= 3)
{
dbPreferenceIds.insertLast(parts[0]);
dbPreferenceX.insertLast(parseInt(parts[1]));
dbPreferenceY.insertLast(parseInt(parts[2]));
}
}
}
void DbSavePreferences(const string &in filename)
{
jjSTREAM save;
for(uint i = 0; i < dbPreferenceIds.length; i++)
save.write(dbPreferenceIds[i] + " " + dbPreferenceX[i] + " " + dbPreferenceY[i] + "\n");
save.save(filename);
}
void DbApplySavedPosition(DbWindow@ window)
{
DbLoadPreferences(window.preferencesFile);
int index = DbPreferenceIndex(window.id);
if(index >= 0)
{
window.x = dbPreferenceX[index];
window.y = dbPreferenceY[index];
}
DbClampToScreen(window);
}
void DbRememberPosition(DbWindow@ window)
{
DbLoadPreferences(window.preferencesFile);
int index = DbPreferenceIndex(window.id);
if(index < 0)
{
dbPreferenceIds.insertLast(window.id);
dbPreferenceX.insertLast(window.x);
dbPreferenceY.insertLast(window.y);
}
else
{
dbPreferenceX[index] = window.x;
dbPreferenceY[index] = window.y;
}
DbSavePreferences(window.preferencesFile);
}
void DbClampToScreen(DbWindow@ window)
{
if(window.x + window.w > jjResolutionWidth) window.x = jjResolutionWidth - window.w;
if(window.y + window.h > jjResolutionHeight) window.y = jjResolutionHeight - window.h;
if(window.x < 0) window.x = 0;
if(window.y < 0) window.y = 0;
}
void DbBegin(DbWindow@ window, const string &in id, const string &in title, int x, int y, int w, int h)
{
if(window.preferencesFile == "") window.preferencesFile = DB_PREFERENCES_FILE;
bool sameWindow = window.id == id;
window.id = id;
window.title = title;
window.w = w;
window.h = h;
window.controls.resize(0);
if(sameWindow)
{
DbClampToScreen(window);
}
else
{
window.x = x;
window.y = y;
DbApplySavedPosition(window);
}
}
void DbClearControls(DbWindow@ window)
{
window.controls.resize(0);
}
void DbAddControl(DbWindow@ window, int x, int y, int w, int h, int action, int a, int b, const string &in text, bool enabled = true)
{
DbControl control;
control.x = x;
control.y = y;
control.w = w;
control.h = h;
control.action = action;
control.a = a;
control.b = b;
control.text = text;
control.enabled = enabled;
window.controls.insertLast(control);
}
array<string> DbWrappedSmallLines(const string &in text, int maxPixels)
{
array<string> lines;
if(maxPixels <= 0) return lines;
array<string> paragraphs = text.split("\n");
for(uint p = 0; p < paragraphs.length; p++)
{
if(paragraphs[p] == "")
{
lines.insertLast("");
continue;
}
array<string> words = paragraphs[p].split(" ");
string line = "";
for(uint i = 0; i < words.length; i++)
{
string remaining = words[i];
while(remaining != "" && jjGetStringWidth(remaining, STRING::SMALL, STRING::NORMAL) > maxPixels)
{
int take = int(remaining.length());
while(take > 1 && jjGetStringWidth(remaining.substr(0, take), STRING::SMALL, STRING::NORMAL) > maxPixels)
take--;
if(line != "")
{
lines.insertLast(line);
line = "";
}
lines.insertLast(remaining.substr(0, take));
remaining = remaining.substr(take);
}
if(remaining == "") continue;
string candidate = line == "" ? remaining : line + " " + remaining;
if(jjGetStringWidth(candidate, STRING::SMALL, STRING::NORMAL) <= maxPixels) line = candidate;
else
{
if(line != "") lines.insertLast(line);
line = remaining;
}
}
if(line != "") lines.insertLast(line);
}
if(lines.length == 0) lines.insertLast("");
return lines;
}
int DbWrappedSmallLineCount(const string &in text, int maxPixels)
{
array<string> lines = DbWrappedSmallLines(text, maxPixels);
return int(lines.length);
}
int DbVisibleSmallLines(int pixelHeight, int lineHeight = 14)
{
int lines = pixelHeight / lineHeight;
return lines < 1 ? 1 : lines;
}
int DbClampSmallScroll(const string &in text, int maxPixels, int pixelHeight, int scrollLine, int lineHeight = 14)
{
int maxScroll = DbWrappedSmallLineCount(text, maxPixels) - DbVisibleSmallLines(pixelHeight, lineHeight);
if(maxScroll < 0) maxScroll = 0;
if(scrollLine < 0) return 0;
if(scrollLine > maxScroll) return maxScroll;
return scrollLine;
}
int DbDrawWrappedSmallScrolled(jjCANVAS@ canvas, int x, int y, const string &in text, int maxPixels, int maxY, int scrollLine, int lineHeight = 14)
{
array<string> lines = DbWrappedSmallLines(text, maxPixels);
for(uint i = 0; i < lines.length; i++)
{
if(int(i) < scrollLine) continue;
if(y + lineHeight > maxY) break;
canvas.drawString(x, y, lines[i], STRING::SMALL, STRING::NORMAL);
y += lineHeight;
}
return y;
}
int DbDrawWrappedSmall(jjCANVAS@ canvas, int x, int y, const string &in text, int maxPixels, int maxY, int lineHeight = 14)
{
return DbDrawWrappedSmallScrolled(canvas, x, y, text, maxPixels, maxY, 0, lineHeight);
}
int DbDrawWrappedSmallInBox(jjCANVAS@ canvas, int x, int y, const string &in text, int maxPixels, int pixelHeight, int lineHeight = 14)
{
return DbDrawWrappedSmall(canvas, x, y, text, maxPixels, y + pixelHeight, lineHeight);
}
int DbDrawScrollableSmallText(jjCANVAS@ canvas, DbWindow@ window, int x, int y, int w, int maxY, const string &in text, int scrollLine, bool scrollButtons = true, int scrollUpAction = DB_ACTION_SCROLL_UP, int scrollDownAction = DB_ACTION_SCROLL_DOWN, int lineHeight = 14)
{
int textW = scrollButtons ? w - 62 : w;
if(textW < 20) textW = w;
int visibleHeight = maxY - y;
if(visibleHeight < lineHeight) visibleHeight = lineHeight;
int maxScroll = DbWrappedSmallLineCount(text, textW) - DbVisibleSmallLines(visibleHeight, lineHeight);
if(maxScroll < 0) maxScroll = 0;
if(scrollLine < 0) scrollLine = 0;
if(scrollLine > maxScroll) scrollLine = maxScroll;
if(scrollButtons && maxScroll > 0)
{
if(scrollLine > 0) DbDrawButton(canvas, window, x + w - 46, y, 42, 14, scrollUpAction, 0, -1, "UP");
if(scrollLine < maxScroll) DbDrawButton(canvas, window, x + w - 58, maxY - 12, 54, 14, scrollDownAction, 0, 1, "DOWN");
}
DbDrawWrappedSmallScrolled(canvas, x, y, text, textW, maxY, scrollLine, lineHeight);
return scrollLine;
}
int DbHandleScrollResult(DbResult@ result, int scrollLine, int scrollUpAction = DB_ACTION_SCROLL_UP, int scrollDownAction = DB_ACTION_SCROLL_DOWN)
{
if(result.action == scrollUpAction)
{
scrollLine--;
return scrollLine;
}
if(result.action == scrollDownAction)
{
scrollLine++;
return scrollLine;
}
return scrollLine;
}
bool DbIsScrollResult(DbResult@ result, int scrollUpAction = DB_ACTION_SCROLL_UP, int scrollDownAction = DB_ACTION_SCROLL_DOWN)
{
return result.action == scrollUpAction || result.action == scrollDownAction;
}
// EDITABLE boxes
int DbClampTextCursor(const string &in text, int cursor)
{
if(cursor < 0) return 0;
if(cursor > int(text.length)) return int(text.length);
return cursor;
}
string DbInsertTextAtCursor(const string &in text, int cursor, const string &in insert)
{
cursor = DbClampTextCursor(text, cursor);
return text.substr(0, cursor) + insert + text.substr(cursor);
}
string DbBackspaceTextAtCursor(const string &in text, int cursor)
{
cursor = DbClampTextCursor(text, cursor);
if(cursor <= 0) return text;
return text.substr(0, cursor - 1) + text.substr(cursor);
}
int DbMoveTextCursor(const string &in text, int cursor, int delta)
{
return DbClampTextCursor(text, cursor + delta);
}
int DbEditableCursorLine(const string &in text, int maxPixels, int cursor)
{
array<string> lines = DbWrappedSmallLines(text, maxPixels);
cursor = DbClampTextCursor(text, cursor);
int remaining = cursor;
for(uint i = 0; i < lines.length; i++)
{
int lineLen = int(lines[i].length);
if(remaining <= lineLen) return int(i);
remaining -= lineLen + 1;
}
return int(lines.length) - 1;
}
int DbClosestCursorInLine(const string &in line, int targetPixels)
{
int best = 0;
int bestDistance = 99999;
for(int i = 0; i <= int(line.length); i++)
{
int width = jjGetStringWidth(line.substr(0, i), STRING::SMALL, STRING::NORMAL);
int distance = width > targetPixels ? width - targetPixels : targetPixels - width;
if(distance < bestDistance)
{
bestDistance = distance;
best = i;
}
}
return best;
}
int DbMoveTextCursorVertical(const string &in text, int maxPixels, int cursor, int delta)
{
array<string> lines = DbWrappedSmallLines(text, maxPixels);
if(lines.length == 0) return 0;
cursor = DbClampTextCursor(text, cursor);
int remaining = cursor;
int currentLine = 0;
int currentColumn = 0;
for(uint i = 0; i < lines.length; i++)
{
int lineLen = int(lines[i].length);
if(remaining <= lineLen || i + 1 == lines.length)
{
currentLine = int(i);
currentColumn = remaining < lineLen ? remaining : lineLen;
break;
}
remaining -= lineLen + 1;
}
int targetLine = currentLine + delta;
if(targetLine < 0) targetLine = 0;
if(targetLine >= int(lines.length)) targetLine = int(lines.length) - 1;
int targetPixels = jjGetStringWidth(lines[currentLine].substr(0, currentColumn), STRING::SMALL, STRING::NORMAL);
int targetColumn = DbClosestCursorInLine(lines[targetLine], targetPixels);
int targetCursor = 0;
for(int i = 0; i < targetLine; i++)
targetCursor += int(lines[i].length) + 1;
targetCursor += targetColumn;
return DbClampTextCursor(text, targetCursor);
}
int DbClampEditableScrollToCursor(const string &in text, int maxPixels, int pixelHeight, int scrollLine, int cursor, int lineHeight = 14)
{
int cursorLine = DbEditableCursorLine(text, maxPixels, cursor);
int visibleLines = DbVisibleSmallLines(pixelHeight, lineHeight);
if(cursorLine < scrollLine) scrollLine = cursorLine;
if(cursorLine >= scrollLine + visibleLines) scrollLine = cursorLine - visibleLines + 1;
return DbClampSmallScroll(text, maxPixels, pixelHeight, scrollLine, lineHeight);
}
int DbSmallSpaceWidth()
{
int width = jjGetStringWidth("x x", STRING::SMALL, STRING::NORMAL) - jjGetStringWidth("xx", STRING::SMALL, STRING::NORMAL);
return width > 0 ? width : 4;
}
int DbSmallTextWidthPreserveTrailingSpaces(const string &in text)
{
int trailingSpaces = 0;
for(int i = int(text.length) - 1; i >= 0 && text[i] == 32; i--)
trailingSpaces++;
if(trailingSpaces <= 0) return jjGetStringWidth(text, STRING::SMALL, STRING::NORMAL);
return jjGetStringWidth(text.substr(0, int(text.length) - trailingSpaces), STRING::SMALL, STRING::NORMAL) + trailingSpaces * DbSmallSpaceWidth();
}
void DbDrawEditableSmallCursor(jjCANVAS@ canvas, int x, int y, const string &in text, int maxPixels, int maxY, int cursor, int scrollLine, int lineHeight = 14, int color = 80)
{
array<string> lines = DbWrappedSmallLines(text, maxPixels);
cursor = DbClampTextCursor(text, cursor);
int remaining = cursor;
for(uint i = 0; i < lines.length; i++)
{
int lineLen = int(lines[i].length);
if(remaining <= lineLen || i + 1 == lines.length)
{
if(int(i) < scrollLine) return;
int cursorY = y + (int(i) - scrollLine) * lineHeight - 5;
if(cursorY + lineHeight > maxY) return;
int cursorChars = remaining < lineLen ? remaining : lineLen;
int extraSpaces = remaining > lineLen ? remaining - lineLen : 0;
int cursorX = x + DbSmallTextWidthPreserveTrailingSpaces(lines[i].substr(0, cursorChars)) + extraSpaces * DbSmallSpaceWidth();
canvas.drawRectangle(cursorX, cursorY - 1, 2, lineHeight, color, SPRITE::NORMAL);
return;
}
remaining -= lineLen + 1;
if(remaining < 0) remaining = 0;
}
canvas.drawRectangle(x, y, 2, lineHeight, color, SPRITE::NORMAL);
}
string DbClipSmall(const string &in text, int maxPixels)
{
return text;
}
void DbDrawFrame(jjCANVAS@ canvas, DbWindow@ window)
{
canvas.drawRectangle(window.x, window.y, window.w, window.h, 0, SPRITE::SHADOW);
canvas.drawRectangle(window.x + 2, window.y + 2, window.w - 4, window.h - 4, window.backgroundColor, SPRITE::TRANSLUCENT);
if(window.titleBar)
{
canvas.drawRectangle(window.x + 2, window.y + 2, window.w - 4, window.titleHeight - 3, window.titleColor, SPRITE::NORMAL);
canvas.drawString(window.x + window.pad, window.y + 10, window.title, STRING::SMALL, STRING::NORMAL);
}
else canvas.drawString(window.x + window.pad, window.y + window.pad, window.title, STRING::MEDIUM, STRING::NORMAL);
if(window.closeButton)
DbDrawButton(canvas, window, window.x + window.w - 24, window.y + (window.titleBar ? 8 : window.pad), 18, 15, DB_ACTION_CLOSE, 0, 0, "X");
}
void DbDrawFocus(jjCANVAS@ canvas, DbWindow@ window, int focusOffsetY = DB_FOCUS_OFFSET_Y, int focusHeight = 0)
{
DbNormalizeFocus(window);
if(window.focusIndex < 0 || window.focusIndex >= int(window.controls.length)) return;
DbControl control = window.controls[window.focusIndex];
if(!control.enabled) return;
int h = focusHeight > 0 ? focusHeight : control.h;
canvas.drawRectangle(control.x - 3, control.y + focusOffsetY, control.w + 6, h, window.focusColor, SPRITE::TRANSLUCENT);
}
void DbDrawButton(jjCANVAS@ canvas, DbWindow@ window, int x, int y, int w, int h, int action, int a, int b, const string &in text, bool enabled = true, bool selected = false)
{
string shown = enabled ? text : "||" + text;
int drawH = h;
int wrappedH = DbWrappedSmallLineCount(shown, w) * 14;
if(wrappedH > drawH) drawH = wrappedH;
DbAddControl(window, x, y, w, drawH, action, a, b, text, enabled);
if(selected)
canvas.drawRectangle(x - 3, y + DB_FOCUS_OFFSET_Y, w + 6, drawH, 24, SPRITE::TRANSLUCENT);
DbDrawWrappedSmall(canvas, x, y, shown, w, y + drawH);
}
void DbDrawTab(jjCANVAS@ canvas, DbWindow@ window, int x, int y, int w, int tab, const string &in text, bool enabled = true)
{
string shown = window.activeTab == tab ? "|||" + text : (enabled ? "||||" + text : "||" + text);
DbDrawButton(canvas, window, x, y, w, 16, DB_ACTION_TAB, tab, 0, shown, enabled, window.activeTab == tab);
}
void DbDrawStepper(jjCANVAS@ canvas, DbWindow@ window, int x, int y, int textW, int action, int a, int prevB, int nextB, const string &in text)
{
DbDrawButton(canvas, window, x, y, 16, 16, action, a, prevB, "<");
DbDrawWrappedSmall(canvas, x + 22, y, text, textW, y + DbWrappedSmallLineCount(text, textW) * 14);
DbDrawButton(canvas, window, x + 26 + textW, y, 16, 16, action, a, nextB, ">");
}
void DbContentRect(DbWindow@ window, int &out x, int &out y, int &out w, int &out h)
{
x = window.x + window.pad;
y = window.y + (window.titleBar ? window.titleHeight + window.pad : window.pad + 20);
w = window.w - window.pad * 2;
h = window.h - (y - window.y) - window.pad;
}
bool DbPointIn(int tx, int ty, int x, int y, int w, int h)
{
return tx >= x && tx <= x + w && ty >= y && ty <= y + h;
}
int DbHitControl(DbWindow@ window, int tx, int ty)
{
for(int i = int(window.controls.length) - 1; i >= 0; i--)
{
DbControl control = window.controls[i];
if(control.enabled && DbPointIn(tx, ty, control.x, control.y, control.w, control.h)) return i;
}
return -1;
}
void DbActivateControl(DbWindow@ window, int index, DbResult@ result)
{
if(index < 0 || index >= int(window.controls.length)) return;
DbControl control = window.controls[index];
if(!control.enabled) return;
result.activated = true;
result.action = control.action;
result.a = control.a;
result.b = control.b;
if(control.action == DB_ACTION_CLOSE) result.closeRequested = true;
else if(control.action == DB_ACTION_TAB)
{
window.activeTab = control.a;
result.tabChanged = true;
result.tab = control.a;
}
}
bool DbProcessMouse(DbWindow@ window, DbResult@ result)
{
bool mouseDown = jjKey[0x01];
int tx = jjMouseX;
int ty = jjMouseY + 8;
bool clicked = mouseDown && !window.mouseWasDown && jjGameTicks > window.lastClickTick + 10;
if(!mouseDown)
{
if(window.dragging)
{
result.positionChanged = true;
DbRememberPosition(window);
}
window.mouseWasDown = false;
window.dragging = false;
}
else window.mouseWasDown = true;
if(window.dragging)
{
window.x = tx - window.dragOffsetX;
window.y = ty - window.dragOffsetY;
DbClampToScreen(window);
result.positionChanged = true;
return true;
}
if(!clicked) return false;
int controlIndex = DbHitControl(window, tx, ty);
if(controlIndex >= 0)
{
window.focusIndex = controlIndex;
window.lastClickTick = jjGameTicks;
DbActivateControl(window, controlIndex, result);
return true;
}
if(window.draggable && window.titleBar && DbPointIn(tx, ty, window.x + 2, window.y + 2, window.w - 4, window.titleHeight - 3))
{
window.dragging = true;
window.dragOffsetX = tx - window.x;
window.dragOffsetY = ty - window.y;
window.lastClickTick = jjGameTicks;
return true;
}
return false;
}
void DbNormalizeFocus(DbWindow@ window)
{
int count = int(window.controls.length);
if(count <= 0) { window.focusIndex = 0; return; }
while(window.focusIndex < 0) window.focusIndex += count;
while(window.focusIndex >= count) window.focusIndex -= count;
for(int attempts = 0; attempts < count && !window.controls[window.focusIndex].enabled; attempts++)
{
window.focusIndex++;
if(window.focusIndex >= count) window.focusIndex = 0;
}
}
void DbMoveFocus(DbWindow@ window, int delta)
{
if(window.controls.length == 0) return;
window.focusIndex += delta;
DbNormalizeFocus(window);
}
bool DbProcessKeyboard(DbWindow@ window, jjPLAYER@ play, DbResult@ result, int repeatTicks = 10)
{
if(jjGameTicks - window.lastKeyTick < repeatTicks) return false;
if(play.keyFire || play.keySelect || jjKey[0x0D])
{
window.lastKeyTick = jjGameTicks;
DbNormalizeFocus(window);
DbActivateControl(window, window.focusIndex, result);
return true;
}
if(play.keyUp || play.keyLeft || jjKey[0x26] || jjKey[0x25])
{
window.lastKeyTick = jjGameTicks;
DbMoveFocus(window, -1);
return true;
}
if(play.keyDown || play.keyRight || jjKey[0x28] || jjKey[0x27])
{
window.lastKeyTick = jjGameTicks;
DbMoveFocus(window, 1);
return true;
}
return false;
}
void DbResetResult(DbResult@ result)
{
result.activated = false;
result.closeRequested = false;
result.tabChanged = false;
result.positionChanged = false;
result.action = DB_ACTION_NONE;
result.a = 0;
result.b = 0;
result.tab = 0;
}
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.