/*
* Copyright (c) 2003-onwards Shaven Puppy Ltd
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of 'Shaven Puppy' nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package worm.screens;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.rmi.Naming;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
import net.puppygames.applet.Game;
import net.puppygames.applet.MiniGame;
import net.puppygames.applet.Screen;
import net.puppygames.applet.screens.TitleScreen;
import net.puppygames.gamecommerce.shared.GenericServerRemote;
import worm.Res;
import worm.SurvivalParams;
import worm.TimeUtil;
import worm.Worm;
import worm.features.WorldFeature;
import com.shavenpuppy.jglib.Resources;
import com.shavenpuppy.jglib.resources.ColorMapFeature;
/**
* Select which level you want to play in Survival mode
*/
public class SelectSurvivalLevelScreen extends Screen {
private static final long serialVersionUID = 1L;
private static SelectSurvivalLevelScreen instance;
private static final Object LOCK = new Object();
private static final String ID_BACK = "back";
private static final String ID_PLAY = "play";
private static final String GROUP_ID_WORLD = "select.world.";
private static final String GROUP_ID_WORLD_SELECTED = "selected.world.";
private static final String ID_WORLD = "select.world.";
private static final String GROUP_ID_TERRAIN = "select.terrain.";
private static final String GROUP_ID_TERRAIN_SELECTED = "selected.terrain.";
private static final String ID_TERRAIN = "select.terrain.";
private static final String GROUP_ID_SIZE = "select.size.";
private static final String GROUP_ID_SIZE_SELECTED = "selected.size.";
private static final String ID_SIZE = "select.size.";
private static final String ID_BEST_TIME = "best_time";
private static final String ID_ONLINE_BEST_TIME = "online_best_time";
private static final String ID_ONLINE_BEST_TIME_LABEL = "online_best_time_label";
private static final float[] DIFFICULTY = {-0.25f, -0.125f, 0.0f, 0.125f, 0.25f};
private static final int[] SIZE = {52, 74, 96}; // Ensure no bigger than WormGameState.ABS_MAX_SIZE
private transient int world = 0;
private transient int template = 0;
private transient int mapsize = 0;
private transient List<Runnable> ops;
private transient ScoreGetter current, pending;
private static class Params {
int world, template, mapsize;
public Params(int world, int template, int mapsize) {
this.world = world;
this.template = template;
this.mapsize = mapsize;
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("Params [world=");
builder.append(world);
builder.append(", template=");
builder.append(template);
builder.append(", mapsize=");
builder.append(mapsize);
builder.append("]");
return builder.toString();
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + mapsize;
result = prime * result + template;
result = prime * result + world;
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
Params other = (Params) obj;
if (mapsize != other.mapsize) {
return false;
}
if (template != other.template) {
return false;
}
if (world != other.world) {
return false;
}
return true;
}
}
private static class Hiscore {
String name;
int score;
}
private transient Map<Params, Hiscore> cache;
private class ScoreGetter extends Thread {
final Params params;
public ScoreGetter(Params params) {
super("Remove hiscore getter");
setDaemon(true);
this.params = params;
}
private Hiscore getHiscore() throws Exception {
GenericServerRemote server = (GenericServerRemote) Naming.lookup(GenericServerRemote.RMI_URL);
String ret;
StringBuilder command = new StringBuilder(256);
addParam("cmd", "getrevengehiscore", command);
addParam("version", Game.getVersion() + Game.getModName(), command);
addParam("world", String.valueOf(params.world), command);
addParam("terrain", String.valueOf(params.template), command);
addParam("size", String.valueOf(params.mapsize), command);
System.out.println("Executing remote command "+command);
ret = server.doCommand(command.toString());
System.out.println("Remote command result "+ret);
String result = getParam(ret, "result", "FAILED");
if ("SUCCESS".equals(result)) {
Hiscore hs = new Hiscore();
hs.name = getParam(ret, "name", "");
hs.score = Integer.parseInt(getParam(ret, "score", "0"));
return hs;
} else if ("FAILED".equals(result)) {
throw new Exception("Server result: "+getParam(result, "reason", "unknown"));
} else {
throw new Exception("Don't understand server response "+ret);
}
}
@Override
public void run() {
try {
Hiscore cached = cache.get(params);
if (cached == null) {
ops.add(new Runnable() {
@Override
public void run() {
if (params.world == world && params.template == template && params.mapsize == SIZE[mapsize]) {
setVisible(ID_ONLINE_BEST_TIME, true);
setVisible(ID_ONLINE_BEST_TIME_LABEL, true);
getArea(ID_ONLINE_BEST_TIME).setText("");
getArea(ID_ONLINE_BEST_TIME_LABEL).setText(Game.getMessage("ultraworm.selectsurvival.fetching_online_hiscore")+"...");
}
}
});
cached = getHiscore();
cache.put(params, cached);
} else {
}
final Hiscore hiscore = cached;
ops.add(new Runnable() {
@Override
public void run() {
if (params.world == world && params.template == template && params.mapsize == SIZE[mapsize]) {
setVisible(ID_ONLINE_BEST_TIME, true);
setVisible(ID_ONLINE_BEST_TIME_LABEL, true);
getArea(ID_ONLINE_BEST_TIME_LABEL).setText(Game.getMessage("ultraworm.selectsurvival.online_hiscore_record")+":");
doUpdateBestTime(ID_ONLINE_BEST_TIME, params.world, params.template, params.mapsize, hiscore.name, hiscore.score);
}
}
});
} catch (InterruptedException e) {
// Ignore
} catch (Exception e) {
e.printStackTrace(System.err);
ops.add(new Runnable() {
@Override
public void run() {
if (params.world == world && params.template == template && params.mapsize == SIZE[mapsize]) {
getArea(ID_ONLINE_BEST_TIME_LABEL).setText(Game.getMessage("ultraworm.selectsurvival.failed_to_retrieve"));
}
}
});
} finally {
synchronized (LOCK) {
current = null;
}
}
}
}
private static void addParam(String param, String data, StringBuilder sb) throws UnsupportedEncodingException {
if (sb.length() > 0) {
sb.append('&');
}
sb.append(param);
sb.append('=');
sb.append(URLEncoder.encode(data, "utf8"));
}
private static final String getParam(String data, String param, String _default) {
StringTokenizer st = new StringTokenizer(data, "&", false);
param += "=";
while (st.hasMoreTokens()) {
String t = st.nextToken();
if (t.startsWith(param)) {
try {
return URLDecoder.decode(t.substring(param.length()), "utf8");
} catch (UnsupportedEncodingException e) {
System.err.println("Failed to decode "+data);
e.printStackTrace(System.err);
}
}
}
return _default;
}
/**
* C'tor
*/
public SelectSurvivalLevelScreen(String name) {
super(name);
setAutoCreated();
}
public static void show() {
instance.open();
}
@Override
protected void doRegister() {
instance = this;
}
@Override
protected void doCreateScreen() {
ops = Collections.synchronizedList(new ArrayList<Runnable>());
cache = Collections.synchronizedMap(new HashMap<Params, Hiscore>());
}
@Override
protected void doTick() {
synchronized (LOCK) {
if (current == null && pending != null) {
current = pending;
pending = null;
current.start();
}
}
synchronized (ops) {
if (ops.size() > 0) {
for (Runnable r : ops) {
r.run();
}
ops.clear();
}
}
}
@Override
protected void onOpen() {
cache.clear();
// Always use earth color map
ColorMapFeature.getDefaultColorMap().copy((ColorMapFeature) Resources.get("earth.colormap"));
// Max world available:
int maxWorld = Worm.getMaxWorld();
for (int i = 0; i < 5; i ++) {
if (i == world) {
// Remember last selected world
setGroupVisible(GROUP_ID_WORLD+i, false);
setGroupVisible(GROUP_ID_WORLD_SELECTED+i, true);
} else {
setGroupVisible(GROUP_ID_WORLD_SELECTED+i, false);
}
setGroupEnabled(GROUP_ID_WORLD+i, i < maxWorld);
}
for (int i = 0; i < 4; i ++) {
if (i == template) {
// Remember last selected template
setGroupVisible(GROUP_ID_TERRAIN+i, false);
setGroupVisible(GROUP_ID_TERRAIN_SELECTED+i, true);
} else {
setGroupVisible(GROUP_ID_TERRAIN_SELECTED+i, false);
}
}
for (int i = 0; i < 3; i ++) {
if (i == mapsize) {
// Remember last selected size
setGroupVisible(GROUP_ID_SIZE+i, false);
setGroupVisible(GROUP_ID_SIZE_SELECTED+i, true);
} else {
setGroupVisible(GROUP_ID_SIZE_SELECTED+i, false);
}
}
updateBestTime();
}
@Override
protected void onClose() {
synchronized (LOCK) {
if (current != null) {
current.interrupt();
current = null;
}
pending = null;
}
ops.clear();
}
@Override
protected void onClicked(String id) {
if (ID_BACK.equals(id)) {
TitleScreen.show();
} else if (ID_PLAY.equals(id)) {
// Construct a completely new gamestate at this point
Worm.resetGameState();
Worm.getGameState().doInit(new SurvivalParams(template, WorldFeature.getWorld(world), Res.getSurvivalMapTemplate(world, template), DIFFICULTY[world], SIZE[mapsize], true));
} else if (id.startsWith(ID_WORLD)) {
int newWorld = Character.getNumericValue(id.charAt(ID_WORLD.length()));
if (newWorld!=world){
setGroupVisible(GROUP_ID_WORLD+world, true);
setGroupVisible(GROUP_ID_WORLD_SELECTED+world, false);
world = newWorld;
setGroupVisible(GROUP_ID_WORLD+world, false);
setGroupVisible(GROUP_ID_WORLD_SELECTED+world, true);
updateBestTime();
}
} else if (id.startsWith(ID_TERRAIN)) {
int newTemplate = Character.getNumericValue(id.charAt(ID_TERRAIN.length()));
if (newTemplate!=template){
setGroupVisible(GROUP_ID_TERRAIN+template, true);
setGroupVisible(GROUP_ID_TERRAIN_SELECTED+template, false);
template = newTemplate;
setGroupVisible(GROUP_ID_TERRAIN+template, false);
setGroupVisible(GROUP_ID_TERRAIN_SELECTED+template, true);
updateBestTime();
}
} else if (id.startsWith(ID_SIZE)) {
int newMapsize = Character.getNumericValue(id.charAt(ID_SIZE.length()));
if (newMapsize!=mapsize){
setGroupVisible(GROUP_ID_SIZE+mapsize, true);
setGroupVisible(GROUP_ID_SIZE_SELECTED+mapsize, false);
mapsize = newMapsize;
setGroupVisible(GROUP_ID_SIZE+mapsize, false);
setGroupVisible(GROUP_ID_SIZE_SELECTED+mapsize, true);
updateBestTime();
}
}
}
private void doUpdateBestTime(String area, int w, int t, int ms, String name, int bestTime) {
String msg = "{font:bigfont.glfont}";
if (bestTime == 0) {
msg += Game.getMessage("ultraworm.selectsurvival.not_yet_played");
} else {
msg += TimeUtil.format(bestTime);
}
if (bestTime == 0 || name.equals("")) {
getArea(area).setText(msg);
} else {
getArea(area).setText(msg+"{font:tinyfont.glfont}\n("+name+")");
}
}
private void updateBestTime() {
int bestTime = Game.getRoamingPreferences().getInt("survival.hiscore."+WorldFeature.getWorld(world).getUntranslated()+"."+Res.getSurvivalMapTemplate(world, template).getClass().getName()+"."+SIZE[mapsize]+".time", 0);
String name = Game.getRoamingPreferences().get("survival.hiscore."+WorldFeature.getWorld(world).getUntranslated()+"."+Res.getSurvivalMapTemplate(world, template).getClass().getName()+"."+SIZE[mapsize]+".name", "");
doUpdateBestTime(ID_BEST_TIME, world, template, mapsize, name, bestTime);
if (MiniGame.getSubmitRemoteHiscores()) {
synchronized (LOCK) {
if (current != null) {
current.interrupt();
}
pending = new ScoreGetter(new Params(world, template, SIZE[mapsize]));
}
} else {
setVisible(ID_ONLINE_BEST_TIME, false);
setVisible(ID_ONLINE_BEST_TIME_LABEL, false);
}
}
}