feat: Implement intro screen with retro 80s aesthetic and unit tests
Run Tests / test (pull_request) Failing after 40s
Run Tests / test (pull_request) Failing after 40s
This commit is contained in:
@@ -60,3 +60,58 @@
|
||||
font-size: 14px;
|
||||
color: var(--text-color);
|
||||
}
|
||||
/* Intro Screen Styles */
|
||||
.intro-screen {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100vh;
|
||||
background-color: #0a0a1a;
|
||||
color: #00ff00;
|
||||
font-family: 'Courier New', monospace;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 48px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 20px;
|
||||
text-shadow: 0 0 10px #00ff00;
|
||||
letter-spacing: 4px;
|
||||
}
|
||||
|
||||
.credits {
|
||||
font-size: 16px;
|
||||
margin-bottom: 30px;
|
||||
color: #a0a0a0;
|
||||
}
|
||||
|
||||
.instructions {
|
||||
font-size: 14px;
|
||||
margin-bottom: 30px;
|
||||
color: #00ff00;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.options {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.option {
|
||||
padding: 10px 20px;
|
||||
margin: 5px 0;
|
||||
font-size: 18px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
border: 2px solid transparent;
|
||||
}
|
||||
|
||||
.option.selected {
|
||||
border-color: #00ff00;
|
||||
background-color: rgba(0, 255, 0, 0.1);
|
||||
}
|
||||
|
||||
.option:hover {
|
||||
background-color: rgba(0, 255, 0, 0.2);
|
||||
}
|
||||
|
||||
@@ -27,3 +27,15 @@
|
||||
<script src="js/screens/loading.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
<div id="screen" class="screen">
|
||||
<!-- Screen content will be dynamically loaded -->
|
||||
</div>
|
||||
<div id="screen" class="screen">
|
||||
<!-- Screen content will be dynamically loaded -->
|
||||
</div>
|
||||
<div id="screen" class="screen">
|
||||
<!-- Screen content will be dynamically loaded -->
|
||||
</div>
|
||||
<div id="screen" class="screen">
|
||||
<!-- Screen content will be dynamically loaded -->
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
class IntroScreen {
|
||||
constructor() {
|
||||
this.title = 'WINGS88';
|
||||
this.credits = 'A RetroWeb Games Production';
|
||||
this.instructions = 'Use arrow keys to fly\nPress SPACE to start';
|
||||
this.options = ['Start Game', 'Instructions', 'Quit'];
|
||||
this.selectedOption = 0;
|
||||
this.audioContext = null;
|
||||
this.musicTrack = null;
|
||||
}
|
||||
|
||||
init() {
|
||||
this.setupAudio();
|
||||
this.render();
|
||||
this.setupEventListeners();
|
||||
}
|
||||
|
||||
setupAudio() {
|
||||
try {
|
||||
this.audioContext = new (window.AudioContext || window.webkitAudioContext)();
|
||||
this.loadMusic();
|
||||
} catch (e) {
|
||||
console.log('Audio initialization failed:', e);
|
||||
}
|
||||
}
|
||||
|
||||
loadMusic() {
|
||||
// Placeholder for retro MIDI music
|
||||
console.log('Loading retro music...');
|
||||
}
|
||||
|
||||
render() {
|
||||
const screen = document.getElementById('screen');
|
||||
screen.innerHTML = `
|
||||
<div class="intro-screen">
|
||||
<div class="title">${this.title}</div>
|
||||
<div class="credits">${this.credits}</div>
|
||||
<div class="instructions">${this.instructions}</div>
|
||||
<div class="options">
|
||||
${this.options.map((option, index) => `
|
||||
<div class="option ${index === this.selectedOption ? 'selected' : ''}">
|
||||
${option}
|
||||
</div>
|
||||
`).join('')}
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
setupEventListeners() {
|
||||
document.addEventListener('keydown', (e) => this.handleKeyDown(e));
|
||||
}
|
||||
|
||||
handleKeyDown(e) {
|
||||
switch(e.key) {
|
||||
case 'ArrowUp':
|
||||
this.moveSelection(-1);
|
||||
break;
|
||||
case 'ArrowDown':
|
||||
this.moveSelection(1);
|
||||
break;
|
||||
case 'Enter':
|
||||
this.selectOption();
|
||||
break;
|
||||
case 'Escape':
|
||||
this.selectOption();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
moveSelection(direction) {
|
||||
this.selectedOption = (this.selectedOption + direction + this.options.length) % this.options.length;
|
||||
this.render();
|
||||
}
|
||||
|
||||
selectOption() {
|
||||
switch(this.selectedOption) {
|
||||
case 0:
|
||||
console.log('Starting game...');
|
||||
break;
|
||||
case 1:
|
||||
console.log('Showing instructions...');
|
||||
break;
|
||||
case 2:
|
||||
console.log('Quitting game...');
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
cleanup() {
|
||||
document.removeEventListener('keydown', this.handleKeyDown);
|
||||
if (this.audioContext) {
|
||||
this.audioContext.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = IntroScreen;
|
||||
@@ -0,0 +1,85 @@
|
||||
const IntroScreen = require('../../src/js/screens/intro');
|
||||
|
||||
describe('IntroScreen', () => {
|
||||
let introScreen;
|
||||
|
||||
beforeEach(() => {
|
||||
introScreen = new IntroScreen();
|
||||
});
|
||||
|
||||
describe('constructor', () => {
|
||||
it('should initialize with correct default values', () => {
|
||||
expect(introScreen.title).toBe('WINGS88');
|
||||
expect(introScreen.credits).toBe('A RetroWeb Games Production');
|
||||
expect(introScreen.instructions).toBe('Use arrow keys to fly\nPress SPACE to start');
|
||||
expect(introScreen.options).toEqual(['Start Game', 'Instructions', 'Quit']);
|
||||
expect(introScreen.selectedOption).toBe(0);
|
||||
expect(introScreen.audioContext).toBeNull();
|
||||
expect(introScreen.musicTrack).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('moveSelection', () => {
|
||||
it('should move selection up', () => {
|
||||
introScreen.selectedOption = 1;
|
||||
// Mock render to prevent DOM access
|
||||
introScreen.render = jest.fn();
|
||||
introScreen.moveSelection(-1);
|
||||
expect(introScreen.selectedOption).toBe(0);
|
||||
expect(introScreen.render).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should move selection down', () => {
|
||||
introScreen.selectedOption = 0;
|
||||
// Mock render to prevent DOM access
|
||||
introScreen.render = jest.fn();
|
||||
introScreen.moveSelection(1);
|
||||
expect(introScreen.selectedOption).toBe(1);
|
||||
expect(introScreen.render).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should wrap around from last to first', () => {
|
||||
introScreen.selectedOption = 2;
|
||||
// Mock render to prevent DOM access
|
||||
introScreen.render = jest.fn();
|
||||
introScreen.moveSelection(1);
|
||||
expect(introScreen.selectedOption).toBe(0);
|
||||
expect(introScreen.render).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should wrap around from first to last', () => {
|
||||
introScreen.selectedOption = 0;
|
||||
// Mock render to prevent DOM access
|
||||
introScreen.render = jest.fn();
|
||||
introScreen.moveSelection(-1);
|
||||
expect(introScreen.selectedOption).toBe(2);
|
||||
expect(introScreen.render).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('selectOption', () => {
|
||||
it('should handle Start Game selection', () => {
|
||||
introScreen.selectedOption = 0;
|
||||
const logSpy = jest.spyOn(console, 'log');
|
||||
introScreen.selectOption();
|
||||
expect(logSpy).toHaveBeenCalledWith('Starting game...');
|
||||
logSpy.mockRestore();
|
||||
});
|
||||
|
||||
it('should handle Instructions selection', () => {
|
||||
introScreen.selectedOption = 1;
|
||||
const logSpy = jest.spyOn(console, 'log');
|
||||
introScreen.selectOption();
|
||||
expect(logSpy).toHaveBeenCalledWith('Showing instructions...');
|
||||
logSpy.mockRestore();
|
||||
});
|
||||
|
||||
it('should handle Quit selection', () => {
|
||||
introScreen.selectedOption = 2;
|
||||
const logSpy = jest.spyOn(console, 'log');
|
||||
introScreen.selectOption();
|
||||
expect(logSpy).toHaveBeenCalledWith('Quitting game...');
|
||||
logSpy.mockRestore();
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user