Adds name changing feature. Tweaks timing rules.

This commit is contained in:
Cam Spry 2026-03-02 06:07:09 -05:00
parent f1e9470710
commit 2f5cdbfaa7
4 changed files with 40 additions and 18 deletions

View File

@ -332,7 +332,9 @@
<div id="layout"> <div id="layout">
<div id="header"> <div id="header">
Y.A.R.N. HTML5 - Spin a Story! -&nbsp;<span id="room-display"></span> <img src="../images/yarnlogo.png" /> &nbsp; <span id="room-display"></span>
<button id="change-name-btn" style="margin-left:auto;margin-right:1rem;font-size:.9rem;padding:.3rem .6rem;">Change Name</button>
<button id="leave-game-btn" style="margin-right:1rem;font-size:.9rem;padding:.3rem .6rem;">Leave Game</button>
</div> </div>
<div id="main-text"> <div id="main-text">
@ -789,6 +791,21 @@
updateGameBar(); updateGameBar();
} }
// === Header Functions ===
document.getElementById('change-name-btn').addEventListener('click', () => {
const newName = prompt('Enter a new name (1-20 characters):', playerName);
if (newName && newName.trim() && newName.trim() !== playerName) {
socket.emit('setName', { name: newName.trim() });
}
});
document.getElementById('leave-game-btn').addEventListener('click', () => {
if (confirm('Are you sure you want to leave the game?')) {
window.location.href = '/';
}
});
// === Chat Functions === // === Chat Functions ===
document.getElementById('chat-input').addEventListener('keypress', function(e) { document.getElementById('chat-input').addEventListener('keypress', function(e) {

BIN
public/images/yarnlogo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

View File

@ -3,7 +3,7 @@
<head> <head>
<meta charset="UTF-8"/> <meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/> <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<meta property="og:title" content="Y.A.R.N HTML5" /> <meta property="og:title" content="Y.A.R.N. HTML5" />
<meta property="og:description" content="A collaborative story-writing game. Remake of the 2000s GameSpy Arcade classic." /> <meta property="og:description" content="A collaborative story-writing game. Remake of the 2000s GameSpy Arcade classic." />
<title>Y.A.R.N. HTML5</title> <title>Y.A.R.N. HTML5</title>
@ -104,8 +104,8 @@
<body> <body>
<div id="splash"> <div id="splash">
<h2>Y.A.R.N.</h2> <img src="images/yarnlogo.png" />
<h4>HTML5 Edition</h4> <h4 style="font-style: italic; color:orangered">HTML5 Edition</h4>
<!-- ENTER GAME --> <!-- ENTER GAME -->
<div class="section"> <div class="section">
@ -127,14 +127,14 @@
<!-- ABOUT OVERLAY --> <!-- ABOUT OVERLAY -->
<div id="about-overlay" class="hidden"> <div id="about-overlay" class="hidden">
<div id="about-box"> <div id="about-box">
<h3>What is Y.A.R.N HTML5?</h3> <h3>What is Y.A.R.N. HTML5?</h3>
<p>Y.A.R.N (Yet Another Random Narrative) was a collaborative story-writing "parlor game" originally included in GameSpy Arcade in the early 2000s, but removed shortly thereafter due to security issues that were never resolved.</p> <p>Y.A.R.N. (Yet Another Random Narrative) was a collaborative story-writing "parlor game" originally included in GameSpy Arcade in the early 2000s, but removed shortly thereafter due to security issues that were never resolved.</p>
<p><strong>Y.A.R.N. HTML5</strong> is a modern remake of (what I can remember from) the original, designed to run in-browser, and with extra features such as (optional) LLM-based bots.</p> <p><strong>Y.A.R.N. HTML5</strong> is a modern remake of (what I can remember from) the original, designed to run in-browser, and with extra features such as (optional) LLM-based bots.</p>
<p><strong>The rules are simple:</strong></p> <p style="text-align: left;"><strong>The rules are simple:</strong></p>
<ul> <ul style="text-align: left;">
<li>Players have a limited amount of time to write a sentence, selected by the host at game start.</li> <li>Players have a limited amount of time to write a sentence, selected by the host at game start.</li>
<li>Once all players have submitted their sentences, players have 30 seconds to vote for their favorite.</li> <li>Once all players have submitted their sentences, players have 30 seconds to vote for their favorite.</li>
<li>The player whose sentence is chosen receives 3 bonus points, and all players receive one point for each vote they received.</li> <li>All players receive one point for each vote their sentence receives. The player whose sentence receives the most votes is awarded 3 bonus points.</li>
<li>In the case of a tie, the longest sentence receives two extra points and the other sentences tied receive 1 point each.</li> <li>In the case of a tie, the longest sentence receives two extra points and the other sentences tied receive 1 point each.</li>
<li>When a player reaches the point limit, they win the game.</li> <li>When a player reaches the point limit, they win the game.</li>
</ul> </ul>

View File

@ -200,8 +200,9 @@ class YarnGame {
async startVotingPhase() { async startVotingPhase() {
this.currentPhase = 'voting'; this.currentPhase = 'voting';
this.votedPlayers = new Set(); this.votedPlayers = new Set();
this.timeRemaining = 30; // 30 seconds for voting const entriesCount = this.round_data.length
await this.processBotVoting() this.timeRemaining = 10 * entriesCount; // 10 seconds * num of entries for voting
this.processBotVoting()
} }
submitVote(playerId, entryIndex) { submitVote(playerId, entryIndex) {
@ -226,10 +227,17 @@ class YarnGame {
tallyVotes() { tallyVotes() {
if (this.round_data.length === 0) return null; if (this.round_data.length === 0) return null;
// ----- helper to award points, possibly doubled -----
const givePoints = (player, pts) => {
if (!player) return;
const multiplier =
this.currentRound + 1 === this.gameSettings.roundLimit ? 2 : 1;
player.score += pts * multiplier;
};
// 1. Award 1 point per vote to every sentence // 1. Award 1 point per vote to every sentence
for (const entry of this.round_data) { for (const entry of this.round_data) {
const p = this.getPlayer(entry.player_id); givePoints(this.getPlayer(entry.player_id), entry.votes.length);
if (p) p.score += entry.votes.length;
} }
// 2. Find the top vote count // 2. Find the top vote count
@ -239,9 +247,7 @@ class YarnGame {
// 3. Decide bonus points // 3. Decide bonus points
if (leaders.length === 1) { if (leaders.length === 1) {
// Single winner: +3 // Single winner: +3
const winner = leaders[0]; givePoints(this.getPlayer(leaders[0].player_id), 3);
const p = this.getPlayer(winner.player_id);
if (p) p.score += 3;
} else { } else {
// Tie: longest gets +2, rest +1 // Tie: longest gets +2, rest +1
const longest = leaders.reduce( const longest = leaders.reduce(
@ -249,8 +255,7 @@ class YarnGame {
leaders[0] leaders[0]
); );
for (const l of leaders) { for (const l of leaders) {
const p = this.getPlayer(l.player_id); givePoints(this.getPlayer(l.player_id), l === longest ? 2 : 1);
if (p) p.score += l === longest ? 2 : 1;
} }
} }