mirror of
https://gitgud.io/BondageProjects/Bondage-College.git
synced 2025-04-25 17:59:34 +00:00
1066 lines
47 KiB
JavaScript
1066 lines
47 KiB
JavaScript
"use strict";
|
|
var GameLARPBackground = "Sheet";
|
|
var GameLARPClass = [
|
|
{
|
|
Name: "Matron",
|
|
Bonus: [0.20, 0.00],
|
|
Ability: ["Charge", "Control", "Detain"]
|
|
},
|
|
{
|
|
Name: "Seducer",
|
|
Bonus: [0.16, 0.04],
|
|
Ability: ["Expose", "Inspire", "Seduce"]
|
|
},
|
|
{
|
|
Name: "Trickster",
|
|
Bonus: [0.12, 0.08],
|
|
Ability: ["Confuse", "Hide", "Immobilize"]
|
|
},
|
|
{
|
|
Name: "Artist",
|
|
Bonus: [0.08, 0.12],
|
|
Ability: ["Cheer", "Costume", "Evasion"]
|
|
},
|
|
{
|
|
Name: "Servant",
|
|
Bonus: [0.04, 0.16],
|
|
Ability: ["Rescue", "Silence", "Ungag"]
|
|
},
|
|
{
|
|
Name: "Protector",
|
|
Bonus: [0.00, 0.20],
|
|
Ability: ["Cover", "Dress", "Support"]
|
|
},
|
|
];
|
|
var GameLARPTeamList = ["None", "Red", "Green", "Blue", "Yellow", "Cyan", "Purple", "Orange", "White", "Gray", "Black"];
|
|
var GameLARPTimerDelay = [20, 60];
|
|
var GameLARPEntryClass = "";
|
|
var GameLARPEntryTeam = "";
|
|
var GameLARPProgress = [];
|
|
var GameLARPPlayer = [];
|
|
var GameLARPOption = [];
|
|
var GameLARPAction = "";
|
|
var GameLARPInventory = [];
|
|
var GameLARPInventoryOffset = 0;
|
|
var GameLARPTurnAdmin = 0;
|
|
var GameLARPTurnPosition = 0;
|
|
var GameLARPTurnAscending = true;
|
|
var GameLARPTurnTimer = null;
|
|
var GameLARPTurnTimerDelay = GameLARPTimerDelay[0];
|
|
var GameLARPTurnFocusCharacter = null;
|
|
var GameLARPTurnFocusGroup = null;
|
|
|
|
/**
|
|
* Gets the current state of LARP.
|
|
* @returns {OnlineGameStatus}
|
|
*/
|
|
function GameLARPGetStatus() {
|
|
if (Player.Game && Player.Game.LARP && ["", "Running"].includes(Player.Game.LARP.Status))
|
|
return Player.Game.LARP.Status;
|
|
return "";
|
|
}
|
|
|
|
/**
|
|
* Set the current state of LARP.
|
|
* @param {OnlineGameStatus} s
|
|
* @returns {void}
|
|
*/
|
|
function GameLARPSetStatus(s) {
|
|
if (!["", "Running"].includes(s))
|
|
return;
|
|
|
|
if (Player.Game == null || Player.Game.LARP == null)
|
|
GameLARPLoad();
|
|
|
|
/* @ts-ignore */
|
|
Player.Game.LARP.Status = s;
|
|
ServerAccountUpdate.QueueData({ Game: Player.Game }, true);
|
|
}
|
|
|
|
/**
|
|
* Checks if the character is an admin or the LARP admin while the game is going.
|
|
* @param {Character} C - Character to check for
|
|
* @returns {boolean} - Returns TRUE if that character is an admin/the game administrator
|
|
*/
|
|
function GameLARPIsAdmin(C) {
|
|
if (GameLARPGetStatus() == "")
|
|
return (ChatRoomData.Admin.indexOf(C.MemberNumber) >= 0);
|
|
else
|
|
return (GameLARPTurnAdmin == C.MemberNumber);
|
|
}
|
|
|
|
/**
|
|
* Draws the LARP class/team icon of a character
|
|
* @param {Character} C - Character for which to draw the icons
|
|
* @param {number} X - Position on the X axis of the canvas
|
|
* @param {number} Y - Position on the Y axis of the canvas
|
|
* @param {number} Zoom - Zoom factor of the character
|
|
* @returns {void} - Nothing
|
|
*/
|
|
function GameLARPDrawIcon(C, X, Y, Zoom) {
|
|
if ((C != null) && (C.Game != null) && (C.Game.LARP != null) && (C.Game.LARP.Class != null) && (C.Game.LARP.Team != null) && (C.Game.LARP.Team != "") && (C.Game.LARP.Team != "None"))
|
|
DrawImageZoomCanvas("Icons/LARP/" + C.Game.LARP.Class + C.Game.LARP.Team + ".png", MainCanvas, 0, 0, 100, 100, X, Y, 100 * Zoom, 100 * Zoom);
|
|
}
|
|
|
|
/**
|
|
* Loads the LARP game.
|
|
* @returns {void} - Nothing
|
|
*/
|
|
function GameLARPLoad() {
|
|
if (Player.Game == null) Player.Game = {};
|
|
let game = Player.Game.LARP;
|
|
Player.Game.LARP = {
|
|
Class: (game && typeof game.Class !== undefined ? game.Class : GameLARPClass[0].Name),
|
|
Team: (game && typeof game.Team !== undefined ? game.Team : GameLARPTeamList[0]),
|
|
TimerDelay: (game && typeof game.TimerDelay !== undefined ? game.TimerDelay : GameLARPTimerDelay[0]),
|
|
Status: "",
|
|
Level: (game && typeof game.Level != undefined ? game.Level : []),
|
|
};
|
|
|
|
GameLARPEntryClass = Player.Game.LARP.Class;
|
|
GameLARPEntryTeam = Player.Game.LARP.Team;
|
|
if (GameLARPGetStatus() == "") GameLARPProgress = [];
|
|
}
|
|
|
|
/**
|
|
* Runs and draws the LARP game.
|
|
* @returns {void} - Nothing
|
|
*/
|
|
function GameLARPRun() {
|
|
|
|
// Draw the character, text and buttons
|
|
DrawCharacter(Player, 50, 50, 0.9);
|
|
MainCanvas.textAlign = "left";
|
|
DrawText(TextGet("Title"), 550, 125, "Black", "Gray");
|
|
DrawText(TextGet("SelectClass"), 550, 225, "Black", "Gray");
|
|
DrawText(TextGet("SelectTeam"), 550, 425, "Black", "Gray");
|
|
if (GameLARPGetStatus() != "") DrawText(TextGet("Class" + Player.Game.LARP.Class), 900, 225, "Black", "Gray");
|
|
DrawText(TextGet("LevelProgress"), 550, 325, "Black", "Gray");
|
|
DrawText(GameLARPGetClassLevel(Player.Game.LARP) + " (" + Math.floor(GameLARPGetClassProgress(Player.Game.LARP) / 10).toString() + "%)", 900, 325, "Black", "Gray");
|
|
if (GameLARPGetStatus() != "") DrawText(TextGet("Color" + Player.Game.LARP.Team), 900, 425, "Black", "Gray");
|
|
DrawText(TextGet((GameLARPGetStatus() == "") ? "StartCondition" : "RunningGame"), 550, 525, "Black", "Gray");
|
|
MainCanvas.textAlign = "center";
|
|
DrawButton(1815, 75, 90, 90, "", "White", "Icons/Exit.png");
|
|
if (GameLARPGetStatus() == "") DrawBackNextButton(900, 193, 400, 64, TextGet("Class" + Player.Game.LARP.Class), "White", "", () => "", () => "");
|
|
if (GameLARPGetStatus() == "") DrawBackNextButton(900, 393, 400, 64, TextGet("Color" + Player.Game.LARP.Team), "White", "", () => "", () => "");
|
|
GameLARPDrawIcon(Player, 1400, 225, 2);
|
|
if (GameLARPCanLaunchGame()) DrawBackNextButton(550, 600, 400, 65, TextGet("TimerDelay" + Player.Game.LARP.TimerDelay), "White", "", () => "", () => "");
|
|
if (GameLARPCanLaunchGame()) DrawButton(1050, 600, 400, 65, TextGet("StartGame"), "White");
|
|
|
|
}
|
|
|
|
/**
|
|
* Runs the game from the chat room
|
|
* @returns {void} - Nothing
|
|
*/
|
|
function GameLARPRunProcess() {
|
|
|
|
// If the player is an admin, she can make player skip their turns
|
|
if ((GameLARPGetStatus() == "Running") && (TimerGetTime() > GameLARPTurnTimer) && GameLARPIsAdmin(Player)) {
|
|
GameLARPTurnTimer = TimerGetTime() + (GameLARPTurnTimerDelay * 1000);
|
|
ServerSend("ChatRoomGame", { GameProgress: "Skip" });
|
|
}
|
|
|
|
// Clears the focused character if it's not the player turn
|
|
if ((GameLARPTurnFocusCharacter != null) && ((GameLARPGetStatus() != "Running") || (GameLARPPlayer[GameLARPTurnPosition].ID != 0))) GameLARPTurnFocusCharacter = null;
|
|
|
|
// If we must show the focused character and available abilities
|
|
if (GameLARPTurnFocusCharacter != null) {
|
|
|
|
// Draw the room dark background
|
|
DrawImageZoomCanvas("Backgrounds/" + ChatRoomData.Background + ".jpg", MainCanvas, 500, 0, 1000, 1000, 0, 0, 1000, 1000);
|
|
DrawRect(0, 0, 1000, 1000, "rgba(0,0,0," + 0.5 + ")");
|
|
|
|
// In inventory selection mode
|
|
if (GameLARPTurnFocusGroup != null) {
|
|
|
|
// Draw the label and buttons
|
|
DrawText(OnlineGameDictionaryText("ItemSelect"), 263, 50, "White", "Gray");
|
|
if (GameLARPInventory.length > 12) DrawButton(525, 20, 200, 60, OnlineGameDictionaryText("ItemNext"), "White");
|
|
DrawButton(775, 20, 200, 60, OnlineGameDictionaryText("ItemCancel"), "White");
|
|
|
|
// Prepares a 4x3 square selection with inventory from the buffer
|
|
var X = 15;
|
|
var Y = 110;
|
|
for (let A = GameLARPInventoryOffset; (A < GameLARPInventory.length) && (A < GameLARPInventoryOffset + 12); A++) {
|
|
const asset = GameLARPInventory[A];
|
|
const Hover = MouseIn(X, Y, 225, 275) && !CommonIsMobile;
|
|
const Hidden = CharacterAppearanceItemIsHidden(asset.Name, asset.Group.Name);
|
|
if (Hidden) DrawPreviewBox(X, Y, "Icons/HiddenItem.png", asset.Description, { Background: Hover ? "cyan" : "#fff" });
|
|
else DrawAssetPreview(X, Y, asset, {Hover: true});
|
|
|
|
X = X + 250;
|
|
if (X > 800) {
|
|
X = 15;
|
|
Y = Y + 300;
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
// Draw all the possible options
|
|
DrawCharacter(GameLARPTurnFocusCharacter, 500, 0, 1);
|
|
for (let O = 0; O < GameLARPOption.length; O++)
|
|
DrawButton(50, 35 + (O * 100), 400, 65, OnlineGameDictionaryText("Option" + GameLARPOption[O].Name).replace("OptionOdds", Math.round(GameLARPOption[O].Odds * 100)), "White");
|
|
DrawButton(50, 900, 400, 65, OnlineGameDictionaryText("BackToCharacters"), "White");
|
|
|
|
// Draw the timer
|
|
MainCanvas.font = CommonGetFont(108);
|
|
var Time = Math.ceil((GameLARPTurnTimer - TimerGetTime()) / 1000);
|
|
DrawText(((Time < 0) || (Time > GameLARPTimerDelay[GameLARPTimerDelay.length - 1])) ? OnlineGameDictionaryText("TimerNA") : Time.toString(), 250, 800, "Red", "White");
|
|
MainCanvas.font = CommonGetFont(36);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Reset any notification that may have been raised
|
|
if (document.hasFocus()) NotificationReset(NotificationEventType.LARP);
|
|
}
|
|
|
|
/**
|
|
* Builds the inventory selection list for a given asset group.
|
|
* @param {string} FocusGroup - Asset group for which to build the inventory.
|
|
* @returns {void} - Nothing
|
|
*/
|
|
function GameLARPBuildInventory(FocusGroup) {
|
|
GameLARPTurnFocusGroup = FocusGroup;
|
|
GameLARPInventory = [];
|
|
GameLARPInventoryOffset = 0;
|
|
for (let A = 0; A < Player.Inventory.length; A++)
|
|
if ((Player.Inventory[A].Asset != null) && (Player.Inventory[A].Asset.Group.Name == FocusGroup) && Player.Inventory[A].Asset.Enable && Player.Inventory[A].Asset.Wear && Player.Inventory[A].Asset.Random)
|
|
GameLARPInventory.push(Player.Inventory[A].Asset);
|
|
GameLARPInventory.sort((a,b) => (a.Description > b.Description) ? 1 : ((b.Description > a.Description) ? -1 : 0));
|
|
}
|
|
|
|
/**
|
|
* Triggered when an option is selected for the current target character. The inventory for it is built and the action is published
|
|
* @param {string} Name - Name of the selected option
|
|
* @returns {void} - Nothing
|
|
*/
|
|
function GameLARPClickOption(Name) {
|
|
GameLARPAction = Name;
|
|
if ((Name == "RestrainLegs") || (Name == "Immobilize")) return GameLARPBuildInventory("ItemFeet");
|
|
if ((Name == "RestrainMouth") || (Name == "Silence")) return GameLARPBuildInventory("ItemMouth");
|
|
if ((Name == "RestrainArms") || (Name == "Detain")) return GameLARPBuildInventory("ItemArms");
|
|
if ((Name == "Costume") || (Name == "Dress")) return GameLARPBuildInventory("Cloth");
|
|
ServerSend("ChatRoomGame", { GameProgress: "Action", Action: Name, Target: GameLARPTurnFocusCharacter.MemberNumber });
|
|
}
|
|
|
|
/**
|
|
* Handles clicks during the LARP game.
|
|
* @returns {boolean} - Returns TRUE if the click was handled by this LARP click handler
|
|
*/
|
|
function GameLARPClickProcess() {
|
|
|
|
// Do not handle any click if no character is selected, a target is required here
|
|
if (GameLARPTurnFocusCharacter == null) return false;
|
|
|
|
// In inventory selection mode
|
|
if (GameLARPTurnFocusGroup != null) {
|
|
|
|
// If "Next" or "Cancel" is clicked
|
|
if ((GameLARPInventory.length > 12) && MouseIn(525, 20, 200, 60)) {
|
|
GameLARPInventoryOffset = GameLARPInventoryOffset + 12;
|
|
if (GameLARPInventoryOffset >= GameLARPInventory.length) GameLARPInventoryOffset = 0;
|
|
}
|
|
if (MouseIn(775, 20, 200, 60)) GameLARPTurnFocusGroup = null;
|
|
|
|
// Checks if one of the 4x3 inventory square is clicked
|
|
var X = 15;
|
|
var Y = 110;
|
|
for (let A = GameLARPInventoryOffset; (A < GameLARPInventory.length) && (A < GameLARPInventoryOffset + 12); A++) {
|
|
if ((MouseX >= X) && (MouseX <= X + 225) && (MouseY >= Y) && (MouseY <= Y + 275))
|
|
ServerSend("ChatRoomGame", { GameProgress: "Action", Action: GameLARPAction, Item: GameLARPInventory[A].Name, Target: GameLARPTurnFocusCharacter.MemberNumber });
|
|
X = X + 250;
|
|
if (X > 800) {
|
|
X = 15;
|
|
Y = Y + 300;
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
// If we must catch the click on one of the buttons
|
|
for (let O = 0; O < GameLARPOption.length; O++)
|
|
if ((MouseX >= 50) && (MouseX < 450) && (MouseY >= 35 + (O * 100)) && (MouseY <= 100 + (O * 100)))
|
|
GameLARPClickOption(GameLARPOption[O].Name);
|
|
|
|
// If we must exit from the currently focused character
|
|
if (MouseIn(50, 900, 400, 65)) GameLARPTurnFocusCharacter = null;
|
|
|
|
}
|
|
|
|
// Flags the click as being handled
|
|
return true;
|
|
|
|
}
|
|
|
|
/**
|
|
* Starts a LARP match.
|
|
* @returns {void} - Nothing
|
|
*/
|
|
function GameLARPStartProcess() {
|
|
|
|
// Gives a delay in seconds, based on the player preference
|
|
GameLARPTurnTimer = TimerGetTime() + (GameLARPTurnTimerDelay * 1000);
|
|
|
|
// Notices everyone in the room that the game starts
|
|
var Dictionary = [];
|
|
Dictionary.push({Tag: "SourceCharacter", Text: Player.Name, MemberNumber: Player.MemberNumber});
|
|
ServerSend("ChatRoomChat", { Content: "LARPGameStart", Type: "Action" , Dictionary: Dictionary});
|
|
|
|
// Changes the game status and exits
|
|
ServerSend("ChatRoomGame", { GameProgress: "Start" });
|
|
GameLARPSetStatus("Running");
|
|
ChatRoomCharacterUpdate(Player);
|
|
}
|
|
|
|
/**
|
|
* Handles clicks in the LARP chat Admin screen
|
|
* @returns {void} - Nothing
|
|
*/
|
|
function GameLARPClick() {
|
|
|
|
// When the user exits
|
|
if (MouseIn(1815, 75, 90, 90)) GameLARPExit();
|
|
|
|
// When the user selects a new class
|
|
if (MouseIn(900, 193, 400, 64) && (GameLARPGetStatus() == "")) {
|
|
var Index = 0;
|
|
for (let I = 0; I < GameLARPClass.length; I++)
|
|
if (GameLARPClass[I].Name == Player.Game.LARP.Class)
|
|
Index = I;
|
|
if (MouseX <= 1100) Index = (Index <= 0) ? GameLARPClass.length - 1 : Index - 1;
|
|
else Index = (Index >= GameLARPClass.length - 1) ? 0 : Index + 1;
|
|
Player.Game.LARP.Class = GameLARPClass[Index].Name;
|
|
}
|
|
|
|
// When the user selects a new team
|
|
if (MouseIn(900, 393, 400, 64) && (GameLARPGetStatus() == "")) {
|
|
if (MouseX <= 1100) Player.Game.LARP.Team = (GameLARPTeamList.indexOf(Player.Game.LARP.Team) <= 0) ? GameLARPTeamList[GameLARPTeamList.length - 1] : GameLARPTeamList[GameLARPTeamList.indexOf(Player.Game.LARP.Team) - 1];
|
|
else Player.Game.LARP.Team = (GameLARPTeamList.indexOf(Player.Game.LARP.Team) >= GameLARPTeamList.length - 1) ? GameLARPTeamList[0] : GameLARPTeamList[GameLARPTeamList.indexOf(Player.Game.LARP.Team) + 1];
|
|
}
|
|
|
|
// When the user selects a new timer delay
|
|
if (MouseIn(550, 600, 400, 65) && GameLARPCanLaunchGame()) {
|
|
if (MouseX <= 750) Player.Game.LARP.TimerDelay = (GameLARPTimerDelay.indexOf(Player.Game.LARP.TimerDelay) <= 0) ? GameLARPTimerDelay[GameLARPTimerDelay.length - 1] : GameLARPTimerDelay[GameLARPTimerDelay.indexOf(Player.Game.LARP.TimerDelay) - 1];
|
|
else Player.Game.LARP.TimerDelay = (GameLARPTimerDelay.indexOf(Player.Game.LARP.TimerDelay) >= GameLARPTimerDelay.length - 1) ? GameLARPTimerDelay[0] : GameLARPTimerDelay[GameLARPTimerDelay.indexOf(Player.Game.LARP.TimerDelay) + 1];
|
|
}
|
|
|
|
// If the administrator wants to start the game
|
|
if (MouseIn(1050, 600, 400, 65) && GameLARPCanLaunchGame()) {
|
|
|
|
// Updates the player data
|
|
ServerAccountUpdate.QueueData({ Game: Player.Game }, true);
|
|
ChatRoomCharacterUpdate(Player);
|
|
|
|
// Shuffles all players in the chat room
|
|
for (let C = 0; C < ChatRoomCharacter.length; C++) {
|
|
if (ChatRoomCharacter[C].MemberNumber != Player.MemberNumber) {
|
|
ServerSend("ChatRoomAdmin", { MemberNumber: ChatRoomCharacter[C].MemberNumber, Action: "Shuffle" });
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Give time for the server to shuffle the room
|
|
setTimeout(GameLARPStartProcess, 4000);
|
|
CommonSetScreen("Online", "ChatRoom");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* Triggered when the player exits the LARP info screen.
|
|
* @returns {void} - Nothing
|
|
*/
|
|
function GameLARPExit() {
|
|
|
|
// When the game isn't running, we allow to change the class or team
|
|
if (GameLARPGetStatus() == "") {
|
|
|
|
// Notices everyone in the room of the change, if there is any
|
|
if (GameLARPEntryClass != Player.Game.LARP.Class || GameLARPEntryTeam != Player.Game.LARP.Team) {
|
|
var Dictionary = [];
|
|
Dictionary.push({ Tag: "SourceCharacter", Text: Player.Name, MemberNumber: Player.MemberNumber });
|
|
ServerSend("ChatRoomChat", { Content: "LARPChangeTeamClass", Type: "Action", Dictionary: Dictionary });
|
|
}
|
|
|
|
// Updates the player and go back to the chat room
|
|
ServerAccountUpdate.QueueData({ Game: Player.Game }, true);
|
|
ChatRoomCharacterUpdate(Player);
|
|
CommonSetScreen("Online", "ChatRoom");
|
|
|
|
} else {
|
|
Player.Game.LARP.Class = GameLARPEntryClass;
|
|
Player.Game.LARP.Team = GameLARPEntryTeam;
|
|
CommonSetScreen("Online", "ChatRoom");
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* Checks if a LARP match can be launched. The player must be an admin and two different teams must be selected.
|
|
* @returns {boolean} - Returns TRUE if the game can be launched
|
|
*/
|
|
function GameLARPCanLaunchGame() {
|
|
if (Player.Game.LARP.Class == null || Player.Game.LARP.Class == "") return false;
|
|
if (Player.Game.LARP.Team == null || Player.Game.LARP.Team == "None") return false;
|
|
if (GameLARPGetStatus() != "") return false;
|
|
if (!GameLARPIsAdmin(Player)) return false;
|
|
var Team = "";
|
|
for (let C = 0; C < ChatRoomCharacter.length; C++)
|
|
if ((ChatRoomCharacter[C].Game != null) && (ChatRoomCharacter[C].Game.LARP != null) && (ChatRoomCharacter[C].Game.Team != null) && (ChatRoomCharacter[C].Game.LARP.Team != "") && (ChatRoomCharacter[C].Game.LARP.Team != "None") && (InventoryGet(ChatRoomCharacter[C], "ItemArms") == null)) {
|
|
if (Team == "")
|
|
Team = ChatRoomCharacter[C].Game.LARP.Team;
|
|
else
|
|
if (Team != ChatRoomCharacter[C].Game.LARP.Team)
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Gets a specific bonus from a given character's class.
|
|
* @param {Character} Target - Character to check for a specific bonus value.
|
|
* @param {number} BonusType - The bonus type to get the value of.
|
|
* @returns {number} - Total bonuses for the given character.
|
|
*/
|
|
function GameLARPGetBonus(Target, BonusType) {
|
|
|
|
// Gets the base class bonus
|
|
var ClassBonus = 0;
|
|
for (let C = 0; C < GameLARPClass.length; C++)
|
|
if (Target.Game.LARP.Class == GameLARPClass[C].Name)
|
|
ClassBonus = GameLARPClass[C].Bonus[BonusType];
|
|
|
|
// Gets the class level bonus (0 gives no bonus, 10 gives a 50% to class bonus)
|
|
var LevelBonus = 0;
|
|
if ((Target.Game.LARP.Level != null) && (ClassBonus > 0))
|
|
for (let L = 0; L < Target.Game.LARP.Level.length; L++)
|
|
if ((Target.Game.LARP.Level[L].Name == Target.Game.LARP.Class) && (Target.Game.LARP.Level[L].Level != null) && (typeof Target.Game.LARP.Level[L].Level === "number"))
|
|
if ((Target.Game.LARP.Level[L].Level >= 0) && (Target.Game.LARP.Level[L].Level <= 10))
|
|
LevelBonus = Math.round((ClassBonus * 0.05 * Target.Game.LARP.Level[L].Level) * 100) / 100;
|
|
|
|
// The ability bonuses only work for a full cycle (GameLARPPlayer.length * 2)
|
|
var AbilityBonus = 0;
|
|
for (let P = ((GameLARPProgress.length - GameLARPPlayer.length * 2 + 1 > 0) ? GameLARPProgress.length - GameLARPPlayer.length * 2 + 1 : 0); P < GameLARPProgress.length; P++)
|
|
if ((GameLARPProgress[P].Success != null) && (GameLARPProgress[P].Data.GameProgress == "Action")) {
|
|
var Source = GameLARPGetPlayer(GameLARPProgress[P].Sender);
|
|
if ((Source.Game.LARP.Team == Target.Game.LARP.Team) && (GameLARPProgress[P].Data.Action == "Charge") && (BonusType == 0)) AbilityBonus = 0.25;
|
|
if ((Source.Game.LARP.Team == Target.Game.LARP.Team) && (GameLARPProgress[P].Data.GameProgress == "Action") && (GameLARPProgress[P].Data.Action == "Support") && (BonusType == 1)) AbilityBonus = 0.25;
|
|
if ((GameLARPProgress[P].Data.Target == Target.MemberNumber) && (GameLARPProgress[P].Data.GameProgress == "Action") && (GameLARPProgress[P].Data.Action == "Cheer")) AbilityBonus = 0.25;
|
|
}
|
|
|
|
// Returns both bonuses
|
|
return ClassBonus + LevelBonus + AbilityBonus;
|
|
|
|
}
|
|
|
|
/**
|
|
* Gets the odds of successfully doing an offensive action on a given character.
|
|
* @param {string} Action - Action attempted.
|
|
* @param {Character} Source - Character doing the move.
|
|
* @param {Character} Target - Character targetted by the move.
|
|
* @returns {number} - Odds of successfully doing an offensive action. The number has two decimals.
|
|
*/
|
|
function GameLARPGetOdds(Action, Source, Target) {
|
|
|
|
// The basic odds are 50% + Offensive bonus of source - Defensive bonus of target
|
|
var Odds = 0.5 + GameLARPGetBonus(Source, 0) - GameLARPGetBonus(Target, 1);
|
|
|
|
// Struggling starts at 10% + 10% for each new unsuccessful tries, tightening the bonds will reset it to 10%
|
|
if (Action == "Struggle") {
|
|
Odds = 0.05;
|
|
for (let P = 0; P < GameLARPProgress.length; P++)
|
|
if ((GameLARPProgress[P].Success != null) && (GameLARPProgress[P].Data.GameProgress == "Action")) {
|
|
if ((GameLARPProgress[P].Sender == Source.MemberNumber) && (GameLARPProgress[P].Data.Target == Source.MemberNumber) && (GameLARPProgress[P].Data.Action == "Struggle") && !GameLARPProgress[P].Success) Odds = Odds + 0.05;
|
|
if ((GameLARPProgress[P].Sender == Source.MemberNumber) && (GameLARPProgress[P].Data.Target == Source.MemberNumber) && (GameLARPProgress[P].Data.Action == "Struggle") && GameLARPProgress[P].Success) Odds = 0.05;
|
|
if ((GameLARPProgress[P].Data.Target == Source.MemberNumber) && (GameLARPProgress[P].Data.Action == "RestrainArms") && GameLARPProgress[P].Success) Odds = 0.05;
|
|
if ((GameLARPProgress[P].Data.Target == Source.MemberNumber) && (GameLARPProgress[P].Data.Action == "Tighten") && GameLARPProgress[P].Success) Odds = 0.05;
|
|
}
|
|
}
|
|
|
|
// Many actions have fixed %
|
|
if (["Pass", "Charge", "Control", "Hide", "Evasion", "Support", "Dress"].indexOf(Action) >= 0) return (Source.MemberNumber == Target.MemberNumber) ? 1 : 0;
|
|
if (["Inspire", "Cheer", "Costume", "Rescue", "Cover", "Ungag"].indexOf(Action) >= 0) return ((Source.MemberNumber != Target.MemberNumber) && (Source.Game.LARP.Team == Target.Game.LARP.Team)) ? 1 : 0;
|
|
if (["Detain", "Expose", "Seduce", "Confuse", "Immobilize", "Silence", "Tighten"].indexOf(Action) >= 0) return ((Source.MemberNumber != Target.MemberNumber) && (Source.Game.LARP.Team != Target.Game.LARP.Team)) ? 1 : 0;
|
|
|
|
// Returns the % between 0 and 1
|
|
if (Odds > 1) Odds = 1;
|
|
if (Odds < 0) Odds = 0;
|
|
return Math.round(Odds * 100) / 100;
|
|
|
|
}
|
|
|
|
/**
|
|
* In LARP, check if the given character can talk.
|
|
* @param {Character} C - Character to check.
|
|
* @returns {boolean} - Whether the character can talk or not
|
|
*/
|
|
function GameLARPCanTalk(C) { return (InventoryGet(C, "ItemMouth") == null); }
|
|
/**
|
|
* In LARP, check if the given character can walk.
|
|
* @param {Character} C - Character to check.
|
|
* @returns {boolean} - Whether the character can walk or not
|
|
*/
|
|
function GameLARPCanWalk(C) { return (InventoryGet(C, "ItemFeet") == null); }
|
|
/**
|
|
* In LARP, check if the given character can act.
|
|
* @param {Character} C - Character to check.
|
|
* @returns {boolean} - Whether the character can act or not
|
|
*/
|
|
function GameLARPCanAct(C) { return (InventoryGet(C, "ItemArms") == null); }
|
|
/**
|
|
* In LARP, check if the given character is wearing clothes.
|
|
* @param {Character} C - Character to check.
|
|
* @returns {boolean} - Whether the character is wearing clothes or not
|
|
*/
|
|
function GameLARPClothed(C) { return (InventoryGet(C, "Cloth") != null); }
|
|
|
|
/**
|
|
* Checks if an item can be removed in LARP.
|
|
* @param {Character} C - Character to check for a lock on the given group.
|
|
* @param {string} Zone - Group to check for a lock.
|
|
* @returns {boolean} - Returns TRUE if we can remove an item at a specific zone (cannot remove if there's a custom lock)
|
|
*/
|
|
function GameLARPCanRemoveItem(C, Zone) {
|
|
var Item = InventoryGet(C, Zone);
|
|
if (Item == null) return false;
|
|
if (InventoryGetLock(Item) != null) return false;
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Adds all available class abilities to the built basic options
|
|
* @param {Character} Source - Character about to do an action.
|
|
* @param {Character} Target - The character on which an action is about to be done.
|
|
* @param {Array.<{ Name: string, Odds: number}>} Option - List of the basic options the source character can perform
|
|
* @param {string} Ability - Character's ability.
|
|
* @returns {void} - Nothing
|
|
*/
|
|
function GameLARPBuildOptionAbility(Source, Target, Option, Ability) {
|
|
|
|
// Only the "Evasion" special ability can be used when arms are restrained
|
|
if ((Ability != "Evasion") && !GameLARPCanAct(Source)) return;
|
|
|
|
// If the ability was already used in that battle, it cannot be used again, the ability "Inspire" makes it usable once again
|
|
var AlreadyUsed = false;
|
|
for (let P = 0; P < GameLARPProgress.length; P++) {
|
|
if ((GameLARPProgress[P].Sender == Source.MemberNumber) && (GameLARPProgress[P].Data.GameProgress == "Action") && (GameLARPProgress[P].Data.Action == Ability)) AlreadyUsed = true;
|
|
if ((GameLARPProgress[P].Success != null) && GameLARPProgress[P].Success && (GameLARPProgress[P].Data.GameProgress == "Action") && (GameLARPProgress[P].Data.Action == "Inspire") && (GameLARPProgress[P].Data.Target == Source.MemberNumber)) AlreadyUsed = false;
|
|
}
|
|
if (AlreadyUsed) return;
|
|
|
|
// If "Control" or "Confuse" is in progress for this cycle, no class abilities can be used
|
|
for (let P = ((GameLARPProgress.length - GameLARPPlayer.length * 2 + 1 > 0) ? GameLARPProgress.length - GameLARPPlayer.length * 2 + 1 : 0); P < GameLARPProgress.length; P++) {
|
|
if ((GameLARPProgress[P].Success != null) && (GameLARPProgress[P].Data.GameProgress == "Action") && (GameLARPProgress[P].Data.Action == "Control")) return;
|
|
if ((GameLARPProgress[P].Success != null) && (GameLARPProgress[P].Data.GameProgress == "Action") && (GameLARPProgress[P].Data.Action == "Confuse") && (GameLARPProgress[P].Data.Target == Source.MemberNumber)) return;
|
|
}
|
|
|
|
// If the player targets herself
|
|
if (Source.MemberNumber == Target.MemberNumber) {
|
|
|
|
// Abilities that can be used on yourself
|
|
let Odds = GameLARPGetOdds(Ability, Source, Source);
|
|
if ((Ability == "Charge") && GameLARPCanWalk(Source)) Option.push({ Name: Ability, Odds: Odds });
|
|
if ((Ability == "Control") && GameLARPCanTalk(Source)) Option.push({ Name: Ability, Odds: Odds });
|
|
if (Ability == "Hide") Option.push({ Name: Ability, Odds: Odds });
|
|
if ((Ability == "Evasion") && (GameLARPCanRemoveItem(Source, "ItemFeet") || GameLARPCanRemoveItem(Source, "ItemArms"))) Option.push({ Name: Ability, Odds: Odds });
|
|
if ((Ability == "Support") && GameLARPCanTalk(Source)) Option.push({ Name: Ability, Odds: Odds });
|
|
if (Ability == "Dress") Option.push({ Name: Ability, Odds: Odds });
|
|
|
|
} else {
|
|
|
|
// If the player targets someone from her team
|
|
let Odds = GameLARPGetOdds(Ability, Source, Target);
|
|
if (Source.Game.LARP.Team == Target.Game.LARP.Team) {
|
|
|
|
// Abilities that can be used on someone from your team
|
|
if (Ability == "Inspire") Option.push({ Name: Ability, Odds: Odds });
|
|
if ((Ability == "Cheer") && GameLARPCanTalk(Source)) Option.push({ Name: Ability, Odds: Odds });
|
|
if ((Ability == "Costume") && GameLARPCanWalk(Source)) Option.push({ Name: Ability, Odds: Odds });
|
|
if ((Ability == "Rescue") && GameLARPCanWalk(Source) && (GameLARPCanRemoveItem(Target, "ItemFeet") || GameLARPCanRemoveItem(Target, "ItemArms"))) Option.push({ Name: Ability, Odds: Odds });
|
|
if ((Ability == "Cover") && GameLARPCanWalk(Source)) Option.push({ Name: Ability, Odds: Odds });
|
|
if ((Ability == "Ungag") && GameLARPCanRemoveItem(Target, "ItemMouth")) Option.push({ Name: Ability, Odds: Odds });
|
|
|
|
} else {
|
|
|
|
// Abilities that are used on players from another team, cannot be used if target arms are restrained
|
|
if (InventoryGet(Target, "ItemArms") != null) return;
|
|
if ((Ability == "Detain") && !GameLARPClothed(Target) && !GameLARPCanTalk(Target) && !GameLARPCanWalk(Target)) Option.push({ Name: Ability, Odds: Odds });
|
|
if ((Ability == "Expose") && GameLARPClothed(Target)) Option.push({ Name: Ability, Odds: Odds });
|
|
if ((Ability == "Seduce") && GameLARPCanTalk(Source)) Option.push({ Name: Ability, Odds: Odds });
|
|
if ((Ability == "Confuse") && GameLARPCanTalk(Source)) Option.push({ Name: Ability, Odds: Odds });
|
|
if ((Ability == "Immobilize") && !GameLARPClothed(Target) && GameLARPCanWalk(Target)) Option.push({ Name: Ability, Odds: Odds });
|
|
if ((Ability == "Silence") && !GameLARPClothed(Target) && GameLARPCanTalk(Target)) Option.push({ Name: Ability, Odds: Odds });
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* Builds the available options a character can perform on another for the LARP menu.
|
|
* @param {Character} Source - Character about to do an action.
|
|
* @param {Character} Target - The character on which an action is about to be done.
|
|
* @returns {Array.<{ Name: string, Odds: number}>} - Options the character can perform
|
|
*/
|
|
function GameLARPBuildOption(Source, Target) {
|
|
|
|
// If the source clicks on herself, she can always pass her turn and do nothing
|
|
var Option = [];
|
|
if (Source.MemberNumber == Target.MemberNumber) Option.push({ Name: "Pass", Odds: GameLARPGetOdds("Pass", Source, Source) });
|
|
|
|
// If seduce is in progress on the source, all she can do is pass her turn
|
|
var PassTurn = false;
|
|
for (let P = ((GameLARPProgress.length - GameLARPPlayer.length * 2 + 1 > 0) ? GameLARPProgress.length - GameLARPPlayer.length * 2 + 1 : 0); P < GameLARPProgress.length; P++) {
|
|
if ((GameLARPProgress[P].Success != null) && GameLARPProgress[P].Success && (GameLARPProgress[P].Data.GameProgress == "Action") && (GameLARPProgress[P].Data.Action == "Seduce") && (GameLARPProgress[P].Data.Target == Source.MemberNumber)) PassTurn = true;
|
|
if ((GameLARPProgress[P].Data.GameProgress == "Action") && (GameLARPProgress[P].Data.Action == "Pass") && (GameLARPProgress[P].Sender == Source.MemberNumber)) PassTurn = false;
|
|
}
|
|
if (PassTurn) return Option;
|
|
|
|
// If the source is restrained, she only has the struggle option on herself
|
|
if ((InventoryGet(Source, "ItemArms") != null) && (Source.MemberNumber == Target.MemberNumber))
|
|
Option.push({ Name: "Struggle", Odds: GameLARPGetOdds("Struggle", Source, Source) });
|
|
|
|
// If "Hide" or "Cover" are in progress, no offensive abilities can be used
|
|
if (Source.Game.LARP.Team != Target.Game.LARP.Team) {
|
|
|
|
// Checks for "Hide"
|
|
var CanTarget = true;
|
|
for (let P = ((GameLARPProgress.length - GameLARPPlayer.length * 2 + 1 > 0) ? GameLARPProgress.length - GameLARPPlayer.length * 2 + 1 : 0); P < GameLARPProgress.length; P++)
|
|
if (GameLARPProgress[P].Sender == Target.MemberNumber)
|
|
CanTarget = !((GameLARPProgress[P].Success != null) && GameLARPProgress[P].Success && (GameLARPProgress[P].Data.GameProgress == "Action") && (GameLARPProgress[P].Data.Action == "Hide") && (GameLARPProgress[P].Sender == Target.MemberNumber));
|
|
if (!CanTarget) return Option;
|
|
|
|
// Checks for "Cover"
|
|
for (let P = ((GameLARPProgress.length - GameLARPPlayer.length * 2 + 1 > 0) ? GameLARPProgress.length - GameLARPPlayer.length * 2 + 1 : 0); P < GameLARPProgress.length; P++)
|
|
if ((GameLARPProgress[P].Success != null) && GameLARPProgress[P].Success && (GameLARPProgress[P].Data.GameProgress == "Action") && (GameLARPProgress[P].Data.Action == "Cover") && (GameLARPProgress[P].Data.Target == Target.MemberNumber))
|
|
return Option;
|
|
|
|
}
|
|
|
|
// Gets all abilities for the class and assigns which one can be used
|
|
var Ability = [];
|
|
for (let C = 0; C < GameLARPClass.length; C++)
|
|
if (Source.Game.LARP.Class == GameLARPClass[C].Name)
|
|
Ability = GameLARPClass[C].Ability;
|
|
for (let A = 0; A < Ability.length; A++)
|
|
GameLARPBuildOptionAbility(Source, Target, Option, Ability[A]);
|
|
|
|
// Builds the "Strip" & "Restrain" options if the target isn't in the source team
|
|
if ((Target.Game.LARP.Team != Source.Game.LARP.Team) && (InventoryGet(Source, "ItemArms") == null)) {
|
|
|
|
// Some actions are different based on the target current restrains
|
|
if (GameLARPClothed(Target)) Option.push({ Name: "Strip", Odds: GameLARPGetOdds("Strip", Source, Target) });
|
|
else if (!GameLARPCanAct(Target)) Option.push({ Name: "Tighten", Odds: GameLARPGetOdds("Tighten", Source, Target) });
|
|
else if (!GameLARPCanWalk(Target) && !GameLARPCanTalk(Target)) Option.push({ Name: "RestrainArms", Odds: GameLARPGetOdds("RestrainArms", Source, Target) });
|
|
else {
|
|
if (GameLARPCanWalk(Target)) Option.push({ Name: "RestrainLegs", Odds: GameLARPGetOdds("RestrainLegs", Source, Target) });
|
|
if (GameLARPCanTalk(Target)) Option.push({ Name: "RestrainMouth", Odds: GameLARPGetOdds("RestrainMouth", Source, Target) });
|
|
}
|
|
|
|
}
|
|
|
|
// Returns all valid options
|
|
return Option;
|
|
|
|
}
|
|
|
|
/**
|
|
* Gets a character from the LARP game by member number
|
|
* @param {number} MemberNumber - Member number of the character to get.
|
|
* @returns {Character | null} - The corresponding character, if it exists.
|
|
*/
|
|
function GameLARPGetPlayer(MemberNumber) {
|
|
for (let C = 0; C < GameLARPPlayer.length; C++)
|
|
if (GameLARPPlayer[C].MemberNumber == MemberNumber)
|
|
return GameLARPPlayer[C];
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Processes an action for a player.
|
|
* @param {string} Action - Action attempted.
|
|
* @param {string} ItemName - Name of the item to attempt to use.
|
|
* @param {Character} Source - Source character of the action
|
|
* @param {Character} Target - Character targetted by the action
|
|
* @param {number} RNG - Random odds received for which the character's odds will be compared.
|
|
* @returns {void} - Nothing
|
|
*/
|
|
function GameLARPProcessAction(Action, ItemName, Source, Target, RNG) {
|
|
|
|
// Skip if the characters aren't valid
|
|
if ((Source == null) || (Target == null)) return;
|
|
|
|
// Gets the item description in the user language
|
|
var ItemDesc = "N/A";
|
|
if (ItemName != "") {
|
|
var A;
|
|
if ((Action == "RestrainLegs") || (Action == "Immobilize")) A = AssetGet(Target.AssetFamily, "ItemFeet", ItemName);
|
|
if ((Action == "RestrainArms") || (Action == "Detain")) A = AssetGet(Target.AssetFamily, "ItemArms", ItemName);
|
|
if ((Action == "RestrainMouth") || (Action == "Silence")) A = AssetGet(Target.AssetFamily, "ItemMouth", ItemName);
|
|
if ((Action == "Dress") || (Action == "Costume")) A = AssetGet(Target.AssetFamily, "Cloth", ItemName);
|
|
if ((A != null) && (A.Description != null)) ItemDesc = A.Description;
|
|
}
|
|
|
|
// If the odds are successful (0% never succeeds, 100% always succeeds)
|
|
var Odds = GameLARPGetOdds(Action, Source, Target);
|
|
if ((Odds >= 0.01) && ((Odds >= 1) || (Odds >= Math.round(RNG * 100) / 100))) {
|
|
|
|
// Regular restrain actions
|
|
ChatRoomAllowCharacterUpdate = false;
|
|
if ((Action == "RestrainLegs") || (Action == "Immobilize")) InventoryWear(Target, ItemName, "ItemFeet", null, 6);
|
|
if ((Action == "RestrainArms") || (Action == "Detain")) InventoryWear(Target, ItemName, "ItemArms", null, 6);
|
|
if ((Action == "RestrainMouth") || (Action == "Silence")) InventoryWear(Target, ItemName, "ItemMouth", null, 6);
|
|
if ((Action == "Dress") || (Action == "Costume")) InventoryWear(Target, ItemName, "Cloth");
|
|
ChatRoomAllowCharacterUpdate = true;
|
|
|
|
// Struggle and evasion can remove some restraints
|
|
if (Action == "Struggle") InventoryRemove(Target, "ItemArms");
|
|
if (Action == "Ungag") InventoryRemove(Target, "ItemMouth");
|
|
if ((Action == "Evasion") || (Action == "Rescue")) {
|
|
if (InventoryGet(Target, "ItemArms") != null) InventoryRemove(Target, "ItemArms");
|
|
else InventoryRemove(Target, "ItemFeet");
|
|
}
|
|
|
|
// Strip / Expose removes the cloth items
|
|
if ((Action == "Strip") || (Action == "Expose")) {
|
|
InventoryRemove(Target, "Cloth");
|
|
InventoryRemove(Target, "ClothLower");
|
|
InventoryRemove(Target, "ClothAccessory");
|
|
}
|
|
|
|
// Publishes the success
|
|
GameLARPAddChatLog("Option" + Action + "Success", Source, Target, ItemDesc, RNG, Odds, "#00B000");
|
|
GameLARPProgress[GameLARPProgress.length - 1].Success = true;
|
|
|
|
} else {
|
|
|
|
// Publishes the failure
|
|
GameLARPAddChatLog("Option" + Action + "Fail", Source, Target, ItemDesc, RNG, Odds, "#B00000");
|
|
GameLARPProgress[GameLARPProgress.length - 1].Success = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* Processes the LARP game clicks. This method is called from the generic OnlineGameClickCharacter function when the current game is LARP.
|
|
* @param {Character} C - Character clicked on
|
|
* @returns {boolean} - returns TRUE if the code handles the click
|
|
*/
|
|
function GameLARPCharacterClick(C) {
|
|
|
|
// If it's the player turn, we allow clicking on a character to get the abilities menu
|
|
if ((GameLARPGetStatus() == "Running") && (GameLARPPlayer[GameLARPTurnPosition].ID == 0) && (C.Game != null) && (C.Game.LARP != null) && (C.Game.LARP.Team != null) && (C.Game.LARP.Team != "") && (C.Game.LARP.Team != "None")) {
|
|
GameLARPTurnFocusCharacter = C;
|
|
GameLARPTurnFocusGroup = null;
|
|
GameLARPOption = GameLARPBuildOption(Player, GameLARPTurnFocusCharacter);
|
|
}
|
|
|
|
// Flags that transaction as being handled
|
|
return true;
|
|
|
|
}
|
|
|
|
/**
|
|
* Adds a LARP message to the chat log.
|
|
* @param {string} Msg - Message tag
|
|
* @param {Character} Source - Source character of the message
|
|
* @param {Character} Target - Character targetted by the message
|
|
* @param {string} Description - Description of the message (item name, team name, etc.)
|
|
* @param {number} RNG - The number given by RNG.
|
|
* @param {number} Odds - The number required for the move to work.
|
|
* @param {string} [Color] - Color of the message to add.
|
|
* @returns {void} - Nothing
|
|
*/
|
|
function GameLARPAddChatLog(Msg, Source, Target, Description, RNG, Odds, Color) {
|
|
|
|
// The first message of the game is blue
|
|
if (GameLARPProgress.length == 0) Color = "#0000B0";
|
|
|
|
// Gets the message from the dictionary
|
|
Msg = OnlineGameDictionaryText(Msg);
|
|
Msg = Msg.replace("SourceName", Source.Name);
|
|
Msg = Msg.replace("SourceNumber", Source.MemberNumber.toString());
|
|
Msg = Msg.replace("TargetName", Target.Name);
|
|
Msg = Msg.replace("TargetNumber", Target.MemberNumber.toString());
|
|
Msg = Msg.replace("ActionRNG", Math.round(RNG * 100).toString());
|
|
Msg = Msg.replace("ActionOdds", Math.round(Odds * 100).toString());
|
|
Msg = Msg.replace("ItemDesc", Description);
|
|
Msg = Msg.replace("TeamName", Description);
|
|
|
|
// Adds the message and scrolls down unless the user has scrolled up
|
|
var div = document.createElement("div");
|
|
div.setAttribute('class', 'ChatMessage ChatMessageServerMessage');
|
|
div.setAttribute('data-time', ChatRoomCurrentTime());
|
|
if ((Color != null) && (Color != "")) div.style.color = Color;
|
|
div.innerHTML = Msg;
|
|
var Refocus = document.activeElement.id == "InputChat";
|
|
var ShouldScrollDown = ElementIsScrolledToEnd("TextAreaChatLog");
|
|
if (document.getElementById("TextAreaChatLog") != null) {
|
|
document.getElementById("TextAreaChatLog").appendChild(div);
|
|
if (ShouldScrollDown) ElementScrollToEnd("TextAreaChatLog");
|
|
if (Refocus) ElementFocus("InputChat");
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* Sets the new turn player and publish it in the chat room
|
|
* @param {number} NewPlayerPosition - Position of the new player
|
|
* @param {boolean} Ascending - Whether or not the turn is ascending
|
|
* @param {string} Msg - Message tag to display such as TurnStart, TurnSkip and TurnNext
|
|
* @returns {void} - Nothing
|
|
*/
|
|
function GameLARPNewTurnPublish(NewPlayerPosition, Ascending, Msg) {
|
|
|
|
// Sets the new position and turn order, the timer is divided by 2 if the are restrained, then publish in the chat log
|
|
GameLARPTurnPosition = NewPlayerPosition;
|
|
GameLARPTurnAscending = Ascending;
|
|
GameLARPTurnTimer = TimerGetTime() + (GameLARPTurnTimerDelay * (GameLARPPlayer[GameLARPTurnPosition].CanInteract() ? 1000 : 500));
|
|
GameLARPAddChatLog(Msg, Player, GameLARPPlayer[GameLARPTurnPosition], "", 0, 0);
|
|
|
|
}
|
|
|
|
/**
|
|
* Generates a new turn for the LARP game.
|
|
* @param {string} Msg - Content of the turn message such as TurnNext, TurnStart or TurnSkip
|
|
* @returns {void} - Nothing
|
|
*/
|
|
function GameLARPNewTurn(Msg) {
|
|
|
|
// Resets the focus
|
|
GameLARPTurnFocusCharacter = null;
|
|
GameLARPTurnFocusGroup = null;
|
|
|
|
// Cycles in the game player array ascending or descending and shifts the position
|
|
if ((GameLARPTurnAscending) && (GameLARPTurnPosition < GameLARPPlayer.length - 1)) GameLARPNewTurnPublish(GameLARPTurnPosition + 1, true, Msg);
|
|
else if ((GameLARPTurnAscending) && (GameLARPTurnPosition == GameLARPPlayer.length - 1)) GameLARPNewTurnPublish(GameLARPTurnPosition, false, Msg);
|
|
else if ((!GameLARPTurnAscending) && (GameLARPTurnPosition > 0)) GameLARPNewTurnPublish(GameLARPTurnPosition - 1, false, Msg);
|
|
else if ((!GameLARPTurnAscending) && (GameLARPTurnPosition == 0)) GameLARPNewTurnPublish(GameLARPTurnPosition, true, Msg);
|
|
|
|
// Raise a notification if it's the player's turn and they're away
|
|
if (!document.hasFocus() && GameLARPPlayer[GameLARPTurnPosition].ID === 0) {
|
|
NotificationRaise(NotificationEventType.LARP);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Builds the full LARP player list. Someone with no team is not playing the match.
|
|
* @returns {void} - Nothing
|
|
*/
|
|
function GameLARPBuildPlayerList() {
|
|
GameLARPPlayer = [];
|
|
for (let C = 0; C < ChatRoomCharacter.length; C++)
|
|
if ((ChatRoomCharacter[C].Game != null) && (ChatRoomCharacter[C].Game.LARP != null) && (ChatRoomCharacter[C].Game.LARP.Team != null) && (ChatRoomCharacter[C].Game.LARP.Team != "") && (ChatRoomCharacter[C].Game.LARP.Team != "None"))
|
|
GameLARPPlayer.push(ChatRoomCharacter[C]);
|
|
}
|
|
|
|
/**
|
|
* Each time a game is over, in victory or defeat, the player progresses toward the next class level
|
|
* @param {number} NewProgress - The progress factor to apply
|
|
* @returns {void} - Nothing
|
|
*/
|
|
function GameLARPLevelProgress(NewProgress) {
|
|
if (NewProgress > 50) NewProgress = 50;
|
|
if (Player.Game.LARP.Level == null) Player.Game.LARP.Level = [];
|
|
var Level = 0;
|
|
var Progress = 0;
|
|
var Found = false;
|
|
for (let L = 0; L < Player.Game.LARP.Level.length; L++)
|
|
if (Player.Game.LARP.Level[L].Name == Player.Game.LARP.Class) {
|
|
Level = Player.Game.LARP.Level[L].Level;
|
|
Progress = Player.Game.LARP.Level[L].Progress;
|
|
Found = true;
|
|
}
|
|
if (Found == false) Player.Game.LARP.Level.push({ Name: Player.Game.LARP.Class, Level: 0, Progress: 0 });
|
|
if (Level >= 10) return;
|
|
NewProgress = NewProgress * (12 - Level) * 5;
|
|
if (Progress + NewProgress >= 1000) {
|
|
Level++;
|
|
Progress = 0;
|
|
} else Progress = Progress + NewProgress;
|
|
for (let L = 0; L < Player.Game.LARP.Level.length; L++)
|
|
if (Player.Game.LARP.Level[L].Name == Player.Game.LARP.Class) {
|
|
Player.Game.LARP.Level[L].Level = Level;
|
|
Player.Game.LARP.Level[L].Progress = Progress;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns the class level for a LARP player, based on their LARP object
|
|
* @param {object} LARP - The LARP object, coming from the Character.Game object
|
|
* @returns {number} - The level between 0 and 10
|
|
*/
|
|
function GameLARPGetClassLevel(LARP) {
|
|
if (LARP.Level == null) return 0;
|
|
for (let L = 0; L < LARP.Level.length; L++)
|
|
if ((LARP.Level[L].Name == LARP.Class) && (typeof LARP.Level[L].Level === "number"))
|
|
if ((LARP.Level[L].Level >= 0) && (LARP.Level[L].Level <= 10))
|
|
return LARP.Level[L].Level;
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Returns the class level progress for a LARP player, based on their LARP object
|
|
* @param {object} LARP - The LARP object, coming from the Character.Game object
|
|
* @returns {number} - The level progress between 0 and 1000
|
|
*/
|
|
function GameLARPGetClassProgress(LARP) {
|
|
if (LARP.Level == null) return 0;
|
|
for (let L = 0; L < LARP.Level.length; L++)
|
|
if ((LARP.Level[L].Name == LARP.Class) && (typeof LARP.Level[L].Progress === "number"))
|
|
if ((LARP.Level[L].Progress >= 0) && (LARP.Level[L].Progress <= 1000))
|
|
return LARP.Level[L].Progress;
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Moves forward in the LARP game. If there are less than 2 teams with free arms, the game is over.
|
|
* @returns {boolean} - Returns TRUE if the game ends and runs the end scripts.
|
|
*/
|
|
function GameLARPContinue() {
|
|
|
|
// See if there's at least 2 teams in which players have free arms, return TRUE if that's the case
|
|
var Team = "";
|
|
for (let C = 0; C < GameLARPPlayer.length; C++)
|
|
if ((GameLARPPlayer[C].Game.LARP.Team != "") && (GameLARPPlayer[C].Game.LARP.Team != "None") && (InventoryGet(GameLARPPlayer[C], "ItemArms") == null) && OnlineGameCharacterInChatRoom(GameLARPPlayer[C].MemberNumber)) {
|
|
if (Team == "")
|
|
Team = GameLARPPlayer[C].Game.LARP.Team;
|
|
else
|
|
if (Team != GameLARPPlayer[C].Game.LARP.Team)
|
|
return true;
|
|
}
|
|
|
|
// If there's a winning team, we announce it and stop the game
|
|
if (Team != "") {
|
|
|
|
// Progresses toward the next class level
|
|
GameLARPLevelProgress(GameLARPProgress.length);
|
|
|
|
// Shows the winning team and updates the player status
|
|
GameLARPAddChatLog("EndGame", Player, Player, OnlineGameDictionaryText("Team" + Team), 0, 0, "#0000B0");
|
|
GameLARPReset();
|
|
ServerAccountUpdate.QueueData({ Game: Player.Game }, true);
|
|
|
|
// Calculate the reputation gained, the longer the game took, the higher it will rise the rep, times 2 if the player team won
|
|
var RepGain = Math.round(GameLARPProgress.length / GameLARPPlayer.length * ((Player.Game.LARP.Team == Team) ? 0.5 : 0.25));
|
|
if (RepGain > 10) RepGain = 10;
|
|
if (RepGain > 0) DialogChangeReputation("LARP", RepGain);
|
|
ChatRoomCharacterUpdate(Player);
|
|
|
|
// If the player is one the winning team, she earns some money based on game length, split by the number of winners
|
|
if ((Player.Game.LARP.Team == Team) && (GameLARPProgress.length >= 5)) {
|
|
var PlayersInWinningTeam = 0;
|
|
for (let C = 0; C < GameLARPPlayer.length; C++)
|
|
if (GameLARPPlayer[C].Game.LARP.Team == Team)
|
|
PlayersInWinningTeam++;
|
|
var MoneyGain = Math.round(GameLARPPlayer.length * Math.sqrt(GameLARPProgress.length) / PlayersInWinningTeam);
|
|
if (MoneyGain > 30) MoneyGain = 30;
|
|
if (MoneyGain > 0) CharacterChangeMoney(Player, MoneyGain);
|
|
}
|
|
|
|
return false;
|
|
|
|
} else return true;
|
|
|
|
}
|
|
|
|
/**
|
|
* Processes the LARP game messages for turns and actions.
|
|
* @param {IChatRoomGameResponse} P - Data object containing the message data.
|
|
* @returns {void} - Nothing
|
|
*/
|
|
function GameLARPProcess(P) {
|
|
if ((P != null) && (typeof P === "object") && (P.Data != null) && (typeof P.Data === "object") && (P.Sender != null) && (typeof P.Sender === "number") && (P.RNG != null) && (typeof P.RNG === "number")) {
|
|
|
|
// The administrator can start the LARP game, he becomes the turn admin in the process
|
|
if ((ChatRoomData.Admin.indexOf(P.Sender) >= 0) && (P.Data.GameProgress == "Start")) {
|
|
GameLARPSetStatus("Running");
|
|
GameLARPTurnAdmin = P.Sender;
|
|
GameLARPTurnPosition = -1;
|
|
GameLARPTurnAscending = true;
|
|
GameLARPBuildPlayerList();
|
|
GameLARPProgress = [];
|
|
for (let C = 0; C < GameLARPPlayer.length; C++)
|
|
if (ChatRoomData.Admin.indexOf(GameLARPPlayer[C].MemberNumber) >= 0)
|
|
GameLARPTurnTimerDelay = GameLARPPlayer[C].Game.LARP.TimerDelay;
|
|
if ((typeof GameLARPTurnTimerDelay !== "number") || (GameLARPTurnTimerDelay < GameLARPTimerDelay[0]) || (GameLARPTurnTimerDelay > GameLARPTimerDelay[GameLARPTimerDelay.length - 1])) GameLARPTurnTimerDelay = GameLARPTimerDelay[0];
|
|
GameLARPNewTurn("TurnStart");
|
|
}
|
|
|
|
// The turn administrator can skip turns after the delay has ran out
|
|
if ((GameLARPGetStatus() == "Running") && (GameLARPTurnAdmin == P.Sender) && (P.Data.GameProgress == "Skip")) {
|
|
GameLARPProgress.push({ Sender: P.Sender, Time: CurrentTime, RNG: P.RNG, Data: P.Data });
|
|
if (GameLARPContinue()) GameLARPNewTurn("TurnSkip");
|
|
}
|
|
|
|
// The current turn player can trigger an action
|
|
if ((GameLARPGetStatus() == "Running") && (GameLARPPlayer[GameLARPTurnPosition].MemberNumber == P.Sender) && (P.Data.GameProgress == "Action") && (P.Data.Action != null) && (P.Data.Target != null)) {
|
|
|
|
// Before we process it, we make sure the action is valid by checking all possible options
|
|
var Source = GameLARPGetPlayer(P.Sender);
|
|
var Target = GameLARPGetPlayer(P.Data.Target);
|
|
if ((Source != null) && (Target != null)) {
|
|
var Option = GameLARPBuildOption(Source, Target);
|
|
for (let O = 0; O < Option.length; O++)
|
|
if (Option[O].Name == P.Data.Action) {
|
|
GameLARPProgress.push({ Sender: P.Sender, Time: CurrentTime, RNG: P.RNG, Data: P.Data });
|
|
GameLARPProcessAction(P.Data.Action, P.Data.Item, Source, Target, P.RNG);
|
|
if (GameLARPContinue()) GameLARPNewTurn("TurnNext");
|
|
return;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Resets the LARP game so a new game might be started
|
|
* @returns {void} - Nothing
|
|
*/
|
|
function GameLARPReset() {
|
|
GameLARPSetStatus("");
|
|
}
|
|
|
|
/**
|
|
* Ensure all character's MagicBattle game status are the same
|
|
*/
|
|
function GameLARPLoadStatus() {
|
|
for (let C = 0; C < ChatRoomCharacter.length; C++)
|
|
if ((ChatRoomData.Admin.indexOf(ChatRoomCharacter[C].MemberNumber) >= 0) && (ChatRoomCharacter[C].Game != null) && (ChatRoomCharacter[C].Game.LARP != null) && (ChatRoomCharacter[C].Game.LARP.Status != "")) {
|
|
GameLARPSetStatus(ChatRoomCharacter[C].Game.LARP.Status);
|
|
return;
|
|
}
|
|
GameLARPReset();
|
|
}
|
|
|
|
|
|
/**
|
|
* Draws the online game images/text needed on the characters
|
|
* @param {Character} C - Character to draw the info for
|
|
* @param {number} X - Position of the character the X axis
|
|
* @param {number} Y - Position of the character the Y axis
|
|
* @param {number} Zoom - Amount of zoom the character has (Height)
|
|
* @returns {void} - Nothing
|
|
*/
|
|
function GameLARPDrawCharacter(C, X, Y, Zoom) {
|
|
|
|
// LARP draws the timer if needed and the icon linked to team and class
|
|
if ((CurrentModule == "Online") && (CurrentScreen == "ChatRoom")) {
|
|
GameLARPDrawIcon(C, X + 70 * Zoom, Y + 800 * Zoom, 0.6 * Zoom);
|
|
if ((GameLARPPlayer.length > 0) && (C.MemberNumber == GameLARPPlayer[GameLARPTurnPosition].MemberNumber) && (GameLARPGetStatus() == "Running") && (GameLARPTurnFocusCharacter == null)) {
|
|
MainCanvas.font = CommonGetFont(72);
|
|
var Time = Math.ceil((GameLARPTurnTimer - TimerGetTime()) / 1000);
|
|
DrawText(((Time < 0) || (Time > GameLARPTimerDelay[GameLARPTimerDelay.length - 1])) ? OnlineGameDictionaryText("TimerNA") : Time.toString(), X + 250 * Zoom, Y + 830 * Zoom, "Red", "Black");
|
|
MainCanvas.font = CommonGetFont(36);
|
|
}
|
|
}
|
|
}
|