split game struct
This commit is contained in:
parent
3468fff882
commit
e07fac4211
|
@ -1,12 +1,12 @@
|
||||||
use crate::{Currency, Progressable};
|
use crate::Currency;
|
||||||
use servicepoint::{CharGridMutExt, GridMut, WindowMut};
|
use servicepoint::{CharGridMutExt, GridMut, WindowMut};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub struct Bar {
|
pub struct Bar {
|
||||||
name: &'static str,
|
name: &'static str,
|
||||||
pub(crate) enabled: bool,
|
|
||||||
progress: f64,
|
progress: f64,
|
||||||
|
pub(crate) enabled: bool,
|
||||||
pub(crate) speed: f64,
|
pub(crate) speed: f64,
|
||||||
pub(crate) productivity: f64,
|
pub(crate) productivity: f64,
|
||||||
}
|
}
|
||||||
|
@ -15,10 +15,10 @@ impl Bar {
|
||||||
pub(crate) fn new(productivity: f64, speed: f64, name: &'static str) -> Self {
|
pub(crate) fn new(productivity: f64, speed: f64, name: &'static str) -> Self {
|
||||||
Self {
|
Self {
|
||||||
name,
|
name,
|
||||||
enabled: false,
|
|
||||||
productivity,
|
productivity,
|
||||||
progress: 0f64,
|
|
||||||
speed,
|
speed,
|
||||||
|
enabled: false,
|
||||||
|
progress: 0f64,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
96
src/game.rs
96
src/game.rs
|
@ -1,65 +1,62 @@
|
||||||
use crate::upgrades::{Upgrade, get_upgrades};
|
use crate::unlocks::UnlockSystem;
|
||||||
use crate::{Currency, Progressable, bar::Bar};
|
use crate::{Currency, bar::Bar};
|
||||||
use log::log;
|
|
||||||
use servicepoint::{Bitmap, CharGrid, CharGridMutExt, TILE_SIZE, WindowMut};
|
use servicepoint::{Bitmap, CharGrid, CharGridMutExt, TILE_SIZE, WindowMut};
|
||||||
use std::collections::VecDeque;
|
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Game<const STEP_COUNT: usize = 11> {
|
pub struct Game {
|
||||||
pub(crate) currency: Currency,
|
|
||||||
pub(crate) bars: [Bar; STEP_COUNT],
|
|
||||||
unlocks: usize,
|
|
||||||
unlock_queue: VecDeque<Upgrade>,
|
|
||||||
total_currency: Currency,
|
total_currency: Currency,
|
||||||
pub(crate) global_speed: f64,
|
unlocks: UnlockSystem,
|
||||||
pub(crate) global_productivity: f64,
|
state: State,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct State<const STEP_COUNT: usize = 11> {
|
||||||
|
pub(crate) currency: Currency,
|
||||||
|
pub(crate) speed: f64,
|
||||||
|
pub(crate) productivity: f64,
|
||||||
|
pub(crate) bars: [Bar; STEP_COUNT],
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Game {
|
impl Game {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
currency: 0f64,
|
|
||||||
total_currency: 0f64,
|
total_currency: 0f64,
|
||||||
global_speed: 1f64,
|
unlocks: UnlockSystem::new(),
|
||||||
global_productivity: 1f64,
|
state: State {
|
||||||
unlocks: 0,
|
currency: 0f64,
|
||||||
unlock_queue: get_upgrades(),
|
speed: 1f64,
|
||||||
bars: [
|
productivity: 1f64,
|
||||||
Bar::new(1f64, 1.0, "Powering infrastructure"),
|
bars: [
|
||||||
Bar::new(2f64, 0.5, "Dusting ServicePoint"),
|
Bar::new(1f64, 1.0, "Powering infrastructure"),
|
||||||
Bar::new(4f64, 0.25, "Activating colorful lights"),
|
Bar::new(2f64, 0.5, "Dusting ServicePoint"),
|
||||||
Bar::new(8f64, 0.125, "Dimming darkroom"),
|
Bar::new(4f64, 0.25, "Activating colorful lights"),
|
||||||
Bar::new(16f64, 0.0625, "Refilling Matemat"),
|
Bar::new(8f64, 0.125, "Dimming darkroom"),
|
||||||
Bar::new(32f64, 0.03125, "Pre-heating convectiomat"),
|
Bar::new(16f64, 0.0625, "Refilling Matemat"),
|
||||||
Bar::new(64f64, 0.015625, "Resetting chair heights"),
|
Bar::new(32f64, 0.03125, "Pre-heating convectiomat"),
|
||||||
Bar::new(128f64, 0.0078125, "Untangling 'block chain'"),
|
Bar::new(64f64, 0.015625, "Resetting chair heights"),
|
||||||
Bar::new(256f64, 0.00390625, "Refilling sticker box"),
|
Bar::new(128f64, 0.0078125, "Untangling 'block chain'"),
|
||||||
Bar::new(512f64, 0.001953125, "Setting room to public"),
|
Bar::new(256f64, 0.00390625, "Refilling sticker box"),
|
||||||
Bar::new(1024f64, 0.000976562, "Welcoming creatures"),
|
Bar::new(512f64, 0.001953125, "Setting room to public"),
|
||||||
],
|
Bar::new(1024f64, 0.000976562, "Welcoming creatures"),
|
||||||
|
],
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn progress(&mut self, delta: Duration) {
|
pub(crate) fn progress(&mut self, delta: Duration) {
|
||||||
let adjusted_delta = delta.mul_f64(self.global_speed);
|
let adjusted_delta = delta.mul_f64(self.state.speed);
|
||||||
|
|
||||||
self.currency += self.global_productivity
|
let extra_currency = self.state.productivity
|
||||||
* self
|
* self
|
||||||
|
.state
|
||||||
.bars
|
.bars
|
||||||
.iter_mut()
|
.iter_mut()
|
||||||
.map(|bar| bar.progress(adjusted_delta))
|
.map(|bar| bar.progress(adjusted_delta))
|
||||||
.sum::<Currency>();
|
.sum::<Currency>();
|
||||||
|
self.state.currency += extra_currency;
|
||||||
if let Some(next_upgrade) = self.unlock_queue.front() {
|
self.total_currency += extra_currency;
|
||||||
if next_upgrade.cost <= self.currency {
|
self.unlocks.check_next(&mut self.state);
|
||||||
log::info!("Applying upgrade {:?}", next_upgrade);
|
|
||||||
let next_upgrade = self.unlock_queue.pop_front().unwrap();
|
|
||||||
self.currency -= next_upgrade.cost;
|
|
||||||
(next_upgrade.apply)(self);
|
|
||||||
self.unlocks += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn draw(
|
pub(crate) fn draw(
|
||||||
|
@ -74,10 +71,10 @@ impl Game {
|
||||||
text_layer
|
text_layer
|
||||||
.window_mut(middle, 0, middle, 1)
|
.window_mut(middle, 0, middle, 1)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.set_row_str(0, &format!(" Completions: {}", self.currency.floor()))
|
.set_row_str(0, &format!(" Hacks: {}", self.state.currency.floor()))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
for (index, bar) in self.bars.iter().enumerate() {
|
for (index, bar) in self.state.bars.iter().enumerate() {
|
||||||
let row = 2 + index;
|
let row = 2 + index;
|
||||||
let mut bar_window = pixel_layer
|
let mut bar_window = pixel_layer
|
||||||
.window_mut(0, row * TILE_SIZE, pixel_layer.width(), TILE_SIZE)
|
.window_mut(0, row * TILE_SIZE, pixel_layer.width(), TILE_SIZE)
|
||||||
|
@ -88,24 +85,21 @@ impl Game {
|
||||||
bar.draw(&mut label_window, &mut bar_window);
|
bar.draw(&mut label_window, &mut bar_window);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(next_upgrade) = self.unlock_queue.front() {
|
if let Some(next_upgrade) = self.unlocks.peek_next() {
|
||||||
text_layer
|
text_layer
|
||||||
.window_mut(0, text_layer.height() - 2, text_layer.width(), 1)
|
.window_mut(0, text_layer.height() - 2, text_layer.width(), 1)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.set_row_str(
|
.set_row_str(0, &format!("Next unlock for {} Hacks", next_upgrade.cost))
|
||||||
0,
|
|
||||||
&format!("Next unlock: {} {}", next_upgrade.cost, next_upgrade.name),
|
|
||||||
)
|
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
text_layer
|
text_layer
|
||||||
.window_mut(0, text_layer.height() - 1, text_layer.width() / 2, 1)
|
.window_mut(0, text_layer.height() - 1, text_layer.width() / 2, 1)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.set_row_str(0, &format!("Score: {}", self.total_currency))
|
.set_row_str(0, &format!("Hack Score: {:.2}", self.total_currency))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
if self.unlocks > 0 {
|
if self.unlocks.bought() > 0 {
|
||||||
text_layer
|
text_layer
|
||||||
.window_mut(
|
.window_mut(
|
||||||
text_layer.width() / 2,
|
text_layer.width() / 2,
|
||||||
|
@ -114,7 +108,7 @@ impl Game {
|
||||||
1,
|
1,
|
||||||
)
|
)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.set_row_str(0, &format!(" Unlocks: {}", self.unlocks))
|
.set_row_str(0, &format!(" Unlocks: {}", self.unlocks.bought()))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
13
src/main.rs
13
src/main.rs
|
@ -3,23 +3,14 @@ use servicepoint::{
|
||||||
BinaryOperation, BitVecCommand, Bitmap, CharGrid, CharGridCommand, ClearCommand,
|
BinaryOperation, BitVecCommand, Bitmap, CharGrid, CharGridCommand, ClearCommand,
|
||||||
CompressionCode, FRAME_PACING, TILE_HEIGHT, TILE_WIDTH, UdpSocketExt,
|
CompressionCode, FRAME_PACING, TILE_HEIGHT, TILE_WIDTH, UdpSocketExt,
|
||||||
};
|
};
|
||||||
use std::{
|
use std::{net::UdpSocket, thread::sleep, time::Instant};
|
||||||
net::UdpSocket,
|
|
||||||
thread::sleep,
|
|
||||||
time::{Duration, Instant},
|
|
||||||
};
|
|
||||||
|
|
||||||
mod bar;
|
mod bar;
|
||||||
mod game;
|
mod game;
|
||||||
mod upgrades;
|
mod unlocks;
|
||||||
|
|
||||||
type Currency = f64;
|
type Currency = f64;
|
||||||
|
|
||||||
trait Progressable: Sized {
|
|
||||||
#[must_use]
|
|
||||||
fn progress(&self, delta: Duration) -> (Self, Currency);
|
|
||||||
}
|
|
||||||
|
|
||||||
const DESTINATION: &str = "127.0.0.1:2342";
|
const DESTINATION: &str = "127.0.0.1:2342";
|
||||||
//const DESTINATION: &str = "172.23.42.29:2342";
|
//const DESTINATION: &str = "172.23.42.29:2342";
|
||||||
|
|
||||||
|
|
168
src/unlocks.rs
Normal file
168
src/unlocks.rs
Normal file
|
@ -0,0 +1,168 @@
|
||||||
|
use crate::Currency;
|
||||||
|
use crate::game::{Game, State};
|
||||||
|
use std::collections::VecDeque;
|
||||||
|
use std::fmt::{Debug, Formatter};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub(crate) struct UnlockSystem {
|
||||||
|
bought: usize,
|
||||||
|
unlock_queue: VecDeque<Unlock>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) struct Unlock {
|
||||||
|
pub(crate) name: &'static str,
|
||||||
|
pub(crate) cost: Currency,
|
||||||
|
pub(crate) apply: fn(&mut State),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Debug for Unlock {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.write_fmt(format_args!(
|
||||||
|
"Upgrade {{ name: {:?}, cost: {:?} }}",
|
||||||
|
self.name, self.cost
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UnlockSystem {
|
||||||
|
pub(crate) fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
bought: 0,
|
||||||
|
unlock_queue: vec![
|
||||||
|
Unlock {
|
||||||
|
name: "Start Powering infrastructure",
|
||||||
|
cost: 0f64,
|
||||||
|
apply: |game| {
|
||||||
|
game.bars[0].enabled = true;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Unlock {
|
||||||
|
name: "More power",
|
||||||
|
cost: 10f64,
|
||||||
|
apply: |game| {
|
||||||
|
game.bars[0].productivity *= 2.0;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Unlock {
|
||||||
|
name: "",
|
||||||
|
cost: 23f64,
|
||||||
|
apply: |game| {
|
||||||
|
game.productivity *= 1.1;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Unlock {
|
||||||
|
name: "The answer",
|
||||||
|
cost: 42f64,
|
||||||
|
apply: |game| {
|
||||||
|
game.speed *= 1.1;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Unlock {
|
||||||
|
name: "Start Dusting ServicePoint",
|
||||||
|
cost: 64f64,
|
||||||
|
apply: |game| {
|
||||||
|
game.bars[1].enabled = true;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Unlock {
|
||||||
|
name: "Dust filters",
|
||||||
|
cost: 100f64,
|
||||||
|
apply: |game| {
|
||||||
|
game.bars[1].productivity *= 2.0;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Unlock {
|
||||||
|
name: "HASS automation",
|
||||||
|
cost: 128f64,
|
||||||
|
apply: |game| {
|
||||||
|
game.bars[0].speed *= 1.5;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Unlock {
|
||||||
|
name: "Start Activating colorful lights",
|
||||||
|
cost: 256f64,
|
||||||
|
apply: |game| {
|
||||||
|
game.bars[2].enabled = true;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Unlock {
|
||||||
|
name: "Start Dimming darkroom",
|
||||||
|
cost: 1024f64,
|
||||||
|
apply: |game| {
|
||||||
|
game.bars[3].enabled = true;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Unlock {
|
||||||
|
name: "Start Refilling Matemat",
|
||||||
|
cost: 4096f64,
|
||||||
|
apply: |game| {
|
||||||
|
game.bars[4].enabled = true;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Unlock {
|
||||||
|
name: "Start Pre-heating convectiomat",
|
||||||
|
cost: 16384f64,
|
||||||
|
apply: |game| {
|
||||||
|
game.bars[5].enabled = true;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Unlock {
|
||||||
|
name: "Start Resetting chair heights",
|
||||||
|
cost: 65536f64,
|
||||||
|
apply: |game| {
|
||||||
|
game.bars[6].enabled = true;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Unlock {
|
||||||
|
name: "Start Untangling 'block chain'",
|
||||||
|
cost: 262144f64,
|
||||||
|
apply: |game| {
|
||||||
|
game.bars[7].enabled = true;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Unlock {
|
||||||
|
name: "Start Refilling sticker box",
|
||||||
|
cost: 1048576f64,
|
||||||
|
apply: |game| {
|
||||||
|
game.bars[8].enabled = true;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Unlock {
|
||||||
|
name: "Start Setting room to public",
|
||||||
|
cost: 4194304f64,
|
||||||
|
apply: |game| {
|
||||||
|
game.bars[9].enabled = true;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Unlock {
|
||||||
|
name: "Start Welcoming creatures",
|
||||||
|
cost: 16777216f64,
|
||||||
|
apply: |game| {
|
||||||
|
game.bars[10].enabled = true;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn peek_next(&self) -> Option<&Unlock> {
|
||||||
|
self.unlock_queue.front()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn check_next(&mut self, state: &mut State) {
|
||||||
|
if let Some(next_upgrade) = self.peek_next() {
|
||||||
|
if next_upgrade.cost <= state.currency {
|
||||||
|
log::info!("Applying upgrade {:?}", next_upgrade);
|
||||||
|
let next_upgrade = self.unlock_queue.pop_front().unwrap();
|
||||||
|
state.currency -= next_upgrade.cost;
|
||||||
|
(next_upgrade.apply)(state);
|
||||||
|
self.bought += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn bought(&self) -> usize {
|
||||||
|
self.bought
|
||||||
|
}
|
||||||
|
}
|
123
src/upgrades.rs
123
src/upgrades.rs
|
@ -1,123 +0,0 @@
|
||||||
use crate::Currency;
|
|
||||||
use crate::game::Game;
|
|
||||||
use std::collections::VecDeque;
|
|
||||||
use std::fmt::{Debug, Formatter, Write};
|
|
||||||
|
|
||||||
pub(crate) struct Upgrade {
|
|
||||||
pub(crate) name: &'static str,
|
|
||||||
pub(crate) cost: Currency,
|
|
||||||
pub(crate) apply: Box<dyn Fn(&mut Game)>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Debug for Upgrade {
|
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
|
||||||
f.write_fmt(format_args!(
|
|
||||||
"Upgrade {{ name: {:?}, cost: {:?} }}",
|
|
||||||
self.name, self.cost
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn get_upgrades() -> VecDeque<Upgrade> {
|
|
||||||
vec![
|
|
||||||
Upgrade {
|
|
||||||
name: "Start Powering infrastructure",
|
|
||||||
cost: 0f64,
|
|
||||||
apply: Box::new(|game| {
|
|
||||||
game.bars[0].enabled = true;
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
Upgrade {
|
|
||||||
name: "More power",
|
|
||||||
cost: 10f64,
|
|
||||||
apply: Box::new(|game| {
|
|
||||||
game.bars[0].productivity *= 2.0;
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
Upgrade {
|
|
||||||
name: "",
|
|
||||||
cost: 23f64,
|
|
||||||
apply: Box::new(|game| {
|
|
||||||
game.global_productivity *= 1.1;
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
Upgrade {
|
|
||||||
name: "The answer",
|
|
||||||
cost: 42f64,
|
|
||||||
apply: Box::new(|game| {
|
|
||||||
game.global_speed *= 1.1;
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
Upgrade {
|
|
||||||
name: "Start Dusting ServicePoint",
|
|
||||||
cost: 64f64,
|
|
||||||
apply: Box::new(|game| {
|
|
||||||
game.bars[1].enabled = true;
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
Upgrade {
|
|
||||||
name: "Start Activating colorful lights",
|
|
||||||
cost: 256f64,
|
|
||||||
apply: Box::new(|game| {
|
|
||||||
game.bars[2].enabled = true;
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
Upgrade {
|
|
||||||
name: "Start Dimming darkroom",
|
|
||||||
cost: 1024f64,
|
|
||||||
apply: Box::new(|game| {
|
|
||||||
game.bars[3].enabled = true;
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
Upgrade {
|
|
||||||
name: "Start Refilling Matemat",
|
|
||||||
cost: 4096f64,
|
|
||||||
apply: Box::new(|game| {
|
|
||||||
game.bars[4].enabled = true;
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
Upgrade {
|
|
||||||
name: "Start Pre-heating convectiomat",
|
|
||||||
cost: 16384f64,
|
|
||||||
apply: Box::new(|game| {
|
|
||||||
game.bars[5].enabled = true;
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
Upgrade {
|
|
||||||
name: "Start Resetting chair heights",
|
|
||||||
cost: 65536f64,
|
|
||||||
apply: Box::new(|game| {
|
|
||||||
game.bars[6].enabled = true;
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
Upgrade {
|
|
||||||
name: "Start Untangling 'block chain'",
|
|
||||||
cost: 262144f64,
|
|
||||||
apply: Box::new(|game| {
|
|
||||||
game.bars[7].enabled = true;
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
Upgrade {
|
|
||||||
name: "Start Refilling sticker box",
|
|
||||||
cost: 1048576f64,
|
|
||||||
apply: Box::new(|game| {
|
|
||||||
game.bars[8].enabled = true;
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
Upgrade {
|
|
||||||
name: "Start Setting room to public",
|
|
||||||
cost: 4194304f64,
|
|
||||||
apply: Box::new(|game| {
|
|
||||||
game.bars[9].enabled = true;
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
Upgrade {
|
|
||||||
name: "Start Welcoming creatures",
|
|
||||||
cost: 16777216f64,
|
|
||||||
apply: Box::new(|game| {
|
|
||||||
game.bars[10].enabled = true;
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
]
|
|
||||||
.into()
|
|
||||||
}
|
|
Loading…
Reference in a new issue