mirror of
https://gitgud.io/BondageProjects/Bondage-College.git
synced 2025-04-03 15:09:13 +00:00
340 lines
No EOL
13 KiB
JavaScript
340 lines
No EOL
13 KiB
JavaScript
// Main variables
|
|
var CurrentIntro;
|
|
var CurrentStage;
|
|
var CurrentText;
|
|
var CurrentChapter;
|
|
var CurrentScreen;
|
|
var CurrentLanguageTag = "EN";
|
|
var OverridenIntroText;
|
|
var OverridenIntroImage;
|
|
var LeaveChapter = "";
|
|
var LeaveScreen = "";
|
|
var LeaveIcon = "";
|
|
var MouseX = 0;
|
|
var MouseY = 0;
|
|
var KeyPress = "";
|
|
var IsMobile = false;
|
|
var TextPhase = 0;
|
|
var CSVCache = {};
|
|
var MaxFightSequence = 500;
|
|
var MaxRaceSequence = 1000;
|
|
var AllowCheats = false;
|
|
|
|
// Array variables
|
|
var IntroStage = 0;
|
|
var IntroLoveReq = 1;
|
|
var IntroSubReq = 2;
|
|
var IntroVarReq = 3;
|
|
var IntroText = 4;
|
|
var IntroImage = 5;
|
|
var StageNumber = 0;
|
|
var StageLoveReq = 1;
|
|
var StageSubReq = 2;
|
|
var StageVarReq = 3;
|
|
var StageInteractionText = 4;
|
|
var StageInteractionResult = 5;
|
|
var StageNextStage = 6;
|
|
var StageLoveMod = 7;
|
|
var StageSubMod = 8;
|
|
var StageFunction = 9;
|
|
var TextTag = 0;
|
|
var TextContent = 1;
|
|
var FightMoveType = 0;
|
|
var FightMoveTime = 1;
|
|
var RaceMoveType = 0;
|
|
var RaceMoveTime = 1;
|
|
|
|
// Common variables
|
|
var Common_BondageAllowed = true;
|
|
var Common_SelfBondageAllowed = true;
|
|
var Common_PlayerName = "";
|
|
var Common_PlayerOwner = "";
|
|
var Common_PlayerLover = "";
|
|
var Common_PlayerRestrained = false;
|
|
var Common_PlayerGagged = false;
|
|
var Common_PlayerBlinded = false;
|
|
var Common_PlayerChaste = false;
|
|
var Common_PlayerNotRestrained = true;
|
|
var Common_PlayerNotGagged = true;
|
|
var Common_PlayerNotBlinded = true;
|
|
var Common_PlayerClothed = true;
|
|
var Common_PlayerUnderwear = false;
|
|
var Common_PlayerNaked = false;
|
|
var Common_PlayerCloth = "";
|
|
var Common_PlayerCostume = "";
|
|
var Common_PlayerPose = "";
|
|
var Common_ActorIsLover = false;
|
|
var Common_ActorIsOwner = false;
|
|
var Common_ActorIsOwned = false;
|
|
var Common_Number = "";
|
|
|
|
// Returns TRUE if the variable is a number
|
|
function IsNumeric(n) {
|
|
return !isNaN(parseFloat(n)) && isFinite(n);
|
|
}
|
|
|
|
// Returns the current date and time in a yyyy-mm-dd hh:mm:ss format
|
|
function GetFormatDate() {
|
|
var d = new Date();
|
|
var yyyy = d.getFullYear();
|
|
var mm = d.getMonth() < 9 ? "0" + (d.getMonth() + 1) : (d.getMonth() + 1); // getMonth() is zero-based
|
|
var dd = d.getDate() < 10 ? "0" + d.getDate() : d.getDate();
|
|
var hh = d.getHours() < 10 ? "0" + d.getHours() : d.getHours();
|
|
var min = d.getMinutes() < 10 ? "0" + d.getMinutes() : d.getMinutes();
|
|
var ss = d.getSeconds() < 10 ? "0" + d.getSeconds() : d.getSeconds();
|
|
return "".concat(yyyy).concat("-").concat(mm).concat("-").concat(dd).concat(" ").concat(hh).concat(":").concat(min).concat(":").concat(ss);
|
|
}
|
|
|
|
// Used to detect whether the users browser is an mobile browser
|
|
function DetectMobile() {
|
|
|
|
// First check
|
|
if (sessionStorage.desktop) return false;
|
|
else if (localStorage.mobile) return true;
|
|
|
|
// Alternative check
|
|
var mobile = ['iphone','ipad','android','blackberry','nokia','opera mini','windows mobile','windows phone','iemobile','mobile/'];
|
|
for (var i in mobile) if (navigator.userAgent.toLowerCase().indexOf(mobile[i].toLowerCase()) > 0) return true;
|
|
|
|
// If nothing is found, we assume desktop
|
|
return false;
|
|
}
|
|
|
|
// Parse a CSV file
|
|
function ParseCSV(str) {
|
|
|
|
var arr = [];
|
|
var quote = false; // true means we're inside a quoted field
|
|
|
|
// iterate over each character, keep track of current row and column (of the returned array)
|
|
for (var row = col = c = 0; c < str.length; c++) {
|
|
var cc = str[c], nc = str[c+1]; // current character, next character
|
|
arr[row] = arr[row] || []; // create a new row if necessary
|
|
arr[row][col] = arr[row][col] || ''; // create a new column (start with empty string) if necessary
|
|
|
|
// If the current character is a quotation mark, and we're inside a
|
|
// quoted field, and the next character is also a quotation mark,
|
|
// add a quotation mark to the current column and skip the next character
|
|
if (cc == '"' && quote && nc == '"') { arr[row][col] += cc; ++c; continue; }
|
|
|
|
// If it's just one quotation mark, begin/end quoted field
|
|
if (cc == '"') { quote = !quote; continue; }
|
|
|
|
// If it's a comma and we're not in a quoted field, move on to the next column
|
|
if (cc == ',' && !quote) { ++col; continue; }
|
|
|
|
// If it's a newline and we're not in a quoted field, move on to the next
|
|
// row and move to column 0 of that new row
|
|
if (cc == '\n' && !quote) { ++row; col = 0; continue; }
|
|
|
|
// Otherwise, append the current character to the current column
|
|
arr[row][col] += cc;
|
|
}
|
|
return arr;
|
|
}
|
|
|
|
// Read a CSV file from the web site
|
|
function ReadCSV(Array, ChapterOrPath, Screen, Type, Language) {
|
|
// Changed from a single path to various arguments and internally concatenate them
|
|
// This ternary operator is used to keep backward compatibility
|
|
var Path = (Screen && Type)
|
|
? ChapterOrPath + "/" + Screen + "/" + Type + (Language ? "_" : "") + (Language || "") + ".csv"
|
|
: ChapterOrPath;
|
|
|
|
if (CSVCache[Path]) {
|
|
window[Array] = CSVCache[Path];
|
|
return;
|
|
}
|
|
|
|
// Opens the file, parse it and returns the result in an array
|
|
Get(Path + (TranslationCheatAllow ? "?force_" + TranslationCacheCounter : ""), function() {
|
|
if (this.status == 200) {
|
|
CSVCache[Path] = ParseCSV(this.responseText);
|
|
window[Array] = CSVCache[Path];
|
|
} else if (this.status == 404 && Language && Language != "EN") { // If language isn't EN and the file doesn't exist, then fallback to EN
|
|
ReadCSV(Array, ChapterOrPath, Screen, Type, "EN");
|
|
}
|
|
});
|
|
}
|
|
|
|
// AJAX utility
|
|
function Get(Path, Callback) {
|
|
var xhr = new XMLHttpRequest();
|
|
xhr.open("GET", Path);
|
|
xhr.onreadystatechange = function() { if (this.readyState == 4) Callback.bind(this)(); };
|
|
xhr.send(null);
|
|
}
|
|
|
|
// Shuffles all array elements at random
|
|
function ArrayShuffle(a) {
|
|
var j, x, i;
|
|
for (i = a.length - 1; i > 0; i--) {
|
|
j = Math.floor(Math.random() * (i + 1));
|
|
x = a[i];
|
|
a[i] = a[j];
|
|
a[j] = x;
|
|
}
|
|
return a;
|
|
}
|
|
|
|
// Returns a working language if translation isn't fully ready
|
|
function GetWorkingLanguage() {
|
|
return GetWorkingLanguageForChapter(CurrentChapter);
|
|
}
|
|
|
|
// Returns a working language for a specific chapter
|
|
function GetWorkingLanguageForChapter(Chapter) {
|
|
if ((CurrentLanguageTag == "FR") && ["C000_Intro", "C001_BeforeClass", "C002_FirstClass", "C003_MorningDetention", "C004_ArtClass", "C005_GymClass", "C006_Isolation", "C007_LunchBreak", "C008_DramaClass", "C009_Library", "C999_Common"].indexOf(Chapter) >= 0) return "FR";
|
|
if ((CurrentLanguageTag == "DE") && ["C000_Intro", "C001_BeforeClass", "C002_FirstClass", "C003_MorningDetention", "C004_ArtClass", "C005_GymClass", "C006_Isolation", "C007_LunchBreak", "C008_DramaClass", "C009_Library", "C010_Revenge", "C011_LiteratureClass", "C012_AfterClass", "C013_BondageClub", "C101_KinbakuClub", "C999_Common"].indexOf(Chapter) >= 0) return "DE";
|
|
if ((CurrentLanguageTag == "PL") && ["C000_Intro"].indexOf(Chapter) >= 0) return "PL";
|
|
if ((CurrentLanguageTag == "ES") && ["C000_Intro", "C001_BeforeClass", "C002_FirstClass", "C003_MorningDetention"].indexOf(Chapter) >= 0) return "ES";
|
|
if ((CurrentLanguageTag == "CN") && ["C000_Intro", "C001_BeforeClass", "C002_FirstClass", "C003_MorningDetention", "C004_ArtClass", "C005_GymClass", "C006_Isolation", "C007_LunchBreak", "C008_DramaClass", "C009_Library", "C010_Revenge", "C011_LiteratureClass", "C012_AfterClass", "C013_BondageClub", "C101_KinbakuClub", "C999_Common"].indexOf(Chapter) >= 0) return "CN";
|
|
if ((CurrentLanguageTag == "RU") && ["C000_Intro", "C001_BeforeClass"].indexOf(Chapter) >= 0) return "RU";
|
|
if ((CurrentLanguageTag == "CS") && ["C000_Intro", "C001_BeforeClass", "C002_FirstClass", "C003_MorningDetention", "C004_ArtClass", "C005_GymClass", "C006_Isolation", "C007_LunchBreak", "C999_Common"].indexOf(Chapter) >= 0) return "CS";
|
|
return "EN";
|
|
//return CurrentLanguageTag;
|
|
}
|
|
|
|
// Load the interactions from a scene and keep it in common variable
|
|
function LoadInteractions() {
|
|
ReadCSV("CurrentIntro", CurrentChapter, CurrentScreen, "Intro", GetWorkingLanguage());
|
|
ReadCSV("CurrentStage", CurrentChapter, CurrentScreen, "Stage", GetWorkingLanguage());
|
|
LoadText();
|
|
}
|
|
|
|
// Load the custom texts from a scene and keep it in common variable
|
|
function LoadText() {
|
|
ReadCSV("CurrentText", CurrentChapter, CurrentScreen, "Text", GetWorkingLanguage());
|
|
}
|
|
|
|
// Calls a dynamic function (if it exists)
|
|
function DynamicFunction(FunctionName) {
|
|
if (typeof window[FunctionName.substr(0, FunctionName.indexOf("("))] == "function") {
|
|
var Fct = new Function(FunctionName);
|
|
Fct();
|
|
} else console.log("Trying to launch invalid function: " + FunctionName);
|
|
}
|
|
|
|
// Set the current scene (chapter and screen)
|
|
function SetScene(Chapter, Screen) {
|
|
|
|
// Keep the chapter and screen
|
|
CurrentStage = null;
|
|
CurrentIntro = null;
|
|
CurrentText = null;
|
|
CurrentActor = "";
|
|
CurrentChapter = Chapter;
|
|
CurrentScreen = Screen;
|
|
OverridenIntroText = "";
|
|
OverridenIntroImage = "";
|
|
LeaveIcon = "";
|
|
LeaveScreen = "";
|
|
LeaveChapter = Chapter;
|
|
Common_ActorIsLover = false;
|
|
Common_ActorIsOwner = false;
|
|
Common_ActorIsOwned = false;
|
|
|
|
// Load the screen code
|
|
DynamicFunction(CurrentChapter + "_" + CurrentScreen + "_Load()");
|
|
|
|
}
|
|
|
|
// Validates if any interaction was clicked
|
|
function ClickInteraction(CurrentStagePosition) {
|
|
|
|
// Make sure the current stage is loaded
|
|
if (CurrentStage != null) {
|
|
|
|
// If a regular option was clicked, we process it
|
|
var Pos = 0;
|
|
for (var L = 0; L < CurrentStage.length; L++)
|
|
if (CurrentStage[L][StageNumber] == CurrentStagePosition)
|
|
if (ActorInteractionAvailable(CurrentStage[L][StageLoveReq], CurrentStage[L][StageSubReq], CurrentStage[L][StageVarReq], CurrentStage[L][StageInteractionText], false)) {
|
|
if ((MouseX >= (Pos % 2) * 300) && (MouseX <= ((Pos % 2) * 300) + 299) && (MouseY >= 151 + (Math.round((Pos - 1) / 2) * 90)) && (MouseY <= 240 + (Math.round((Pos - 1) / 2) * 90))) {
|
|
window[CurrentChapter + "_" + CurrentScreen + "_CurrentStage"] = CurrentStage[L][StageNextStage];
|
|
OverridenIntroText = CurrentStage[L][StageInteractionResult];
|
|
ActorChangeAttitude(CurrentStage[L][StageLoveMod], CurrentStage[L][StageSubMod]);
|
|
// Check if the interaction has a time tag
|
|
var MinuteString = "ADD_MINUTES:";
|
|
var MinuteStringIndex = CurrentStage[L][StageInteractionText].indexOf(MinuteString);
|
|
if (MinuteStringIndex >= 0) {
|
|
var MinuteCount = parseInt(CurrentStage[L][StageInteractionText].substring(MinuteStringIndex + MinuteString.length));
|
|
// If the text after the tag isn't a valid number, output a log message and assume the default time
|
|
if (isNaN(MinuteCount)) {
|
|
console.log("Invalid minute expression in interaction: " + CurrentStage[L][StageInteractionText]);
|
|
CurrentTime = CurrentTime + 10000;
|
|
}
|
|
else CurrentTime = CurrentTime + MinuteCount * 60000;
|
|
}
|
|
else CurrentTime = CurrentTime + 10000;
|
|
if (CurrentStage[L][StageFunction].trim() != "") DynamicFunction(CurrentChapter + "_" + CurrentScreen + "_" + CurrentStage[L][StageFunction].trim());
|
|
return;
|
|
}
|
|
Pos = Pos + 1;
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Returns the text for the current scene associated with the tag
|
|
function GetText(Tag) {
|
|
|
|
// Make sure the text CSV file is loaded
|
|
if (CurrentText != null) {
|
|
|
|
// Cycle the text to find a matching tag and returns the text content
|
|
Tag = Tag.trim().toUpperCase();
|
|
for (var T = 0; T < CurrentText.length; T++)
|
|
if (CurrentText[T][TextTag].trim().toUpperCase() == Tag)
|
|
return CurrentText[T][TextContent].trim();
|
|
|
|
// Returns an error message
|
|
return "MISSING TEXT FOR TAG: " + Tag.trim();
|
|
|
|
} else return "";
|
|
|
|
}
|
|
|
|
// Returns the text for a specific CSV associated with the tag
|
|
function GetCSVText(CSVText, Tag) {
|
|
|
|
// Make sure the text CSV file is loaded
|
|
if (CSVText != null) {
|
|
|
|
// Cycle the text to find a matching tag and returns the text content
|
|
Tag = Tag.trim().toUpperCase();
|
|
for (var T = 0; T < CSVText.length; T++)
|
|
if (CSVText[T][TextTag].trim().toUpperCase() == Tag)
|
|
return CSVText[T][TextContent].trim();
|
|
|
|
// Returns an error message
|
|
return "MISSING TEXT FOR TAG: " + Tag.trim();
|
|
|
|
} else return "";
|
|
|
|
}
|
|
|
|
// Triggers the leave or wait button if needed
|
|
function LeaveButtonClick() {
|
|
|
|
// If the wait option was clicked, we skip 2 minutes
|
|
if (LeaveIcon == "Wait")
|
|
if ((MouseX >= 1125) && (MouseX <= 1200) && (MouseY >= 600) && (MouseY <= 675))
|
|
CurrentTime = CurrentTime + 120000;
|
|
|
|
// If the leave option was clicked, we return to the previous screen
|
|
if ((LeaveIcon == "Leave") && (LeaveScreen != ""))
|
|
if ((MouseX >= 1125) && (MouseX <= 1200) && (MouseY >= 600) && (MouseY <= 675))
|
|
SetScene(LeaveChapter, LeaveScreen);
|
|
|
|
}
|
|
|
|
// Creates a path from the supplied paths parts
|
|
function GetPath(paths) {
|
|
var path = arguments[0];
|
|
for (var index = 1; index < arguments.length; index++) {
|
|
path += "/" + arguments[index];
|
|
}
|
|
return path;
|
|
} |