Aim with mouse and shoot with left click. Move with arrow keys
xxxxxxxxxx
let gameState = "start"; // Possible values: "start", "playing", "gameOver", "nextLevel" müzik yok
let fireballImg, boltImg, fireStandImg, wizzStandImg, wizAttackImg, undeadImg, lichImg;
let player;
let enemies = [];
let bullets = [];
let score = 0;
let gameOver = false;
let soundtrack;
let mapBgImg, mapBgImg2, mapBgImg3;
let endingIllustrationImg;
let towerImg
let gardenImg
let wizardrestImg
function preload() {
fireballImg = loadImage('fireball.png');
boltImg = loadImage('bolt.png');
fireStandImg = loadImage('firestand.png');
wizzStandImg = loadImage('wizzstand.png');
wizAttackImg = loadImage('wizattak.png');
undeadImg = loadImage('Undead.png'); // Load the undead enemy sprite
soundtrack = loadSound('ambush.mp3');
mapBgImg = loadImage('floor.jpg');
mapBgImg2 = loadImage('mapBackground2.jpg');
mapBgImg3 = loadImage('mapBackground3.png');
lichImg = loadImage('lich.png');
endingIllustrationImg = loadImage('final.jpg');
towerImg = loadImage('tower3.jpg');
gardenImg = loadImage('garden.jpg');
wizardrestImg = loadImage('wizardrest.jpg');
}
function setup() {
createCanvas(800, 800);
player = new Player(width / 2, height / 2, 50);
frameRate(60);
soundtrack.loop();
soundtrack.setVolume(0.1);
}
function draw() {
if (gameState === "start") {
displayStartScreen();
} else if (gameState === "playing") {
image(mapBgImg, 400, 400, width, height);
gameplayLogic();
} else if (gameState === "nextLevel") {
background(0);
gameplayLogic();
} else if (gameState === "scene2") {
image(mapBgImg2, 400, 400, width, height);
gameplayLogic();
} else if (gameState === "nextLevel2") {
background(0);
gameplayLogic();
} else if (gameState === "scene3") {
image(mapBgImg3, 400, 400, width, height);
gameplayLogic();
} else if (gameState === "nextLevel3") {
displayNextLevel3Screen();
} else if (gameState === "nextLevel4") {
displayNextLevel4Screen();
} else if (gameState === "gameOver") {
displayGameOver();
}
}
function gameplayLogic() {
player.display();
player.move();
if (frameCount % 60 === 0) {
spawnEnemy();
}
updateAndDisplayBullets();
updateAndDisplayEnemies();
checkCollisions();
displayScore();
if (score >= 1000 && gameState === "playing") {
gameState = "nextLevel";
enemies = []; // Clear enemies for next level
bullets = [];
player.x = width - 400;
player.y = height - 400;
// Reset player position or other settings as needed
} else if (gameState === "nextLevel") {
displayNextLevelScreen();
} else if (gameState === "gameOver") {
displayGameOver();
} else if (score >= 3000 && gameState === "scene2") {
gameState = "nextLevel2";
enemies = []; // Clear enemies for next level
bullets = [];
player.x = width - 400;
player.y = height - 400;
// Reset player position or other settings as needed
} else if (gameState === "nextLevel2") {
displayNextLevel2Screen();
} else if (gameState === "gameOver") {
displayGameOver();
}
}
function displayStartScreen() {
background(0);
image(towerImg, width / 400, height / 400, width, height);
fill(255);
textSize(32);
textAlign(CENTER, CENTER);
text('Wizard Defense', width / 2, height / 4);
textSize(18);
let backstory = "After practicing years of magic, passing countless exams, stupid projects, here we are, stuck in a tower with a lich and his army. Well... I will not go so gently into that night. It will be bloody. I WILL TAKE AS MUCH AS I CAN WITH ME. BRING IT ON YOU LICH BASTARD!!!";
textWrap(WORD);
text(backstory, width * 0.1, height / 3, width * 0.8);
textSize(40);
text('ENTER to start', width / 2, height * 0.75);
}
function displayNextLevelScreen() {
background(50); // Choose a suitable background color
image(wizardrestImg, width / 2, height / 2, width, height);
fill(255); // Text color
textSize(24);
textAlign(CENTER, CENTER);
// Title or main message
text('Whoa! I am still alive...', width / 2, height / 2 - 100);
// Detailed message
textSize(18);
text('They even stopped coming, maybe I have a chance.', width / 2, height / 2 - 60);
text('If I could get to my garden, I might run from the forest.', width / 2, height / 2 - 30);
// Instruction to continue
textSize(20);
text('Press ENTER to continue', width / 2, height / 2 + 40);
}
function displayNextLevel2Screen() {
background(70);
image(gardenImg, width / 2, height / 2, width, height);
fill(255);
textSize(24);
textAlign(CENTER, CENTER);
// Main message
text("In the name of all-knowing mistress, what the hell.", width / 2, height / 2 - 120);
text("Those undead... I've never seen ones that fast nor strong.", width / 2, height / 2 - 90);
text("This lich must be close. I must move fast.", width / 2, height / 2 - 60);
// Reflective message
textSize(18);
text("In old days, when a wizard came against another,", width / 2, height / 2 - 20);
text("at least they would message and try negotiations.", width / 2, height / 2 + 10);
text("Nowadays, nobody cares about the ancient ways of wizarding...", width / 2, height / 2 + 40);
// Instruction to continue
textSize(20);
text('Press ENTER to continue', width / 2, height / 2 + 80);
}
function displayNextLevel3Screen() {
background(100);
fill(255);
textSize(24);
textAlign(CENTER, CENTER);
// Victory declaration
text("OHH MISTRA THE GOD OF MAGIC, MY MISTRESS, BE WITNESS,", width / 2, height / 2 - 80);
text("I HAVE TRULY DEFEATED A LICH.", width / 2, height / 2 - 40);
// Light-hearted comment
textSize(20);
text("Well, I guess... skill issue, Lich. See you later.", width / 2, height / 2);
// Instruction to continue
textSize(20);
text('Press ENTER to continue', width / 2, height / 2 + 80);
}
function displayNextLevel4Screen() {
// Display the ending illustration
image(endingIllustrationImg, width / 2, height / 2, width, height);
// Overlay the "Thanks for playing" message
fill(255);
textSize(32);
textAlign(CENTER, CENTER);
text('Thanks for playing!', width / 2, height - 100);
}
function mouseClicked() {
if (gameState === "playing" || gameState === "scene2" || gameState === "scene3") {
// Add bullet firing logic here, applies to both playing and scene2 states
bullets.push(new Bullet(player.x, player.y, mouseX, mouseY, true));
player.isShooting = true;
setTimeout(() => player.isShooting = false, 100); // Simulate shooting delay
}
}
function keyPressed() {
if (keyCode === ENTER) {
switch(gameState) {
case "start":
// If you have an introductory text or menu and want to start the game by pressing Enter
gameState = "playing";
resetGame();
break;
case "nextLevel":
// Move from the end of one level to the beginning of the next
gameState = "scene2";
resetGameForScene2(); // Assuming you have a specific reset function for scene2
break;
case "nextLevel2":
// Move from the end of the second level to the beginning of the third
gameState = "scene3";
resetGameForScene2(); // Assuming a reset for scene3, adjust as necessary
break;
case "nextLevel3":
// Advance from the victory message over the Lich to the ending illustration
gameState = "nextLevel4";
break;
case "nextLevel4":
// After showing the final illustration and thanks, possibly return to the start or main menu
gameState = "start";
resetGame(); // Reset the game to its initial state for a new playthrough
break;
case "gameOver":
// Allow players to restart from the game over screen
gameState = "start";
resetGame();
break;
// Add more cases as needed for other game states
}
}
}
function resetGame() {
player = new Player(width / 2, height / 2, 50); // Reset player position
enemies = []; // Clear any existing enemies
bullets = []; // Clear any existing bullets
if (gameState === "start") {
score = 0; // Only reset score when starting a new game
}
gameOver = false;
}
function resetGameForScene2() {
// Reset or adjust game elements for scene2, keeping some game progress like score
player.x = width / 2; // Reset player position horizontally
player.y = height / 2; // Reset player position vertically or to a specific spot for scene2
enemies = []; // Clear enemies for scene2
bullets = []; // Clear bullets to start scene2 fresh
// Note: We do NOT reset the score here to keep the game progress
// Any other adjustments needed for scene2
}
function spawnEnemy() {
if (gameState === "nextLevel" || gameState === "nextLevel2") {
return; // Early exit from the function, no enemies will be spawned
}
let side = floor(random(4));
let x, y;
switch (side) {
case 0: x = random(width); y = 0; break;
case 1: x = width; y = random(height); break;
case 2: x = random(width); y = height; break;
case 3: x = 0; y = random(height); break;
}
// Adjust the spawn rate based on the game state
let spawnRate;
if (gameState === "scene2") {
spawnRate = 2;
} else if (gameState === "scene3") {
spawnRate = 360; // Adjusted spawn rate for "scene3"
} else {
spawnRate = 120; // Default spawn rate for other states
}
// Spawn logic for "scene3"
if (gameState === "scene3" && frameCount % spawnRate === 0) {
// Only spawn Undead in "scene3"
enemies.push(new Undead(x, y));
// Check if the Lich boss has already been spawned
const lichExists = enemies.some(enemy => enemy instanceof Lich);
if (!lichExists) {
// Spawn one Lich boss
enemies.push(new Lich(width / 2, 0)); // You can adjust the spawn position as needed
}
} else if (frameCount % spawnRate === 0) {
// Existing spawn logic for other game states
if (gameState === "scene2") {
// In 'scene2', spawn Undead more frequently
enemies.push(new Undead(x, y));
} else {
// For any other state, spawn normal enemies and occasionally Undead
let isUndead = random() < 0.2; // Adjusted for correct probability (0.02 -> 0.2 if 20% is intended)
if (isUndead) {
enemies.push(new Undead(x, y));
} else {
enemies.push(new Enemy(x, y, 60));
}
}
}
}
function updateAndDisplayBullets() {
for (let i = bullets.length - 1; i >= 0; i--) {
bullets[i].move();
bullets[i].display();
if (bullets[i].offScreen()) {
bullets.splice(i, 1);
}
}
}
function updateAndDisplayEnemies() {
for (let enemy of enemies) {
enemy.moveTowards(player.x, player.y);
enemy.display();
if (enemy instanceof Lich) {
enemy.decideNextAttack();
enemy.performAttack();
} else if (enemy.canShoot && frameCount % 120 === 0) {
bullets.push(new Bullet(enemy.x, enemy.y, player.x, player.y, false));
}
}
}
function checkCollisions() {
// Check for bullet collisions with enemies
for (let i = bullets.length - 1; i >= 0; i--) {
if (bullets[i].isPlayerBullet) {
for (let j = enemies.length - 1; j >= 0; j--) {
if (bullets[i].hits(enemies[j])) {
// Special handling for Undead or similar enemies with health
if (enemies[j] instanceof Undead /* || enemies[j] instanceof Lich */) {
enemies[j].health -= 25; // Assume each bullet does 25 damage
if (enemies[j].health <= 0) {
// Only remove the enemy if its health drops to 0 or below
enemies.splice(j, 1);
score += 50; // Adjust scoring as needed
}
} else if (enemies[j] instanceof Lich /* || enemies[j] instanceof Lich */) {
enemies[j].health -= 25; // Assume each bullet does 25 damage
if (enemies[j].health <= 0) {
// Only remove the enemy if its health drops to 0 or below
gameState = "nextLevel3";
enemies.splice(j, 1);
score += 5000; // Adjust scoring as needed
}
} else {
// Immediate removal for all other enemies
enemies.splice(j, 1);
score += 25; // Adjust scoring as needed
}
bullets.splice(i, 1); // Remove the bullet in any case
break;
}
}
} else {
// Handling for bullets hitting the player remains unchanged
if (bullets[i].hits(player)) {
console.log("Skill issue!");
gameOver = true;
gameState = "gameOver";
}
}
}
// Player-enemy collision detection remains unchanged
for (let j = enemies.length - 1; j >= 0; j--) {
if (dist(player.x, player.y, enemies[j].x, enemies[j].y) <= (player.size / 2 + enemies[j].size / 2)) {
console.log("Skill issue!");
gameOver = true;
gameState = "gameOver";
break;
}
}
}
function displayScore() {
fill(255);
textSize(16);
text(`Score: ${score}`, 30, 60);
}
function displayGameOver() {
fill(255);
textSize(32);
textAlign(CENTER, CENTER);
text('Game Over', width / 2, height / 2);
text(`Final Score: ${score}`, width / 2, height / 2 + 40);
}
function drawHealthBar(currentHealth, maxHealth, x, y, barWidth, barHeight) {
noFill();
stroke(255);
rect(x, y, barWidth, barHeight); // Draw the outline of the health bar
let healthPercentage = currentHealth / maxHealth;
let healthBarWidth = healthPercentage * barWidth; // Calculate the width of the current health
fill(255, 0, 0);
noStroke();
rect(x, y, healthBarWidth, barHeight); // Draw the current health
}
// Enemy class definition
class Enemy {
constructor(x, y, size) {
this.x = x;
this.y = y;
this.size = size;
this.speed = 2;
this.img = fireStandImg; // Default image, can be overridden in subclasses
this.canShoot = true; // By default, enemies can shoot
}
moveTowards(px, py) {
let angle = atan2(py - this.y, px - this.x);
this.x += this.speed * cos(angle);
this.y += this.speed * sin(angle);
}
display() {
imageMode(CENTER);
image(this.img, this.x, this.y, this.size, this.size);
}
}
// Undead class definition, inherits from Enemy
class Undead extends Enemy {
constructor(x, y) {
super(x, y, 60); // Size can be adjusted
this.speed = 8;
this.img = undeadImg;
this.canShoot = false; // By default, enemies can shoot
this.health = 50; // Health for the Undead
// Undead specific attributes can be added here
}
display() {
// Existing code to display the Undead sprite
imageMode(CENTER);
image(this.img, this.x, this.y, this.size, this.size);
// Draw health bar below the sprite
this.drawHealthBar();
}
// Override methods or add new behavior specific to Undead
drawHealthBar() {
let barWidth = this.size; // Health bar width same as the enemy size
let barHeight = 5; // A thinner health bar
let x = this.x - this.size / 2; // Align the left edge of the health bar with the left edge of the sprite
let y = this.y + this.size / 2 + 5; // Position the health bar just below the sprite
noStroke(); // No border for the health bar
// Red background for total health
fill(255, 0, 0);
rect(x, y, barWidth, barHeight);
// Green foreground for current health
let healthPercentage = this.health / 50; // Assuming max health is 50 as set in the constructor
fill(0, 255, 0);
rect(x, y, barWidth * healthPercentage, barHeight);
}
}
class Lich extends Enemy {
constructor(x, y) {
super(x, y, 100); // Assuming Lich is larger
this.speed = 0.50; // Example speed, can be adjusted
this.img = lichImg; // Assuming you'll have a specific image for the Lich
this.health = 2000; // Lich has more health
this.lastAttackTime = millis(); // Track time since last attack
this.attackMode = 0; // Initialize attackMode
this.rotationAngle = 0; // Current rotation angle of the area attack
this.rotationDirection = random([1, -1]);
this.bulletSpawnInterval = 10; // Interval in frames between bullet spawns
this.framesSinceLastBullet = 0;
}
decideNextAttack() {
if (millis() - this.lastAttackTime > 5000) { // Every 5 seconds, change the attack
this.attackMode = (this.attackMode + 1) % 3; // Cycle through the attack modes
console.log("To", this.attackMode);
this.lastAttackTime = millis();
this.rotationDirection = random([1, -1]); // Re-decide the direction
}
}
performAttack() {
switch (this.attackMode) {
case 0:
this.areaAttack();
break;
case 1:
this.directedStream(player.x, player.y);
break;
case 2:
this.lightAttack();
break;
}
}
areaAttack() {
// Define the angle between the bullets
let angleStep = TWO_PI / 6; // For 12 bullets evenly spaced in a circle
// Define the rotation speed (how quickly they rotate around the lich)
let rotationSpeed = PI / 150; // Slower rotation speed than before
// Check if it's time to spawn a new set of bullets
if (this.framesSinceLastBullet >= this.bulletSpawnInterval) {
// Reset the counter
this.framesSinceLastBullet = 0;
// Spawn a circle of bullets
for (let angle = 0; angle < TWO_PI; angle += angleStep) {
// Calculate the bullet's direction based on the angle
let dx = cos(angle + this.rotationAngle);
let dy = sin(angle + this.rotationAngle);
// Adjust the speed of the bullet if necessary
let bulletSpeed = 1;
// Create a new bullet moving in the direction specified by angle
bullets.push(new Bullet(this.x, this.y, this.x + dx * bulletSpeed, this.y + dy * bulletSpeed, false));
}
}
// Increment the counter
this.framesSinceLastBullet++;
// Update the rotation angle for the next frame to achieve the rotation effect
this.rotationAngle += rotationSpeed * this.rotationDirection;
// Prevent the rotation angle from exceeding the range [0, TWO_PI]
this.rotationAngle %= TWO_PI;
}
directedStream(targetX, targetY) {
// Logic for spawning bullets directed at the player's position
const bulletCount = 5; // Number of bullets in a stream
const delayBetweenShots = 200; // Milliseconds
for (let i = 0; i < bulletCount; i++) {
setTimeout(() => {
bullets.push(new Bullet(this.x, this.y, targetX, targetY, false));
}, i * delayBetweenShots);
}
}
lightAttack() {
// Logic for a lighter, more spread out attack
let angleStep = PI / 4; // Larger angle step for wider spread
for (let angle = 0; angle < TWO_PI; angle += angleStep) {
let dx = cos(angle) * 5;
let dy = sin(angle) * 5;
bullets.push(new Bullet(this.x, this.y, this.x + dx, this.y + dy, false));
}
}
move() {
// Optional: Implement some movement logic, e.g., teleportation
}
display() {
// Existing code to display the Undead sprite
imageMode(CENTER);
image(this.img, this.x, this.y, this.size, this.size);
// Draw health bar below the sprite
this.drawHealthBar();
}
// Override methods or add new behavior specific to Undead
drawHealthBar() {
let barWidth = this.size; // Health bar width same as the enemy size
let barHeight = 5; // A thinner health bar
let x = this.x - this.size / 2; // Align the left edge of the health bar with the left edge of the sprite
let y = this.y + this.size / 2 + 5; // Position the health bar just below the sprite
noStroke(); // No border for the health bar
// Red background for total health
fill(255, 0, 0);
rect(x, y, barWidth, barHeight);
// Green foreground for current health
let healthPercentage = this.health / 2200; // Assuming max health is 50 as set in the constructor
fill(0, 255, 0);
rect(x, y, barWidth * healthPercentage, barHeight);
} // Additional Lich-specific methods...
}
// Bullet class definition
class Bullet {
constructor(x, y, tx, ty, isPlayerBullet) {
this.x = x;
this.y = y;
this.tx = tx;
this.ty = ty;
this.speed = 10;
this.size = 30;
this.isPlayerBullet = isPlayerBullet;
let angle = atan2(ty - y, tx - x);
this.vx = this.speed * cos(angle);
this.vy = this.speed * sin(angle);
}
move() {
this.x += this.vx;
this.y += this.vy;
}
display() {
imageMode(CENTER);
let img = this.isPlayerBullet ? boltImg : fireballImg;
// Adjust size for player bullet
let bulletSize = this.isPlayerBullet ? this.size * 1.5 : this.size; // Increase player bullet size by 50%
image(img, this.x, this.y, bulletSize, bulletSize);
}
offScreen() {
return this.x < 0 || this.x > width || this.y < 0 || this.y > height;
}
hits(target) {
let d = dist(this.x, this.y, target.x, target.y);
return d < this.size / 2 + target.size / 2;
}
}
// Player class definition
class Player {
constructor(x, y, size) {
this.x = x;
this.y = y;
this.size = 60;
this.speed = 5;
this.isShooting = false;
}
display() {
imageMode(CENTER);
let img = this.isShooting ? wizAttackImg : wizzStandImg;
image(img, this.x, this.y, this.size, this.size);
}
move() {
if (keyIsDown(LEFT_ARROW)) {
this.x -= this.speed;
}
if (keyIsDown(RIGHT_ARROW)) {
this.x += this.speed;
}
if (keyIsDown(UP_ARROW)) {
this.y -= this.speed;
}
if (keyIsDown(DOWN_ARROW)) {
this.y += this.speed;
}
}
}