Skip to content

Commit

Permalink
docs: example for programmatic table creation (#65)
Browse files Browse the repository at this point in the history
  • Loading branch information
francisdb authored Apr 21, 2024
1 parent c40f385 commit b1462b7
Show file tree
Hide file tree
Showing 18 changed files with 596 additions and 403 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,6 @@ Cargo.lock

# MSVC Windows builds of rustc generate these, which store debugging information
*.pdb

# output from example
basic.vpx
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ Join [#vpxtool on "Virtual Pinball Chat" discord](https://discord.gg/eYsvyMu8) f

https://docs.rs/vpin

## Example code

Check the [examples folder](examples/)

## Projects using vpin

https:/francisdb/vpxtool
Expand Down
15 changes: 15 additions & 0 deletions basic.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
[TableOverride]
ViewCabMode = 2
ViewCabScaleX = 1.280866
ViewCabScaleY = 1.000000
ViewCabScaleZ = 1.000000
ViewCabRotation = 0.000000
ViewCabHOfs = 0.000000
ViewCabVOfs = -11.195301
ViewCabWindowTop = 400.000000
ViewCabWindowBot = 210.000000

[Player]
ScreenPlayerX = 0.000000
ScreenPlayerY = -5.000000
ScreenPlayerZ = 75.000000
38 changes: 38 additions & 0 deletions examples/basic.vbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
Option Explicit
Randomize

ExecuteGlobal GetTextFile("controller.vbs")

Dim bFlippersEnabled

Sub Table1_Init
debug.print "Hello, World!"
'add a ball
bFlippersEnabled = True
End Sub

Sub Table1_KeyDown(ByVal Keycode)
debug.print "Down Keycode: " & Keycode
If keycode = LeftFlipperKey and bFlippersEnabled Then
LeftFlipper.RotateToEnd
End If
If keycode = RightFlipperKey and bFlippersEnabled Then
RightFlipper.RotateToEnd
End If
If keycode = PlungerKey then
Plunger.PullBack
End If
End Sub

Sub Table1_KeyUp(ByVal Keycode)
debug.print "Up Keycode: " & Keycode
If keycode = LeftFlipperKey and bFlippersEnabled Then
LeftFlipper.RotateToStart
End If
If keycode = RightFlipperKey and bFlippersEnabled Then
RightFlipper.RotateToStart
End If
If keycode = PlungerKey then
Plunger.Fire
End If
End Sub
64 changes: 64 additions & 0 deletions examples/create_basic_vpx_file.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
use std::path::Path;
use vpin::vpx;
use vpin::vpx::color::ColorNoAlpha;
use vpin::vpx::gameitem::bumper::Bumper;
use vpin::vpx::gameitem::flipper::Flipper;
use vpin::vpx::gameitem::GameItemEnum;
use vpin::vpx::material::Material;
use vpin::vpx::VPX;

fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut vpx = VPX::default();

// playfield material
let mut material = Material::default();
material.name = "Playfield".to_string();
// material defaults to purple
material.base_color = ColorNoAlpha::from_rgb(0x966F33); // Wood
vpx.gamedata.materials = Some(vec![material]);

// black background (default is bluish gray)
vpx.gamedata.backdrop_color = ColorNoAlpha::from_rgb(0x060606); // Dark Gray
vpx.gamedata.playfield_material = "Playfield".to_string();

// add a plunger
let mut plunger = vpx::gameitem::plunger::Plunger::default();
plunger.name = "Plunger".to_string();
plunger.center.x = 898.027;
plunger.center.y = 2105.312;
vpx.add_game_item(GameItemEnum::Plunger(plunger));

// add a bumper in the center of the playfield
let mut bumper = Bumper::default();
bumper.name = "Bumper1".to_string();
bumper.center.x = (vpx.gamedata.left + vpx.gamedata.right) / 2.;
bumper.center.y = (vpx.gamedata.top + vpx.gamedata.bottom) / 2.;
vpx.add_game_item(GameItemEnum::Bumper(bumper));

// add 2 flippers
let mut flipper_left = Flipper::default();
flipper_left.name = "LeftFlipper".to_string();
flipper_left.center.x = 278.2138;
flipper_left.center.y = 1803.271;
flipper_left.start_angle = 120.5;
flipper_left.end_angle = 70.;
vpx.add_game_item(GameItemEnum::Flipper(flipper_left));

let mut flipper_right = Flipper::default();
flipper_right.name = "RightFlipper".to_string();
flipper_right.center.x = 595.869;
flipper_right.center.y = 1803.271;
flipper_right.start_angle = -120.5;
flipper_right.end_angle = -70.;
vpx.add_game_item(GameItemEnum::Flipper(flipper_right));

// add a script
let script = std::fs::read_to_string(Path::new("examples").join("basic.vbs"))?;
vpx.set_script(script);

vpx::write("basic.vpx", &vpx)?;

println!("Wrote basic.vpx.");
println!(r#"Try running it with "VPinballX_GL -play basic.vpx""#);
Ok(())
}
98 changes: 79 additions & 19 deletions src/vpx/color.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,83 @@ use serde::{Deserialize, Serialize};

use super::biff::BiffWriter;

#[derive(Debug, PartialEq, Clone, Copy, Dummy)]
pub struct ColorNoAlpha {
r: u8,
g: u8,
b: u8,
}

/// Serialize as a string in the format "#RRGGBB".
impl Serialize for ColorNoAlpha {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let s = format!("#{:02x}{:02x}{:02x}", self.r, self.g, self.b);
serializer.serialize_str(&s)
}
}

// Deserialize from a string in the format "#RRGGBB".
impl<'de> Deserialize<'de> for ColorNoAlpha {
fn deserialize<D>(deserializer: D) -> Result<ColorNoAlpha, D::Error>
where
D: serde::Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
if s.len() != 7 {
return Err(serde::de::Error::custom(
"Invalid color format, expected #RRGGBB",
));
}
if &s[0..1] != "#" {
return Err(serde::de::Error::custom(
"Invalid color format, expected #RRGGBB",
));
}
let r = u8::from_str_radix(&s[1..3], 16).map_err(serde::de::Error::custom)?;
let g = u8::from_str_radix(&s[3..5], 16).map_err(serde::de::Error::custom)?;
let b = u8::from_str_radix(&s[5..7], 16).map_err(serde::de::Error::custom)?;
Ok(ColorNoAlpha { r, g, b })
}
}

impl ColorNoAlpha {
pub fn from_rgb(arg: u32) -> Self {
let r = ((arg >> 16) & 0xff) as u8;
let g = ((arg >> 8) & 0xff) as u8;
let b = (arg & 0xff) as u8;
ColorNoAlpha { r, g, b }
}

pub fn to_rgb(&self) -> u32 {
let r = (self.r as u32) << 16;
let g = (self.g as u32) << 8;
let b = self.b as u32;
r | g | b
}

pub fn rgb(r: u8, g: u8, b: u8) -> Self {
Self { r, g, b }
}

pub fn biff_read(reader: &mut BiffReader<'_>) -> ColorNoAlpha {
let r = reader.get_u8();
let g = reader.get_u8();
let b = reader.get_u8();
let _ = reader.get_u8();
ColorNoAlpha { r, g, b }
}

pub fn biff_write(&self, writer: &mut BiffWriter) {
writer.write_u8(self.r);
writer.write_u8(self.g);
writer.write_u8(self.b);
writer.write_u8(0);
}
}

#[derive(Debug, PartialEq, Clone, Copy, Dummy)]
pub struct Color {
a: u8,
Expand Down Expand Up @@ -134,37 +211,20 @@ impl Color {
b: 0,
};

// TODO do we want a BiffRead with a parameter?

pub fn biff_read_argb(reader: &mut BiffReader<'_>) -> Color {
pub fn biff_read_bgr(reader: &mut BiffReader<'_>) -> Color {
let a = reader.get_u8();
let r = reader.get_u8();
let g = reader.get_u8();
let b = reader.get_u8();
Color { a, r, g, b }
}

pub fn biff_read_bgr(reader: &mut BiffReader<'_>) -> Color {
let a = reader.get_u8();
let b = reader.get_u8();
let g = reader.get_u8();
let r = reader.get_u8();
Color { a, r, g, b }
}

pub fn biff_write_argb(&self, writer: &mut BiffWriter) {
pub fn biff_write_bgr(&self, writer: &mut BiffWriter) {
writer.write_u8(self.a);
writer.write_u8(self.r);
writer.write_u8(self.g);
writer.write_u8(self.b);
}

pub fn biff_write_bgr(&self, writer: &mut BiffWriter) {
writer.write_u8(self.a);
writer.write_u8(self.b);
writer.write_u8(self.g);
writer.write_u8(self.r);
}
}

impl std::fmt::Display for Color {
Expand Down
14 changes: 7 additions & 7 deletions src/vpx/gamedata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use super::{
version::Version,
};
use crate::vpx::biff::{BiffRead, BiffWrite};
use crate::vpx::color::{Color, ColorJson};
use crate::vpx::color::{Color, ColorJson, ColorNoAlpha};
use crate::vpx::json::F32WithNanInf;
use crate::vpx::material::{Material, SaveMaterial, SavePhysicsMaterial};
use crate::vpx::math::{dequantize_u8, quantize_u8};
Expand Down Expand Up @@ -172,7 +172,7 @@ pub struct GameData {
pub glass_bottom_height: Option<f32>, // GLAB 70.5 (added in 10.8)
pub table_height: Option<f32>, // TBLH 71 (optional in 10.8)
pub playfield_material: String, // PLMA 72
pub backdrop_color: u32, // BCLR 73 (color bgr)
pub backdrop_color: ColorNoAlpha, // BCLR 73 (color bgr)
pub global_difficulty: f32, // TDFT 74
pub light_ambient: u32, // LZAM 75 (color)
pub light0_emission: u32, // LZDI 76 (color)
Expand Down Expand Up @@ -341,7 +341,7 @@ pub(crate) struct GameDataJson {
pub glass_bottom_height: Option<f32>,
pub table_height: Option<f32>,
pub playfield_material: String,
pub backdrop_color: u32,
pub backdrop_color: ColorNoAlpha,
pub global_difficulty: f32,
pub light_ambient: u32,
pub light0_emission: u32,
Expand Down Expand Up @@ -827,7 +827,7 @@ impl Default for GameData {
glass_bottom_height: None, // new default 210 for both
table_height: None, //0.0,
playfield_material: "".to_string(),
backdrop_color: 0x232323ff, // bgra
backdrop_color: ColorNoAlpha::from_rgb(0x626E8E), // Waikawa/Bluish Gray
global_difficulty: 0.2,
light_ambient: 0x000000ff, // TODO what is the format for all these?
light0_emission: 0xfffff0ff, // TODO is this correct?
Expand Down Expand Up @@ -1125,7 +1125,7 @@ pub fn write_all_gamedata_records(gamedata: &GameData, version: &Version) -> Vec
writer.write_tagged_f32("TBLH", table_height);
}
writer.write_tagged_string("PLMA", &gamedata.playfield_material);
writer.write_tagged_u32("BCLR", gamedata.backdrop_color);
writer.write_tagged_with("BCLR", &gamedata.backdrop_color, ColorNoAlpha::biff_write);
writer.write_tagged_f32("TDFT", gamedata.global_difficulty);
writer.write_tagged_u32("LZAM", gamedata.light_ambient);
writer.write_tagged_u32("LZDI", gamedata.light0_emission);
Expand Down Expand Up @@ -1373,7 +1373,7 @@ pub fn read_all_gamedata_records(input: &[u8], version: &Version) -> GameData {
"GLAB" => gamedata.glass_bottom_height = Some(reader.get_f32()),
"TBLH" => gamedata.table_height = Some(reader.get_f32()),
"PLMA" => gamedata.playfield_material = reader.get_string(),
"BCLR" => gamedata.backdrop_color = reader.get_u32(),
"BCLR" => gamedata.backdrop_color = ColorNoAlpha::biff_read(reader),
"TDFT" => gamedata.global_difficulty = reader.get_f32(),
"LZAM" => gamedata.light_ambient = reader.get_u32(),
"LZDI" => gamedata.light0_emission = reader.get_u32(),
Expand Down Expand Up @@ -1593,7 +1593,7 @@ mod tests {
glass_bottom_height: Some(123.0),
table_height: Some(12.0),
playfield_material: "material_pf".to_string(),
backdrop_color: 0x333333ff,
backdrop_color: ColorNoAlpha::rgb(0x11, 0x22, 0x33),
global_difficulty: 0.3,
light_ambient: 0x11223344,
light0_emission: 0xaabbccdd,
Expand Down
1 change: 1 addition & 0 deletions src/vpx/gameitem.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ pub mod lightsequencer;
pub mod plunger;
pub mod primitive;
pub mod ramp;
pub mod ramp_image_alignment;
pub mod reel;
pub mod rubber;
pub mod spinner;
Expand Down
Loading

0 comments on commit b1462b7

Please sign in to comment.