Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs: example for programmatic table creation #65

Merged
merged 10 commits into from
Apr 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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://github.com/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