Expand description
The simulation engine of Verticopolis, a tower building game inspired by SimTower (1994).
This crate is the whole simulation: every rule, price, build limit, command, save format and gameplay event. The web game runs the same engine compiled to WebAssembly. Your code owns everything a player sees and hears; the engine owns what happens in the tower.
§Quick start
use verticopolis_engine::clock::GameMode;
use verticopolis_engine::facilities::Kind;
use verticopolis_engine::sim::Simulation;
// A new Classic game from seed 42.
let mut sim = Simulation::new_game(42, GameMode::Classic);
// A lobby along the ground floor, then an office above it.
let tiles: Vec<i64> = (100..124).collect();
sim.paint_brush(Kind::Lobby, 1, &tiles);
let quote = sim.can_build(Kind::Office, 2, 106);
assert!(quote.ok);
assert!(sim.build(Kind::Office, 2, 106).ok);
// One game day, an hour at a time, draining the gameplay events.
for _ in 0..24 {
sim.tick(60.0);
for event in sim.gameplay.drain() {
println!("{}", event.to_json());
}
}
println!("population {}, money {}", sim.population(), sim.money);§What a host calls
| To | Call |
|---|---|
| Start a game | Simulation::new_game, new_game_with |
| Run time | tick, or FrameStepper to drive it from display frames |
| Build and sell | can_build, build, paint_brush, can_build_transport, build_transport, sell_at, toggle_auto_bridge |
| Change elevators and remove things | add_car, remove_car, begin_extend, extend_transport, end_extend, remove_facility |
| Set prices | adjust_rent, price_unit, set_no_rate, apply_rent_batch |
| Answer the game | resolve_choice, set_film_policy, change_film |
| Draw the tower | view: structure_view, ViewReader, meta_since; or the older flat frame |
| React to what happened | gameplay: drain sim.gameplay after each frame |
| Show prices and rules | catalog::catalog |
| Save and load | load: encode_simulation_vctower, decode_vctower, deserialize |
| Open a 1994 save | import_tdt, export_tdt |
| Name the engine | identity::identity |
| Compare two games | canonical::digest of snapshot::state_view |
§How the engine behaves
- One
Simulationis one game. Its public fields are for reading; change a game only through its commands, which apply the rules and charge the money. - Commands answer instead of panicking. A refused command changes nothing and returns a reason a player can read.
- No I/O. The engine reads no file, clock, environment or network and starts no thread. You pass in time, file contents and timestamps, and get bytes back to store where you like.
- Deterministic. The same seed, commands and sequence of
tickcalls give the same game on any machine, here and in the WebAssembly build. The conformance suite in the repository is how a host proves it. - Coordinates. Floor 1 is the ground floor; 0 and below are basements,
down to
MIN_FLOOR. A tile columnxruns from 0 across the lot’sLOT_WIDTH. - Two modes.
GameMode::Classicfollows the 1994 original;GameMode::Modernadds rooms and options. The catalog lists every difference a frontend shows.
§Features
None are on by default. wasm builds the JavaScript binding, and
conformance adds the harness that replays the conformance suite.
The guides at docs.verticopolis.com cover adding the crate, saves, the conformance suite and the JavaScript package.
Modules§
- brush
- The floor and lobby brush:
Simulation::paint_brushlays a run of floor or lobby tiles in one command. - build
- Placing and selling:
Simulation::buildfor floors, lobbies and rooms,Simulation::build_transportfor elevators, stairs and escalators, andSimulation::sell_at. Each build has a dry run (Simulation::can_build,Simulation::can_build_transport) that answers with the same refusal and the full price, for a placement ghost. - canonical
- Canonical JSON and the digest the conformance suite compares: object keys sorted, numbers and strings written one way, so two equal values always give the same text and the same hash on any platform.
- catalog
- The catalog: engine-authored data a frontend draws menus, prices and placement ghosts from, resolved for one mode so the frontend never applies simulation rules itself.
- charges
- Commands that cost or refund money after a build: elevator cars
(
Simulation::add_car,Simulation::remove_car), stretching a shaft (Simulation::extend_transport) and removing a room or a shaft (Simulation::remove_facility). - clock
- Game time: the
ClockonSimulation::clock, theGameModea game is started in, and the two calendars. - events
- Emergencies and the choices they put to the player.
- facilities
- What can be built:
Kindnames every floor tile, room and transport, and the constants here give the lot’s size. - fixed_
step - Driving the game from display frames. A host owes the game some game
minutes each frame, set by the game speed it shows;
FrameSteppercuts them into whole steps sized from the game’s own clock, so the tower comes out the same at any frame rate. The web game runs this same stepper, so a host that uses it matches the browser at the same speed. - frame
- The flat frame: everything a renderer reads each frame, as one
Vec<f64>(frame_view), and the new log messages (log_since). - gameplay
- Gameplay events: facts the engine reports the moment something happens, such as a room placed, a star reached or a fire started, for a host to feed achievements, analytics or sound.
- identity
- The engine’s identity record: which engine this is and which contracts it speaks (crate version, save version, frame, view and catalog layout versions, and the conformance lock’s digest), so a frontend, a bug report and a benchmark all name the same engine.
- load
- Saves: writing a game to a
.vctowerfile and reading one back. - rent
- Setting what rooms charge: one room at a time
(
Simulation::adjust_rent,Simulation::price_unit,Simulation::set_no_rate) or every room of a kind at once (Simulation::apply_rent_batch). - sim
- One game:
Simulationholds a whole tower, its people, its money and its clock, and every player command and time step goes through it. - snapshot
- The hashed state view: the saved game without its prose, which the
conformance suite hashes at every checkpoint. Hash it with
crate::canonical::digestto tell whether two games are in the same state. - view
- What a renderer reads, sized for a frame budget. Three reads cover it:
Functions§
- export_
tdt - Writes a Classic game’s save JSON (the text of
Simulation::serialize) as a 1994 SimTower.TDTfile. The error is a sentence a player can read, or says what made the JSON unusable. - import_
tdt - Reads a 1994 SimTower
.TDTsave into this engine’s save JSON, plus warnings about anything it could not carry over. Load the JSON withdeserialize_tdtafter parsing it. The error is a sentence a player can read.