void / static /goals.js
Death242's picture
Static site
4651f6c verified
Raw History Blame Contribute Delete
4.54 kB
// Challenges: fifteen goals tracked from what you do (kept on this device), each worth a trophy and 1000 points.
const store = { get(k) { try { return JSON.parse(localStorage.getItem(k)); } catch { return null; } }, set(k, v) { try { localStorage.setItem(k, JSON.stringify(v)); } catch { } } };
export const GOALS = [
{ id: "speed", icon: "🏎️", name: "Speed demon", text: "Hit 300 km/h", stat: "top", need: 300 },
{ id: "smash", icon: "💥", name: "Wrecking ball", text: "Smash 25 cars", stat: "smashed", need: 25 },
{ id: "drift", icon: "🌀", name: "Drift king", text: "One drift worth 1500", stat: "drift", need: 1500 },
{ id: "air", icon: "✈️", name: "Frequent flyer", text: "Spend 5 minutes in the air", stat: "air", need: 300 },
{ id: "pads", icon: "🛬", name: "Rooftop lander", text: "Land on 3 helipads", stat: "pads", need: 3 },
{ id: "butter", icon: "🧈", name: "Butter", text: "Make a butter-smooth landing", stat: "butter", need: 1 },
{ id: "race", icon: "🏁", name: "Champion", text: "Win a race", stat: "wins", need: 1 },
{ id: "cops", icon: "🚓", name: "Getaway", text: "Escape the police", stat: "escapes", need: 1 },
{ id: "rings", icon: "💍", name: "Ring master", text: "Fly through 10 rings", stat: "rings", need: 10 },
{ id: "road", icon: "🛣️", name: "Road trip", text: "Travel 25 km", stat: "km", need: 25 },
{ id: "fuel", icon: "⛽", name: "Pit stop", text: "Fill up at a station", stat: "refuels", need: 1 },
{ id: "coins", icon: "🪙", name: "Collector", text: "Find 20 hidden coins", stat: "coins", need: 20 },
{ id: "allcoins", icon: "👑", name: "Treasure hunter", text: "Find all 80 hidden coins", stat: "coins", need: 80 },
{ id: "cams", icon: "📸", name: "Flash mob", text: "Pass 10 speed cameras at 250 km/h or more", stat: "cams", need: 10 },
{ id: "taxi", icon: "🚕", name: "Cabbie", text: "Complete 5 taxi fares", stat: "fares", need: 5 },
{ id: "tuner", icon: "🔧", name: "Tuner", text: "Buy 4 garage upgrades", stat: "upg", need: 4 },
{ id: "grind", icon: "🛹", name: "Rail slider", text: "Grind 100 m of rails on the hoverboard", stat: "grind", need: 100 },
{ id: "b720", icon: "🌀", name: "Sky skater", text: "Land a 720 on the hoverboard", stat: "b720", need: 1 },
{ id: "bcombo", icon: "🔥", name: "Combo king", text: "Land 5 flip combos on the hoverboard", stat: "combo", need: 5 },
{ id: "brooklyn", icon: "🌉", name: "Bridge and tunnel", text: "Cross the bridge to Brooklyn", stat: "brooklyn", need: 1 },
];
export function createGoals(onDone) {
const S = Object.assign({ top: 0, smashed: 0, drift: 0, air: 0, pads: 0, butter: 0, wins: 0, escapes: 0, rings: 0, km: 0, refuels: 0, brooklyn: 0, best: 0, coins: 0, fares: 0, upg: 0, steals: 0, meds: 0, grind: 0, b720: 0, combo: 0, cams: 0 }, store.get("void.stats") || {});
const done = new Set(store.get("void.goals") || []);
let saveT = 0, lastSmash = null, lastRings = null, lastFuel = null, pads = new Set();
function check() { for (const g of GOALS) if (!done.has(g.id) && S[g.stat] >= g.need) { done.add(g.id); store.set("void.goals", [...done]); onDone(g); } }
return {
stats: S, done, GOALS,
// words from the score feed tell us about races, escapes and landings
note(why) {
if (/1st/.test(why)) S.wins++; if (/Fare \$/.test(why)) S.fares++; if (/got away/i.test(why)) S.escapes++; if (/Butter/.test(why)) S.butter++;
if (/^Landed at (?!PARK)/.test(why)) S.pads++; if (/^Drift/.test(why)) { const n = +(why.match(/\d+/) || [0])[0]; }
check();
},
drift(points) { S.drift = Math.max(S.drift, points); check(); },
update(dt, st, speedKmh) {
if (!st || !st.started) return;
S.top = Math.max(S.top, speedKmh); S.km += speedKmh / 3600 * dt;
if (st.mode === "air") S.air += dt;
if (lastSmash != null && st.smashed > lastSmash) S.smashed += st.smashed - lastSmash; lastSmash = st.smashed;
if (lastRings != null && st.rings > lastRings) S.rings += st.rings - lastRings; lastRings = st.rings;
if (lastFuel != null && st.fuel - lastFuel > .02 && Math.hypot(st.vel.x, st.vel.z) < 4) { if (!st._fueling) { S.refuels++; st._fueling = true; } } else if (st.fuel < lastFuel) st._fueling = false;
lastFuel = st.fuel;
if (st.x > 1760 && Math.abs(st.z) < 900) S.brooklyn = 1;
S.best = Math.max(S.best, st.score || 0);
if ((saveT += dt) > 3) { saveT = 0; store.set("void.stats", S); check(); }
},
};
}