기술/beginning games2011. 6. 2. 11:22
download download
public interface Game {
public Input getInput(); 
public FileIO getFileIO(); 
public Graphics getGraphics(); 
public Audio getAudio(); 
public void setScreen(Screen screen); 
public Screen getCurrentScreen(); 
public Screen getStartScreen(); 
}




public abstract class Screen {
protected final Game game;
public Screen(Game game) {
this.game = game;
}
public abstract void update(float deltaTime);
public abstract void present(float deltaTime); 
public abstract void pause(); 
public abstract void resume(); 
public abstract void dispose(); 
}



public class Pool<T> {   // Garbage Collector를 사용함
public interface PoolObjectFactory<T>{
public T createObject();
}
private final List<T> freeObjects; 
private final PoolObjectFactory<T> factory; 
private final int maxSize;
public Pool(PoolObjectFactory<T> factory, int maxSize) {
this.factory = factory;
this.maxSize = maxSize;
this.freeObjects = new ArrayList<T>(maxSize); 
}
public T newObject() {
T object = null;
if (freeObjects.size() == 0)
object = factory.createObject(); 
else
object = freeObjects.remove(freeObjects.size() - 1); 
return object;
}
public void free(T object) {
if (freeObjects.size() < maxSize)
freeObjects.add(object); 
}
}
 
download download download
Posted by yachtie_leo
기술/beginning games2011. 6. 2. 09:57
download download
public interface Graphics {
public static enum PixmapFormat {
ARGB8888, ARGB4444, RGB565 
}
public Pixmap newPixmap(String fileName, PixmapFormat format); 
public void clear(int color);
public void drawPixel(int x, int y, int color); 
public void drawLine(int x, int y, int x2, int y2, int color);
public void drawRect(int x, int y, int width, int height, int color); 
public void drawPixmap(Pixmap pixmap, int x, int y, int srcX, int srcY, int srcWidth, int srcHeight);  
public void drawPixmap(Pixmap pixmap, int x, int y); 
public int getWidth();
public int getHeight(); 
}


 
public interface Pixmap {
public int getWidth(); 
public int getHeight(); 
public PixmapFormat getFormat();
public void dispose(); 

 


import com.badlogic.androidgames.framework.Graphics;
import com.badlogic.androidgames.framework.Pixmap;

public class AndroidGraphics implements Graphics {
AssetManager assets;
Bitmap frameBuffer;
Canvas canvas;
Paint paint;
Rect srcRect = new Rect();
Rect dstRect = new Rect();
public AndroidGraphics(AssetManager assets, Bitmap frameBuffer){
this.assets = assets;
this.frameBuffer = frameBuffer;
this.canvas = new Canvas(frameBuffer);
this.paint = new Paint();
}

@Override
public Pixmap newPixmap(String fileName, PixmapFormat format) {
Config config = null;
if(format == PixmapFormat.RGB565)
config = Config.RGB_565;
else if(format==PixmapFormat.ARGB4444)
config = config.ARGB_4444;
else
config = config.ARGB_8888;
Options options = new Options();
options.inPreferredConfig = config;
InputStream in = null;
Bitmap bitmap = null;
try{
in = assets.open(fileName);
bitmap = BitmapFactory.decodeStream(in);
if(bitmap == null)
throw new RuntimeException("couldn't load bitmap from asset '" + fileName + "'");
} catch (IOException e){
throw new RuntimeException("couldn't load bitmap from asset '" + fileName + "'");
} finally {
if(in != null){
try{
in.close();
} catch (IOException e){
}
}
}
if(bitmap.getConfig()==Config.RGB_565)
format = PixmapFormat.RGB565;
else if(bitmap.getConfig()==Config.ARGB_4444)
format = PixmapFormat.ARGB4444;
else
format = PixmapFormat.ARGB8888;
return new AndroidPixmap(bitmap, format);
}

@Override
public void clear(int color) {
canvas.drawRGB((color & 0xff0000) >> 16, (color & 0xff00) >> 8, (color & 0xff));
}

@Override
public void drawPixel(int x, int y, int color) {
paint.setColor(color);
canvas.drawPoint(x, y, paint);
}

@Override
public void drawLine(int x, int y, int x2, int y2, int color) {
paint.setColor(color);
canvas.drawLine(x, y, x2, y2, paint);
}

@Override
public void drawRect(int x, int y, int width, int height, int color) {
paint.setColor(color);
paint.setStyle(Style.FILL);
canvas.drawRect(x, y, x + width - 1, y + width - 1, paint);
}

@Override
public void drawPixmap(Pixmap pixmap, int x, int y, int srcX, int srcY,
int srcWidth, int srcHeight) {
srcRect.left = srcX;
srcRect.top = srcY;
srcRect.right = srcX + srcWidth - 1;
srcRect.bottom = srcY + srcHeight - 1;
dstRect.left = x;
dstRect.top = y;
dstRect.right = x + srcWidth - 1;
dstRect.bottom = y + srcHeight - 1;
canvas.drawBitmap(((AndroidPixmap) pixmap).bitmap, srcRect, dstRect, null);
}

@Override
public void drawPixmap(Pixmap pixmap, int x, int y) {
canvas.drawBitmap(((AndroidPixmap)pixmap).bitmap, x, y, null);
}

@Override
public int getWidth() {
return frameBuffer.getWidth();
}

@Override
public int getHeight() {
return frameBuffer.getHeight();
}
}
download download download
Posted by yachtie_leo
기술/beginning games2011. 5. 31. 20:12
download download
public interface Audio {
public Music newMusic(String filename); 
public Sound newSound(String filename); 
}

 

public interface Music {
public void play(); 
public void stop(); 
public void pause();
public void setLooping(boolean looping); 
public void setVolume(float volume); 
public boolean isPlaying(); 
public boolean isStopped(); 
public boolean isLooping(); 
public void dispose(); 
}


 public interface Sound {
public void play(float volume);
public void dispose(); 
}

소리 관련 인터페이스 생성

 public class AndroidMusic implements Music, OnCompletionListener{
MediaPlayer mediaPlayer;
boolean isPrepared = false;
public AndroidMusic(AssetFileDescriptor assetDescriptor){
mediaPlayer = new MediaPlayer();
try {
mediaPlayer.setDataSource(assetDescriptor.getFileDescriptor(),
assetDescriptor.getStartOffset(),
assetDescriptor.getLength());
mediaPlayer.prepare();
isPrepared = true;
mediaPlayer.setOnCompletionListener(this);
} catch (Exception e){
throw new RuntimeException("Couldn't load music");
}
}

@Override
public void onCompletion(MediaPlayer arg0) {
synchronized(this){
isPrepared = false;
}
}

@Override
public void play() {
if(mediaPlayer.isPlaying()) return;
try{
synchronized(this){
if(!isPrepared)
mediaPlayer.prepare();
mediaPlayer.start();
}
} catch (IllegalStateException e){
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}

@Override
public void stop() {
mediaPlayer.stop();
synchronized(this){
isPrepared = false;
}
}

@Override
public void pause() {
if(mediaPlayer.isPlaying()) mediaPlayer.pause();
}

@Override
public void setLooping(boolean looping) {
mediaPlayer.setLooping(looping);
}

@Override
public void setVolume(float volume) {
mediaPlayer.setVolume(volume, volume);
}

@Override
public boolean isPlaying() {
return mediaPlayer.isPlaying();
}

@Override
public boolean isStopped() {
return !isPrepared;
}

@Override
public boolean isLooping() {
return mediaPlayer.isLooping();
}

@Override
public void dispose() {
if(mediaPlayer.isPlaying())
mediaPlayer.stop();
mediaPlayer.release();
}
}


public class AndroidSound implements Sound{
int soundId;
SoundPool soundPool;
public AndroidSound(SoundPool soundPool, int soundId){
this.soundId = soundId;
this.soundPool = soundPool;
}
@Override
public void play(float volume) {
soundPool.play(soundId, volume, volume, 0, 0, 1);
}

@Override
public void dispose() {
soundPool.unload(soundId);
}
download download download
Posted by yachtie_leo
기술/beginning games2011. 5. 31. 20:07
download download
public interface FileIO {         // 인터페이스 생성
public InputStream readAsset(String fileName) throws IOException;    //Asset Input
public InputStream readFile(String fileName) throws IOException;      //File Input
public OutputStream writeFile(String fileName) throws IOException;   //File Output
}


import com.badlogic.androidgames.framework.FileIO;

public class AndroidFileIO implements FileIO{
AssetManager assets;
String externalStoragePath;

public AndroidFileIO(AssetManager assets){
this.assets = assets;
this.externalStoragePath = Environment.getExternalStorageDirectory().getAbsolutePath()+File.separator;  //외부 저장장치 경로 설정
}
@Override
public InputStream readAsset(String fileName) throws IOException {
return assets.open(fileName);
}

@Override
public InputStream readFile(String fileName) throws IOException {
return new FileInputStream(externalStoragePath + fileName);
}

@Override
public OutputStream writeFile(String fileName) throws IOException {
return new FileOutputStream(externalStoragePath + fileName);
}
}
download download download
Posted by yachtie_leo
기술/beginning games2011. 5. 31. 19:58
download download
public interface TouchHandler extends OnTouchListener{   //인터페이스 생성
public boolean isTouchDown(int pointer);
public int getTouchX(int pointer);
public int getTouchY(int pointer);
public List<TouchEvent> getTouchEvents();
}



public class SingleTouchHandler implements TouchHandler{  //터치 핸들러 상속
boolean isTouched;
int touchX;
int touchY;
Pool<TouchEvent> touchEventPool;
List<TouchEvent> touchEvents = new ArrayList<TouchEvent>();
List<TouchEvent> touchEventsBuffer = new ArrayList<TouchEvent>();
float scaleX;
float scaleY;
public SingleTouchHandler(View view, float scaleX, float scaleY){
PoolObjectFactory<TouchEvent> factory = new PoolObjectFactory<TouchEvent>(){
@Override
public TouchEvent createObject() {
return new TouchEvent();
}
};
touchEventPool = new Pool<TouchEvent>(factory, 100);
view.setOnTouchListener(this);
this.scaleX = scaleX;
this.scaleY = scaleY;
}
@Override
public boolean onTouch(View v, MotionEvent event) {
synchronized(this){   
TouchEvent touchEvent = touchEventPool.newObject();
switch(event.getAction()){
case MotionEvent.ACTION_DOWN:
touchEvent.type = TouchEvent.TOUCH_DOWN;
isTouched = true;
break;
case MotionEvent.ACTION_MOVE:
touchEvent.type = TouchEvent.TOUCH_DRAGGED;
isTouched = true;
break;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
touchEvent.type = TouchEvent.TOUCH_UP;
isTouched = false;
break;
}
touchEvent.x = touchX = (int)(event.getX()*scaleX);
touchEvent.y = touchY = (int)(event.getY()*scaleY);
touchEventsBuffer.add(touchEvent);
return true;
}
}

@Override
public boolean isTouchDown(int pointer) {
synchronized(this){
if(pointer==0)
return isTouched;
else
return false;
}
}

@Override
public int getTouchX(int pointer) {
synchronized(this){
return touchX;
}
}

@Override
public int getTouchY(int pointer) {
synchronized(this){
return touchY;
}
}

@Override
public List<TouchEvent> getTouchEvents() {
synchronized(this){
int len = touchEvents.size();
for(int i=0; i < len; i++)
touchEventPool.free(touchEvents.get(i));
touchEvents.clear();
touchEvents.addAll(touchEventsBuffer);
touchEventsBuffer.clear();
return touchEvents;
}
}




 public class MultiTouchHandler implements TouchHandler{   //멀티 터치
boolean[] isTouched = new boolean[20];
int[] touchX = new int[20];
int[] touchY = new int[20];
Pool<TouchEvent> touchEventPool;
List<TouchEvent> touchEvents = new ArrayList<TouchEvent>();
List<TouchEvent> touchEventsBuffer = new ArrayList<TouchEvent>();
float scaleX;
float scaleY;
public MultiTouchHandler(View view, float scaleX, float scaleY) {
PoolObjectFactory<TouchEvent> factory = new PoolObjectFactory<TouchEvent>() {
@Override
public TouchEvent createObject() {
return new TouchEvent();
}
};
touchEventPool = new Pool<TouchEvent>(factory, 100);
view.setOnTouchListener(this);
this.scaleX = scaleX;
this.scaleY = scaleY;
}
@Override
public boolean onTouch(View v, MotionEvent event) {
synchronized (this) {
int action = event.getAction() & MotionEvent.ACTION_MASK;
int pointerIndex = (event.getAction() & MotionEvent.ACTION_POINTER_ID_MASK) >> MotionEvent.ACTION_POINTER_ID_SHIFT;
int pointerId = event.getPointerId(pointerIndex);
TouchEvent touchEvent;
switch (action) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_POINTER_DOWN:
touchEvent = touchEventPool.newObject();
touchEvent.type = TouchEvent.TOUCH_DOWN;
touchEvent.pointer = pointerId;
touchEvent.x = touchX[pointerId] = (int) (event.getX(pointerIndex) * scaleX);
touchEvent.y = touchY[pointerId] = (int) (event.getY(pointerIndex) * scaleY);
isTouched[pointerId] = true;
touchEventsBuffer.add(touchEvent);
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_POINTER_UP:
case MotionEvent.ACTION_CANCEL:
touchEvent = touchEventPool.newObject();
touchEvent.type = TouchEvent.TOUCH_UP;
touchEvent.pointer = pointerId;
touchEvent.x = touchX[pointerId] = (int) (event.getX(pointerIndex) * scaleX);
touchEvent.y = touchY[pointerId] = (int) (event.getY(pointerIndex) * scaleY);
isTouched[pointerId] = false;
touchEventsBuffer.add(touchEvent);
break;
case MotionEvent.ACTION_MOVE:
int pointerCount = event.getPointerCount();
for (int i = 0; i < pointerCount; i++) {
pointerIndex = i;
pointerId = event.getPointerId(pointerIndex);
touchEvent = touchEventPool.newObject();
touchEvent.type = TouchEvent.TOUCH_DRAGGED;
touchEvent.pointer = pointerId;
touchEvent.x = touchX[pointerId] = (int) (event.getX(pointerIndex) * scaleX);
touchEvent.y = touchY[pointerId] = (int) (event.getY(pointerIndex) * scaleY);
touchEventsBuffer.add(touchEvent);
}
break;
}
return true;
}
}

@Override
public boolean isTouchDown(int pointer) {
synchronized (this) {
if (pointer < 0 || pointer >= 20)
return false;
else
return isTouched[pointer];
}
}

@Override
public int getTouchX(int pointer) {
synchronized (this) {
if (pointer < 0 || pointer >= 20)
return 0;
else
return touchX[pointer];
}
}

@Override
public int getTouchY(int pointer) {
synchronized (this) {
if (pointer < 0 || pointer >= 20)
return 0;
else
return touchY[pointer];
}
}

@Override
public List<TouchEvent> getTouchEvents() {
synchronized (this) {
int len = touchEvents.size();
for (int i = 0; i < len; i++)
touchEventPool.free(touchEvents.get(i));
touchEvents.clear();
touchEvents.addAll(touchEventsBuffer);
touchEventsBuffer.clear();
return touchEvents;
}
}
}
download download download
Posted by yachtie_leo
기술/beginning games2011. 5. 31. 19:45
download download
import com.badlogic.androidgames.framework.Input.KeyEvent;
import com.badlogic.androidgames.framework.Pool;
import com.badlogic.androidgames.framework.Pool.PoolObjectFactory;

public class KeyboardHandler implements OnKeyListener{      //키보드 리스너 상속
boolean[] pressedKeys = new boolean[28];
Pool<KeyEvent> keyEventPool;
List<KeyEvent> keyEventsBuffer = new ArrayList<KeyEvent>();
List<KeyEvent> keyEvents = new ArrayList<KeyEvent>();
public KeyboardHandler(View view){
PoolObjectFactory<KeyEvent> factory = new PoolObjectFactory<KeyEvent>(){   Pool

@Override
public KeyEvent createObject() {
return new KeyEvent();
}
};
keyEventPool = new Pool<KeyEvent>(factory, 100);   // 키 입력을 Pool을 사용하여 100개의 object만 생성되도록 Garbage Collector 작동
view.setOnKeyListener(this);
view.setFocusableInTouchMode(true);  //터치모드 중에도 키보드 모드 사용 가능하게 설정(다중모드 지원)
view.requestFocus();    //뷰에 포커스 적용
}
@Override
public boolean onKey(View v, int keyCode, android.view.KeyEvent event) {
if(event.getAction() == android.view.KeyEvent.ACTION_MULTIPLE) return false;    // 같은키를 여러번 눌렀다면 리턴
synchronized(this){
KeyEvent keyEvent = keyEventPool.newObject();     //KeyEvent Input 객체 생성
keyEvent.keyCode = keyCode;
keyEvent.keyChar = (char)event.getUnicodeChar();
if(event.getAction()== android.view.KeyEvent.ACTION_DOWN){
keyEvent.type = KeyEvent.KEY_DOWN;       //type은 키가 눌림으로 설정
if(keyCode > 0 && keyCode < 127)
pressedKeys[keyCode] = true;        // 눌려진 KeyCode Array에 눌렸다는 true 표시
}
if(event.getAction()==android.view.KeyEvent.ACTION_UP){
keyEvent.type = KeyEvent.KEY_UP;
if(keyCode > 0 && keyCode < 127)
pressedKeys[keyCode] = false;
}
keyEventsBuffer.add(keyEvent);
}
return false;
}
public boolean isKeyPressed(int keyCode){      // 눌려진 키 상태를 리턴
if(keyCode < 0 || keyCode > 127)
return false;
return pressedKeys[keyCode];
}

public List<KeyEvent> getKeyEvents(){
synchronized(this){
int len = keyEvents.size();
for(int i=0; i < len; i++)
keyEventPool.free(keyEvents.get(i));
keyEvents.clear();
keyEvents.addAll(keyEventsBuffer);
keyEventsBuffer.clear();
return keyEvents;
}
}
}

download download download
Posted by yachtie_leo
기술/beginning games2011. 5. 31. 19:00
download download
가속센서 핸들러

- 센서는 독립된 하드웨어 장비이므로 별도의 명령을 내리지 않더라도 부팅한 직후부터 항상 동작중이다. 따라서 초기화를 따로 할 필요는 없으며 필요할 때 원하는 값을 센서로부터 전달받기만 된다. 센서값은 끊임없이 변하며 이때마다 리스너로 변경된 값을 전달한다. 센서 관리자의 다음 메서드로 리스너를 설치하고 대기한다.

public class AccelerometerHandler implements SensorEventListener{
float accelX;
float accelY;
float accelZ;
public AccelerometerHandler(Context context){
SensorManager manager = (SensorManager)context.getSystemService(Context.SENSOR_SERVICE);  // 센서 서비스 메니저 생성

if(manager.getSensorList(Sensor.TYPE_ACCELEROMETER).size() != 0){   // 가속 센서가 있다면 
Sensor accelermeter = manager.getSensorList(Sensor.TYPE_ACCELEROMETER).get(0);  // 가속센서를 가져 온다.
manager.registerListener(this, accelermeter,SensorManager.SENSOR_DELAY_GAME);  //게임에 접합한 정도의 주기로 가속 센서를 등록한다.
}
}

@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
}

@Override
public void onSensorChanged(SensorEvent event) {
accelX = event.values[0];
accelY = event.values[1];
accelZ = event.values[2];
}
public float getAccelX(){
return accelX;
}

public float getAccelY(){
return accelY;
}

public float getAccelZ(){
return accelZ;
}
}
download download download
Posted by yachtie_leo
기술/beginning games2011. 5. 31. 18:48
download download
import com.badlogic.androidgames.framework.Input;

public class AndroidInput implements Input{      // Input를 상속받습니다.
AccelerometerHandler accelHandler;     // 가속센서 핸드러를 생성
KeyboardHandler keyHandler;              //키보드 핸들러 생성
TouchHandler touchHandler;               //터치 핸드러 생성
public AndroidInput(Context context, View view, float scaleX, float scaleY){
accelHandler = new AccelerometerHandler(context);
keyHandler = new KeyboardHandler(view);
if(Integer.parseInt(VERSION.SDK)< 5)     // 안드로이드SDK 버전 5 이상은 멀티터치 지원, 이하는 미지원
touchHandler = new SingleTouchHandler(view, scaleX, scaleY);
else 
touchHandler = new MultiTouchHandler(view, scaleX, scaleY);
}

@Override
public boolean isKeyPressed(int keyCode) {
return keyHandler.isKeyPressed(keyCode);
}

@Override
public boolean isTouchDown(int pointer) {
return touchHandler.isTouchDown(pointer);
}

@Override
public int getTouchX(int pointer) {
return touchHandler.getTouchX(pointer);
}

@Override
public int getTouchY(int pointer) {
return touchHandler.getTouchY(pointer);
}

@Override
public float getAccelX() {
return accelHandler.getAccelX();
}

@Override
public float getAccelY() {
return accelHandler.getAccelY();
}

@Override
public float getAccelZ() {
return accelHandler.getAccelZ();
}

@Override
public List<KeyEvent> getKeyEvents() {
return keyHandler.getKeyEvents();
}

@Override
public List<TouchEvent> getTouchEvents() {
return touchHandler.getTouchEvents();
}
}
download download download
Posted by yachtie_leo
기술/beginning games2011. 5. 31. 18:38
download download
Apress - Beginning Android Games 예제소스를 보고 삽질 분석.

Input를 Interface로 생성하여 뼈대 만들기...

가속센서, 키보드, 터치를 통일된 형식으로 입력 하도록


public interface Input {   
public static class KeyEvent {
public static final int KEY_DOWN = 0;      // Key가 눌리면 0 아니면 1
public static final int KEY_UP = 1;
public int type;
public int keyCode;
public char keyChar;
public String toString(){
StringBuilder builder = new StringBuilder();
if(type==KEY_DOWN)
builder.append("key down, ");
else
builder.append("key up, ");
builder.append(keyCode);
builder.append(",");
builder.append(keyChar);
return builder.toString();
}
}
public static class TouchEvent {
public static final int TOUCH_DOWN=0;    //터치하면 0, 띄면 1, 드래그 하면 2
public static final int TOUCH_UP=1;
public static final int TOUCH_DRAGGED=2;
public int type;
public int x,y;
public int pointer;
public String toString(){
StringBuilder builder = new StringBuilder();
if(type == TOUCH_DOWN)
builder.append("touch down, ");
else if(type==TOUCH_DRAGGED)
builder.append("touch dragged, ");
else
builder.append("touch up, ");
builder.append(pointer);
builder.append(",");
builder.append(x);
builder.append(",");
builder.append(y);
return builder.toString();
}
}
public boolean isKeyPressed(int keyCode);
public boolean isTouchDown(int pointer);
public int getTouchX(int pointer);   //Touch X 값 가져 오기
public int getTouchY(int pointer);  //Touch Y 값 가져 오기
public float getAccelX();    //가속도 센서 X
public float getAccelY();    //가속도 센서 Y
public float getAccelZ();    //가속도 센서 Z
public List<KeyEvent> getKeyEvents();    //입력된 Key Code 저장
public List<TouchEvent> getTouchEvents();  // 입력된 TouchEvent 저장
}
download download download
Posted by yachtie_leo
기술/beginning games2011. 5. 25. 13:28
download download
public class FontTest extends Activity {
class RenderView extends View {
Paint paint;
Typeface font;
Rect bounds = new Rect();
public RenderView(Context context) {
super(context);
paint = new Paint();
font = Typeface.createFromAsset(context.getAssets(), "anklepan.ttf");
}
protected void onDraw(Canvas c){
paint.setColor(Color.YELLOW);
paint.setTypeface(font);
paint.setTextSize(28);
paint.setTextAlign(Paint.Align.CENTER);
c.drawText("Test!!!", c.getWidth()/2, 100, paint);
String text = "한글 o_O";
paint.setColor(Color.WHITE);
paint.setTextSize(18);
paint.setTextAlign(Paint.Align.LEFT);
paint.getTextBounds(text, 0, text.length(), bounds);
c.drawText(text, c.getWidth() - bounds.width(), 140,paint);
invalidate();
}
}
@Override
public void onCreate(Bundle savedInstanceState) {
   super.onCreate(savedInstanceState);
   requestWindowFeature(Window.FEATURE_NO_TITLE);
   getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
   setContentView(new RenderView(this));
}
}
download download download
Posted by yachtie_leo