Skip to content
Snippets Groups Projects
spniCore.js 48.1 KiB
Newer Older
  • Learn to ignore specific revisions
  •  ************************************************************/
    function returnToPreviousScreen (screen) {
        if (screen == $selectScreen) {
            /* return to the title screen */
            $selectScreen.hide();
            $titleScreen.show();
        }
    }
    
    /************************************************************
    
     * Resets the game state so that the game can be restarted.
    
     ************************************************************/
    
    function resetPlayers () {
    
        players.forEach(function(p) {
            p.resetState();
        });
        updateAllBehaviours(null, null, SELECTED);
    
    }
    
    /************************************************************
     * Restarts the game.
     ************************************************************/
    function restartGame () {
        KEYBINDINGS_ENABLED = false;
    
    
    	clearTimeout(timeoutID); // No error if undefined or no longer valid
    	timeoutID = autoForfeitTimeoutID = undefined;
    	stopCardAnimations();
    
    	/* enable table opacity */
    	tableOpacity = 1;
    	$gameTable.css({opacity:1});
        $gamePlayerClothingArea.show();
        $gamePlayerCardArea.show();
    
    	/* trigger screen refreshes */
    	updateSelectionVisuals();
    	updateAllGameVisuals();
        selectTitleCandy();
    
        forceTableVisibility(true);
    
    	/* there is only one call to this right now */
    	$epilogueSelectionModal.hide();
    	$gameScreen.hide();
    	$epilogueScreen.hide();
    
    spnati_edit's avatar
    spnati_edit committed
    	clearEpilogue();
    
    	$titleScreen.show();
    }
    
    /**********************************************************************
     *****                    Interaction Functions                   *****
     **********************************************************************/
    
    /*
     * Bug Report Modal functions
     */
    
    function getBugReportJSON() {
        var desc = $('#bug-report-desc').val();
        var type = $('#bug-report-type').val();
        var character = undefined;
    
        var report = compileBaseErrorReport(desc, type);
        return JSON.stringify(report);
    }
    
    
    function updateBugReportSendButton() {
        if($('#bug-report-desc').val().trim().length > 0) {
            $("#bug-report-modal-send-button").removeAttr('disabled');
        } else {
            $("#bug-report-modal-send-button").attr('disabled', 'true');
        }
    }
    
    $('#bug-report-desc').keyup(updateBugReportSendButton);
    
    
    /* Update the bug report text dump. */
    
    function updateBugReportOutput() {
    
        $('#bug-report-output').val(getBugReportJSON());
    
        $('#bug-report-status').text("");
    
        updateBugReportSendButton();
    
    }
    
    function copyBugReportOutput() {
        var elem = $('#bug-report-output')[0];
        elem.select();
        document.execCommand("copy");
    }
    
    function sendBugReport() {
    
        if($('#bug-report-desc').val().trim().length == 0) {
            $('#bug-report-status').text("Please enter a description first!");
            return false;
        }
    
        $.ajax({
            url: BUG_REPORTING_ENDPOINT,
            method: 'POST',
            data: getBugReportJSON(),
            contentType: 'application/json',
            error: function (jqXHR, status, err) {
                console.error("Could not send bug report - error "+status+": "+err);
    
                $('#bug-report-status').text("Failed to send bug report (error "+status+")");
    
            success: function () {
                $('#bug-report-status').text("Bug report sent!");
    
                $('#bug-report-desc').val("");
                closeBugReportModal();
    
    }
    
    $('#bug-report-type').change(updateBugReportOutput);
    $('#bug-report-desc').change(updateBugReportOutput);
    $('#bug-report-copy-btn').click(copyBugReportOutput);
    
     /************************************************************
      * The player clicked a bug-report button. Shows the bug reports modal.
      ************************************************************/
    function showBugReportModal () {
    
        /* Set up possible bug report types. */
    
        var bugReportTypes = [
            ['freeze', 'Game Freeze or Crash'],
            ['display', 'Game Graphical Problem'],
            ['other', 'Other Game Issue'],
        ]
    
        for (var i=1;i<5;i++) {
            if (players[i]) {
                var mixedCaseID = players[i].id.charAt(0).toUpperCase()+players[i].id.substring(1);
    
                bugReportTypes.push(['character:'+players[i].id, 'Character Defect ('+mixedCaseID+')']);
    
        $('#bug-report-type').empty().append(bugReportTypes.map(function (t) {
            return $('<option value="'+t[0]+'">'+t[1]+'</option>');
        }));
    
        $('#bug-report-modal span[data-toggle="tooltip"]').tooltip();
        updateBugReportOutput();
    
        KEYBINDINGS_ENABLED = false;
    
        $bugReportModal.modal('show');
    }
    
    function closeBugReportModal() {
        KEYBINDINGS_ENABLED = true;
        $bugReportModal.modal('hide');
    }
    
    /*
     * Show the usage tracking consent modal.
     */
    
    function showUsageTrackingModal() {
        $usageTrackingModal.modal('show');
    }
    
    function enableUsageTracking() {
        save.data.askedUsageTracking = true;
        USAGE_TRACKING = true;
    
        save.saveOptions();
    }
    
    function disableUsageTracking() {
        save.data.askedUsageTracking = true;
        USAGE_TRACKING = false;
    
    /************************************************************
     * The player clicked the credits button. Shows the credits modal.
     ************************************************************/
    function showCreditModal () {
        $creditModal.modal('show');
    }
    
    /************************************************************
     * The player clicked the version button. Shows the version modal.
     ************************************************************/
    function showVersionModal () {
        $versionModal.modal('show');
    }
    
    
    /************************************************************
     * The player clicked the player tags button. Shows the player tags modal.
     ************************************************************/
    function showPlayerTagsModal () {
        if (document.forms['player-tags'].elements.length <= 6) {
            // Safari doesn't support color inputs properly!
            var hairColorPicker = document.getElementById('hair_color_picker');
            var selectionType;
            try {
                selectionType = typeof hairColorPicker.selectionStart;
            } catch(e) {
                selectionType = null;
            }
    
            for (var choiceName in playerTagOptions) {
                var replace = (choiceName != 'skin_color' || selectionType === 'number');
                var $existing = $('form#player-tags [name="'+choiceName+'"]');
                if (!replace && $existing.length) continue;
                var $select = $('<select>', { name: choiceName });
    
                $select.append('<option>', playerTagOptions[choiceName].values.map(function(opt) {
                    return $('<option>').val(opt.value).addClass(opt.gender).append(opt.text || opt.value.replace(/_/g, ' ').initCap());
    
                }));
                if ($existing.length) {
                    $existing.parent().replaceWith($select);
                } else {
                    var $label = $('<label class="player-tag-select">');
                    if (playerTagOptions[choiceName].gender) {
                        $select.addClass(playerTagOptions[choiceName].gender);
                        $label.addClass(playerTagOptions[choiceName].gender);
                    }
                    $label.append('Choose your ' + choiceName.replace(/_/g, ' ') + ':', $select);
                    $('form#player-tags').append($label);
                }
    
            }
    
            var rgb2hsv = function(rgb) {
              var r = parseInt(rgb.slice(1,3), 16)/255;
              var g = parseInt(rgb.slice(3,5), 16)/255;
              var b = parseInt(rgb.slice(5), 16)/255;
    
              var min = Math.min(r, Math.min(g,b));
              var max = Math.max(r, Math.max(g,b));
    
              if (max === 0) {
                return [0,0,0];
              }
    
              var maxOffset = max === r ? 0 : (max === g ? 2 : 4);
              var delta = max === r ? g-b : (max === g ? b-r : r-g);
    
              var h = 60 * (maxOffset + delta / (max - min));
              if (h < 0) {
                h += 360;
              }
    
              return [h, (max - min) / max * 100, max * 100];
            }
    
            /* convert the raw colors to corresponding tags and display next to selector */
            $('input[type=color]').on('input', function() {
                var h, s, v;
                [h,s,v] = rgb2hsv(this.value);
    
                var tag;
                color2tag:
                if (this.id === 'hair_color_picker') {
                  if (v < 10) {
                    tag = 'black_hair';
                    break color2tag;
                  }
    
                  if (s < 25) {
                    if (v < 30) {
                      tag = 'black_hair';
                    } else {
                      tag = 'white_hair';
                    }
                    break color2tag;
                  }
    
                  if (s < 50 && h > 20 && h < 50) {
                    tag = 'brunette';
                    break color2tag;
                  }
    
                  if (h < 50) {
                    tag = 'ginger';
                  } else if (h < 65) {
                    tag = 'blonde';
                  } else if (h < 325) {
                    if (h < 145) {
                      tag = 'green_hair';
                    } else if (h < 255) {
                      tag = 'blue_hair';
                    } else if (h < 290) {
                      tag = 'purple_hair';
                    } else {
                      tag = 'pink_hair';
                    }
                  } else {
                    tag = 'ginger';
                  }
                } else if (this.id === 'eye_color_picker') {
    
                  if (v < 25) {
                    tag = 'dark_eyes';
                    break color2tag;
                  }
    
                  if (s < 20) {
                    tag = 'pale_eyes';
                    break color2tag;
                  }
    
                  if (h < 15) {
                    tag = 'red_eyes';
                  } else if (h < 65) {
                    tag = 'amber_eyes';
                  } else if (h < 145) {
                    tag = 'green_eyes';
                  } else if (h < 255) {
                    tag = 'blue_eyes';
                  } else if (h < 325) {
                    tag = 'violet_eyes';
                  } else {
                    tag = 'red_eyes';
                  }
                }
    
                this.previousElementSibling.value = tag || '';
            });
    
    
            $('input[name=skin_color]').on('input', function() {
    
                for (var i = 0; i < playerTagOptions['skin_color'].values.length; i++) {
                    if (this.value <= playerTagOptions['skin_color'].values[i].to) {
                        tag = playerTagOptions['skin_color'].values[i].value;
                        break;
                    }
    
                }
    
                this.previousElementSibling.value = tag || '';
            });
    
            $('.modal-button.clearSelections').click(function() {
                var formElements = document.forms['player-tags'].elements;
                for (var i = 0; i < formElements.length; i++) {
                    if (formElements[i].type !== 'color') {
                        formElements[i].value = '';
                    }
                }
            });
        }
    
    
        for (var choiceName in playerTagOptions) {
    
            $('form#player-tags [name="'+choiceName+'"]').val(playerTagSelections[choiceName]).trigger('input');
    
        }
        $('#player-tags-confirm').one('click', function() {
            playerTagSelections = {};
            for (var choiceName in playerTagOptions) {
                if (!('gender' in playerTagOptions[choiceName]) || playerTagOptions[choiceName].gender == players[HUMAN_PLAYER].gender) {
                    var val = $('form#player-tags [name="'+choiceName+'"]').val();
                    if (val) {
                        playerTagSelections[choiceName] = val;
                    }
                }
            }
        });
    
        $playerTagsModal.modal('show');
    }
    
    
    /************************************************************
     * The player clicked on a table opacity button.
     ************************************************************/
    function toggleTableVisibility () {
    	if (tableOpacity > 0) {
    		$gameTable.fadeOut();
    		tableOpacity = 0;
    	} else {
    		$gameTable.fadeIn();
    		tableOpacity = 1;
    	}
    }
    
    function forceTableVisibility(state) {
        if (!state) {
    		$gameTable.fadeOut();
    		tableOpacity = 0;
    	} else {
    		$gameTable.fadeIn();
    		tableOpacity = 1;
    	}
    }
    
    /**********************************************************************
     *****                     Utility Functions                      *****
     **********************************************************************/
    
    /************************************************************
     * Returns a random number in a range.
     ************************************************************/
    function getRandomNumber (min, max) {
    	return Math.floor(Math.random() * (max - min) + min);
    }
    
    
    /************************************************************
     * Changes the first letter in a string to upper case.
     ************************************************************/
    String.prototype.initCap = function() {
    	return this.substr(0, 1).toUpperCase() + this.substr(1);
    }
    
    /************************************************************
    
     * Counts the number of elements that evaluate as true, or,
    
     * if a function is provided, passes the test implemented by it.
     ************************************************************/
    Array.prototype.countTrue = function(func) {
        var count = 0;
        for (var i = 0; i < this.length; i++) {
            if (i in this
                && (func ? func(this[i], i, this) : this[i])) {
                count++;
            }
        }
        return count;
    }
    
    
    /************************************************************
     * Generate a random alphanumeric ID.
     ************************************************************/
    function generateRandomID() {
        var ret = ''
        for (let i=0;i<10;i++) {
            ret += 'abcdefghijklmnopqrstuvwxyz1234567890'[getRandomNumber(0,36)]
        }
    
    /**********************************************************************
     * Returns the width of the visible screen in pixels.
     **/
    
    FarawayVision's avatar
    FarawayVision committed
    function getScreenWidth ()
    
    {
    	/* fetch all game screens */
    	var screens = document.getElementsByClassName('screen');
    
    	/* figure out which screen is visible */
    
    FarawayVision's avatar
    FarawayVision committed
    	for (var i = 0; i < screens.length; i++)
    
    FarawayVision's avatar
    FarawayVision committed
    		if (screens[i].offsetWidth > 0)
    
            {
    			/* this screen is currently visible */
    			return screens[i].offsetWidth;
    		}
    	}
    }
    
    /**********************************************************************
     * Automatically adjusts the size of all font based on screen width.
     **/
    
    FarawayVision's avatar
    FarawayVision committed
    function autoResizeFont ()
    
    {
    	/* resize font */
    	var screenWidth = getScreenWidth();
    	document.body.style.fontSize = (14*(screenWidth/1000))+'px';
    
    
    	if (backgroundImage && backgroundImage.height && backgroundImage.width) {
    		var w = window.innerWidth, h = window.innerHeight;
    		if (h > (3/4) * w) {
    			h = (3/4) * w;
    		} else {
    
    			w = 4 * h / 3;
    
    		var ar = backgroundImage.width / backgroundImage.height;
    		if (ar > 4/3) {
    			var scale = Math.sqrt(16/9 / ar);
    			$("body").css("background-size", "auto " + Math.round(scale * h) + "px");
    
    			var scale = Math.sqrt(ar);
    			$("body").css("background-size", Math.round(scale * w) + "px auto");
    
    	/* set up future resizing */
    	window.onresize = autoResizeFont;
    
    
    /* Get the number of players loaded, including the human player.*/
    function countLoadedOpponents() {
        return players.reduce(function (a, v) { return a + (v ? 1 : 0); }, 0);
    }