mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2024-12-26 11:48:55 +01:00
parent
fc995a47ee
commit
63677e0e50
@ -21,6 +21,7 @@ package main
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"image/color"
|
"image/color"
|
||||||
|
"log"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -286,7 +287,9 @@ func init() {
|
|||||||
auto.init()
|
auto.init()
|
||||||
}
|
}
|
||||||
|
|
||||||
func update(screen *ebiten.Image) error {
|
type Game struct{}
|
||||||
|
|
||||||
|
func (g *Game) Update(screen *ebiten.Image) error {
|
||||||
reset := false
|
reset := false
|
||||||
|
|
||||||
if inpututil.IsKeyJustPressed(ebiten.KeyB) {
|
if inpututil.IsKeyJustPressed(ebiten.KeyB) {
|
||||||
@ -310,16 +313,10 @@ func update(screen *ebiten.Image) error {
|
|||||||
|
|
||||||
auto.step()
|
auto.step()
|
||||||
|
|
||||||
if ebiten.IsDrawingSkipped() {
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
draw(screen)
|
func (g *Game) Draw(screen *ebiten.Image) {
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func draw(screen *ebiten.Image) {
|
|
||||||
screen.DrawImage(canvas, nil)
|
screen.DrawImage(canvas, nil)
|
||||||
ebitenutil.DebugPrintAt(
|
ebitenutil.DebugPrintAt(
|
||||||
screen,
|
screen,
|
||||||
@ -343,9 +340,15 @@ func draw(screen *ebiten.Image) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (g *Game) Layout(outsideWidth, outsideHeight int) (int, int) {
|
||||||
|
return width, height
|
||||||
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
ebiten.SetMaxTPS(250)
|
ebiten.SetMaxTPS(250)
|
||||||
if err := ebiten.Run(update, width, height, scale, "Squirals (Ebiten Demo)"); err != nil {
|
ebiten.SetWindowSize(width*scale, height*scale)
|
||||||
panic(err)
|
ebiten.SetWindowTitle("Squirals (Ebiten Demo)")
|
||||||
|
if err := ebiten.RunGame(&Game{}); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -59,8 +59,41 @@ func init() {
|
|||||||
tilesImage, _ = ebiten.NewImageFromImage(img, ebiten.FilterDefault)
|
tilesImage, _ = ebiten.NewImageFromImage(img, ebiten.FilterDefault)
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
type Game struct {
|
||||||
layers = [][]int{
|
layers [][]int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *Game) Update(screen *ebiten.Image) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *Game) Draw(screen *ebiten.Image) {
|
||||||
|
// Draw each tile with each DrawImage call.
|
||||||
|
// As the source images of all DrawImage calls are always same,
|
||||||
|
// this rendering is done very effectively.
|
||||||
|
// For more detail, see https://pkg.go.dev/github.com/hajimehoshi/ebiten#Image.DrawImage
|
||||||
|
const xNum = screenWidth / tileSize
|
||||||
|
for _, l := range g.layers {
|
||||||
|
for i, t := range l {
|
||||||
|
op := &ebiten.DrawImageOptions{}
|
||||||
|
op.GeoM.Translate(float64((i%xNum)*tileSize), float64((i/xNum)*tileSize))
|
||||||
|
|
||||||
|
sx := (t % tileXNum) * tileSize
|
||||||
|
sy := (t / tileXNum) * tileSize
|
||||||
|
screen.DrawImage(tilesImage.SubImage(image.Rect(sx, sy, sx+tileSize, sy+tileSize)).(*ebiten.Image), op)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ebitenutil.DebugPrint(screen, fmt.Sprintf("TPS: %0.2f", ebiten.CurrentTPS()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *Game) Layout(outsideWidth, outsideHeight int) (int, int) {
|
||||||
|
return screenWidth, screenHeight
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
g := &Game{
|
||||||
|
layers: [][]int{
|
||||||
{
|
{
|
||||||
243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243,
|
243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243,
|
||||||
243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243,
|
243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243,
|
||||||
@ -99,37 +132,12 @@ var (
|
|||||||
0, 0, 0, 0, 0, 0, 0, 245, 242, 0, 0, 0, 0, 0, 0,
|
0, 0, 0, 0, 0, 0, 0, 245, 242, 0, 0, 0, 0, 0, 0,
|
||||||
0, 0, 0, 0, 0, 0, 0, 245, 242, 0, 0, 0, 0, 0, 0,
|
0, 0, 0, 0, 0, 0, 0, 245, 242, 0, 0, 0, 0, 0, 0,
|
||||||
},
|
},
|
||||||
}
|
},
|
||||||
)
|
|
||||||
|
|
||||||
func update(screen *ebiten.Image) error {
|
|
||||||
if ebiten.IsDrawingSkipped() {
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw each tile with each DrawImage call.
|
ebiten.SetWindowSize(screenWidth*2, screenHeight*2)
|
||||||
// As the source images of all DrawImage calls are always same,
|
ebiten.SetWindowTitle("Tiles (Ebiten Demo)")
|
||||||
// this rendering is done very effectively.
|
if err := ebiten.RunGame(g); err != nil {
|
||||||
// For more detail, see https://pkg.go.dev/github.com/hajimehoshi/ebiten#Image.DrawImage
|
|
||||||
const xNum = screenWidth / tileSize
|
|
||||||
for _, l := range layers {
|
|
||||||
for i, t := range l {
|
|
||||||
op := &ebiten.DrawImageOptions{}
|
|
||||||
op.GeoM.Translate(float64((i%xNum)*tileSize), float64((i/xNum)*tileSize))
|
|
||||||
|
|
||||||
sx := (t % tileXNum) * tileSize
|
|
||||||
sy := (t / tileXNum) * tileSize
|
|
||||||
screen.DrawImage(tilesImage.SubImage(image.Rect(sx, sy, sx+tileSize, sy+tileSize)).(*ebiten.Image), op)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ebitenutil.DebugPrint(screen, fmt.Sprintf("TPS: %0.2f", ebiten.CurrentTPS()))
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
if err := ebiten.Run(update, screenWidth, screenHeight, 2, "Tiles (Ebiten Demo)"); err != nil {
|
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,9 +25,9 @@ import (
|
|||||||
"github.com/hajimehoshi/ebiten/inpututil"
|
"github.com/hajimehoshi/ebiten/inpututil"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
const (
|
||||||
text = "Type on the keyboard:\n"
|
screenWidth = 320
|
||||||
counter = 0
|
screenHeight = 240
|
||||||
)
|
)
|
||||||
|
|
||||||
// repeatingKeyPressed return true when key is pressed considering the repeat state.
|
// repeatingKeyPressed return true when key is pressed considering the repeat state.
|
||||||
@ -46,47 +46,61 @@ func repeatingKeyPressed(key ebiten.Key) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func update(screen *ebiten.Image) error {
|
type Game struct {
|
||||||
|
text string
|
||||||
|
counter int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *Game) Update(screen *ebiten.Image) error {
|
||||||
// Add a string from InputChars, that returns string input by users.
|
// Add a string from InputChars, that returns string input by users.
|
||||||
// Note that InputChars result changes every frame, so you need to call this
|
// Note that InputChars result changes every frame, so you need to call this
|
||||||
// every frame.
|
// every frame.
|
||||||
text += string(ebiten.InputChars())
|
g.text += string(ebiten.InputChars())
|
||||||
|
|
||||||
// Adjust the string to be at most 10 lines.
|
// Adjust the string to be at most 10 lines.
|
||||||
ss := strings.Split(text, "\n")
|
ss := strings.Split(g.text, "\n")
|
||||||
if len(ss) > 10 {
|
if len(ss) > 10 {
|
||||||
text = strings.Join(ss[len(ss)-10:], "\n")
|
g.text = strings.Join(ss[len(ss)-10:], "\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the enter key is pressed, add a line break.
|
// If the enter key is pressed, add a line break.
|
||||||
if repeatingKeyPressed(ebiten.KeyEnter) || repeatingKeyPressed(ebiten.KeyKPEnter) {
|
if repeatingKeyPressed(ebiten.KeyEnter) || repeatingKeyPressed(ebiten.KeyKPEnter) {
|
||||||
text += "\n"
|
g.text += "\n"
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the backspace key is pressed, remove one character.
|
// If the backspace key is pressed, remove one character.
|
||||||
if repeatingKeyPressed(ebiten.KeyBackspace) {
|
if repeatingKeyPressed(ebiten.KeyBackspace) {
|
||||||
if len(text) >= 1 {
|
if len(g.text) >= 1 {
|
||||||
text = text[:len(text)-1]
|
g.text = g.text[:len(g.text)-1]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
counter++
|
g.counter++
|
||||||
|
|
||||||
if ebiten.IsDrawingSkipped() {
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (g *Game) Draw(screen *ebiten.Image) {
|
||||||
// Blink the cursor.
|
// Blink the cursor.
|
||||||
t := text
|
t := g.text
|
||||||
if counter%60 < 30 {
|
if g.counter%60 < 30 {
|
||||||
t += "_"
|
t += "_"
|
||||||
}
|
}
|
||||||
ebitenutil.DebugPrint(screen, t)
|
ebitenutil.DebugPrint(screen, t)
|
||||||
return nil
|
}
|
||||||
|
|
||||||
|
func (g *Game) Layout(outsideWidth, outsideHeight int) (int, int) {
|
||||||
|
return screenWidth, screenHeight
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
if err := ebiten.Run(update, 320, 240, 2.0, "Typewriter (Ebiten Demo)"); err != nil {
|
g := &Game{
|
||||||
|
text: "Type on the keyboard:\n",
|
||||||
|
counter: 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
ebiten.SetWindowSize(screenWidth*2, screenHeight*2)
|
||||||
|
ebiten.SetWindowTitle("TypeWriter (Ebiten Demo)")
|
||||||
|
if err := ebiten.RunGame(g); err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -443,63 +443,74 @@ func (c *CheckBox) SetOnCheckChanged(f func(c *CheckBox)) {
|
|||||||
c.onCheckChanged = f
|
c.onCheckChanged = f
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
type Game struct {
|
||||||
button1 = &Button{
|
button1 *Button
|
||||||
|
button2 *Button
|
||||||
|
checkBox *CheckBox
|
||||||
|
textBoxLog *TextBox
|
||||||
|
}
|
||||||
|
|
||||||
|
var g Game
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
g.button1 = &Button{
|
||||||
Rect: image.Rect(16, 16, 144, 48),
|
Rect: image.Rect(16, 16, 144, 48),
|
||||||
Text: "Button 1",
|
Text: "Button 1",
|
||||||
}
|
}
|
||||||
button2 = &Button{
|
g.button2 = &Button{
|
||||||
Rect: image.Rect(160, 16, 288, 48),
|
Rect: image.Rect(160, 16, 288, 48),
|
||||||
Text: "Button 2",
|
Text: "Button 2",
|
||||||
}
|
}
|
||||||
checkBox = &CheckBox{
|
g.checkBox = &CheckBox{
|
||||||
X: 16,
|
X: 16,
|
||||||
Y: 64,
|
Y: 64,
|
||||||
Text: "Check Box!",
|
Text: "Check Box!",
|
||||||
}
|
}
|
||||||
textBoxLog = &TextBox{
|
g.textBoxLog = &TextBox{
|
||||||
Rect: image.Rect(16, 96, 624, 464),
|
Rect: image.Rect(16, 96, 624, 464),
|
||||||
}
|
}
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
g.button1.SetOnPressed(func(b *Button) {
|
||||||
button1.SetOnPressed(func(b *Button) {
|
g.textBoxLog.AppendLine("Button 1 Pressed")
|
||||||
textBoxLog.AppendLine("Button 1 Pressed")
|
|
||||||
})
|
})
|
||||||
button2.SetOnPressed(func(b *Button) {
|
g.button2.SetOnPressed(func(b *Button) {
|
||||||
textBoxLog.AppendLine("Button 2 Pressed")
|
g.textBoxLog.AppendLine("Button 2 Pressed")
|
||||||
})
|
})
|
||||||
checkBox.SetOnCheckChanged(func(c *CheckBox) {
|
g.checkBox.SetOnCheckChanged(func(c *CheckBox) {
|
||||||
msg := "Check box check changed"
|
msg := "Check box check changed"
|
||||||
if c.Checked() {
|
if c.Checked() {
|
||||||
msg += " (Checked)"
|
msg += " (Checked)"
|
||||||
} else {
|
} else {
|
||||||
msg += " (Unchecked)"
|
msg += " (Unchecked)"
|
||||||
}
|
}
|
||||||
textBoxLog.AppendLine(msg)
|
g.textBoxLog.AppendLine(msg)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func update(screen *ebiten.Image) error {
|
func (g *Game) Update(screen *ebiten.Image) error {
|
||||||
button1.Update()
|
g.button1.Update()
|
||||||
button2.Update()
|
g.button2.Update()
|
||||||
checkBox.Update()
|
g.checkBox.Update()
|
||||||
textBoxLog.Update()
|
g.textBoxLog.Update()
|
||||||
|
|
||||||
if ebiten.IsDrawingSkipped() {
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (g *Game) Draw(screen *ebiten.Image) {
|
||||||
screen.Fill(color.RGBA{0xeb, 0xeb, 0xeb, 0xff})
|
screen.Fill(color.RGBA{0xeb, 0xeb, 0xeb, 0xff})
|
||||||
button1.Draw(screen)
|
g.button1.Draw(screen)
|
||||||
button2.Draw(screen)
|
g.button2.Draw(screen)
|
||||||
checkBox.Draw(screen)
|
g.checkBox.Draw(screen)
|
||||||
textBoxLog.Draw(screen)
|
g.textBoxLog.Draw(screen)
|
||||||
return nil
|
}
|
||||||
|
|
||||||
|
func (g *Game) Layout(outsideWidth, outsideHeight int) (int, int) {
|
||||||
|
return screenWidth, screenHeight
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
if err := ebiten.Run(update, screenWidth, screenHeight, 1, "UI (Ebiten Demo)"); err != nil {
|
ebiten.SetWindowSize(screenWidth, screenHeight)
|
||||||
|
ebiten.SetWindowTitle("UI (Ebiten Demo)")
|
||||||
|
if err := ebiten.RunGame(&g); err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -171,25 +171,34 @@ func drawWave(screen *ebiten.Image, counter int) {
|
|||||||
path.Fill(screen, op)
|
path.Fill(screen, op)
|
||||||
}
|
}
|
||||||
|
|
||||||
var counter = 0
|
type Game struct {
|
||||||
|
counter int
|
||||||
|
}
|
||||||
|
|
||||||
func update(screen *ebiten.Image) error {
|
func (g *Game) Update(screen *ebiten.Image) error {
|
||||||
counter++
|
g.counter++
|
||||||
if ebiten.IsDrawingSkipped() {
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (g *Game) Draw(screen *ebiten.Image) {
|
||||||
screen.Fill(color.White)
|
screen.Fill(color.White)
|
||||||
drawEbitenText(screen)
|
drawEbitenText(screen)
|
||||||
drawEbitenLogo(screen, 20, 90)
|
drawEbitenLogo(screen, 20, 90)
|
||||||
drawWave(screen, counter)
|
drawWave(screen, g.counter)
|
||||||
|
|
||||||
ebitenutil.DebugPrint(screen, fmt.Sprintf("TPS: %0.2f\nFPS: %0.2f", ebiten.CurrentTPS(), ebiten.CurrentFPS()))
|
ebitenutil.DebugPrint(screen, fmt.Sprintf("TPS: %0.2f\nFPS: %0.2f", ebiten.CurrentTPS(), ebiten.CurrentFPS()))
|
||||||
return nil
|
}
|
||||||
|
|
||||||
|
func (g *Game) Layout(outsideWidth, outsideHeight int) (int, int) {
|
||||||
|
return screenWidth, screenHeight
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
if err := ebiten.Run(update, screenWidth, screenHeight, 1, "Vector (Ebiten Demo)"); err != nil {
|
g := &Game{counter: 0}
|
||||||
|
|
||||||
|
ebiten.SetWindowSize(screenWidth, screenHeight)
|
||||||
|
ebiten.SetWindowTitle("Vector (Ebiten Demo)")
|
||||||
|
if err := ebiten.RunGame(g); err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,15 +32,17 @@ const (
|
|||||||
sampleRate = 44100
|
sampleRate = 44100
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
type Game struct {
|
||||||
audioContext *audio.Context
|
audioContext *audio.Context
|
||||||
audioPlayer *audio.Player
|
audioPlayer *audio.Player
|
||||||
)
|
}
|
||||||
|
|
||||||
|
var g Game
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
var err error
|
var err error
|
||||||
// Initialize audio context.
|
// Initialize audio context.
|
||||||
audioContext, err = audio.NewContext(sampleRate)
|
g.audioContext, err = audio.NewContext(sampleRate)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -56,42 +58,48 @@ func init() {
|
|||||||
// return err
|
// return err
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
// d, err := wav.Decode(audioContext, f)
|
// d, err := wav.Decode(g.audioContext, f)
|
||||||
// ...
|
// ...
|
||||||
|
|
||||||
// Decode wav-formatted data and retrieve decoded PCM stream.
|
// Decode wav-formatted data and retrieve decoded PCM stream.
|
||||||
d, err := wav.Decode(audioContext, audio.BytesReadSeekCloser(raudio.Jab_wav))
|
d, err := wav.Decode(g.audioContext, audio.BytesReadSeekCloser(raudio.Jab_wav))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create an audio.Player that has one stream.
|
// Create an audio.Player that has one stream.
|
||||||
audioPlayer, err = audio.NewPlayer(audioContext, d)
|
g.audioPlayer, err = audio.NewPlayer(g.audioContext, d)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func update(screen *ebiten.Image) error {
|
func (g *Game) Update(screen *ebiten.Image) error {
|
||||||
if ebiten.IsKeyPressed(ebiten.KeyP) && !audioPlayer.IsPlaying() {
|
if ebiten.IsKeyPressed(ebiten.KeyP) && !g.audioPlayer.IsPlaying() {
|
||||||
// As audioPlayer has one stream and remembers the playing position,
|
// As audioPlayer has one stream and remembers the playing position,
|
||||||
// rewinding is needed before playing when reusing audioPlayer.
|
// rewinding is needed before playing when reusing audioPlayer.
|
||||||
audioPlayer.Rewind()
|
g.audioPlayer.Rewind()
|
||||||
audioPlayer.Play()
|
g.audioPlayer.Play()
|
||||||
}
|
|
||||||
if ebiten.IsDrawingSkipped() {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if audioPlayer.IsPlaying() {
|
|
||||||
ebitenutil.DebugPrint(screen, "Bump!")
|
|
||||||
} else {
|
|
||||||
ebitenutil.DebugPrint(screen, "Press P to play the wav")
|
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (g *Game) Draw(screen *ebiten.Image) {
|
||||||
|
if g.audioPlayer.IsPlaying() {
|
||||||
|
ebitenutil.DebugPrint(screen, "Bump!")
|
||||||
|
} else {
|
||||||
|
ebitenutil.DebugPrint(screen, "Press P to play the wav")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *Game) Layout(outsideWidth, outsideHeight int) (int, int) {
|
||||||
|
return screenWidth, screenHeight
|
||||||
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
if err := ebiten.Run(update, screenWidth, screenHeight, 2, "WAV (Ebiten Demo)"); err != nil {
|
ebiten.SetWindowSize(screenWidth*2, screenHeight*2)
|
||||||
|
ebiten.SetWindowTitle("WAV (Ebiten Demo)")
|
||||||
|
if err := ebiten.RunGame(&g); err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user