Skip to content

Commit 6bdc4fb

Browse files
committed
Partially implement draw instructions via custom buildings (TODO: tests)
1 parent 3628ead commit 6bdc4fb

File tree

8 files changed

+301
-24
lines changed

8 files changed

+301
-24
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ clap-stdin = { version = "0.6.0", optional = true }
5959
crossterm = { version = "0.29.0", optional = true }
6060
cursive = { version = "0.21.1", optional = true }
6161
indicatif = { version = "0.18.0", optional = true }
62+
bitflags = "2.9.1"
6263

6364
[build-dependencies]
6465
lalrpop = { version = "0.22.2", optional = true }

src/vm/buildings.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,11 @@ pub trait CustomBuildingData {
365365
InstructionResult::Ok
366366
}
367367

368+
#[must_use]
369+
fn drawflush(&mut self, state: &mut ProcessorState, vm: &LogicVM) -> InstructionResult {
370+
InstructionResult::Ok
371+
}
372+
368373
#[must_use]
369374
fn printflush(&mut self, state: &mut ProcessorState, vm: &LogicVM) -> InstructionResult {
370375
InstructionResult::Ok

src/vm/draw.rs

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
use bitflags::bitflags;
2+
use widestring::U16String;
3+
4+
use super::Content;
5+
6+
pub(super) const SCALE_STEP: f32 = 0.05;
7+
8+
// note: this allows larger values than mindustry does
9+
#[derive(Debug, Clone, PartialEq, Eq)]
10+
pub enum DrawCommand {
11+
Clear {
12+
r: u8,
13+
g: u8,
14+
b: u8,
15+
},
16+
Color {
17+
r: u8,
18+
g: u8,
19+
b: u8,
20+
a: u8,
21+
},
22+
Stroke {
23+
width: i16,
24+
},
25+
Line {
26+
x1: i16,
27+
y1: i16,
28+
x2: i16,
29+
y2: i16,
30+
},
31+
Rect {
32+
x: i16,
33+
y: i16,
34+
width: i16,
35+
height: i16,
36+
fill: bool,
37+
},
38+
Poly {
39+
x: i16,
40+
y: i16,
41+
sides: i16,
42+
radius: i16,
43+
rotation: i16,
44+
fill: bool,
45+
},
46+
Triangle {
47+
x1: i16,
48+
y1: i16,
49+
x2: i16,
50+
y2: i16,
51+
x3: i16,
52+
y3: i16,
53+
},
54+
Image {
55+
x: i16,
56+
y: i16,
57+
image: Option<Content>,
58+
size: i16,
59+
rotation: i16,
60+
},
61+
Print {
62+
x: i16,
63+
y: i16,
64+
alignment: TextAlignment,
65+
text: U16String,
66+
},
67+
Translate {
68+
x: i16,
69+
y: i16,
70+
},
71+
Scale {
72+
x: i16,
73+
y: i16,
74+
},
75+
Rotate {
76+
degrees: i16,
77+
},
78+
Reset,
79+
}
80+
81+
bitflags! {
82+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
83+
pub struct TextAlignment: u8 {
84+
const CENTER = 1 << 0;
85+
const TOP = 1 << 1;
86+
const BOTTOM = 1 << 2;
87+
const LEFT = 1 << 3;
88+
const RIGHT = 1 << 4;
89+
90+
const TOP_LEFT = Self::TOP.bits() | Self::LEFT.bits();
91+
const TOP_RIGHT = Self::TOP.bits() | Self::RIGHT.bits();
92+
const BOTTOM_LEFT = Self::BOTTOM.bits() | Self::LEFT.bits();
93+
const BOTTOM_RIGHT = Self::BOTTOM.bits() | Self::RIGHT.bits();
94+
}
95+
}

src/vm/instructions.rs

Lines changed: 164 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,18 @@ use num_traits::float::FloatCore;
1111
use widestring::{U16Str, u16str};
1212

1313
use super::{
14-
BuildingData, Content, LObject, LString, LValue, LVar, LogicVM, ProcessorState, VMLoadError,
15-
VMLoadResult,
14+
BuildingData, Content, DrawCommand, LObject, LString, LValue, LVar, LogicVM, ProcessorState,
15+
TextAlignment, VMLoadError, VMLoadResult,
1616
buildings::borrow_data,
17-
processor::MAX_TEXT_BUFFER,
17+
draw::SCALE_STEP,
18+
processor::{MAX_DRAW_BUFFER, MAX_TEXT_BUFFER},
1819
variables::{Constants, F64_DEG_RAD, F64_RAD_DEG, RAD_DEG},
1920
};
2021
use crate::{
21-
parser::ast::{self, ConditionOp, LogicOp, TileLayer},
22+
parser::ast::{self, ConditionOp, DrawOp, LogicOp, TileLayer},
2223
types::{
2324
ContentType, LAccess, PackedPoint2, Team,
24-
colors::{self, f32_to_double_bits, f64_from_double_bits},
25+
colors::{self, f32_to_double_bits, f64_from_double_bits, from_double_bits},
2526
content,
2627
},
2728
utils::{RapidHashMap, u16format},
@@ -68,10 +69,12 @@ pub enum Instruction {
6869
// input/output
6970
Read,
7071
Write,
72+
Draw,
7173
Print,
7274
PrintChar,
7375
Format,
7476
// block control
77+
DrawFlush,
7578
PrintFlush,
7679
GetLink,
7780
Control,
@@ -192,31 +195,32 @@ impl InstructionBuilder {
192195
.into(),
193196
// TODO: implement draw?
194197
ast::Instruction::Draw {
195-
op: _,
198+
op,
196199
x,
197200
y,
198201
p1,
199202
p2,
200203
p3,
201204
p4,
202-
} => {
203-
lvar(x);
204-
lvar(y);
205-
lvar(p1);
206-
lvar(p2);
207-
lvar(p3);
208-
lvar(p4);
209-
Noop.into()
205+
} => Draw {
206+
op,
207+
p1: lvar(x),
208+
p2: lvar(y),
209+
p3: lvar(p1),
210+
p4: lvar(p2),
211+
p5: lvar(p3),
212+
p6: lvar(p4),
210213
}
214+
.into(),
211215
ast::Instruction::Print { value } => Print { value: lvar(value) }.into(),
212216
ast::Instruction::PrintChar { value } => PrintChar { value: lvar(value) }.into(),
213217
ast::Instruction::Format { value } => Format { value: lvar(value) }.into(),
214218

215219
// block control
216-
ast::Instruction::DrawFlush { target } => {
217-
lvar(target);
218-
Noop.into()
220+
ast::Instruction::DrawFlush { target } => DrawFlush {
221+
target: lvar(target),
219222
}
223+
.into(),
220224
ast::Instruction::PrintFlush { target } => PrintFlush {
221225
target: lvar(target),
222226
}
@@ -498,6 +502,128 @@ impl InstructionTrait for Write {
498502
}
499503
}
500504

505+
#[derive(Debug)]
506+
#[non_exhaustive]
507+
pub struct Draw {
508+
pub op: DrawOp,
509+
pub p1: LVar,
510+
pub p2: LVar,
511+
pub p3: LVar,
512+
pub p4: LVar,
513+
pub p5: LVar,
514+
pub p6: LVar,
515+
}
516+
517+
impl SimpleInstructionTrait for Draw {
518+
fn execute(&self, state: &mut ProcessorState, _: &LogicVM) {
519+
if state.drawbuffer_len >= MAX_DRAW_BUFFER {
520+
return;
521+
}
522+
523+
let p1 = self.p1.get_inner(state, &state.variables);
524+
let p2 = self.p2.get_inner(state, &state.variables);
525+
let p3 = self.p3.get_inner(state, &state.variables);
526+
let p4 = self.p4.get_inner(state, &state.variables);
527+
let p5 = self.p5.get_inner(state, &state.variables);
528+
let p6 = self.p6.get_inner(state, &state.variables);
529+
530+
let mut size = 1;
531+
state.drawbuffer.push(match self.op {
532+
DrawOp::Clear => DrawCommand::Clear {
533+
r: p1.numi() as u8,
534+
g: p2.numi() as u8,
535+
b: p3.numi() as u8,
536+
},
537+
DrawOp::Color => DrawCommand::Color {
538+
r: p1.numi() as u8,
539+
g: p2.numi() as u8,
540+
b: p3.numi() as u8,
541+
a: p4.numi() as u8,
542+
},
543+
DrawOp::Col => {
544+
let (r, g, b, a) = from_double_bits(p1.num());
545+
DrawCommand::Color { r, g, b, a }
546+
}
547+
DrawOp::Stroke => DrawCommand::Stroke {
548+
width: p1.numi() as i16,
549+
},
550+
DrawOp::Line => DrawCommand::Line {
551+
x1: p1.numi() as i16,
552+
y1: p2.numi() as i16,
553+
x2: p3.numi() as i16,
554+
y2: p4.numi() as i16,
555+
},
556+
DrawOp::Rect | DrawOp::LineRect => DrawCommand::Rect {
557+
x: p1.numi() as i16,
558+
y: p2.numi() as i16,
559+
width: p3.numi() as i16,
560+
height: p4.numi() as i16,
561+
fill: self.op == DrawOp::Rect,
562+
},
563+
DrawOp::Poly | DrawOp::LinePoly => DrawCommand::Poly {
564+
x: p1.numi() as i16,
565+
y: p2.numi() as i16,
566+
sides: p3.numi() as i16,
567+
radius: p4.numi() as i16,
568+
rotation: p5.numi() as i16,
569+
fill: self.op == DrawOp::Poly,
570+
},
571+
DrawOp::Triangle => DrawCommand::Triangle {
572+
x1: p1.numi() as i16,
573+
y1: p2.numi() as i16,
574+
x2: p3.numi() as i16,
575+
y2: p4.numi() as i16,
576+
x3: p5.numi() as i16,
577+
y3: p6.numi() as i16,
578+
},
579+
DrawOp::Image => DrawCommand::Image {
580+
x: p1.numi() as i16,
581+
y: p2.numi() as i16,
582+
image: match p3.obj() {
583+
Some(LObject::Content(content)) => Some(*content),
584+
_ => None,
585+
},
586+
size: p4.numi() as i16,
587+
rotation: p5.numi() as i16,
588+
},
589+
DrawOp::Print => {
590+
if state.printbuffer.is_empty() {
591+
return;
592+
}
593+
594+
// newlines don't count toward the length limit
595+
size = state
596+
.printbuffer
597+
.as_slice()
598+
.iter()
599+
.filter(|v| **v != b'\n' as u16)
600+
.count();
601+
602+
DrawCommand::Print {
603+
x: p1.numi() as i16,
604+
y: p2.numi() as i16,
605+
alignment: TextAlignment::from_bits_truncate(p3.numi() as u8),
606+
// FIXME: lazy
607+
text: core::mem::take(&mut state.printbuffer),
608+
}
609+
}
610+
DrawOp::Translate => DrawCommand::Translate {
611+
x: p1.numi() as i16,
612+
y: p2.numi() as i16,
613+
},
614+
DrawOp::Scale => DrawCommand::Scale {
615+
x: (p1.numf() / SCALE_STEP) as i16,
616+
y: (p2.numf() / SCALE_STEP) as i16,
617+
},
618+
DrawOp::Rotate => DrawCommand::Rotate {
619+
degrees: p1.numi() as i16,
620+
},
621+
DrawOp::Reset => DrawCommand::Reset,
622+
});
623+
state.drawbuffer_len += size;
624+
}
625+
}
626+
501627
#[derive(Debug)]
502628
#[non_exhaustive]
503629
pub struct Print {
@@ -601,6 +727,27 @@ impl SimpleInstructionTrait for Format {
601727

602728
// block control
603729

730+
#[derive(Debug)]
731+
#[non_exhaustive]
732+
pub struct DrawFlush {
733+
pub target: LVar,
734+
}
735+
736+
impl InstructionTrait for DrawFlush {
737+
fn execute(&self, state: &mut ProcessorState, vm: &LogicVM) -> InstructionResult {
738+
let result = if let Some(LObject::Building(target)) = self.target.get(state).obj()
739+
&& let Ok(mut data) = target.data.clone().try_borrow_mut()
740+
&& let BuildingData::Custom(custom) = &mut *data
741+
{
742+
custom.drawflush(state, vm)
743+
} else {
744+
InstructionResult::Ok
745+
};
746+
state.drawbuffer.clear();
747+
result
748+
}
749+
}
750+
604751
#[derive(Debug)]
605752
#[non_exhaustive]
606753
pub struct PrintFlush {

src/vm/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use thiserror::Error;
88
use self::variables::Constants;
99
pub use self::{
1010
buildings::{Building, BuildingData, CustomBuildingData},
11+
draw::{DrawCommand, TextAlignment},
1112
instructions::InstructionResult,
1213
processor::{InstructionHook, Processor, ProcessorBuilder, ProcessorState},
1314
variables::{Content, LObject, LString, LValue, LVar},
@@ -17,6 +18,7 @@ use crate::types::{Schematic, SchematicTile};
1718
use crate::{types::PackedPoint2, utils::RapidHashMap};
1819

1920
pub mod buildings;
21+
mod draw;
2022
pub mod instructions;
2123
mod processor;
2224
pub mod variables;

0 commit comments

Comments
 (0)