Add star mob.
This commit is contained in:
parent
ce783a3609
commit
d62e585d9f
10 changed files with 213 additions and 67 deletions
|
@ -11,3 +11,4 @@ piston2d-sprite = "0.36.0"
|
||||||
piston2d-graphics = "0.21.1"
|
piston2d-graphics = "0.21.1"
|
||||||
find_folder = "0.3.0"
|
find_folder = "0.3.0"
|
||||||
uuid = "0.1.17"
|
uuid = "0.1.17"
|
||||||
|
rand = "0.3"
|
||||||
|
|
BIN
assets/soot.png
BIN
assets/soot.png
Binary file not shown.
Before Width: | Height: | Size: 606 B After Width: | Height: | Size: 3 KiB |
BIN
assets/soot.xcf
BIN
assets/soot.xcf
Binary file not shown.
BIN
assets/star.png
Normal file
BIN
assets/star.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.5 KiB |
80
assets/star.svg
Normal file
80
assets/star.svg
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
width="640"
|
||||||
|
height="640"
|
||||||
|
viewBox="0 0 169.33334 169.33334"
|
||||||
|
version="1.1"
|
||||||
|
id="svg8"
|
||||||
|
inkscape:version="0.92.1 r15371"
|
||||||
|
sodipodi:docname="star_template.svg"
|
||||||
|
inkscape:export-filename="/home/cmdln/src/git/susuwatari-game/assets/star.png"
|
||||||
|
inkscape:export-xdpi="9.6000004"
|
||||||
|
inkscape:export-ydpi="9.6000004">
|
||||||
|
<defs
|
||||||
|
id="defs2" />
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="base"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1.0"
|
||||||
|
inkscape:pageopacity="0.0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:zoom="0.35"
|
||||||
|
inkscape:cx="184.19191"
|
||||||
|
inkscape:cy="182.82758"
|
||||||
|
inkscape:document-units="mm"
|
||||||
|
inkscape:current-layer="layer1"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:window-width="1920"
|
||||||
|
inkscape:window-height="1025"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="0"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
units="px" />
|
||||||
|
<metadata
|
||||||
|
id="metadata5">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
<dc:title></dc:title>
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<g
|
||||||
|
inkscape:label="Layer 1"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer1"
|
||||||
|
transform="translate(-13.253985,-58.111215)">
|
||||||
|
<path
|
||||||
|
sodipodi:type="star"
|
||||||
|
style="opacity:0.75499998;fill:#ffaacc;fill-opacity:1;stroke:none;stroke-width:0.60730559;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
id="path8417"
|
||||||
|
sodipodi:sides="5"
|
||||||
|
sodipodi:cx="99.326875"
|
||||||
|
sodipodi:cy="153.95569"
|
||||||
|
sodipodi:r1="86.412148"
|
||||||
|
sodipodi:r2="51.019348"
|
||||||
|
sodipodi:arg1="0.93264992"
|
||||||
|
sodipodi:arg2="1.5609685"
|
||||||
|
inkscape:flatsided="false"
|
||||||
|
inkscape:rounded="0"
|
||||||
|
inkscape:randomized="0"
|
||||||
|
d="m 150.80325,223.36204 -50.974975,-18.38947 -50.603689,19.38781 1.737289,-54.16275 -34.076296,-42.1358 52.04868,-15.08495 29.543381,-45.429166 30.43056,44.839736 52.33511,14.05904 -33.24155,42.79742 z"
|
||||||
|
inkscape:transform-center-x="0.2624264"
|
||||||
|
inkscape:transform-center-y="-8.0016296"
|
||||||
|
inkscape:export-xdpi="96"
|
||||||
|
inkscape:export-ydpi="96" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2.7 KiB |
69
src/lib.rs
69
src/lib.rs
|
@ -3,6 +3,7 @@ extern crate find_folder;
|
||||||
extern crate gfx_device_gl;
|
extern crate gfx_device_gl;
|
||||||
extern crate graphics;
|
extern crate graphics;
|
||||||
extern crate piston_window;
|
extern crate piston_window;
|
||||||
|
extern crate rand;
|
||||||
extern crate sprite;
|
extern crate sprite;
|
||||||
extern crate uuid;
|
extern crate uuid;
|
||||||
|
|
||||||
|
@ -12,50 +13,33 @@ use gfx_device_gl::Resources;
|
||||||
use graphics::Image;
|
use graphics::Image;
|
||||||
use graphics::rectangle::square;
|
use graphics::rectangle::square;
|
||||||
use piston_window::*;
|
use piston_window::*;
|
||||||
use mobs::Hero;
|
use mobs::{Hero, Star};
|
||||||
use sprite::*;
|
use sprite::*;
|
||||||
|
|
||||||
// TODO move this into the player object
|
|
||||||
const MV_FACT: f64 = 2500.0;
|
|
||||||
|
|
||||||
pub struct Game {
|
pub struct Game {
|
||||||
scene: Scene<Texture<Resources>>,
|
scene: Scene<Texture<Resources>>,
|
||||||
player: Hero,
|
player: Hero,
|
||||||
up_d: bool,
|
star: Star,
|
||||||
down_d: bool,
|
|
||||||
left_d: bool,
|
|
||||||
right_d: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Game {
|
impl Game {
|
||||||
pub fn new(w: &mut PistonWindow) -> Game {
|
pub fn new(w: &mut PistonWindow) -> Game {
|
||||||
let mut scene = Scene::new();
|
let mut scene = Scene::new();
|
||||||
|
let star = Star::new(w, &mut scene);
|
||||||
let player = Hero::new(w, &mut scene);
|
let player = Hero::new(w, &mut scene);
|
||||||
Game {
|
Game {
|
||||||
scene: scene,
|
scene: scene,
|
||||||
player: player,
|
player: player,
|
||||||
up_d: false,
|
star: star,
|
||||||
down_d: false,
|
|
||||||
left_d: false,
|
|
||||||
right_d: false,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn on_update(&mut self, e: &Input, upd: UpdateArgs) {
|
pub fn on_update(&mut self, e: &Input, upd: UpdateArgs, w: &PistonWindow) {
|
||||||
self.scene.event(e);
|
self.scene.event(e);
|
||||||
|
|
||||||
if self.up_d {
|
self.star.mov(w, &mut self.scene, upd.dt);
|
||||||
self.player.mov(&mut self.scene, 0.0, -MV_FACT * upd.dt);
|
|
||||||
}
|
self.player.mov(w, &mut self.scene, upd.dt);
|
||||||
if self.down_d {
|
|
||||||
self.player.mov(&mut self.scene, 0.0, MV_FACT * upd.dt);
|
|
||||||
}
|
|
||||||
if self.left_d {
|
|
||||||
self.player.mov(&mut self.scene, -MV_FACT * upd.dt, 0.0);
|
|
||||||
}
|
|
||||||
if self.right_d {
|
|
||||||
self.player.mov(&mut self.scene, MV_FACT * upd.dt, 0.0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn on_draw(&mut self, e: &Input, _: RenderArgs, w: &mut PistonWindow) {
|
pub fn on_draw(&mut self, e: &Input, _: RenderArgs, w: &mut PistonWindow) {
|
||||||
|
@ -72,23 +56,26 @@ impl Game {
|
||||||
Flip::None,
|
Flip::None,
|
||||||
&TextureSettings::new()).unwrap();
|
&TextureSettings::new()).unwrap();
|
||||||
w.draw_2d(e, |c, g| {
|
w.draw_2d(e, |c, g| {
|
||||||
let transform = c.transform.trans(10.0, 100.0);
|
|
||||||
|
|
||||||
clear([1.0, 1.0, 1.0, 1.0], g);
|
clear([1.0, 1.0, 1.0, 1.0], g);
|
||||||
for number in 0..100 {
|
for number in 0..100 {
|
||||||
let x: f64 = (number % 10 * 64).into();
|
let x: f64 = (number % 10 * 64).into();
|
||||||
let y: f64 = (number / 10 * 64).into();
|
let y: f64 = (number / 10 * 64).into();
|
||||||
image.draw(&bg, &Default::default(), c.transform.trans(x, y).zoom(0.1), g);
|
image.draw(&bg, &Default::default(), c.transform.trans(x, y).zoom(0.1), g);
|
||||||
}
|
}
|
||||||
if let Some(player_sprite) = self.scene.child(self.player.sprite_id) {
|
|
||||||
let (x, y) = player_sprite.get_position();
|
text::Text::new_color([0.0, 1.0, 0.0, 1.0], 10).draw(
|
||||||
text::Text::new_color([0.0, 1.0, 0.0, 1.0], 32).draw(
|
&format!("{} x: {}, y: {}", self.player.sprite_id, self.player.x.trunc(), self.player.y.trunc()),
|
||||||
&format!("x: {}, y: {}", x.trunc(), y.trunc()),
|
|
||||||
&mut glyphs,
|
&mut glyphs,
|
||||||
&c.draw_state,
|
&c.draw_state,
|
||||||
transform, g
|
c.transform.trans(10.0, 100.0), g
|
||||||
|
);
|
||||||
|
|
||||||
|
text::Text::new_color([0.0, 1.0, 0.0, 1.0], 10).draw(
|
||||||
|
&format!("{} x: {}, y: {}", self.star.sprite_id, self.star.x.trunc(), self.star.y.trunc()),
|
||||||
|
&mut glyphs,
|
||||||
|
&c.draw_state,
|
||||||
|
c.transform.trans(10.0, 110.0), g
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
self.scene.draw(c.transform, g);
|
self.scene.draw(c.transform, g);
|
||||||
});
|
});
|
||||||
|
@ -100,16 +87,16 @@ impl Game {
|
||||||
Input::Press(but) => {
|
Input::Press(but) => {
|
||||||
match but {
|
match but {
|
||||||
Button::Keyboard(Key::Up) => {
|
Button::Keyboard(Key::Up) => {
|
||||||
self.up_d = true;
|
self.player.dir = (self.player.dir.0, -1.0);
|
||||||
}
|
}
|
||||||
Button::Keyboard(Key::Down) => {
|
Button::Keyboard(Key::Down) => {
|
||||||
self.down_d = true;
|
self.player.dir = (self.player.dir.0, 1.0);
|
||||||
}
|
}
|
||||||
Button::Keyboard(Key::Left) => {
|
Button::Keyboard(Key::Left) => {
|
||||||
self.left_d = true;
|
self.player.dir = (-1.0, self.player.dir.1);
|
||||||
}
|
}
|
||||||
Button::Keyboard(Key::Right) => {
|
Button::Keyboard(Key::Right) => {
|
||||||
self.right_d = true;
|
self.player.dir = (1.0, self.player.dir.1);
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
@ -117,16 +104,16 @@ impl Game {
|
||||||
Input::Release(but) => {
|
Input::Release(but) => {
|
||||||
match but {
|
match but {
|
||||||
Button::Keyboard(Key::Up) => {
|
Button::Keyboard(Key::Up) => {
|
||||||
self.up_d = false;
|
self.player.dir = (self.player.dir.0, 0.0);
|
||||||
}
|
}
|
||||||
Button::Keyboard(Key::Down) => {
|
Button::Keyboard(Key::Down) => {
|
||||||
self.down_d = false;
|
self.player.dir = (self.player.dir.0, 0.0);
|
||||||
}
|
}
|
||||||
Button::Keyboard(Key::Left) => {
|
Button::Keyboard(Key::Left) => {
|
||||||
self.left_d = false;
|
self.player.dir = (0.0, self.player.dir.1);
|
||||||
}
|
}
|
||||||
Button::Keyboard(Key::Right) => {
|
Button::Keyboard(Key::Right) => {
|
||||||
self.right_d = false;
|
self.player.dir = (0.0, self.player.dir.1);
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,12 +6,13 @@ use piston_window::*;
|
||||||
fn main() {
|
fn main() {
|
||||||
let mut window: PistonWindow = WindowSettings::new("Ghostly Adventure", [640, 480])
|
let mut window: PistonWindow = WindowSettings::new("Ghostly Adventure", [640, 480])
|
||||||
.exit_on_esc(true)
|
.exit_on_esc(true)
|
||||||
|
.resizable(false)
|
||||||
.build()
|
.build()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let mut game = ghostly::Game::new(&mut window);
|
let mut game = ghostly::Game::new(&mut window);
|
||||||
while let Some(e) = window.next() {
|
while let Some(e) = window.next() {
|
||||||
match e {
|
match e {
|
||||||
Input::Update(upd) => game.on_update(&e, upd),
|
Input::Update(upd) => game.on_update(&e, upd, &window),
|
||||||
Input::Render(ren) => game.on_draw(&e, ren, &mut window),
|
Input::Render(ren) => game.on_draw(&e, ren, &mut window),
|
||||||
_ => game.on_input(e),
|
_ => game.on_input(e),
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,13 +5,15 @@ use piston_window::*;
|
||||||
use sprite::*;
|
use sprite::*;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
use mobs::wrap;
|
||||||
|
|
||||||
|
|
||||||
// TODO add size of player sprite for boundary checking
|
|
||||||
pub struct Hero {
|
pub struct Hero {
|
||||||
pub sprite_id: Uuid,
|
pub sprite_id: Uuid,
|
||||||
|
pub dir: (f64, f64),
|
||||||
pub x: f64,
|
pub x: f64,
|
||||||
pub y: f64,
|
pub y: f64,
|
||||||
|
w: f64,
|
||||||
|
h: f64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Hero {
|
impl Hero {
|
||||||
|
@ -26,43 +28,34 @@ impl Hero {
|
||||||
.unwrap());
|
.unwrap());
|
||||||
let mut sprite = Sprite::from_texture(tex);
|
let mut sprite = Sprite::from_texture(tex);
|
||||||
sprite.set_position(320.0, 240.0);
|
sprite.set_position(320.0, 240.0);
|
||||||
|
let bounds = sprite.bounding_box();
|
||||||
let sprite_id = scene.add_child(sprite);
|
let sprite_id = scene.add_child(sprite);
|
||||||
Hero {
|
Hero {
|
||||||
x: 320.0,
|
x: 320.0,
|
||||||
y: 240.0,
|
y: 240.0,
|
||||||
|
w: bounds[2],
|
||||||
|
h: bounds[3],
|
||||||
|
dir: (0.0, 0.0),
|
||||||
sprite_id: sprite_id,
|
sprite_id: sprite_id,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn mov(&mut self, scene: &mut Scene<Texture<Resources>>, input_x: f64, input_y: f64) {
|
pub fn mov(&mut self, w: &PistonWindow, scene: &mut Scene<Texture<Resources>>, dt: f64) {
|
||||||
if let Some(sprite) = scene.child(self.sprite_id) {
|
if let Some(sprite) = scene.child(self.sprite_id) {
|
||||||
let (sprite_x, sprite_y) = sprite.get_position();
|
let (sprite_x, sprite_y) = sprite.get_position();
|
||||||
self.x = sprite_x;
|
self.x = sprite_x;
|
||||||
self.y = sprite_y;
|
self.y = sprite_y;
|
||||||
}
|
}
|
||||||
let mut wrapped = false;
|
let (wrapped, new_x, new_y) = wrap((w.size().width.into(), w.size().height.into()), (self.w, self.h), (self.x, self.y));
|
||||||
// TODO pass in window size
|
|
||||||
if self.x > 640.0 + 32.0 {
|
|
||||||
self.x = -32.0;
|
|
||||||
wrapped = true;
|
|
||||||
} else if self.x < -32.0 {
|
|
||||||
self.x = 640.0 + 32.0;
|
|
||||||
wrapped = true;
|
|
||||||
}
|
|
||||||
if self.y > 480.0 + 32.0 {
|
|
||||||
self.y = -32.0;
|
|
||||||
wrapped = true;
|
|
||||||
} else if self.y < -32.0 {
|
|
||||||
self.y = 480.0 + 32.0;
|
|
||||||
wrapped = true;
|
|
||||||
}
|
|
||||||
self.y += input_y;
|
|
||||||
if wrapped {
|
if wrapped {
|
||||||
scene.stop_all(self.sprite_id);
|
self.x = new_x;
|
||||||
|
self.y = new_y;
|
||||||
if let Some(ref mut sprite) = scene.child_mut(self.sprite_id) {
|
if let Some(ref mut sprite) = scene.child_mut(self.sprite_id) {
|
||||||
sprite.set_position(self.x, self.y);
|
sprite.set_position(self.x, self.y);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
scene.run(self.sprite_id, &Action(Ease(EaseFunction::CubicOut, Box::new(MoveBy(1.0, input_x, input_y)))));
|
let mov_x = self.dir.0 * 2.0;
|
||||||
|
let mov_y = self.dir.1 * 2.0;
|
||||||
|
scene.run(self.sprite_id, &Action(Ease(EaseFunction::CubicOut, Box::new(MoveBy(dt * 0.75, mov_x, mov_y)))));
|
||||||
}
|
}
|
||||||
}
|
}
|
22
src/mobs/mod.rs
Normal file
22
src/mobs/mod.rs
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
pub use self::hero::Hero;
|
||||||
|
pub use self::star::Star;
|
||||||
|
|
||||||
|
mod hero;
|
||||||
|
mod star;
|
||||||
|
|
||||||
|
// TODO use window::size
|
||||||
|
pub fn wrap(win: (f64, f64), bounds: (f64, f64), pos: (f64, f64)) -> (bool, f64, f64) {
|
||||||
|
let new_x = wrap_dim(win.0, bounds.0, pos.0);
|
||||||
|
let new_y = wrap_dim(win.1, bounds.1, pos.1);
|
||||||
|
(new_x != pos.0 || new_y != pos.1, new_x, new_y)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn wrap_dim(win_dim: f64, sprite_dim: f64, pos_dim: f64) -> f64 {
|
||||||
|
if pos_dim > win_dim + sprite_dim / 2.0 {
|
||||||
|
-sprite_dim / 2.0
|
||||||
|
} else if pos_dim < -sprite_dim / 2.0 {
|
||||||
|
win_dim + sprite_dim / 2.0
|
||||||
|
} else {
|
||||||
|
pos_dim
|
||||||
|
}
|
||||||
|
}
|
62
src/mobs/star.rs
Normal file
62
src/mobs/star.rs
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
use ai_behavior::Action;
|
||||||
|
use find_folder;
|
||||||
|
use gfx_device_gl::Resources;
|
||||||
|
use piston_window::*;
|
||||||
|
use sprite::*;
|
||||||
|
use uuid::Uuid;
|
||||||
|
use rand;
|
||||||
|
use std::rc::Rc;
|
||||||
|
use super::wrap;
|
||||||
|
|
||||||
|
|
||||||
|
pub struct Star {
|
||||||
|
pub sprite_id: Uuid,
|
||||||
|
pub x: f64,
|
||||||
|
pub y: f64,
|
||||||
|
w: f64,
|
||||||
|
h: f64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Star {
|
||||||
|
pub fn new(w: &mut PistonWindow, scene: &mut Scene<Texture<Resources>>) -> Star {
|
||||||
|
let assets = find_folder::Search::ParentsThenKids(3, 3)
|
||||||
|
.for_folder("assets")
|
||||||
|
.unwrap();
|
||||||
|
let tex = Rc::new(Texture::from_path(&mut w.factory,
|
||||||
|
assets.join("star.png"),
|
||||||
|
Flip::None,
|
||||||
|
&TextureSettings::new())
|
||||||
|
.unwrap());
|
||||||
|
let mut sprite = Sprite::from_texture(tex);
|
||||||
|
sprite.set_scale(0.5, 0.5);
|
||||||
|
sprite.set_position(30.0, 32.0);
|
||||||
|
let bounds = sprite.bounding_box();
|
||||||
|
let sprite_id = scene.add_child(sprite);
|
||||||
|
Star {
|
||||||
|
x: 32.0,
|
||||||
|
y: 32.0,
|
||||||
|
w: bounds[2],
|
||||||
|
h: bounds[3],
|
||||||
|
sprite_id: sprite_id,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mov(&mut self, w: &PistonWindow, scene: &mut Scene<Texture<Resources>>, dt: f64) {
|
||||||
|
if let Some(sprite) = scene.child(self.sprite_id) {
|
||||||
|
let (sprite_x, sprite_y) = sprite.get_position();
|
||||||
|
self.x = sprite_x;
|
||||||
|
self.y = sprite_y;
|
||||||
|
}
|
||||||
|
let (wrapped, new_x, new_y) = wrap((w.size().width.into(), w.size().height.into()), (self.w, self.h), (self.x, self.y));
|
||||||
|
if wrapped {
|
||||||
|
if let Some(ref mut sprite) = scene.child_mut(self.sprite_id) {
|
||||||
|
self.x = new_x;
|
||||||
|
self.y = new_y;
|
||||||
|
sprite.set_position(self.x, self.y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let mov_x = rand::random::<f64>() * 0.5;
|
||||||
|
let mov_y = rand::random::<f64>() * 0.5;
|
||||||
|
scene.run(self.sprite_id, &Action(Ease(EaseFunction::CubicOut, Box::new(MoveBy(dt * 0.75, mov_x, mov_y)))));
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue