/*
* Foogl (Friendly Object Oriented Game Library)
* Copyright (c) 2008 Wachirawut Thamviset.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package kku.cs.fgl;
import java.awt.image.BufferedImage;
import java.awt.image.Raster;
import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.Collections;
import java.util.Comparator;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.Vector;
import javax.imageio.ImageIO;
import kku.cs.fgl.actor.ImageActor;
import org.lwjgl.opengl.GL11;
import org.newdawn.slick.Color;
import org.newdawn.slick.Font;
import org.newdawn.slick.GameContainer;
import org.newdawn.slick.Graphics;
import org.newdawn.slick.Input;
import org.newdawn.slick.Music;
import org.newdawn.slick.SlickException;
import org.newdawn.slick.Sound;
import org.newdawn.slick.opengl.renderer.Renderer;
import org.newdawn.slick.opengl.renderer.SGL;
import org.newdawn.slick.state.BasicGameState;
import org.newdawn.slick.state.StateBasedGame;
import org.newdawn.slick.state.transition.Transition;
/**
* AbstractScene ������Ѻ���ҧ�ҡ ���������ö�������©ҡ �ء�ҡ��ͧ extends
* �ҡ AbstractScene ��кǹ��÷ӧҹ
* <Ol>
* <li>���ҧ�ҡ ���� new ��˹� id ��� gamePane
* <li>��������¡ enterScene(id) <br>
* ����繡�����¡�����á init() �ж١���¡���ӧҹ <br>
* �ҡ��� enter( ) �ж١���¡���ӧҹ
* <li>������ʶҹС���ѹ �ء��ͺ��÷ӧҹ���ա�����¡ <br>
* <code>update( )</code> ���ͤӹdz ��� ��Ѻ��ا������<br>
* <code>paint( )</code> �����Ҵ˹�Ҩ�
*
* <li>������ա�����¡ enterScene 价�� scene ��� <br>
* leave( ) �ж١���¡���ӧҹ
* </ol>
*
* @author Wachirawut Thamviset
*
*/
abstract public class AbstractScene extends BasicGameState implements
ActorGroupListener {
private int ID;
private Vector actorGroups = new Vector();
private Vector viewPorts = new Vector();
private Vector collisionManagers = new Vector();
protected GamePane gamePane;
protected GameContainer screen;
private Color bgcolor = Color.black;
private TreeMap soundMap = new TreeMap();
private boolean inited = false;
String musicfile = null;
private ViewPort defView;
private ActorGroup defGroup;
protected static SGL GL = Renderer.get();
private String screenShotFile = null;
public AbstractScene(int id, GamePane gamePane) {
super();
this.gamePane = gamePane;
this.ID = id;
gamePane.addState(this);
screen = gamePane.getScreen();
defView = new ViewPort(0, this, 0, 0);
defGroup = new ActorGroup(0);
add(defView);
add(defGroup);
}
/**
* ���� Actor ����㹩ҡ ���������㹡��������ش
*
* @param a
*/
public void add(Actor a) {
add(a, 0);
}
/**
* ���� Actor ����㹩ҡ���к������Ţ����� (groupid) ��ҡ��������ѧ�����
* ActorGroup ����ж١���ҧ������ѵ��ѵ�
*
* @param a
* @param groupid
*/
public void add(Actor a, int groupid) {
ActorGroup g = getGroup(groupid);
if (g == null) {
g = new ActorGroup(groupid);
add(g);
}
g.add(a);
}
/**
* ����������ͧ Actor
*
* @param g
*/
public void add(ActorGroup g) {
ActorGroup old = getGroup(g.getId());
if (old != null) {
actorGroups.remove(old);
}
g.scene = this;
g.addActorListListener(this);
actorGroups.add(g);
Collections.sort(actorGroups, g);
}
/**
* ���� CollisionManager ������Ѻ��Ǩ�ͺ��ê��ѹ�ͧ Actor
*
* @param v
*/
public void add(CollisionManager v) {
if (!collisionManagers.contains(v))
collisionManagers.add(v);
}
/**
* ���� ViewPort ������Ѻ�Ǻ������˹���С���ʴ���
*
* @param v
*/
public void add(ViewPort v) {
if (!viewPorts.contains(v))
viewPorts.add(v);
}
/**
* �������§�Ϳ ���кت��ͧ͢���§ ��� ������§
* ���ٻẺ����������� .ogg ��� .wav
*
* @param name
* �繪��ͧ͢���§
* @param soundfile
* �繪������
*/
public void addSound(String name, String soundfile) {
try {
soundMap.put(name, soundfile);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* Clear all scene data. ź�����ŷ���������
* ActorGroup,sound,viewport,collisionManagers
*
*/
public void clear() {
for (int i = 0; i < actorGroups.size(); i++) {
((ActorGroup) actorGroups.get(i)).clear();
}
actorGroups.clear();
soundMap.clear();
viewPorts.clear();
collisionManagers.clear();
add(defView);
add(defGroup);
}
/**
* ���ҧ������ͧ Actor (ActorGroup) ���к� id
*
* @param id
* @return
*/
public void createActorGroup(int... id) {
for (int i : id) {
ActorGroup g = getGroup(i);
if (g == null) {
g = new ActorGroup(i);
add(g);
}
}
}
public ActorGroup getDefGroup() {
return defGroup;
}
public ViewPort getDefView() {
return defView;
}
/**
* ���ҧ��Ǥӹdz��ê��ѹ�ͧ actor ���к� id �ͧ ActorGroup �������Ǩ�ͺ
* ��������ӵ�Ǩ�ͺ��� actor � group1 �ա�ê��ѹ�Ѻ actor � group2
* ������� <br>
* group1 ����繡�����ͧ actor ����繵���Ф� <br>
* group2 ����繡�����ͧ ����ع�
*
* @param group1
* �������� 1 �繡�����鹷ҧ
* @param group2
* �������� 2 �繡�����������
* @return
*/
public CollisionManager createCollisionManager(int group1, int group2) {
ActorGroup g1 = getGroup(group1);
ActorGroup g2 = getGroup(group2);
if (g1 != null && g2 != null) {
CollisionManager m = new CollisionManager();
m.setGroup(g1, g2);
add(m);
return m;
}
return null;
}
abstract public void enter();
/**
* �١���¡������� scene ���١���¡��ӧҹ
*/
public void enter(GameContainer container, StateBasedGame game)
throws SlickException {
// System.out.println("Scene Enter");
if (musicfile != null) {
// System.out.println("play "+musicfile);
MusicPlayer.play(musicfile);
}
if (!inited)
init(container, game);
enter();
}
/**
* ���¡�����������÷ӧҹ� scene ���� ���к� {@link Transition}
*
* @param id
* @param leave
* @param enter
*/
public final void enterScene(int id, Transition leave, Transition enter) {
gamePane.enterScene(id, leave, enter);
}
public final void enterScene(int id) {
gamePane.enterScene(id);
}
public int getActorCount() {
ActorGroup groups[] = new ActorGroup[actorGroups.size()];
actorGroups.toArray(groups);
int n = 0;
for (int i = 0; i < groups.length; i++) {
n = n + groups[i].count();
}
return n;
}
public Font getFont(int font) {
return gamePane.getFont(font);
}
public GamePane getGamePane() {
return gamePane;
}
public ActorGroup getGroup(int id) {
for (int i = 0; i < actorGroups.size(); i++) {
ActorGroup g = (ActorGroup) actorGroups.get(i);
if (g.getId() == id)
return g;
}
return null;
}
public int getID() {
return ID;
}
public Sound getSound(String name) {
Object s = soundMap.get(name);
if (s == null)
return null;
if (!(s instanceof Sound)) {
String file = s.toString();
try {
s = new Sound(file);
} catch (SlickException e) {
e.printStackTrace();
s = null;
}
soundMap.put(name, s);
}
return (Sound) s;
}
abstract public void init();
public void init(GameContainer container, StateBasedGame game)
throws SlickException {
if (!inited)
init();
inited = true;
}
public boolean isInit() {
return inited;
}
abstract public void leave();
public void leave(GameContainer container, StateBasedGame game)
throws SlickException {
if (musicfile != null)
MusicPlayer.stop();
leave();
}
public void paint(Graphics g) {
ViewPort views[] = new ViewPort[viewPorts.size()];
viewPorts.toArray(views);
if (changeActor) {
for (int i = 0; i < views.length; i++) {
views[i].renderSet = new ActorSet();
}
ActorGroup groups[] = new ActorGroup[actorGroups.size()];
actorGroups.toArray(groups);
if (groups.length > 0) {
ActorSet set;
for (int j = 0; j < groups.length; j++) {
if (groups[j].getViewPort() == null) {
set = defView.renderSet;
} else {
set = groups[j].getViewPort().renderSet;
}
for (int i = 0; i < groups[j].count(); i++) {
set.put(groups[j].get(i));
}
}
}
changeActor = false;
} else {
}
for (int i = 0; i < views.length; i++) {
views[i].render(g);
// views[i].renderSet = null;
}
}
public void playMusic(String filename) {
try {
musicfile = filename;
if (inited)
MusicPlayer.play(filename);
} catch (Exception e) {
e.printStackTrace();
}
}
public void playSound(String name) {
Sound s = getSound(name);
if (s != null)
s.play();
}
public void playSound(String name, float x, float y) {
playSound(name, x, 1, y);
}
public void playSound(String name, float x, float y, float z) {
float w = screen.getWidth();
float h = screen.getHeight();
x = 2 * (x - w / 2) / w;
y = 2 * (h / 2 - y) / h;
Sound s = getSound(name);
// System.out.println("play at " + x + "," + y);
if (s != null)
s.playAt(x, y, z);
}
/**
* Remove actor from allGroup
*
*/
public void remove(Actor a) {
for (int i = 0; i < actorGroups.size(); i++) {
getGroup(i).remove(a);
}
}
public void remove(Actor a, int groupno) {
ActorGroup g = getGroup(groupno);
if (g != null)
g.remove(a);
}
public void remove(CollisionManager v) {
collisionManagers.remove(v);
}
public void remove(ViewPort v) {
viewPorts.remove(v);
}
final public void render(GameContainer container, StateBasedGame game,
Graphics g) throws SlickException {
if (!inited) {
// g.setFont(getFont(0));
init(container, game);
}
screen = container;
paint(g);
if (screenShotFile != null) {
saveScreen(g);
screenShotFile = null;
}
}
final public void update(GameContainer container, StateBasedGame game,
int delta) throws SlickException {
if (delta > 60)
delta = 60;
ViewPort views[] = new ViewPort[viewPorts.size()];
viewPorts.toArray(views);
for (int i = 0; i < views.length; i++) {
views[i].update(delta);
}
ActorGroup groups[] = new ActorGroup[actorGroups.size()];
actorGroups.toArray(groups);
// ź actor ��� dead �͡�ҡ list
for (int i = 0; i < groups.length; i++) {
groups[i].removeDead();
}
update(delta);
for (int i = groups.length - 1; i >= 0; i--) {
groups[i].removeDead();
// ���¡ processInput ���ͨѴ��� input
groups[i].processInput(screen.getInput());
// ���¡ update ������� actor ������ update �����ŵ���ͧ
groups[i].update(delta);
}
// ���¡ collisionManagers ���͵�Ǩ�ͺ��ê�
for (int i = 0; i < collisionManagers.size(); i++) {
((CollisionManager) collisionManagers.get(i)).update();
}
for (int i = 0; i < groups.length; i++) {
groups[i].removeDead();
}
}
public Input getInput(){
return screen.getInput();
}
private boolean changeActor = true;
public void actorAdded(Actor a) {
changeActor = true;
}
public void actorRemoved(Actor a) {
changeActor = true;
}
abstract public void update(int delta);
public void screenShot(String screenShotFile) {
this.screenShotFile = screenShotFile;
}
private void saveScreen(Graphics g) {
int w = screen.getWidth();
int h = screen.getHeight();
ByteBuffer target = ByteBuffer.allocateDirect(w * h * 3).order(
ByteOrder.nativeOrder());
int[] framepixels = new int[w * h];
GL11.glReadPixels(0, 0, w, h, GL11.GL_RGB, GL11.GL_UNSIGNED_BYTE,
target);
int bindex = 0;
for (int y = 0; y < h; y++) {
for (int x = 0; x < w; x++) {
bindex = ((y * w) + x) * 3;
framepixels[((h - y - 1) * w) + x] = // framepixels[ (y * width)
// + x];
0xFF000000 // A
| ((target.get(bindex) & 0x000000FF) << 16) // R
| ((target.get(bindex + 1) & 0x000000FF) << 8) // G
| ((target.get(bindex + 2) & 0x000000FF) << 0); // B
}
}
BufferedImage img = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
try {
img.setRGB(0, 0, w, h, framepixels, 0, w);
ImageIO.write(img, "PNG", new File(screenShotFile));
} catch (IOException e) {
e.printStackTrace();
}
}
}