first commit

This commit is contained in:
Brian Fertig 2025-10-19 16:48:52 -06:00
commit 425978dcb4
19 changed files with 2945 additions and 0 deletions

BIN
assets/dark-ages.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

BIN
assets/dark-ages.psd Normal file

Binary file not shown.

58
assets/night-woods.json Normal file
View File

@ -0,0 +1,58 @@
{ "compressionlevel":-1,
"height":18,
"infinite":false,
"layers":[
{
"data":[2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 3, 1, 2147483651, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 1, 1, 2147483651, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 1, 2147483651, 2, 2,
2, 3, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 3, 1, 1, 1, 1, 2147483651, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 2147483651, 2,
2, 1, 1, 1, 1, 2147483651, 2, 2, 2, 2, 2, 3, 1, 1, 1, 1, 1, 1, 1, 1, 2147483651, 2, 2, 2, 2, 2, 3, 1, 1, 1, 1, 2,
2, 1, 1, 1, 1, 1, 2, 2, 2, 3, 1, 1, 1, 1, 4, 2, 2, 2147483652, 1, 1, 1, 1, 2147483651, 2, 2, 2, 1, 1, 1, 1, 1, 2,
2, 1, 1, 1, 1, 1, 2147483651, 3, 1, 1, 1, 1, 1, 4, 2, 2, 2, 2, 2147483652, 1, 1, 1, 1, 1, 2147483651, 3, 1, 1, 1, 1, 1, 2,
2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 4, 2, 2, 2, 3, 2147483651, 2, 2, 2, 2147483652, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2,
2, 1, 1, 1, 1, 1, 1, 1, 1, 4, 2, 2, 2, 3, 1, 1, 1, 1, 2147483651, 2, 2, 2, 2147483652, 1, 1, 1, 1, 1, 1, 1, 1, 2,
2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2,
2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2,
2, 1, 1, 1, 1, 1, 1, 1, 1, 2147483651, 2, 2, 2, 2147483652, 1073741825, 1073741825, 1073741825, 1073741825, 4, 2, 2, 2, 3, 1, 1, 1, 1, 1, 1, 1, 1, 2,
2, 1, 1, 1, 1, 1, 1, 1, 1, 1073741825, 1073741825, 2147483651, 2, 2, 2, 2147483652, 4, 2, 2, 2, 3, 1073741825, 1073741825, 1, 1, 1, 1, 1, 1, 1, 1, 2,
2, 1, 1, 1, 1073741825, 1073741825, 4, 2147483652, 1073741825, 1073741825, 1073741825, 1073741825, 1073741825, 2147483651, 2, 2, 2, 2, 3, 1073741825, 1073741825, 1073741825, 1073741825, 1073741825, 4, 2147483652, 1073741825, 1073741825, 1, 1, 1, 2,
2, 1, 1, 1, 1073741825, 1073741825, 2, 2, 2, 2147483652, 1073741825, 1073741825, 1073741825, 1073741825, 2147483651, 2, 2, 3, 1073741825, 1073741825, 1073741825, 1073741825, 4, 2, 2, 2, 1073741825, 1073741825, 1, 1, 1, 2,
2, 1, 1, 1, 1073741825, 4, 2, 2, 2, 2, 2, 2147483652, 1073741825, 1073741825, 1073741825, 1073741825, 1073741825, 1073741825, 1073741825, 1073741825, 4, 2, 2, 2, 2, 2, 2147483652, 1073741825, 1, 1, 1, 2,
2, 2147483652, 1, 1, 1073741825, 2, 2, 2, 2, 2, 2, 2, 2, 2147483652, 1073741825, 1073741825, 1073741825, 1073741825, 4, 2, 2, 2, 2, 2, 2, 2, 2, 1073741825, 1, 1, 4, 2,
2, 2, 2147483652, 1, 4, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2147483652, 1073741825, 1073741825, 4, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2147483652, 1, 4, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2],
"height":18,
"id":1,
"name":"main",
"opacity":1,
"type":"tilelayer",
"visible":true,
"width":32,
"x":0,
"y":0
}],
"nextlayerid":2,
"nextobjectid":1,
"orientation":"orthogonal",
"renderorder":"right-down",
"tiledversion":"1.11.2",
"tileheight":64,
"tilesets":[
{
"columns":10,
"firstgid":1,
"image":"terrain.png",
"imageheight":640,
"imagewidth":640,
"margin":0,
"name":"terrain",
"spacing":0,
"tilecount":100,
"tileheight":64,
"tilewidth":64
}],
"tilewidth":64,
"type":"map",
"version":"1.10",
"width":32
}

BIN
assets/terrain.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

BIN
assets/terrain.psd Normal file

Binary file not shown.

16
index.html Normal file
View File

@ -0,0 +1,16 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Battle Game</title>
<style>
body { margin: 0; background-color: black; }
canvas { display: block; }
</style>
</head>
<body>
<div id="game-container"></div>
<script src="./src/phaser.min.js"></script>
<script type="module" src="./src/main.js"></script>
</body>
</html>

31
management/map.tmx Normal file
View File

@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8"?>
<map version="1.10" tiledversion="1.11.2" orientation="orthogonal" renderorder="right-down" width="32" height="18" tilewidth="64" tileheight="64" infinite="0" nextlayerid="2" nextobjectid="1">
<editorsettings>
<export target="../assets/night-woods.json" format="json"/>
</editorsettings>
<tileset firstgid="1" name="terrain" tilewidth="64" tileheight="64" tilecount="100" columns="10">
<image source="../assets/terrain.png" width="640" height="640"/>
</tileset>
<layer id="1" name="main" width="32" height="18">
<data encoding="csv">
2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
2,2,3,1,2147483651,2,2,2,2,2,2,2,2,2,3,1,1,2147483651,2,2,2,2,2,2,2,2,2,3,1,2147483651,2,2,
2,3,1,1,1,2,2,2,2,2,2,2,2,3,1,1,1,1,2147483651,2,2,2,2,2,2,2,2,1,1,1,2147483651,2,
2,1,1,1,1,2147483651,2,2,2,2,2,3,1,1,1,1,1,1,1,1,2147483651,2,2,2,2,2,3,1,1,1,1,2,
2,1,1,1,1,1,2,2,2,3,1,1,1,1,4,2,2,2147483652,1,1,1,1,2147483651,2,2,2,1,1,1,1,1,2,
2,1,1,1,1,1,2147483651,3,1,1,1,1,1,4,2,2,2,2,2147483652,1,1,1,1,1,2147483651,3,1,1,1,1,1,2,
2,1,1,1,1,1,1,1,1,1,1,4,2,2,2,3,2147483651,2,2,2,2147483652,1,1,1,1,1,1,1,1,1,1,2,
2,1,1,1,1,1,1,1,1,4,2,2,2,3,1,1,1,1,2147483651,2,2,2,2147483652,1,1,1,1,1,1,1,1,2,
2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,
2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,
2,1,1,1,1,1,1,1,1,2147483651,2,2,2,2147483652,1073741825,1073741825,1073741825,1073741825,4,2,2,2,3,1,1,1,1,1,1,1,1,2,
2,1,1,1,1,1,1,1,1,1073741825,1073741825,2147483651,2,2,2,2147483652,4,2,2,2,3,1073741825,1073741825,1,1,1,1,1,1,1,1,2,
2,1,1,1,1073741825,1073741825,4,2147483652,1073741825,1073741825,1073741825,1073741825,1073741825,2147483651,2,2,2,2,3,1073741825,1073741825,1073741825,1073741825,1073741825,4,2147483652,1073741825,1073741825,1,1,1,2,
2,1,1,1,1073741825,1073741825,2,2,2,2147483652,1073741825,1073741825,1073741825,1073741825,2147483651,2,2,3,1073741825,1073741825,1073741825,1073741825,4,2,2,2,1073741825,1073741825,1,1,1,2,
2,1,1,1,1073741825,4,2,2,2,2,2,2147483652,1073741825,1073741825,1073741825,1073741825,1073741825,1073741825,1073741825,1073741825,4,2,2,2,2,2,2147483652,1073741825,1,1,1,2,
2,2147483652,1,1,1073741825,2,2,2,2,2,2,2,2,2147483652,1073741825,1073741825,1073741825,1073741825,4,2,2,2,2,2,2,2,2,1073741825,1,1,4,2,
2,2,2147483652,1,4,2,2,2,2,2,2,2,2,2,2147483652,1073741825,1073741825,4,2,2,2,2,2,2,2,2,2,2147483652,1,4,2,2,
2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2
</data>
</layer>
</map>

19
src/config/faction.js Normal file
View File

@ -0,0 +1,19 @@
export const FACTION_CONFIG = {
'dark-ages': {
0: {
name: 'Knight',
description: 'Ye Magesty\'s base unit',
stats: {
type: 'melee',
health: 5,
attackMin: 1,
attackMax: 4,
meleeDefense: 1,
rangeDefense: 2,
magicDefense: 1,
sight: 150,
range: 64
},
}
}
}

View File

@ -0,0 +1,58 @@
export const PATH_CONFIG = {
'left': {
1: {
0: {
'x': 4,
'y': 5,
},
1: {
'x': 6,
'y': 8,
},
2: {
'x': 11,
'y': 4,
},
3: {
'x': 15,
'y': 2,
},
4: {
'x': 19,
'y': 4,
},
5: {
'x': 27,
'y': 8,
},
}
},
'right': {
1: {
0: {
'x': 27,
'y': 4,
},
1: {
'x': 25,
'y': 7,
},
2: {
'x': 20,
'y': 4,
},
3: {
'x': 16,
'y': 2,
},
4: {
'x': 11,
'y': 5,
},
5: {
'x': 5,
'y': 7,
},
}
}
}

129
src/faction/factions.js Normal file
View File

@ -0,0 +1,129 @@
import { PATH_CONFIG } from "../config/night-woods-config.js";
import { FACTION_CONFIG } from "../config/faction.js";
export class Faction extends Phaser.GameObjects.Sprite {
constructor(scene, x, y, texture, frame, side, path, faction) {
super(scene, x, y, texture, frame);
this.scene = scene;
scene.add.existing(this);
scene.physics.world.enable(this);
this.body.setCollideWorldBounds(true);
if (side === 'left') {
scene.factionLeft.add(this);
}
if (side === 'right') {
scene.factionRight.add(this);
}
// Stats
this.stats = {};
Object.entries(FACTION_CONFIG[faction][frame].stats).forEach(([key, value]) => {
this.stats[key] = value;
});
console.log(this);
// Path following properties
this.path = path;
this.side = side;
this.currentWaypoint = 0;
this.speed = 50;
this.arrived = false;
this.isPathPaused = false;
this.animKey = `${texture}-${frame}`;
this.createAnim();
this.play(this.animKey);
// Initialize path following
this.initializePath();
}
createAnim() {
if (!this.scene.anims.get(this.animKey)) {
this.scene.anims.create({
key: this.animKey,
frames: [
{ key: this.texture.key, frame: this.frame.name },
{ key: this.texture.key, frame: this.frame.name+1 }
],
frameRate: 2,
repeat: -1
});
}
}
initializePath() {
// Get the path data from config
const pathData = PATH_CONFIG[this.side][this.path];
if (pathData) {
this.waypoints = [];
for (let i = 0; i < Object.keys(pathData).length; i++) {
this.waypoints.push({
x: pathData[i].x*64,
y: pathData[i].y*64
});
}
// Start moving towards first waypoint
if (this.waypoints.length > 0) {
this.moveToWaypoint(0);
}
}
}
moveToWaypoint(waypointIndex) {
if (this.isPathPaused) return;
if (waypointIndex >= this.waypoints.length) {
this.arrived = true;
this.body.setVelocity(0, 0);
this.stop(this.animKey);
return;
}
const target = this.waypoints[waypointIndex];
const distance = Phaser.Math.Distance.Between(this.x, this.y, target.x, target.y);
if (distance < 5) { // Threshold for reaching waypoint
this.currentWaypoint = waypointIndex + 1;
this.moveToWaypoint(this.currentWaypoint);
} else {
// Calculate direction vector
const angle = Phaser.Math.Angle.Between(this.x, this.y, target.x, target.y);
// Set velocity based on angle and speed
this.body.setVelocity(
Math.cos(angle) * this.speed,
Math.sin(angle) * this.speed
);
}
}
pausePath() {
this.isPathPaused = true;
this.body.setVelocity(0, 0);
}
resumePath() {
this.isPathPaused = false;
// Resume from current waypoint
this.moveToWaypoint(this.currentWaypoint);
}
update(time, delta) {
// Flip sprite based on movement direction
if (this.body && this.body.velocity) {
if (this.body.velocity.x > 0) {
this.setFlipX(true); // Moving right, normal orientation
} else if (this.body.velocity.x < 0) {
this.setFlipX(false); // Moving left, flipped
}
}
// Move towards next waypoint if not arrived and path is not paused
if (!this.arrived && !this.isPathPaused) {
this.moveToWaypoint(this.currentWaypoint);
}
}
}

27
src/main.js Normal file
View File

@ -0,0 +1,27 @@
import { Level } from './scenes/level.js'
const GAME_CONFIG = {
type: Phaser.AUTO,
scale: {
mode: Phaser.Scale.FIT,
autoCenter: Phaser.Scale.CENTER_BOTH,
width: 1600,
height: 900,
parent: 'game-container'
},
parent: 'game-container',
backgroundColor: '#bb7432ff',
scene: [
Level,
],
physics: {
default: 'arcade',
arcade: {
gravity: { y: 0 },
debug: false
}
},
};
// Create the game instance
const game = new Phaser.Game(GAME_CONFIG);

1
src/phaser.min.js vendored Normal file

File diff suppressed because one or more lines are too long

312
src/scenes/audioManager.js Normal file
View File

@ -0,0 +1,312 @@
export class AudioManager extends Phaser.Scene {
constructor() {
super({ key: 'AudioManager', active: true });
this.soundInstances = {};
this.currentTrackIndex = 0;
this.isShuffled = false;
this.trackQueue = [];
this.currentSoundtrack = null;
this.tracks = {
'levelMusic1': {
'title': 'Kevin be Wack',
'artist': 'The Scrum Masta',
'misc': '2025 - IT Records'
},
'levelMusic2': {
'title': 'ClienTek is Coming!',
'artist': 'Tha Day-Lee St4nd',
'misc': '2025 - IT Records'
},
'levelMusic3': {
'title': 'How Would Amazon Do it?',
'artist': 'Billy Gitpull Jr.',
'misc': '2025 - IT Records'
},
'levelMusic4': {
'title': 'Commit to the Git',
'artist': 'Peer Review',
'misc': '2025 - IT Records'
},
'levelMusic5': {
'title': 'ClienTek C4shin\' Checks',
'artist': 'MC Cr4iG V',
'misc': '2025 - IT Records'
},
'levelMusic6': {
'title': 'Solutions of the Heart',
'artist': 'Kevin Smooth',
'misc': '2025 - IT Records'
},
'levelMusic7': {
'title': 'Keepin\' Your Website Country',
'artist': 'Git-e-up Gang',
'misc': '2025 - IT Records'
},
'levelMusic8': {
'title': 'Hello Humans',
'artist': 'Robotic Bryce',
'misc': '2025 - IT Records'
},
'levelMusic9': {
'title': '2 o\'clock Zoo o\'clock',
'artist': 'The HR Brothers',
'misc': '2025 - IT Records'
},
'levelMusicA': {
'title': 'Best Launch Ever',
'artist': 'Syntax',
'misc': '2025 - IT Records'
},
'levelMusicB': {
'title': 'Town Hall',
'artist': 'The KEGR Krew',
'misc': '2025 - IT Records'
},
'levelMusicC': {
'title': 'It\'s Stupid',
'artist': 'The Burlimonsters',
'misc': '2025 - IT Records'
},
'levelMusicD': {
'title': 'Fat Hawk',
'artist': 'Oliver Quantum',
'misc': '2025 - IT Records'
},
'levelMusicE': {
'title': 'Kevin Ghould Dance',
'artist': 'Hema-to-logic',
'misc': '2025 - IT Records'
},
};
}
preload() {
this.load.audio('pickup', 'assets/sounds/pickup.mp3');
this.load.audio('catFight1', 'assets/sounds/catFight1.mp3');
this.load.audio('catFight2', 'assets/sounds/catFight2.mp3');
this.load.audio('catFight3', 'assets/sounds/catFight3.mp3');
this.load.audio('chain', 'assets/sounds/chain.mp3');
this.load.audio('monster', 'assets/sounds/monster.mp3');
this.load.audio('hobbies', 'assets/sounds/hobbies.mp3');
this.load.audio('bread', 'assets/sounds/bread.mp3');
this.load.audio('freeze', 'assets/sounds/freeze.mp3');
this.load.audio('launch', 'assets/sounds/launch.mp3');
this.load.audio('banana', 'assets/sounds/banana.mp3');
this.load.audio('bonusDonut', 'assets/sounds/bonusDonut.mp3');
this.load.audio('bonusFreeze', 'assets/sounds/bonusFreeze.mp3');
this.load.audio('bonusKill', 'assets/sounds/bonusKill.mp3');
this.load.audio('explosion', 'assets/sounds/explosion.mp3');
this.load.audio('levelMusic1', 'assets/levelMusic1.mp3');
this.load.audio('levelMusic2', 'assets/menuMusic.mp3');
this.load.audio('levelMusic3', 'assets/levelMusic3.mp3');
this.load.audio('levelMusic4', 'assets/levelMusic4.mp3');
this.load.audio('levelMusic5', 'assets/levelMusic5.mp3');
this.load.audio('levelMusic6', 'assets/levelMusic6.mp3');
this.load.audio('levelMusic7', 'assets/levelMusic7.mp3');
this.load.audio('levelMusic8', 'assets/levelMusic8.mp3');
this.load.audio('levelMusic9', 'assets/levelMusic9.mp3');
this.load.audio('levelMusicA', 'assets/levelMusicA.mp3');
this.load.audio('levelMusicB', 'assets/levelMusicB.mp3');
this.load.audio('levelMusicC', 'assets/levelMusicC.mp3');
this.load.audio('levelMusicD', 'assets/levelMusicD.mp3');
this.load.audio('levelMusicE', 'assets/levelMusicE.mp3');
this.load.spritesheet('cassette', 'assets/cassette.png', {
frameWidth: 424,
frameHeight: 240
});
this.load.audio('playerHit', 'assets/sounds/playerHit.mp3');
this.load.audio('hit', 'assets/sounds/hit.mp3');
}
create() {
}
playManagedSound(key, maxInstances = 1, volume = 1) {
if (!this.soundInstances[key]) {
this.soundInstances[key] = [];
}
// Clean up old sound instances that have finished playing
this.soundInstances[key] = this.soundInstances[key].filter(s => s.isPlaying);
// If we have not reached the max limit, play a new instance
if (this.soundInstances[key].length < maxInstances) {
const sound = this.sound.add(key, { volume: volume });
sound.play();
this.soundInstances[key].push(sound);
}
}
playSoundtrack() {
if (this.trackQueue.length === 0) {
// Reinitialize queue if empty
this.initializeTrackQueue();
}
if (!this.audioInterface) {
this.initializeInterface();
}
const currentKey = this.trackQueue[this.currentTrackIndex];
// Play the current track
this.showTrack(currentKey);
const sound = this.sound.add(currentKey);
sound.play();
// Keep reference to current soundtrack for stopping
this.currentSoundtrack = sound;
// Set up callback for when track finishes
sound.on('complete', () => {
this.nextTrack();
});
// Move to next track in queue
this.currentTrackIndex = (this.currentTrackIndex + 1) % this.trackQueue.length;
}
toggleSoundtrackPause(pause = true) {
if (this.currentSoundtrack) {
if (pause && this.currentSoundtrack.isPlaying) {
this.currentSoundtrack.pause();
} else if (!pause && !this.currentSoundtrack.isPlaying) {
this.currentSoundtrack.resume();
}
}
}
stopSoundtrack() {
if (this.currentSoundtrack) {
this.currentSoundtrack.stop();
this.currentSoundtrack = null;
// Hide the cassette interface if it's visible
if (this.cassette && this.cassette.alpha > 0) {
this.tweens.add({
targets: this.cassette,
alpha: 0,
duration: 500,
onComplete: () => {
this.cassette.x = 1655;
}
});
}
}
}
initializeTrackQueue() {
// Create a copy of track keys and shuffle them
this.trackQueue = Object.keys(this.tracks);
this.shuffleArray(this.trackQueue);
this.isShuffled = true;
}
shuffleArray(array) {
for (let i = array.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[array[i], array[j]] = [array[j], array[i]];
}
}
nextTrack() {
if (this.trackQueue.length === 0) {
return;
}
const currentKey = this.trackQueue[this.currentTrackIndex];
// Play the next track
this.showTrack(currentKey);
const sound = this.sound.add(currentKey);
sound.play();
// Keep reference to current soundtrack for stopping
this.currentSoundtrack = sound;
// Set up callback for when track finishes
sound.on('complete', () => {
this.nextTrack();
});
// Move to next track in queue
this.currentTrackIndex = (this.currentTrackIndex + 1) % this.trackQueue.length;
}
initializeInterface() {
this.audioInterface = true;
this.cassette = this.add.container(1655, 675).setScrollFactor(0).setAlpha(0);
const cassetteSprite = this.add.sprite(0, 0, 'cassette', 0).setOrigin(0.5);//.setScale(.7);
this.cassette.add(cassetteSprite);
this.anims.create({
key: 'cassette-play',
frames: [
{ key: 'cassette', frame: 0 },
{ key: 'cassette', frame: 1 },
{ key: 'cassette', frame: 2 },
],
frameRate: 10,
repeat: -1
});
cassetteSprite.play('cassette-play', true);
this.trackTitle = this.add.text(-125, -88, 'Track Title Here', {
fontSize: '20px',
fill: '#000000ff',
stroke: '#8d3131ff',
strokeThickness: 2,
shadow: {
offsetX: 2,
offsetY: 2,
color: '#000000',
blur: 4,
fill: true
}
}).setOrigin(0, 0.5);
this.cassette.add(this.trackTitle);
this.trackSub1 = this.add.text(-125, -78, 'By: Artist', {
fontSize: '18px',
fill: '#000000ff',
stroke: '#8d3131ff',
strokeThickness: 2,
shadow: {
offsetX: 2,
offsetY: 2,
color: '#000000',
blur: 4,
fill: true
}
});
this.cassette.add(this.trackSub1);
}
showTrack(track) {
this.trackTitle.setText(this.tracks[track].title);
this.trackSub1.setText(`By: ${this.tracks[track].artist}`);
this.tweens.add({
targets: this.cassette,
alpha: 1,
x: 1355,
ease: 'Cubic.Out',
duration: 1000,
onComplete: () => {
this.tweens.add({
targets: this.cassette,
alpha: 0,
duration: 2000,
delay: 5000,
onComplete: () => {
this.cassette.x = 1655;
}
});
}
})
}
}

42
src/scenes/eventConfig.js Normal file
View File

@ -0,0 +1,42 @@
export const EVENT_CONFIG = {
'time_events': {
'nightfall': {
//'start': 16, // number of minutes into the game
//'end': 19, // number of minutes into the game
'waveStart': 11,
//'waveEnd': 2,
'bossEnd': 'bossGhould',
'event': 'evNightfall', // name of event function to start
},
'zoo': {
'waveStart': 14,
'bossEnd': 'bossBollini',
'event': 'evZoo'
},
'matrix': {
'waveStart': 9,
'bossEnd': 'bossJennifer',
'event': 'evMatrix'
},
'cyber': {
'waveStart': 16,
'bossEnd': 'bossBryce',
'event': 'evCyber'
},
'hiphop': {
'waveStart': 3,
'bossEnd': 'bossCraig',
'event': 'evHipHop'
},
'princess': {
'waveStart': 18,
'bossEnd': 'bossFreya',
'event': 'evPrincess'
},
'mechaKevin': {
'waveStart': 21,
'bossEnd': 'bossMechaKevin',
'event': 'evMechaKevin'
}
}
}

755
src/scenes/eventManager.js Normal file
View File

@ -0,0 +1,755 @@
import { EVENT_CONFIG } from "./eventConfig.js";
export class EventManager extends Phaser.Scene {
constructor() {
super({ key: 'EventManager', active: true });
this.schedule = {};
this.triggers = [];
this.currentEvents = [];
this.levelSceneReference = null;
this.lightningTimer = 0;
this.lightningInterval = 2000;
this.evTimer = 0;
}
preload() {
for (let i = 1; i < 10; i++) {
this.load.audio(`zoo${i}`, `assets/sounds/zoo${i}.mp3`);
}
this.load.audio('zooOclockIntro', 'assets/sounds/zooOclockIntro.mp3');
this.load.audio('lightning-1', 'assets/sounds/lightning-1.mp3');
this.load.audio('lightning-2', 'assets/sounds/lightning-2.mp3');
this.load.audio('lightning-3', 'assets/sounds/lightning-3.mp3');
this.load.audio('cyberIntro', 'assets/sounds/cyberIntro.mp3');
this.load.audio('wolfHowl', 'assets/sounds/wolfHowl.mp3');
this.load.audio('hipHop', 'assets/sounds/hipHop.mp3');
this.load.audio('hipHopBeat', 'assets/sounds/hipHopBeat.mp3');
this.load.audio('princess', 'assets/sounds/princess.mp3');
this.load.image('evNightfall', 'assets/evNightfall.png');
this.load.image('ev2Zoo', 'assets/ev2Zoo.png');
this.load.image('ev2ZooSides', 'assets/ev2ZooSides.png');
this.load.image('evMatrix', 'assets/evMatrix.png');
this.load.image('evCyber', 'assets/evCyber.png');
this.load.image('evPrincess', 'assets/evPrincess.png');
this.load.image('blueParticle', 'assets/blueParticle.png');
this.load.spritesheet('graffiti', 'assets/graffiti.png', {
frameWidth: 400,
frameHeight: 250
});
}
create() {
// Build the Event Schedule
Object.values(EVENT_CONFIG.time_events).forEach(event => {
if (event.start) {
const start = event.start * 60 * 1000;
this.schedule[start] = [event.event, 'start'];
}
if (event.end) {
const end = event.end * 60 * 1000;
this.schedule[end] = [event.event, 'end'];
}
if (event.waveStart) {
this.triggers.push([event.waveStart, event.event, 'start']);
}
if (event.waveEnd) {
this.triggers.push([event.waveEnd, event.event, 'end']);
}
if (event.bossEnd) {
this.triggers.push([event.bossEnd, event.event, 'end']);
}
});
// Store reference to Level scene for later use
this.levelSceneReference = this.scene.get('Level');
this.AudioManager = this.scene.get('AudioManager');
}
waveTrigger(wave) {
this.triggers.forEach((event) => {
if (event[0] === wave) {
const eventFunction = event[1];
const eventType = event[2];
if (typeof this[eventFunction] === 'function') {
this[eventFunction](eventType);
} else {
console.log('Not A Function', eventFunction);
}
}
});
}
bossEndTrigger(boss) {
this.triggers.forEach((event) => {
if (event[0] === boss) {
const eventFunction = event[1];
const eventType = event[2];
if (typeof this[eventFunction] === 'function') {
this[eventFunction](eventType);
} else {
console.log('Not A Function', eventFunction);
}
}
});
}
evMechaKevin(event = 'start') {
if (event === 'start') {
this.levelSceneReference.UI.kevinFinal();
this.time.delayedCall(500, () => {
Object.values(this.levelSceneReference.player.weapons).forEach(weapon => {
weapon.destroy();
});
this.levelSceneReference.player.weapons = {};
this.levelSceneReference.drops.children.iterate(drop => {
if (drop && drop.active) {
drop.destroy();
}
});
this.levelSceneReference.enemies.children.iterate(enemy => {
if (enemy && enemy.active) {
enemy.destroy();
}
});
this.levelSceneReference.bonusManager.spawnMaxedWeapons();
this.levelSceneReference.waveManager.spawnMechaKevin();
});
}
else {
}
}
evPrincess(event = 'start') {
let levelScene = this.levelSceneReference || this.scene.get('Level');
if (levelScene && levelScene.mainLayer && levelScene.wallsLayer) {
if (event === 'start') {
this.currentEvents.push('princess');
// Create pink particle system for princess effect
this.princessParticles = this.add.particles(0, 0, 'blueParticle', {
speed: { min: -50, max: 50 },
angle: { min: 0, max: 360 },
scale: { start: 0.5, end: 0 },
quantity: 1,
lifespan: 1000,
emitZone: { type: 'random', source: new Phaser.Geom.Rectangle(0, 0, 1600, 900) },
blendMode: 'ADD',
tint: 0xff69b4
});
// Create sparkle effects
this.sparkles = [];
for (let i = 0; i < 20; i++) {
const sparkle = this.add.circle(
Phaser.Math.Between(0, 1600),
Phaser.Math.Between(0, 900),
Phaser.Math.Between(2, 8),
0xff69b4,
0.8
);
sparkle.setOrigin(0.5);
this.sparkles.push(sparkle);
}
// Create floating hearts
this.hearts = [];
for (let i = 0; i < 15; i++) {
const heart = this.add.circle(
Phaser.Math.Between(0, 1600),
Phaser.Math.Between(0, 900),
Phaser.Math.Between(10, 20),
0xff69b4,
0.6
);
heart.setOrigin(0.5);
this.hearts.push(heart);
}
const logo = this.add.image(800, 200, 'evPrincess').setOrigin(0.5);
this.tweens.add({
targets: logo,
delay: 2000,
duration: 5000,
scale: 0,
onComplete: () => {
logo.destroy();
}
});
// Set princess tint
levelScene.mainLayer.setTint(0xff69b4);
levelScene.wallsLayer.setTint(0xff69b4);
// Play princess sound effect
this.AudioManager.playManagedSound('princess', 1, 1.5);
// Start particle animation
this.princessTimer = 0;
this.princessInterval = 200;
} else if (event === 'end') {
this.currentEvents = this.currentEvents.filter(e => e !== 'princess');
// Clean up princess effects
levelScene.mainLayer.setTint(0xFFFFFF);
levelScene.wallsLayer.setTint(0xFFFFFF);
if (this.princessParticles) {
this.princessParticles.destroy();
this.princessParticles = null;
}
if (this.sparkles) {
this.sparkles.forEach(sparkle => {
sparkle.destroy();
});
this.sparkles = [];
}
if (this.hearts) {
this.hearts.forEach(heart => {
heart.destroy();
});
this.hearts = [];
}
}
}
}
evHipHop(event = 'start') {
let levelScene = this.levelSceneReference || this.scene.get('Level');
if (levelScene && levelScene.mainLayer && levelScene.wallsLayer) {
if (event === 'start') {
this.currentEvents.push('hiphop');
// Create beat drop effect
this.beatDrop = this.add.rectangle(0, 0, 1600, 900, 0x000000, 0.7);
this.beatDrop.setOrigin(0);
// Create beat pulse effect
this.beatPulse = this.add.circle(800, 450, 100, 0xffffff, 0.3);
this.beatPulse.setOrigin(0.5);
// Animate beat drop
this.tweens.add({
targets: this.beatDrop,
alpha: 0,
duration: 300,
ease: 'Linear',
onComplete: () => {
this.beatDrop.destroy();
this.beatDrop = null;
}
});
// Animate beat pulse
this.tweens.add({
targets: this.beatPulse,
scale: 3,
alpha: 0,
duration: 500,
ease: 'Linear',
onComplete: () => {
this.beatPulse.destroy();
this.beatPulse = null;
}
});
// Set hip hop tint
levelScene.mainLayer.setTint(0xff6600);
levelScene.wallsLayer.setTint(0xff6600);
// Create hip hop visual elements
this.hipHopElements = [];
this.graffitiTimer = 0;
// Create beat visualization
this.beatVisualizer = [];
for (let i = 0; i < 20; i++) {
const bar = this.add.rectangle(
100 + i * 30,
800,
20,
Phaser.Math.Between(20, 100),
0x00ffff
);
bar.setOrigin(0, 1);
this.beatVisualizer.push(bar);
}
const logo = this.add.sprite(800, 200, 'graffiti', 5).setOrigin(0.5);
this.AudioManager.playManagedSound('hipHop', 1, 1.5);
this.tweens.add({
targets: logo,
delay: 2000,
duration: 5000,
scale: 0,
onComplete: () => {
logo.destroy();
}
});
// Start beat animation
this.beatTimer = 0;
this.beatInterval = 200;
} else if (event === 'end') {
this.currentEvents = this.currentEvents.filter(e => e !== 'hiphop');
// Clean up hip hop effects
levelScene.mainLayer.setTint(0xFFFFFF);
levelScene.wallsLayer.setTint(0xFFFFFF);
if (this.beatDrop) {
this.beatDrop.destroy();
this.beatDrop = null;
}
if (this.beatPulse) {
this.beatPulse.destroy();
this.beatPulse = null;
}
if (this.hipHopElements) {
this.hipHopElements.forEach(element => {
element.destroy();
});
this.hipHopElements = [];
}
if (this.beatVisualizer) {
this.beatVisualizer.forEach(bar => {
bar.destroy();
});
this.beatVisualizer = [];
}
}
}
}
evCyber(event = 'start') {
if (event === 'start') {
this.currentEvents.push('cyber');
this.createVHSEffect();
this.AudioManager.playManagedSound('cyberIntro', 1, 1);
const logo = this.add.image(800, 300, 'evCyber').setOrigin(0.5);
this.tweens.add({
targets: logo,
delay: 2000,
duration: 5000,
scale: 0,
y: 200,
onComplete: () => {
logo.destroy();
}
});
}
else {
this.currentEvents = this.currentEvents.filter(e => e !== 'cyber');
// Remove VHS effect
if (this.vhsOverlay) {
this.vhsOverlay.destroy();
this.vhsOverlay = null;
}
// Remove scanlines
if (this.scanlines && this.scanlines.length > 0) {
this.scanlines.forEach(scanline => {
if (scanline && scanline.destroy) {
scanline.destroy();
}
});
this.scanlines = [];
}
// Remove color fringing effect
if (this.colorFringing) {
this.colorFringing.destroy();
this.colorFringing = null;
}
// Remove vignette
if (this.vignette) {
this.vignette.destroy();
this.vignette = null;
}
this.levelSceneReference.mainLayer.setTint(0xffffff);
this.levelSceneReference.wallsLayer.setTint(0xffffff);
}
}
createVHSEffect() {
// Create overlay for VHS effect
this.vhsOverlay = this.add.rectangle(0, 0, 1600, 900, 0x000000, 0.1);
this.vhsOverlay.setOrigin(0);
// Create scanlines
this.scanlines = [];
const scanlineCount = 100;
for (let i = 0; i < scanlineCount; i++) {
const scanline = this.add.rectangle(0, i * (900 / scanlineCount), 1600, 1, 0x000000, 0.1);
scanline.setOrigin(0);
this.scanlines.push(scanline);
}
// Create color fringing effect
this.colorFringing = this.add.rectangle(0, 0, 1600, 900, 0x000000, 0);
this.colorFringing.setOrigin(0);
// Create vignette
this.vignette = this.add.rectangle(0, 0, 1600, 900, 0x000000, 0.3);
this.vignette.setOrigin(0);
// Add VHS distortion effect
this.vhsDistortion = 0;
this.vhsDistortionSpeed = 0.02;
// Start VHS animation
this.vhsTimer = 0;
}
evMatrix(event = 'start') {
let levelScene = this.levelSceneReference || this.scene.get('Level');
if (levelScene && levelScene.mainLayer && levelScene.wallsLayer) {
if (event === 'start') {
this.currentEvents.push('matrix');
const matrixImage = this.add.image(800, 200, 'evMatrix').setOrigin(0.5).setScale(2);
this.tweens.add({
targets: matrixImage,
scale: 1,
duration: 1000,
onComplete: () => {
this.AudioManager.playManagedSound('thud', 1, 2);
this.tweens.add({
targets: matrixImage,
delay: 1000,
duration: 5000,
scale: 0,
onComplete: () => {
matrixImage.destroy();
}
});
}
});
levelScene.mainLayer.setTint(0xc3ffcd);
levelScene.wallsLayer.setTint(0xc3ffcd);
// Create matrix rain effect
this.matrixChars = [];
this.matrixSpeed = 200;
this.matrixFontSize = 16;
// Create columns of characters
const columns = Math.ceil(1600 / this.matrixFontSize);
for (let i = 0; i < columns; i++) {
const column = {
x: i * this.matrixFontSize,
y: Phaser.Math.Between(-1000, 0),
speed: Phaser.Math.Between(2, 8),
chars: []
};
// Create a random number of characters for this column
const charCount = Phaser.Math.Between(10, 30);
for (let j = 0; j < charCount; j++) {
const char = this.add.text(column.x, column.y + (j * this.matrixFontSize),
this.getRandomMatrixChar(),
{
fontSize: this.matrixFontSize + 'px',
fill: '#00ff41',
stroke: '#000000',
strokeThickness: 2
}
);
char.setAlpha(0.8);
column.chars.push(char);
}
this.matrixChars.push(column);
}
// Start the animation
this.matrixTimer = 0;
} else if (event === 'end') {
this.currentEvents = this.currentEvents.filter(e => e !== 'matrix');
levelScene.mainLayer.setTint(0xFFFFFF);
levelScene.wallsLayer.setTint(0xFFFFFF);
if (this.matrixChars) {
this.matrixChars.forEach(column => {
column.chars.forEach(char => {
char.destroy();
});
});
}
}
}
}
evNightfall(event = 'start') {
// Use stored reference or get it dynamically
let levelScene = this.levelSceneReference || this.scene.get('Level');
if (levelScene && levelScene.mainLayer && levelScene.wallsLayer) {
if (event === 'start') {
this.currentEvents.push('nightfall');
levelScene.mainLayer.setTint(0x2f2f69);
levelScene.wallsLayer.setTint(0x2f2f69);
const nightfallImage = this.add.image(800, 200, 'evNightfall').setOrigin(0.5);
this.AudioManager.playManagedSound('wolfHowl', 1, 1);
this.tweens.add({
targets: nightfallImage,
delay: 2000,
duration: 5000,
scale: 0,
onComplete: () => {
nightfallImage.destroy();
}
});
} else if (event === 'end') {
this.currentEvents = this.currentEvents.filter(e => e !== 'nightfall');
levelScene.mainLayer.setTint(0xFFFFFF);
levelScene.wallsLayer.setTint(0xFFFFFF);
}
} else {
console.warn('Level scene or layers not found for nightfall event');
}
}
evZoo(event = 'start') {
let levelScene = this.levelSceneReference || this.scene.get('Level');
if (event === 'start') {
this.currentEvents.push('zoo');
levelScene.mainLayer.setTint(0xffcc92);
levelScene.wallsLayer.setTint(0xffcc92);
const zooImage = this.add.image(800, 200, 'ev2Zoo').setOrigin(0.5);
this.zooSide1 = this.add.image(-108, 450, 'ev2ZooSides').setOrigin(0.5);
this.zooSide2 = this.add.image(1708, 450, 'ev2ZooSides').setOrigin(0.5);
this.AudioManager.playManagedSound('zooOclockIntro', 1, 1);
this.zooTimer = 0;
this.tweens.add({
targets: zooImage,
delay: 2000,
duration: 5000,
scale: 0,
onComplete: () => {
zooImage.destroy();
}
});
this.tweens.add({
targets: this.zooSide1,
delay: 2000,
duration: 5000,
x: 0
});
this.tweens.add({
targets: this.zooSide2,
delay: 2000,
duration: 5000,
x: 1600
});
}
else if (event === 'end' && this.zooSide1 && this.zooSide2) {
levelScene.mainLayer.setTint(0xFFFFFF);
levelScene.wallsLayer.setTint(0xFFFFFF);
this.currentEvents = this.currentEvents.filter(e => e !== 'zoo');
this.tweens.add({
targets: this.zooSide1,
duration: 5000,
x: -108,
onComplete: () => {
this.zooSide1.destroy();
}
});
this.tweens.add({
targets: this.zooSide2,
duration: 5000,
x: 1708,
onComplete: () => {
this.zooSide2.destroy();
}
});
}
}
update(time, delta) {
// Create a copy of keys to avoid modification during iteration
const scheduleKeys = Object.keys(this.schedule);
scheduleKeys.forEach(schedule => {
if (time >= parseInt(schedule)) {
const details = this.schedule[schedule];
const eventFunction = details[0];
const eventType = details[1];
delete this.schedule[schedule];
if (typeof this[eventFunction] === 'function') {
this[eventFunction](eventType);
} else {
console.log('Not A Function', eventFunction);
}
}
});
// Hip hop beat animation
if (this.currentEvents.includes('hiphop') && this.beatVisualizer) {
this.beatTimer += delta;
this.graffitiTimer += delta;
if (this.beatTimer > this.beatInterval) {
this.beatTimer = 0;
// Animate beat bars
this.beatVisualizer.forEach((bar, index) => {
const height = Phaser.Math.Between(20, 100);
this.tweens.add({
targets: bar,
height: height,
duration: 100,
ease: 'Power2'
});
});
}
if (this.graffitiTimer > 5000) {
this.graffitiTimer = 0;
const frame = Phaser.Math.Between(0,4);
const quadrant = Phaser.Math.Between(1,4);
const quadCords = {
1: { 'x': 400, 'y': 225 },
2: { 'x': 1200, 'y': 225 },
3: { 'x': 400, 'y': 675 },
4: { 'x': 1200, 'y': 675 }
}
const quadX = Phaser.Math.Between(-100, 100);
const quadY = Phaser.Math.Between(-100, 100);
const graffiti = this.add.sprite(quadCords[quadrant].x + quadX, quadCords[quadrant].y + quadY, 'graffiti', frame)
.setOrigin(0.5).setAlpha(0.8);
this.hipHopElements.push(graffiti);
this.AudioManager.playManagedSound('hipHopBeat', 1, 1);
this.tweens.add({
targets: graffiti,
scale: { from: 1, to: .8 },
alpha: { from: 0.8, to: 0.4 },
duration: 500,
repeat: 3,
onComplete: () => {
if (graffiti && graffiti.active) {
this.tweens.add({
targets: graffiti,
scale: { from: 1, to: .2 },
alpha: 0,
duration: 5000,
onComplete: () => {
if (graffiti && graffiti.active) {
graffiti.destroy();
}
}
});
}
}
});
}
}
// VHS effect update
if (this.currentEvents.includes('cyber') && this.vhsOverlay) {
this.vhsTimer += delta;
// Animate scanlines
this.scanlines.forEach((scanline, index) => {
scanline.y = (index * (900 / 100)) + (Math.sin(this.vhsTimer * 0.005 + index * 0.1) * 5);
});
// Animate color fringing
if (this.vhsTimer % 200 < 100) {
this.colorFringing.setFillStyle(0xff0000, 0.02);
} else {
this.colorFringing.setFillStyle(0x0000ff, 0.02);
}
// Animate VHS distortion
this.vhsDistortion = Math.sin(this.vhsTimer * 0.01) * 0.5;
// Apply distortion to main layers
if (this.levelSceneReference && this.levelSceneReference.mainLayer) {
this.levelSceneReference.mainLayer.setTint(0x00ffff);
this.levelSceneReference.wallsLayer.setTint(0x00ffff);
}
}
// Nightfall Event
if (this.currentEvents.includes('nightfall')) {
this.lightningTimer += delta;
if (this.lightningTimer >= this.lightningInterval) {
this.lightningTimer = 0;
this.lightningInterval = Phaser.Math.Between(2000,5000);
this.createLightning();
}
}
// Zoo O'clock
if (this.currentEvents.includes('zoo')) {
this.zooTimer += delta;
if (this.zooTimer >= 1000) {
this.zooTimer = 0;
this.AudioManager.playManagedSound(`zoo${Phaser.Math.Between(1,9)}`, 1, 1);
}
}
// Matrix effect update
if (this.currentEvents.includes('matrix') && this.matrixChars) {
this.matrixTimer += delta;
if (this.matrixTimer > this.matrixSpeed) {
this.matrixTimer = 0;
this.matrixChars.forEach(column => {
column.y += column.speed;
// Reset column if it goes off screen
if (column.y > 900) {
column.y = Phaser.Math.Between(-1000, -100);
}
// Update character positions
column.chars.forEach((char, index) => {
char.y = column.y + (index * this.matrixFontSize);
char.x = column.x;
});
});
}
}
}
createLightning() {
let levelScene = this.levelSceneReference || this.scene.get('Level');
if (levelScene && levelScene.mainLayer && levelScene.wallsLayer) {
levelScene.mainLayer.setTint(0xFFFFFF);
levelScene.wallsLayer.setTint(0xFFFFFF);
const lightningRand = Phaser.Math.Between(1,3);
this.AudioManager.playManagedSound(`lightning-${lightningRand}`, 2, 1);
this.time.delayedCall(Phaser.Math.Between(100, 300), () => {
if (this.currentEvents.includes('nightfall')) {
levelScene.mainLayer.setTint(0x2f2f69);
levelScene.wallsLayer.setTint(0x2f2f69);
}
});
}
}
getRandomMatrixChar() {
// Matrix-style characters (Latin, numbers, symbols)
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789$#@%&*';
return chars.charAt(Phaser.Math.Between(0, chars.length - 1));
}
}

131
src/scenes/level.js Normal file
View File

@ -0,0 +1,131 @@
import { Faction } from '../faction/factions.js';
export class Level extends Phaser.Scene {
constructor() {
super({ key: 'Level' });
this.map = 'night-woods';
this.isDragging = false;
this.dragStartX = 0;
this.dragStartY = 0;
}
init(data) {
}
preload() {
// Map Stuff
this.load.tilemapTiledJSON('map', `./assets/${this.map}.json`);
this.load.image('terrain', './assets/terrain.png');
this.load.spritesheet('dark-ages', './assets/dark-ages.png', {
frameWidth: 64,
frameHeight: 64
});
}
create() {
const map = this.make.tilemap({ key: 'map' });
const tiles = map.addTilesetImage('terrain', 'terrain');
this.mainLayer = map.createLayer('main', tiles, 0, 0)
this.physics.world.setBounds(0, 0, this.mainLayer.width, this.mainLayer.height);
// Add input listeners for dragging
this.input.on('pointerdown', this.onPointerDown, this);
this.input.on('pointermove', this.onPointerMove, this);
this.input.on('pointerup', this.onPointerUp, this);
this.factionLeft = this.add.group();
this.factionRight = this.add.group();
const test = new Faction(this, 4*64, 5*64, 'dark-ages', 0, 'left', 1, 'dark-ages').setOrigin(0.5);
const test2 = new Faction(this, 27*64, 4*64, 'dark-ages', 0, 'right', 1, 'dark-ages').setOrigin(0.5);
// Add collision detection
this.physics.add.overlap(this.factionLeft, this.factionRight, this.handleFactionCollision, null, this);
}
update(time, delta) {
this.factionLeft.getChildren().forEach(faction => {
if (faction.update) {
faction.update(time, delta);
}
});
this.factionRight.getChildren().forEach(faction => {
if (faction.update) {
faction.update(time, delta);
}
});
}
handleFactionCollision(faction1, faction2) {
// Check if factions are on opposite sides
if (faction1.side !== faction2.side) {
// Calculate distance between factions
const distance = Phaser.Math.Distance.Between(faction1.x, faction1.y, faction2.x, faction2.y);
// If within attack range
if (distance < 150) {
// Pause path following and move towards each other
faction1.pausePath();
faction2.pausePath();
// Move towards each other
const angle = Phaser.Math.Angle.Between(faction1.x, faction1.y, faction2.x, faction2.y);
const speed = 50;
faction1.body.setVelocity(
Math.cos(angle) * speed,
Math.sin(angle) * speed
);
faction2.body.setVelocity(
Math.cos(angle + Math.PI) * speed,
Math.sin(angle + Math.PI) * speed
);
// Flip sprites based on movement direction
if (faction1.body.velocity.x > 0) {
faction1.setFlipX(true);
} else {
faction1.setFlipX(false);
}
if (faction2.body.velocity.x > 0) {
faction2.setFlipX(true);
} else {
faction2.setFlipX(false);
}
} else {
// If they're not close enough, resume normal path following
faction1.resumePath();
faction2.resumePath();
}
}
}
onPointerDown(pointer) {
this.isDragging = true;
this.dragStartX = pointer.x;
this.dragStartY = pointer.y;
}
onPointerMove(pointer) {
if (this.isDragging) {
const deltaX = pointer.x - this.dragStartX;
const deltaY = pointer.y - this.dragStartY;
// Update camera position
this.cameras.main.scrollX -= deltaX;
this.cameras.main.scrollY -= deltaY;
// Update drag start position for next move
this.dragStartX = pointer.x;
this.dragStartY = pointer.y;
}
}
onPointerUp(pointer) {
this.isDragging = false;
}
}

328
src/scenes/menu.js Normal file
View File

@ -0,0 +1,328 @@
import { PLAYER_CONFIG } from '../characters/playerConfig.js';
import { WEAPONS_CONFIG } from '../characters/weaponsConfig.js';
export class Menu extends Phaser.Scene {
constructor() {
super({ key: 'Menu' });
}
preload() {
// Player
this.load.spritesheet('players', './assets/players.png', {
frameWidth: 200,
frameHeight: 200
});
// Weapons
this.load.image('chainWallet', 'assets/weapons/chainWallet.png');
this.load.image('monster', 'assets/weapons/monster.png');
this.load.image('hobbies', 'assets/weapons/hobbies.png');
this.load.spritesheet('hobbie-sprites', 'assets/weapons/hobbie-sprites.png', {
frameWidth: 100,
frameHeight: 100
});
this.load.image('catFight', 'assets/weapons/catFight.png');
this.load.image('bread', 'assets/weapons/bread.png');
this.load.image('leftovers', 'assets/weapons/leftovers.png');
this.load.spritesheet('catFight-sprites', 'assets/weapons/catFight-sprites.png', {
frameWidth: 100,
frameHeight: 100
});
this.load.spritesheet('leftovers-sprites', 'assets/weapons/leftovers-sprites.png', {
frameWidth: 100,
frameHeight: 100
});
this.load.spritesheet('bread-sprites', 'assets/weapons/bread-sprites.png', {
frameWidth: 100,
frameHeight: 100
});
this.load.image('freezeLaunch', 'assets/weapons/freezeLaunch.png');
this.load.spritesheet('freezeLaunch-sprites', 'assets/weapons/freezeLaunch-sprites.png', {
frameWidth: 200,
frameHeight: 100
});
this.load.image('pickles', 'assets/weapons/pickles.png');
this.load.image('pickleCloud1', 'assets/weapons/pickleCloud1.png');
this.load.image('pickleCloud2', 'assets/weapons/pickleCloud2.png');
this.load.image('banana', 'assets/weapons/banana.png');
this.load.spritesheet('banana-sprites', 'assets/weapons/banana-sprites.png', {
frameWidth: 100,
frameHeight: 100
});
// Items
this.load.spritesheet('items', './assets/items.png', {
frameWidth: 100,
frameHeight: 100
});
// Menu
this.load.image('menuLogo', 'assets/menuLogo.png');
this.load.font('Bitwise', 'assets/Bitwise.ttf');
this.load.video('menuVideo', 'assets/menuVideo.mp4');
this.load.audio('menuMusic', 'assets/menuMusic.mp3');
this.load.audio('menuInterface', 'assets/sounds/menuInterface.mp3');
this.load.audio('menuInterfaceUp', 'assets/sounds/menuInterfaceUp.mp3');
this.load.audio('select', 'assets/sounds/select.mp3');
this.load.image('menuKevin', 'assets/menuKevin.png');
this.load.image('menuPanel', 'assets/menuPanel.png');
this.load.image('arrow', 'assets/arrow.png');
}
create() {
// Create video sprite that fills the screen
const video = this.add.video(0, 0, 'menuVideo');
video.setOrigin(0);
video.setScale(this.game.config.width / 848, this.game.config.height / 480);
video.setDepth(-1);
video.play(true);
// Create audio and play in a loop
this.bgMusic = this.sound.add('menuMusic');
this.bgMusic.loop = true;
this.bgMusic.play();
this.menuLogo = this.add.image(800, 350, 'menuLogo').setOrigin(0.5);
// Add start text
this.startPanel = this.add.image(500, 750, 'menuPanel').setOrigin(0.5).setInteractive();
this.startText = this.add.text(500, 750, 'Click Here to Start', {
fontSize: '32px',
fontFamily: 'Bitwise',
fill: '#ffffff',
align: 'center'
}).setOrigin(0.5);
// Add fullscreen text
this.fsPanel = this.add.image(1100, 750, 'menuPanel').setOrigin(0.5).setInteractive();
this.fullscreenText = this.add.text(1100, 750, 'Toggle Fullscreen', {
fontSize: '32px',
fontFamily: 'Bitwise',
fill: '#f6ff78ff',
align: 'center'
}).setOrigin(0.5);
// Add fullscreen functionality
this.fsPanel.on('pointerdown', () => {
if (this.scale.isFullscreen) {
this.scale.stopFullscreen();
} else {
this.scale.startFullscreen();
}
});
this.fsPanel.on('pointerover', () => {
this.tweens.add({
targets: [this.fsPanel, this.fullscreenText],
scale: 1.1,
y: 730,
duration: 200
});
});
this.fsPanel.on('pointerout', () => {
this.tweens.add({
targets: [this.fsPanel, this.fullscreenText],
scale: 1,
y: 750,
duration: 200
});
});
this.characters = this.add.container();
this.menuBackground = this.add.rectangle(1000, 450, 1200, 800, 0xFFFFFF, 0.5).setOrigin(0.5);
this.menuCharacterText = this.add.text(425, 75, 'Choose Your Character', {
fontSize: '32px',
fontFamily: 'Bitwise',
fill: '#00a2ffff',
stroke: '#005e94ff',
align: 'left',
strokeThickness: 2,
shadow: {
offsetX: 2,
offsetY: 2,
color: '#000000',
blur: 4,
fill: true
}
});
this.characters.add(this.menuBackground);
this.characters.add(this.menuCharacterText);
let i = 1;
let charX = 0;
let charY = 0;
Object.entries(PLAYER_CONFIG).forEach(([key, character]) => {
if (key === 'base') return;
const charContainer = this.add.container(425 + charX, 125 + charY);
const charBox = this.add.rectangle(0, 0, 370, 225, 0x000000, 0.6).setOrigin(0).setInteractive();
// Add hover effect
charBox.on('pointerover', () => {
charBox.setFillStyle(0x555555, 0.9);
});
charBox.on('pointerout', () => {
charBox.setFillStyle(0x333333, 0.9);
});
charBox.on('pointerdown', () => {
this.bgMusic.stop();
this.cameras.main.fadeOut();
this.sound.play('select');
this.time.delayedCall(1000, () => {
//this.sound.play('menuInterface');
this.scene.start('Level', {
character: key
});
});
});
const charImage = this.add.sprite(-30, 12.5, character.texture, character.frame).setOrigin(0);
const charWeapon = this.add.image(80, 115, character.weapon).setOrigin(0).setDisplaySize(100, 100);
charWeapon.postFX.addGlow(0xff3c00);
const charName = this.add.text(130, 10, character.name, {
fontSize: '36px',
fontFamily: 'Bitwise',
fill: '#fffb00ff',
stroke: '#919400ff',
align: 'left',
strokeThickness: 2,
shadow: {
offsetX: 2,
offsetY: 2,
color: '#000000',
blur: 4,
fill: true
}
});
const charLikes = this.add.text(130, 60, `Likes:\n${character.likes}`, {
fontSize: '20px',
fontFamily: 'Bitwise',
fill: '#ffffffff',
stroke: '#2e2e2eff',
align: 'left',
strokeThickness: 2,
shadow: {
offsetX: 2,
offsetY: 2,
color: '#000000',
blur: 4,
fill: true
}
});
const charWeaponTitle = this.add.text(190, 110, 'Starting Weapon:', {
fontSize: '20px',
fontFamily: 'Bitwise',
fill: '#ff3c00',
stroke: '#8f2100ff',
align: 'left',
strokeThickness: 2,
shadow: {
offsetX: 2,
offsetY: 2,
color: '#000000',
blur: 4,
fill: true
}
});
const charWeaponInfo = this.add.text(190, 140, `${WEAPONS_CONFIG[character.weapon].info.name}`, {
fontSize: '20px',
fontFamily: 'Bitwise',
fill: '#ffffffff',
stroke: '#2e2e2eff',
align: 'left',
strokeThickness: 2,
shadow: {
offsetX: 2,
offsetY: 2,
color: '#000000',
blur: 4,
fill: true
}
});
const charWeaponInfo2 = this.add.text(190, 160, `${WEAPONS_CONFIG[character.weapon].info.desc}`, {
fontSize: '16px',
fontFamily: 'Bitwise',
fill: '#ffffffff',
stroke: '#2e2e2eff',
align: 'left',
strokeThickness: 2,
shadow: {
offsetX: 2,
offsetY: 2,
color: '#000000',
blur: 4,
fill: true
}
});
charContainer.add(charBox);
charContainer.add(charImage);
charContainer.add(charWeapon);
charContainer.add(charName);
charContainer.add(charLikes);
charContainer.add(charWeaponTitle);
charContainer.add(charWeaponInfo);
charContainer.add(charWeaponInfo2);
this.characters.add(charContainer);
charX += 390;
if (i % 3 === 0) {
charY += 245;
charX = 0;
}
i++;
});
this.characters.y = 1000;
this.kevinMenu = this.add.image(-250, 550, 'menuKevin').setOrigin(0.5, 0.5).setScale(.8).setFlipX(true);
this.startPanel.on('pointerdown', () => {
this.sound.play('menuInterface');
this.tweens.add({
targets: this.kevinMenu,
x: 200,
duration: 2000,
scale: .5,
ease: 'Cubic.Out'
});
this.tweens.add({
targets: [this.startText, this.fullscreenText, this.startPanel, this.fsPanel],
alpha: 0,
duration: 1000,
onComplete: () => {
this.startText.destroy();
}
});
this.tweens.add({
targets: this.menuLogo,
scale: .3,
x: 200,
y: 140,
duration: 1000
});
this.tweens.add({
targets: this.characters,
y: 0,
delay: 500,
ease: 'Cubic.Out',
duration: 2000
});
});
this.startPanel.on('pointerover', () => {
this.tweens.add({
targets: [this.startPanel, this.startText],
scale: 1.1,
y: 730,
duration: 200
});
});
this.startPanel.on('pointerout', () => {
this.tweens.add({
targets: [this.startPanel, this.startText],
scale: 1,
y: 750,
duration: 200
});
});
}
update(time, delta) {
}
}

1037
src/scenes/ui.js Normal file

File diff suppressed because it is too large Load Diff

1
start_web.bat Normal file
View File

@ -0,0 +1 @@
python -m http.server 8000