Created
March 2, 2025 14:50
-
-
Save levelsio/cd46c0d3bbc1f5a06be715219fe9f74c to your computer and use it in GitHub Desktop.
fly.pieter.com alien mothership UFO hack :D
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
if (!window.modLoaded) { | |
window.modLoaded = true; | |
// Alien mothership creation and management | |
const mothershipScale = 250; // Massive scale | |
let mothership; | |
let mothershipSound; | |
let mothershipOscillator; | |
let mothershipGain; | |
// Function to create a text sprite with specified text | |
function createSpriteText(text) { | |
const canvas = document.createElement('canvas'); | |
canvas.width = 512; | |
canvas.height = 128; | |
const context = canvas.getContext('2d'); | |
// Clear canvas with transparent background | |
context.fillStyle = "rgba(0, 0, 0, 0)"; | |
context.fillRect(0, 0, canvas.width, canvas.height); | |
// Set up a mysterious, glowy style using a monospace font and a gradient | |
context.font = "bold 64px Courier New, monospace"; | |
const gradient = context.createLinearGradient(0, 0, canvas.width, 0); | |
gradient.addColorStop(0, "#00ff00"); | |
gradient.addColorStop(1, "#004400"); | |
context.fillStyle = gradient; | |
context.textAlign = "center"; | |
context.textBaseline = "middle"; | |
context.fillText(text, canvas.width / 2, canvas.height / 2); | |
const texture = new THREE.CanvasTexture(canvas); | |
const material = new THREE.SpriteMaterial({ map: texture, transparent: true, opacity: 0.8 }); | |
const sprite = new THREE.Sprite(material); | |
// Adjust scale to suit your scene; here it is set to 80x20 units. | |
sprite.scale.set(160, 40, 1); | |
return sprite; | |
} | |
// Create the mothership | |
function createMothership() { | |
const mothershipGroup = new THREE.Group(); | |
// Main disc body | |
const discGeometry = new THREE.CylinderGeometry(mothershipScale, mothershipScale * 1.2, mothershipScale * 0.2, 16, 1, false); | |
const discMaterial = new THREE.MeshPhongMaterial({ | |
color: 0x1a1a1a, | |
metalness: 0.8, | |
roughness: 0.2, | |
emissive: 0x0a0a0a, | |
transparent: true, | |
opacity: 0 | |
}); | |
const disc = new THREE.Mesh(discGeometry, discMaterial); | |
mothershipGroup.add(disc); | |
// Central dome | |
const domeGeometry = new THREE.SphereGeometry(mothershipScale * 0.4, 32, 32, 0, Math.PI * 2, 0, Math.PI * 0.5); | |
const domeMaterial = new THREE.MeshPhongMaterial({ | |
color: 0x000000, | |
metalness: 0.9, | |
roughness: 0.1, | |
opacity: 0, | |
transparent: true | |
}); | |
const dome = new THREE.Mesh(domeGeometry, domeMaterial); | |
dome.position.y = mothershipScale * 0.1; | |
mothershipGroup.add(dome); | |
// Create mysterious text and add it inside the dome | |
const mysteriousText = createSpriteText("8487e477"); | |
// Position the text sprite in the dome; adjust the Y offset if needed | |
mysteriousText.position.set(0, domeGeometry.parameters.radius * 0.5, 0); | |
dome.add(mysteriousText); | |
// Position the mothership above the mountain | |
mothershipGroup.position.set(300, 500, -200); // Above the mountain | |
mothershipGroup.userData.fadeStartTime = Date.now(); | |
scene.add(mothershipGroup); | |
return mothershipGroup; | |
} | |
// Setup scary sound | |
function setupMothershipSound() { | |
if (!window.audioContext) return; | |
mothershipOscillator = window.audioContext.createOscillator(); | |
mothershipGain = window.audioContext.createGain(); | |
// Create complex, eerie sound | |
mothershipOscillator.type = 'sawtooth'; | |
mothershipOscillator.frequency.setValueAtTime(50, window.audioContext.currentTime); | |
// Add modulation | |
const modulator = window.audioContext.createOscillator(); | |
modulator.frequency.setValueAtTime(0.2, window.audioContext.currentTime); | |
const modulatorGain = window.audioContext.createGain(); | |
modulatorGain.gain.setValueAtTime(20, window.audioContext.currentTime); | |
modulator.connect(modulatorGain); | |
modulatorGain.connect(mothershipOscillator.frequency); | |
// Add filters for scary effect | |
const filter = window.audioContext.createBiquadFilter(); | |
filter.type = 'lowpass'; | |
filter.frequency.setValueAtTime(1000, window.audioContext.currentTime); | |
// Connect everything | |
mothershipOscillator.connect(filter); | |
filter.connect(mothershipGain); | |
mothershipGain.connect(window.audioContext.destination); | |
// Start with no volume and fade in | |
const now = window.audioContext.currentTime; | |
mothershipGain.gain.setValueAtTime(0, now); | |
mothershipGain.gain.linearRampToValueAtTime(0.3, now + 3); // 3 second fade in | |
// Start the oscillators | |
mothershipOscillator.start(); | |
modulator.start(); | |
} | |
// Update mothership effects | |
function updateMothership() { | |
if (!mothership) return; | |
// Handle fade in | |
const fadeInDuration = 3000; // 3 seconds fade in | |
const timeSinceCreation = Date.now() - mothership.userData.fadeStartTime; | |
const fadeProgress = Math.min(timeSinceCreation / fadeInDuration, 1); | |
// Apply fade to all materials | |
mothership.traverse(child => { | |
if (child instanceof THREE.Mesh) { | |
if (Array.isArray(child.material)) { | |
child.material.forEach(mat => { | |
if (mat.transparent) { | |
mat.opacity = fadeProgress; | |
} | |
}); | |
} else if (child.material.transparent) { | |
child.material.opacity = fadeProgress; | |
} | |
} | |
if (child instanceof THREE.PointLight) { | |
child.intensity = child.userData.targetIntensity * fadeProgress; | |
} | |
}); | |
// Slow hovering motion | |
const time = Date.now() * 0.001; | |
mothership.position.y = 500 + Math.sin(time * 0.2) * 20; | |
mothership.rotation.y += 0.001; | |
// Update lights | |
mothership.children.forEach(child => { | |
if (child.userData.phase !== undefined) { | |
const intensity = 0.5 + Math.sin(time * 2 + child.userData.phase) * 0.5; | |
child.material.emissiveIntensity = intensity; | |
} | |
}); | |
// Update sound based on distance | |
if (mothershipGain && player) { | |
const distance = player.position.distanceTo(mothership.position); | |
const maxDistance = 1000; | |
let volume = Math.max(0, 1 - (distance / maxDistance)); | |
volume = volume * volume * 0.3; // Square for more dramatic falloff | |
if (!isMuted) { | |
mothershipGain.gain.setTargetAtTime(volume, window.audioContext.currentTime, 0.1); | |
mothershipOscillator.frequency.setTargetAtTime( | |
50 + (1 - volume) * 20, | |
window.audioContext.currentTime, | |
0.1 | |
); | |
} else { | |
mothershipGain.gain.setTargetAtTime(0, window.audioContext.currentTime, 0.1); | |
} | |
} | |
} | |
function updateMothershipAbduction() { | |
// Define the abduction radius and gravity strength for the mothership | |
const abductionRadius = 500; // The effective radius of the mothership's gravity field | |
const gravityStrength = 0.5; // Medium gravity force | |
// Calculate the vector from the player to the mothership | |
const forceDirection = new THREE.Vector3(); | |
forceDirection.subVectors(mothership.position, player.position); | |
const distance = forceDirection.length(); | |
// Only apply the effect if within the abduction radius | |
if (distance < abductionRadius) { | |
// Normalize the direction and scale the force based on how close the player is | |
forceDirection.normalize(); | |
// Create a force that increases as the player gets closer (e.g., linearly) | |
const forceMagnitude = gravityStrength * (1 - distance / abductionRadius); | |
const force = forceDirection.multiplyScalar(forceMagnitude); | |
// Apply this force to the player. Depending on your system you can adjust player's position directly, | |
// or add it to a velocity property if you have a physics integration. | |
player.position.add(force); | |
// Optionally, you might trigger an "abduction" event if the player gets too close. | |
if (distance < 100) { | |
broadcastCrash("disappeared"); | |
currentDeaths++; | |
gameOver(""); | |
} | |
} | |
} | |
/* ===== Triangle-Shaped Beings Code Start ===== */ | |
// Global arrays for triangle beings and their bullets | |
const triangleBeings = []; | |
const triangleBullets = []; | |
// How many triangle beings to spawn | |
const NUM_TRIANGLE_BEINGS = 100; | |
// Create a triangle-shaped enemy using an extruded triangle shape | |
function createTriangleBeing() { | |
// Define a triangle shape (points in 2D) | |
const triangleShape = new THREE.Shape(); | |
triangleShape.moveTo(0, 5); | |
triangleShape.lineTo(-5, -5); | |
triangleShape.lineTo(5, -5); | |
triangleShape.lineTo(0, 5); | |
// Extrude the shape to give it some depth | |
const extrudeSettings = { | |
depth: 2, | |
bevelEnabled: false | |
}; | |
const geometry = new THREE.ExtrudeGeometry(triangleShape, extrudeSettings); | |
const material = new THREE.MeshPhongMaterial({ color: 0x1a1a1a }); | |
const being = new THREE.Mesh(geometry, material); | |
being.castShadow = true; | |
being.receiveShadow = true; | |
// Custom properties: 2 hit–points (killed with 2 shots), a shooting cooldown, and movement speed | |
being.userData.health = 2; | |
being.userData.shootCooldown = 0; // in milliseconds | |
being.userData.speed = 0.5; // movement speed (adjust as needed) | |
return being; | |
} | |
// Spawn a number of triangle beings in random positions | |
function spawnTriangleBeings() { | |
for (let i = 0; i < NUM_TRIANGLE_BEINGS; i++) { | |
const being = createTriangleBeing(); | |
// Position them randomly within a broad area; adjust the bounds as needed | |
being.position.set( | |
// Math.random() * 2000 - 1000, | |
// 50 + Math.random() * 200, // Y between 50 and 250 | |
// Math.random() * 2000 - 1000 | |
300 + (Math.random() - 0.5) * 2200, | |
250 + (Math.random() - 0.5) * 2250, | |
-200 + (Math.random() - 0.5) * 2200 | |
); | |
scene.add(being); | |
triangleBeings.push(being); | |
} | |
} | |
// Function for a triangle being to shoot at a target (the closest player) | |
function triangleBeingShoot(being, target) { | |
// Create a bullet as a small red sphere | |
const bulletGeometry = new THREE.SphereGeometry(1, 8, 8); | |
const bulletMaterial = new THREE.MeshPhongMaterial({ color: 0xff0000 }); | |
const bullet = new THREE.Mesh(bulletGeometry, bulletMaterial); | |
bullet.castShadow = true; | |
bullet.receiveShadow = true; | |
bullet.position.copy(being.position); | |
// Compute the normalized direction from the being to the target | |
const direction = new THREE.Vector3().subVectors(target.position, being.position).normalize(); | |
bullet.userData.velocity = direction.multiplyScalar(2); // bullet speed; adjust as needed | |
bullet.userData.damage = 2; // Each bullet deals 2 damage | |
triangleBullets.push(bullet); | |
scene.add(bullet); | |
} | |
// Update each triangle being: move toward the closest player and shoot when ready | |
function updateTriangleBeings() { | |
// Loop through each being | |
for (let i = triangleBeings.length - 1; i >= 0; i--) { | |
const being = triangleBeings[i]; | |
// Determine the closest player (local player plus any in otherPlayers) | |
let closestPlayer = player; | |
let closestDistance = being.position.distanceTo(player.position); | |
otherPlayers.forEach((otherPlane) => { | |
const d = being.position.distanceTo(otherPlane.position); | |
if (d < closestDistance) { | |
closestDistance = d; | |
closestPlayer = otherPlane; | |
} | |
}); | |
// Move the being toward the closest player | |
const moveDir = new THREE.Vector3().subVectors(closestPlayer.position, being.position).clone().sub(new THREE.Vector3(0, 0, -50)).normalize(); | |
being.position.add(moveDir.multiplyScalar(being.userData.speed * deltaTime * 60)); | |
// Rotate to face the target (optional visual polish) | |
being.lookAt(closestPlayer.position); | |
// Handle shooting: if cooldown expired, shoot and reset cooldown | |
if (being.userData.shootCooldown <= 0) { | |
triangleBeingShoot(being, closestPlayer); | |
being.userData.shootCooldown = Math.random() * 15000; // 15 seconds cooldown (adjust as needed) | |
} else { | |
being.userData.shootCooldown -= deltaTime * 1000; | |
} | |
} | |
} | |
// Update triangle bullets: move them and check collision with players | |
function updateTriangleBullets() { | |
for (let i = triangleBullets.length - 1; i >= 0; i--) { | |
const bullet = triangleBullets[i]; | |
bullet.position.add(bullet.userData.velocity.clone().multiplyScalar(deltaTime * 60)); | |
// Simple collision check: if bullet is within a certain distance from a player, register a hit. | |
const bulletRadius = 1; | |
const collisionRadius = 5; // approximate collision radius for a player | |
const checkCollision = (target) => bullet.position.distanceTo(target.position) < (bulletRadius + collisionRadius); | |
let hit = false; | |
// Check collision with local player | |
if (checkCollision(player)) { | |
currentHealth -= bullet.userData.damage; | |
playSelfHitSound(); | |
hit = true; | |
} | |
// Check collision with each other player | |
otherPlayers.forEach((otherPlane) => { | |
if (checkCollision(otherPlane)) { | |
// otherPlane.userData.health = (otherPlane.userData.health || 100) - bullet.userData.damage; | |
updatePlaneColorBasedOnHealth(otherPlane); | |
hit = true; | |
} | |
}); | |
// Remove the bullet if it hit something or if it flies too far | |
if (hit || bullet.position.length() > 5000) { | |
scene.remove(bullet); | |
triangleBullets.splice(i, 1); | |
} | |
if (currentHealth <= 0) { | |
gameOver(""); | |
} | |
} | |
} | |
// This function should be called when a player's shot (or missile) hits a triangle being. | |
// It reduces the being's health by 1; if health reaches 0 or below, the being is removed. | |
function hitTriangleBeing(being) { | |
being.userData.health -= 1; | |
if (being.userData.health <= 0) { | |
scene.remove(being); | |
const index = triangleBeings.indexOf(being); | |
if (index !== -1) { | |
triangleBeings.splice(index, 1); | |
} | |
// Optionally add explosion effects or score bonus | |
score += 10; | |
updateScoreDisplay(true); | |
} | |
} | |
originalCheckCollisions = checkCollisions; | |
checkCollisions = function() { | |
originalCheckCollisions(); | |
for (let i = missiles.length - 1; i >= 0; i--) { | |
const missile = missiles[i]; | |
const missileBox = new THREE.Box3().setFromObject(missile); | |
let missileHit = false; | |
// Example: Existing collision detection with other players, castle, houses, etc. | |
// (Assume these sections already exist in your function) | |
// ... | |
// NEW: Check collision with triangle-shaped beings | |
for (let j = triangleBeings.length - 1; j >= 0; j--) { | |
const being = triangleBeings[j]; | |
const beingBox = new THREE.Box3().setFromObject(being); | |
if (missileBox.intersectsBox(beingBox)) { | |
// Reduce the being's health; if it falls to zero or below, it will be removed | |
hitTriangleBeing(being); | |
// Remove the missile after it hits a triangle being | |
scene.remove(missile); | |
missiles.splice(i, 1); | |
missileHit = true; | |
break; // Exit the loop once a collision is detected | |
} | |
} | |
if (missileHit) continue; | |
// Continue with other collision checks if necessary: | |
// ... (castle, buildings, balloons, etc.) | |
// Update missile movement and apply gravity | |
missile.position.x += missile.velocity.x * deltaTime * 60; | |
missile.position.y += missile.velocity.y * deltaTime * 60; | |
missile.position.z += missile.velocity.z * deltaTime * 60; | |
missile.velocity.y -= 0.0005 * deltaTime * 60; | |
// Remove missile if it travels too far | |
if (missile.position.distanceTo(player.position) > MAX_PROJECTILE_DISTANCE) { | |
scene.remove(missile); | |
missiles.splice(i, 1); | |
} | |
} | |
} | |
// Toggle mothership visibility | |
let mothershipVisible = false; | |
function enableAlienInvasion() { | |
if (!mothershipVisible) { | |
mothership = createMothership(); | |
setupMothershipSound(); | |
mothershipVisible = true; | |
setTimeout(() => { | |
setTimeout(() => { | |
spawnTriangleBeings(); | |
}, 10000); | |
}, 3000); | |
} | |
} | |
function disableAlienInvasion() { | |
if (mothershipVisible) { | |
if (mothership) { | |
scene.remove(mothership); | |
mothership = null; | |
} | |
if (mothershipOscillator) { | |
mothershipOscillator.stop(); | |
mothershipOscillator = null; | |
} | |
mothershipVisible = false; | |
} | |
} | |
setTimeout(() => { | |
enableAlienInvasion(); | |
}, 5000); | |
// Add to animation loop | |
const originalAnimate = animate; | |
animate = function() { | |
originalAnimate(); | |
if (mothershipVisible) { | |
updateMothership(); | |
updateMothershipAbduction(); | |
updateTriangleBeings(); | |
updateTriangleBullets(); | |
} | |
}; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment