Skip to content

Instantly share code, notes, and snippets.

@levelsio
Created March 2, 2025 14:50
Show Gist options
  • Save levelsio/cd46c0d3bbc1f5a06be715219fe9f74c to your computer and use it in GitHub Desktop.
Save levelsio/cd46c0d3bbc1f5a06be715219fe9f74c to your computer and use it in GitHub Desktop.
fly.pieter.com alien mothership UFO hack :D
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