CHANGELOG, tornado showcase shot, capture script update
@@ -0,0 +1,31 @@
|
|||||||
|
# Changelog
|
||||||
|
|
||||||
|
All notable changes to PolyCity are documented here.
|
||||||
|
|
||||||
|
## 1.0.0 — initial public release
|
||||||
|
|
||||||
|
### Gameplay
|
||||||
|
- Procedurally generated island maps with rivers, beaches and forests
|
||||||
|
- RCI zoning with demand-driven development and a three-tier density ladder
|
||||||
|
- City-wide power grid (coal / solar / wind, coastal wind bonus) with
|
||||||
|
farthest-first brownouts
|
||||||
|
- Land value model (services, pollution, waterfront, leisure) driving upgrades
|
||||||
|
- Service buildings: police, fire, hospital, school, park, plaza, stadium
|
||||||
|
- Pollution-driven fires that spread unless covered by fire stations
|
||||||
|
- Tornado disasters that flatten streets into rubble (toggleable)
|
||||||
|
- Budget with live tax slider, itemized income/upkeep ledger
|
||||||
|
- Happiness, RCI demand bars, milestones up to Megalopolis, quest onboarding
|
||||||
|
- Traffic agents flowing along the road network
|
||||||
|
- Day/night cycle with lit windows at night
|
||||||
|
|
||||||
|
### Systems
|
||||||
|
- Autosave plus 3 manual slots, JSON export/import
|
||||||
|
- PWA: installable, offline play via service worker
|
||||||
|
- Adaptive render resolution for weak GPUs; optional shadows
|
||||||
|
- Touch controls: one-finger paint, two-finger orbit/zoom
|
||||||
|
|
||||||
|
### Quality
|
||||||
|
- 45-assertion engine test suite (pure Node)
|
||||||
|
- 40-year economy soak test (11 assertions)
|
||||||
|
- Headless browser end-to-end smoke test
|
||||||
|
- GitHub Pages workflow + Netlify config for one-click publishing
|
||||||
|
Before Width: | Height: | Size: 114 KiB After Width: | Height: | Size: 115 KiB |
|
Before Width: | Height: | Size: 142 KiB After Width: | Height: | Size: 165 KiB |
|
Before Width: | Height: | Size: 143 KiB After Width: | Height: | Size: 168 KiB |
|
Before Width: | Height: | Size: 182 KiB After Width: | Height: | Size: 202 KiB |
|
Before Width: | Height: | Size: 73 KiB After Width: | Height: | Size: 74 KiB |
|
Before Width: | Height: | Size: 164 KiB After Width: | Height: | Size: 183 KiB |
|
Before Width: | Height: | Size: 73 KiB After Width: | Height: | Size: 74 KiB |
|
Before Width: | Height: | Size: 191 KiB After Width: | Height: | Size: 227 KiB |
|
Before Width: | Height: | Size: 326 KiB After Width: | Height: | Size: 366 KiB |
|
Before Width: | Height: | Size: 235 KiB After Width: | Height: | Size: 266 KiB |
|
Before Width: | Height: | Size: 232 KiB After Width: | Height: | Size: 258 KiB |
|
After Width: | Height: | Size: 365 KiB |
@@ -523,8 +523,7 @@ export class UI {
|
|||||||
<button class="btn" id="mImport">📂 Import Save</button>
|
<button class="btn" id="mImport">📂 Import Save</button>
|
||||||
</div>
|
</div>
|
||||||
<h3>Save slots</h3>
|
<h3>Save slots</h3>
|
||||||
<div id="slots"></div>
|
<div id="slots"></div> <h3>Settings</h3>
|
||||||
<h3>Settings</h3>
|
|
||||||
<div class="settings">
|
<div class="settings">
|
||||||
<label><input type="checkbox" id="setSound" ${this.settings.sound ? 'checked' : ''}> Sound effects</label>
|
<label><input type="checkbox" id="setSound" ${this.settings.sound ? 'checked' : ''}> Sound effects</label>
|
||||||
<label><input type="checkbox" id="setShadow" ${this.settings.shadows ? 'checked' : ''}> Shadows (pretty, costs FPS)</label>
|
<label><input type="checkbox" id="setShadow" ${this.settings.shadows ? 'checked' : ''}> Shadows (pretty, costs FPS)</label>
|
||||||
|
|||||||
@@ -85,6 +85,16 @@ await page.evaluate(() => { window.POLYCITY._parts.renderer.timeOfDay = 0.75; })
|
|||||||
await page.waitForTimeout(300);
|
await page.waitForTimeout(300);
|
||||||
await snap('scene-night.png');
|
await snap('scene-night.png');
|
||||||
|
|
||||||
|
// tornado aftermath
|
||||||
|
await page.evaluate(() => {
|
||||||
|
const c = window.POLYCITY.city;
|
||||||
|
window.POLYCITY._parts.renderer.timeOfDay = 0.30;
|
||||||
|
c.notify = () => {}; // keep the frame clean
|
||||||
|
c.spawnTornado(); c.spawnTornado();
|
||||||
|
});
|
||||||
|
await page.waitForTimeout(500);
|
||||||
|
await snap('scene-tornado.png');
|
||||||
|
|
||||||
const stats = await page.evaluate(() => {
|
const stats = await page.evaluate(() => {
|
||||||
const c = window.POLYCITY.city;
|
const c = window.POLYCITY.city;
|
||||||
return { pop: c.stats.pop, dev: [...c.grid.level].filter(v => v > 0).length, happy: c.stats.happiness };
|
return { pop: c.stats.pop, dev: [...c.grid.level].filter(v => v > 0).length, happy: c.stats.happiness };
|
||||||
|
|||||||