Skip to main content
Verticopolis engineGitHub

Crate verticopolis_engine

Crate verticopolis_engine 

Source
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

ToCall
Start a gameSimulation::new_game, new_game_with
Run timetick, or FrameStepper to drive it from display frames
Build and sellcan_build, build, paint_brush, can_build_transport, build_transport, sell_at, toggle_auto_bridge
Change elevators and remove thingsadd_car, remove_car, begin_extend, extend_transport, end_extend, remove_facility
Set pricesadjust_rent, price_unit, set_no_rate, apply_rent_batch
Answer the gameresolve_choice, set_film_policy, change_film
Draw the towerview: structure_view, ViewReader, meta_since; or the older flat frame
React to what happenedgameplay: drain sim.gameplay after each frame
Show prices and rulescatalog::catalog
Save and loadload: encode_simulation_vctower, decode_vctower, deserialize
Open a 1994 saveimport_tdt, export_tdt
Name the engineidentity::identity
Compare two gamescanonical::digest of snapshot::state_view

§How the engine behaves

  • One Simulation is 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 tick calls 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 column x runs from 0 across the lot’s LOT_WIDTH.
  • Two modes. GameMode::Classic follows the 1994 original; GameMode::Modern adds 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_brush lays a run of floor or lobby tiles in one command.
build
Placing and selling: Simulation::build for floors, lobbies and rooms, Simulation::build_transport for elevators, stairs and escalators, and Simulation::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 Clock on Simulation::clock, the GameMode a game is started in, and the two calendars.
events
Emergencies and the choices they put to the player.
facilities
What can be built: Kind names 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; FrameStepper cuts 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 .vctower file 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: Simulation holds 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::digest to 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 .TDT file. The error is a sentence a player can read, or says what made the JSON unusable.
import_tdt
Reads a 1994 SimTower .TDT save into this engine’s save JSON, plus warnings about anything it could not carry over. Load the JSON with deserialize_tdt after parsing it. The error is a sentence a player can read.