add very basic clicker game

This commit is contained in:
zyl 2024-10-24 13:10:33 -07:00
parent 3ccd5b0df8
commit 19ffac4a90
Signed by: zyl
SSH key fingerprint: SHA256:uxxbSXbdroP/OnKBGnEDk5q7EKB2razvstC/KmzdXXs
9 changed files with 335 additions and 17 deletions

View file

@ -1,7 +1,7 @@
base_url: "https://zyl.gay"
title: zyl is gay
description: "zyl's website."
sass_styles: [index.scss, "pet.scss"]
sass_styles: [index.scss, pet.scss, click.scss]
images_per_page: 10
blog_posts_per_page: 20
cdn_url: "https://i.zyl.gay"

10
site/pages/click.md Normal file
View file

@ -0,0 +1,10 @@
---
title: click
scripts: ["js/click.js"]
styles: ["click.css"]
embed:
title: click
site_name: zyl.gay
description: click click click
extra: click
---

11
site/pages/games.md Normal file
View file

@ -0,0 +1,11 @@
---
title: games!
---
# games
little games i've made on here :3
<h2><a href="/pet">pet game</a></h2>
<h2><a href="/click">clicker game</a></h2>

222
site/root/js/click.js Normal file
View file

@ -0,0 +1,222 @@
(function () {
"use strict";
const click = document.querySelector("#click");
const petsCounter = click.querySelector("#pets");
const petsPerSecondCounter = click.querySelector("#pets-per-second");
const barksCounter = click.querySelector("#barks");
const barksPerSecondCounter = click.querySelector("#barks-per-second");
const kissesCounter = click.querySelector("#kisses");
const kissesPerSecondCounter = click.querySelector("#kisses-per-second");
const barker = click.querySelector("#barker");
const toolsEl = click.querySelector(".tools");
const toolPriceFactor = 0.1;
const upgradePriceFactor = 0.2;
const upgradeProductionFactor = 1.1;
const toolData = {
hand: {
priceIn: "barks",
basePrice: 10,
petsPerSecond: 0.5,
},
puppy: {
priceIn: "pets",
basePrice: 5,
barksPerSecond: 0.5,
},
foodBowl: {
priceIn: "barks",
basePrice: 50,
barksPerSecond: 1.3,
},
kisser: {
priceIn: "pets",
basePrice: 500,
kissesPerSecond: 0.25,
},
};
let barks = 0;
let pets = 0;
let kisses = 0;
let tools = {};
let petsPerSecond = 0;
let barksPerSecond = 0;
let kissesPerSecond = 0;
function calcPrice(base, count) {
return Math.floor(base ** (1 + toolPriceFactor * count));
}
function calcUpgradePrice(base, count) {
return Math.floor((base * 2) ** (1 + upgradePriceFactor * count));
}
const getValue = (name) => {
if (name === "pets") {
return pets;
} else if (name === "barks") {
return barks;
} else if (name === "kisses") {
return kisses;
} else if (name === "petsPerSecond") {
return petsPerSecond;
} else if (name === "barksPerSecond") {
return barksPerSecond;
} else if (name === "kissesPerSecond") {
return kissesPerSecond;
}
};
const setValue = (name, value) => {
if (name === "pets") {
pets = value;
} else if (name === "barks") {
barks = value;
} else if (name === "kisses") {
kisses = value;
} else if (name === "petsPerSecond") {
petsPerSecond = value;
} else if (name === "barksPerSecond") {
barksPerSecond = value;
} else if (name === "kissesPerSecond") {
kissesPerSecond = value;
}
};
const updatePerSecondValues = () => {
let pets = 0;
let barks = 0;
let kisses = 0;
for (const [id, tool] of Object.entries(tools)) {
pets +=
(toolData[id].petsPerSecond || 0) *
tool.count *
tool.upgrades *
upgradeProductionFactor;
barks +=
(toolData[id].barksPerSecond || 0) *
tool.count *
tool.upgrades *
upgradeProductionFactor;
kisses +=
(toolData[id].kissesPerSecond || 0) *
tool.count *
tool.upgrades *
upgradeProductionFactor;
}
petsPerSecond = pets;
barksPerSecond = barks;
kissesPerSecond = kisses;
};
const updateDisplay = () => {
petsCounter.innerText = pets;
petsPerSecondCounter.innerText = petsPerSecond.toFixed(2);
barksCounter.innerText = barks;
barksPerSecondCounter.innerText = barksPerSecond.toFixed(2);
kissesCounter.innerText = kisses;
kissesPerSecondCounter.innerText = kissesPerSecond.toFixed(2);
};
for (const el of toolsEl.querySelectorAll(".tool")) {
const id = el.getAttribute("data-tool");
if (id) {
const data = toolData[id];
if (data) {
const toolInfo = {
count: 0,
upgrades: 1,
};
tools[id] = toolInfo;
const count = el.querySelector(".count");
const level = el.querySelector(".level");
const buy = el.querySelector(".buy");
const upgrade = el.querySelector(".upgrade");
const updateText = () => {
count.innerText = toolInfo.count;
level.innerText = toolInfo.upgrades;
const price = calcPrice(data.basePrice, toolInfo.count);
const upgradePrice = calcUpgradePrice(
data.basePrice,
toolInfo.upgrades
);
buy.innerText = `buy - ${price} ${data.priceIn}`;
upgrade.innerText = `upgrade - ${upgradePrice} kisses`;
};
updateText();
buy.addEventListener("click", () => {
const price = calcPrice(data.basePrice, toolInfo.count);
const v = getValue(data.priceIn);
if (v >= price) {
setValue(data.priceIn, v - price);
toolInfo.count += 1;
updatePerSecondValues();
updateText();
updateDisplay();
}
});
upgrade.addEventListener("click", () => {
const price = calcUpgradePrice(data.basePrice, toolInfo.upgrades);
if (kisses >= price) {
kisses -= price;
toolInfo.upgrades += 1;
updatePerSecondValues();
updateText();
updateDisplay();
}
});
}
}
}
barker.addEventListener("click", () => {
barks += 1;
updateDisplay();
});
let lastUpdate = 0;
let petsQueued = 0;
let barksQueued = 0;
let kissesQueued = 0;
const checkQueue = (name, queued) => {
const perSecond = getValue(`${name}PerSecond`);
if (perSecond > 0) {
const amount = 1000 / perSecond;
const toAdd = Math.floor(queued / amount);
setValue(name, getValue(name) + toAdd);
updateDisplay();
queued -= toAdd * amount;
} else {
queued = 0;
}
return queued;
};
const update = (ts) => {
requestAnimationFrame(update);
const diff = ts - lastUpdate;
petsQueued += diff;
barksQueued += diff;
kissesQueued += diff;
petsQueued = checkQueue("pets", petsQueued);
barksQueued = checkQueue("barks", barksQueued);
kissesQueued = checkQueue("kisses", kissesQueued);
lastUpdate = ts;
};
requestAnimationFrame(update);
})();

27
site/sass/click.scss Normal file
View file

@ -0,0 +1,27 @@
#click {
.resources {
display: grid;
grid-template-columns: repeat(3, 0fr);
grid-auto-flow: row;
& > span {
margin-right: 5px;
width: max-content;
&.resource {
font-weight: bold;
}
}
}
#barker {
font-size: 2rem;
padding: 8px;
}
.tools {
.name {
font-weight: bold;
}
}
}

View file

@ -23,7 +23,7 @@
<span class="pronouns">it/puppy(/she)</span>
</span>
<span class="spacer"></span>
<a href="/pet">creature</a> |
<a href="/games">games</a> |
<a href="/projects">my projects</a> |
<a href="/blog/">blog</a> |
<a href="/images/">images</a> |

View file

@ -0,0 +1,30 @@
<div id="click">
<p>WARNING: no save mechanic is implemented yet!!</p>
<h1>click</h1>
<noscript>
<h1>javascript is required for the clicker game!!</h1>
</noscript>
<div class="resources">
{{#*inline "resource"}}
<span class="resource">{{name}}</span><span id={{id}}>0</span> <span>(<span
id="{{id}}-per-second">0</span>/s)</span>
{{/inline}}
{{> resource id="pets" name="pets"}}
{{> resource id="barks" name="barks"}}
{{> resource id="kisses" name="kisses"}}
</div>
<button id="barker">bark</button>
<div class="tools">
{{#*inline "tool"}}
<div class="tool" data-tool={{id}}>
<p class="name">{{name}} (<span class="count">0</span>, lvl <span class="level">1</span>)</p>
<p class="description">{{description}}</p>
<button class="buy">buy</button> <button class="upgrade">upgrade</button>
</div>
{{/inline}}
{{> tool id="hand" name="hand" description="don't bite the hand that pets you"}}
{{> tool id="puppy" name="puppy" description="arf arf wruff :3"}}
{{> tool id="foodBowl" name="food bowl" description="more food for more barking"}}
{{> tool id="kisser" name="kisser wow" description="someone to kiss all those poor puppies,,"}}
</div>
</div>

View file

@ -10,9 +10,7 @@ use pulldown_cmark::{Options, Parser};
use serde::Serialize;
use url::Url;
use crate::{
extras::Extra, resource::ResourceBuilder, util, PageMetadata, Site, ROOT_PATH, SASS_PATH,
};
use crate::{resource::ResourceBuilder, util, PageMetadata, Site, ROOT_PATH, SASS_PATH};
/// Struct containing data to be sent to templates when rendering them.
#[derive(Debug, Serialize)]
@ -283,8 +281,8 @@ impl<'a> SiteBuilder<'a> {
// Modify HTML output
let mut out = self.rewrite_html(out)?;
if let Some(Extra::HtmlModification(f)) = extra {
out = f(out, self)?;
if let Some(extra) = extra {
out = extra.handle(out, self)?;
}
if !self.serving {

View file

@ -5,17 +5,46 @@ use crate::{blog::BlogPostMetadata, builder::SiteBuilder, resource::ResourceTemp
#[derive(Debug)]
pub enum Extra {
Basic(&'static str),
HtmlModification(fn(page: String, builder: &SiteBuilder) -> eyre::Result<String>),
}
impl Extra {
/// runs the handler for the extra
pub fn handle(&self, page: String, builder: &SiteBuilder) -> eyre::Result<String> {
match self {
Self::Basic(template) => {
println!("{template}");
let content = builder.reg.render(template, &())?;
append_to(&page, &content, "main.page")
}
Self::HtmlModification(f) => (f)(page, builder),
}
}
}
/// Gets the extra for the given value.
pub fn get_extra(extra: &str) -> Option<Extra> {
match extra {
"index" => Some(Extra::HtmlModification(index)),
"click" => Some(Extra::Basic("extras/click")),
_ => None,
}
}
fn append_to(page: &str, content: &str, selector: &str) -> eyre::Result<String> {
Ok(lol_html::rewrite_str(
page,
RewriteStrSettings {
element_content_handlers: vec![element!(selector, move |el| {
el.append(content, lol_html::html_content::ContentType::Html);
Ok(())
})],
..Default::default()
},
)?)
}
/// Extra to add a sidebar to the index page with recent blog posts on it.
fn index(page: String, builder: &SiteBuilder) -> eyre::Result<String> {
#[derive(Debug, Serialize)]
@ -42,14 +71,5 @@ fn index(page: String, builder: &SiteBuilder) -> eyre::Result<String> {
},
)?;
Ok(lol_html::rewrite_str(
&page,
RewriteStrSettings {
element_content_handlers: vec![element!("#content", move |el| {
el.append(&sidebar, lol_html::html_content::ContentType::Html);
Ok(())
})],
..Default::default()
},
)?)
append_to(&page, &sidebar, "#content")
}