# HG changeset patch # User Medo # Date 1344804383 -7200 # Node ID 0481bd74267cce2e7bdc5090b554586c96c6afdb # Parent d70a5b0d11902313910efd1f9078d4953627048c Hedgeroid: - (hopefully) completed the frontlib JNA mappings - added documentation - Changed more code to use frontlib for ini reading/writing - tried to make everything work again that was working before - Teamlist can now be used to add and remove teams - Netplay now correctly handles team additions when being room chief - Fixed TeamCreatorActivity so that editing teams works diff -r d70a5b0d1190 -r 0481bd74267c project_files/Android-build/SDL-android-project/assets/assetsversion.txt --- a/project_files/Android-build/SDL-android-project/assets/assetsversion.txt Sun Aug 12 22:37:57 2012 +0200 +++ b/project_files/Android-build/SDL-android-project/assets/assetsversion.txt Sun Aug 12 22:46:23 2012 +0200 @@ -1,1 +1,1 @@ -4 \ No newline at end of file +5 \ No newline at end of file diff -r d70a5b0d1190 -r 0481bd74267c project_files/Android-build/SDL-android-project/res/layout/team_creation.xml --- a/project_files/Android-build/SDL-android-project/res/layout/team_creation.xml Sun Aug 12 22:37:57 2012 +0200 +++ b/project_files/Android-build/SDL-android-project/res/layout/team_creation.xml Sun Aug 12 22:46:23 2012 +0200 @@ -15,8 +15,6 @@ android:layout_width="fill_parent" android:layout_height="fill_parent" android:layout_weight="1"> - - - - - - Clean - - 101000900001000001100000000000000000000000000000100000 - - - 040504054160065554655446477657666666615551010111541111 - - - 000000000000000000000000000000000000000000000000000000 - - - 131111031211111112311411111111111111121111110111111111 - - - diff -r d70a5b0d1190 -r 0481bd74267c project_files/Android-build/SDL-android-project/res/raw/weapon_crazy --- a/project_files/Android-build/SDL-android-project/res/raw/weapon_crazy Sun Aug 12 22:37:57 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,17 +0,0 @@ - - - Crazy - - 999999999999999999299999999999999929999999990999999229 - - - 111111011111111111111111111111111111111111110111111111 - - - 000000000000000000000000000000000000000000000000000000 - - - 131111031211111112311411111111111111121111010111111111 - - - diff -r d70a5b0d1190 -r 0481bd74267c project_files/Android-build/SDL-android-project/res/raw/weapon_default --- a/project_files/Android-build/SDL-android-project/res/raw/weapon_default Sun Aug 12 22:37:57 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,17 +0,0 @@ - - - Default - - 939192942219912103223511100120100000021111010101111991 - - - 040504054160065554655446477657666666615551010111541111 - - - 000000000000020550000004000700400000000022000000060000 - - - 131111031211111112311411111111111111121111110111111111 - - - diff -r d70a5b0d1190 -r 0481bd74267c project_files/Android-build/SDL-android-project/res/raw/weapon_mines --- a/project_files/Android-build/SDL-android-project/res/raw/weapon_mines Sun Aug 12 22:37:57 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,17 +0,0 @@ - - - Mines - - 000000990009000000030000000000000000000000000000000000 - - - 000000000000000000000000000000000000000000000000000000 - - - 000000000000020550000004000700400000000020000000060000 - - - 111111111111111111111111111111111111111111110111111111 - - - diff -r d70a5b0d1190 -r 0481bd74267c project_files/Android-build/SDL-android-project/res/raw/weapon_portals --- a/project_files/Android-build/SDL-android-project/res/raw/weapon_portals Sun Aug 12 22:37:57 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,17 +0,0 @@ - - - Portals - - 900000900200000000210000000000000011000009000000000000 - - - 040504054160065554655446477657666666615551010111541111 - - - 000000000000020550000004000700400000000020000000060000 - - - 131111031211111112311411111111111111121111110111111111 - - - diff -r d70a5b0d1190 -r 0481bd74267c project_files/Android-build/SDL-android-project/res/raw/weapon_promode --- a/project_files/Android-build/SDL-android-project/res/raw/weapon_promode Sun Aug 12 22:37:57 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,17 +0,0 @@ - - - Pro Mode - - 909000900000000000000900000000000000000000000000000000 - - - 000000000000000000000000000000000000000000000000000000 - - - 000000000000020550000004000700400000000020000000000000 - - - 111111111111111111111111111111111111111110010111111111 - - - diff -r d70a5b0d1190 -r 0481bd74267c project_files/Android-build/SDL-android-project/res/raw/weapon_shoppa --- a/project_files/Android-build/SDL-android-project/res/raw/weapon_shoppa Sun Aug 12 22:37:57 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,17 +0,0 @@ - - - Shoppa - - 000000990000000000000000000000000000000000000000000000 - - - 444441004424440221011212122242200000000200040001001111 - - - 000000000000000000000000000000000000000000000000000000 - - - 111111111111111111111111111111111111111110110111111111 - - - diff -r d70a5b0d1190 -r 0481bd74267c project_files/Android-build/SDL-android-project/res/raw/weapons_builtin.ini --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/project_files/Android-build/SDL-android-project/res/raw/weapons_builtin.ini Sun Aug 12 22:46:23 2012 +0200 @@ -0,0 +1,8 @@ +[General] +%44efault=9391929422199121032235111001201000000211110101011111011040504054160065554655446477657666666615551010111541101100000000000002055000000400070040000000002200000006000001311110312111111123114111111111111111211111101111111010 +%43razy=9999999999999999992999999999999999299999999909999992099111111011111111111111111111111111111111111110111111101100000000000000000000000000000000000000000000000000000001311110312111111123114111111111111111211110101111111011 +%50ro%20%4dode=9090009000000000000009000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002055000000400070040000000002000000000000001111111111111111111111111111111111111111100101111111011 +%53hoppa=0000009900000000000000000000000000000000000000000000000444441004424440221011212122242200000000200040001001100000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111111101101111111001 +%43lean%20%53late=1010009000010000011000000000000000000000000000001000000040504054160065554655446477657666666615551010111541101100000000000000000000000000000000000000000000000000000001311110312111111123114111111111111111211111101111111011 +%4dinefield=0000009900090000000300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002055000000400070040000000002000000006000001111111111111111111111111111111111111111111101111111011 +%54hinking%20with%20%50ortals=9000009002000000002100000000000000110000090000000000000040504054160065554655446477657666666615551010111541101100000000000002055000000400070040000000002000000006000001311110312111111123114111111111111111211111101111111011 diff -r d70a5b0d1190 -r 0481bd74267c project_files/Android-build/SDL-android-project/res/values/frontend_data_pointers.xml --- a/project_files/Android-build/SDL-android-project/res/values/frontend_data_pointers.xml Sun Aug 12 22:37:57 2012 +0200 +++ b/project_files/Android-build/SDL-android-project/res/values/frontend_data_pointers.xml Sun Aug 12 22:46:23 2012 +0200 @@ -1,16 +1,5 @@ - - - @raw/weapon_default - @raw/weapon_clean - @raw/weapon_crazy - @raw/weapon_mines - @raw/weapon_portals - @raw/weapon_promode - @raw/weapon_shoppa - - @raw/team_one @raw/team_two diff -r d70a5b0d1190 -r 0481bd74267c project_files/Android-build/SDL-android-project/res/values/strings.xml --- a/project_files/Android-build/SDL-android-project/res/values/strings.xml Sun Aug 12 22:37:57 2012 +0200 +++ b/project_files/Android-build/SDL-android-project/res/values/strings.xml Sun Aug 12 22:46:23 2012 +0200 @@ -123,7 +123,8 @@ Unable to authenticate for your username. The connection to the server was lost. Saving has failed. - + Error: Either the sdcard is not available, or files are missing from the Data directory. + Error: This team uses assets which we do not have. Try downloading the big data package from the main menu. Please wait @@ -134,6 +135,7 @@ remember password Room name Create new room + Add team Disconnected: %1$s The room was closed because the owner left. diff -r d70a5b0d1190 -r 0481bd74267c project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/FrontendDataUtils.java --- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/FrontendDataUtils.java Sun Aug 12 22:37:57 2012 +0200 +++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/FrontendDataUtils.java Sun Aug 12 22:46:23 2012 +0200 @@ -20,15 +20,14 @@ package org.hedgewars.hedgeroid.Datastructures; import java.io.File; +import java.io.FileNotFoundException; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import org.hedgewars.hedgeroid.R; -import org.hedgewars.hedgeroid.StartGameActivity; import org.hedgewars.hedgeroid.Utils; -import org.hedgewars.hedgeroid.Datastructures.Map.MapType; import android.content.Context; import android.graphics.Bitmap; @@ -36,59 +35,75 @@ public class FrontendDataUtils { - - public static ArrayList getMaps(Context c){ + /** + * @throws FileNotFoundException if the sdcard isn't available or the Maps directory doesn't exist + */ + public static ArrayList getMaps(Context c) throws FileNotFoundException { File[] files = Utils.getFilesFromRelativeDir(c,"Maps"); - ArrayList ret = new ArrayList(); + ArrayList ret = new ArrayList(); - for(File f : files){ - if(Utils.hasFileWithSuffix(f, ".lua")){ - ret.add(new Map(f,MapType.TYPE_MISSION, c)); - }else{ - ret.add(new Map(f, MapType.TYPE_DEFAULT,c)); - } + for(File f : files) { + boolean isMission = Utils.hasFileWithSuffix(f, ".lua"); + ret.add(new MapFile(f.getName(), isMission)); } - Collections.sort(ret); + Collections.sort(ret, MapFile.MISSIONS_FIRST_NAME_ORDER); return ret; } - public static List getGameplay(Context c){ - String[] files = Utils.getFileNamesFromRelativeDir(c, "Scripts/Multiplayer"); + /** + * Returns a list of all multiplayer scripts (game styles) + * @throws FileNotFoundException if the sdcard isn't available or the Scripts/Multiplayer directory doesn't exist + */ + public static List getGameStyles(Context c) throws FileNotFoundException { + File[] files = Utils.getFilesFromRelativeDir(c, "Scripts/Multiplayer"); ArrayList ret = new ArrayList(); - - for(int i = 0; i < files.length; i++){ - if(files[i].endsWith(".lua")){ - ret.add(files[i].replace('_', ' ').substring(0, files[i].length()-4)); //replace _ by a space and removed the last four characters (.lua) + /* + * Caution: It is important that the "empty" style has this exact name, because + * it will be interpreted as "don't load a script" by the frontlib, and also by + * the QtFrontend in a netgame. This should probably be improved some time + * (maybe TODO add a dummy script called "Normal" to the MP scripts?) + */ + ret.add("Normal"); + for(int i = 0; i < files.length; i++) { + String name = files[i].getName(); + if(name.endsWith(".lua")){ + //replace _ by a space and removed the last four characters (.lua) + ret.add(name.replace('_', ' ').substring(0, name.length()-4)); } } - ret.add(0,"None"); - Collections.sort(ret); - return ret; + Collections.sort(ret, String.CASE_INSENSITIVE_ORDER); + return ret; } - public static List getThemes(Context c){ + /** + * @throws FileNotFoundException if the sdcard isn't available or the Themes directory doesn't exist + */ + public static List getThemes(Context c) throws FileNotFoundException { List list = Utils.getDirsWithFileSuffix(c, "Themes", "icon.png"); - Collections.sort(list); + Collections.sort(list, String.CASE_INSENSITIVE_ORDER); return list; } - public static List getWeapons(Context c){ + public static List getWeaponsets(Context c) { // TODO stub, re-implement /*List list = Weapon.getWeapons(c); Collections.sort(list);*/ return Collections.emptyList(); } - public static ArrayList> getGraves(Context c){ - String pathPrefix = Utils.getDataPath(c) + "Graphics/Graves/"; - ArrayList names = Utils.getFilesFromDirWithSuffix(c,"Graphics/Graves", ".png", true); + /** + * @throws FileNotFoundException if the sdcard isn't available or the Graphics/Graves directory doesn't exist + */ + public static ArrayList> getGraves(Context c) throws FileNotFoundException { + File gravePath = new File(new File(Utils.getDataPathFile(c), "Graphics"), "Graves"); + ArrayList names = Utils.getFileNamesFromDirWithSuffix(c,"Graphics/Graves", ".png", true); ArrayList> data = new ArrayList>(names.size()); for(String s : names){ HashMap map = new HashMap(); map.put("txt", s); - Bitmap b = BitmapFactory.decodeFile(pathPrefix + s + ".png");//create a full path - decode to to a bitmap + Bitmap b = BitmapFactory.decodeFile(new File(gravePath, s + ".png").getAbsolutePath()); int width = b.getWidth(); if(b.getHeight() > width){//some pictures contain more 'frames' underneath each other, if so we only use the first frame Bitmap tmp = Bitmap.createBitmap(width, width, b.getConfig()); @@ -104,22 +119,28 @@ return data; } - public static ArrayList> getFlags(Context c){ - String pathPrefix = Utils.getDataPath(c) + "Graphics/Flags/"; - ArrayList names = Utils.getFilesFromDirWithSuffix(c, "Graphics/Flags", ".png", true); + /** + * @throws FileNotFoundException if the sdcard isn't available or the Graphics/Graves directory doesn't exist + */ + public static ArrayList> getFlags(Context c) throws FileNotFoundException { + File flagsPath = new File(new File(Utils.getDataPathFile(c), "Graphics"), "Flags"); + ArrayList names = Utils.getFileNamesFromDirWithSuffix(c, "Graphics/Flags", ".png", true); ArrayList> data = new ArrayList>(names.size()); for(String s : names){ HashMap map = new HashMap(); map.put("txt", s); - Bitmap b = BitmapFactory.decodeFile(pathPrefix + s + ".png");//create a full path - decode to to a bitmap + Bitmap b = BitmapFactory.decodeFile(new File(flagsPath, s + ".png").getAbsolutePath()); map.put("img", b); data.add(map); } return data; } - public static ArrayList getVoices(Context c){ + /** + * @throws FileNotFoundException if the sdcard isn't available or the Sounds/voices directory doesn't exist + */ + public static ArrayList getVoices(Context c) throws FileNotFoundException { File[] files = Utils.getFilesFromRelativeDir(c, "Sounds/voices"); ArrayList ret = new ArrayList(); @@ -129,9 +150,14 @@ return ret; } - public static ArrayList getForts(Context c){ - return Utils.getFilesFromDirWithSuffix(c,"Forts", "L.png", true); + /** + * @throws FileNotFoundException if the sdcard isn't available or the Forts directory doesn't exist + */ + public static ArrayList getForts(Context c) throws FileNotFoundException { + return Utils.getFileNamesFromDirWithSuffix(c,"Forts", "L.png", true); } + + // TODO wat public static ArrayList> getTypes(Context c){ ArrayList> data = new ArrayList>(6); String[] levels = {c.getString(R.string.human), c.getString(R.string.bot5), c.getString(R.string.bot4), c.getString(R.string.bot3), c.getString(R.string.bot2), c.getString(R.string.bot1)}; @@ -147,9 +173,12 @@ return data; } - public static ArrayList> getHats(Context c){ - ArrayList files = Utils.getFilesFromDirWithSuffix(c,"Graphics/Hats", ".png", true); - String pathPrefix = Utils.getDataPath(c) + "Graphics/Hats/"; + /** + * @throws FileNotFoundException if the sdcard isn't available or the Graphics/Hats directory doesn't exist + */ + public static ArrayList> getHats(Context c) throws FileNotFoundException { + ArrayList files = Utils.getFileNamesFromDirWithSuffix(c,"Graphics/Hats", ".png", true); + File hatsPath = new File(new File(Utils.getDataPathFile(c), "Graphics"), "Hats"); int size = files.size(); ArrayList> data = new ArrayList>(size); @@ -157,7 +186,7 @@ for(String s : files){ hashmap = new HashMap(); hashmap.put("txt", s); - Bitmap b = BitmapFactory.decodeFile(pathPrefix + s + ".png");//create a full path - decode to to a bitmap + Bitmap b = BitmapFactory.decodeFile(new File(hatsPath, s + ".png").getAbsolutePath()); b = Bitmap.createBitmap(b, 0,0,b.getWidth()/2, b.getWidth()/2); hashmap.put("img", b); data.add(hashmap); @@ -166,8 +195,8 @@ return data; } - public static List getTeamFiles(Context c) { - List ret = new ArrayList(); + public static List getTeams(Context c) { + List ret = new ArrayList(); File teamsDir = new File(c.getFilesDir(), Team.DIRECTORY_TEAMS); File[] teamFileNames = teamsDir.listFiles(); @@ -175,15 +204,10 @@ for(File file : teamFileNames){ Team team = Team.load(file); if(team != null){ - ret.add(new TeamFile(team, file)); + ret.add(team); } } } return ret; } - - public static Scheme[] getSchemes(StartGameActivity startGameActivity) { - // TODO Auto-generated method stub - return null; - } } diff -r d70a5b0d1190 -r 0481bd74267c project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/GameConfig.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/GameConfig.java Sun Aug 12 22:46:23 2012 +0200 @@ -0,0 +1,54 @@ +/* + * Hedgewars for Android. An Android port of Hedgewars, a free turn based strategy game + * Copyright (c) 2011-2012 Richard Deurwaarder + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License + * + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +package org.hedgewars.hedgeroid.Datastructures; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + + +/** + * Game configuration from the point of view of the UI. This differs slightly from the + * frontlib view, because the engine allows setting a separate initial health and weapon set + * for each hog, while the Android UI currently only supports both attributes on a per-game + * basis (initial health is contained in the scheme). + * + * This difference means that creating a GameConfig object from a frontlib object can, in + * theory, lose information. This does not cause problems at the moment because for now + * weaponset and initial health are always per-game, but that might change in the future. + */ +public final class GameConfig { + public static final String DEFAULT_STYLE = "Normal"; + public static final String DEFAULT_SCHEME = "Default"; + public static final String DEFAULT_WEAPONSET = "Default"; + + public final String style; + public final Scheme scheme; + public final MapRecipe map; + public final List teams; + public final Weaponset weaponset; + + public GameConfig(String style, Scheme scheme, MapRecipe map, List teams, Weaponset weaponset) { + this.style = style; + this.scheme = scheme; + this.map = map; + this.teams = Collections.unmodifiableList(new ArrayList(teams)); + this.weaponset = weaponset; + } +} diff -r d70a5b0d1190 -r 0481bd74267c project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/Map.java --- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/Map.java Sun Aug 12 22:37:57 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,122 +0,0 @@ -/* - * Hedgewars for Android. An Android port of Hedgewars, a free turn based strategy game - * Copyright (c) 2011-2012 Richard Deurwaarder - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License - * - * 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA - */ - -package org.hedgewars.hedgeroid.Datastructures; - -import java.io.File; - -import android.content.Context; -import android.graphics.drawable.Drawable; - -public class Map implements Comparable { - - private static final String MISSION_PREFIX = "Mission: "; - - private String name; - private String path; - private String previewPath; - private MapType type; - - public Map(File mapDir, MapType _type, Context c){ - type = _type; - - name = mapDir.getName(); - path = mapDir.getAbsolutePath(); - previewPath = path + "/preview.png"; - - /*switch(type){ - case TYPE_DEFAULT: - - break; - case TYPE_GENERATED: - //TODO - break; - case TYPE_MISSION: - name = MISSION_PREFIX + mapDir.getName(); - path = mapDir.getAbsolutePath(); - break; - }*/ - - - } - - public String toString(){ - switch(type){ - default: - case TYPE_DEFAULT: - return name; - case TYPE_GENERATED: - return "bla"; - case TYPE_MISSION: - return MISSION_PREFIX + name; - } - } - - public MapType getType(){ - return type; - } - - public Drawable getDrawable(){ - switch(type){ - case TYPE_MISSION: - case TYPE_DEFAULT: - return Drawable.createFromPath(previewPath); - case TYPE_GENERATED: - - default: - return null; - } - } - - public int compareTo(Map another) { - switch(type){ - case TYPE_GENERATED: - switch(another.getType()){ - case TYPE_GENERATED: - return name.compareTo(another.name); - case TYPE_MISSION: - return -1; - case TYPE_DEFAULT: - return -1; - } - case TYPE_MISSION: - switch(another.getType()){ - case TYPE_GENERATED: - return 1; - case TYPE_MISSION: - return name.compareTo(another.name); - case TYPE_DEFAULT: - return -1; - } - case TYPE_DEFAULT: - switch(another.getType()){ - case TYPE_GENERATED: - return 1; - case TYPE_MISSION: - return 1; - case TYPE_DEFAULT: - return name.compareTo(another.name); - } - } - return 0;//default case this should never happen - } - - public enum MapType{ - TYPE_DEFAULT, TYPE_MISSION, TYPE_GENERATED - } -} diff -r d70a5b0d1190 -r 0481bd74267c project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/MapFile.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/MapFile.java Sun Aug 12 22:46:23 2012 +0200 @@ -0,0 +1,52 @@ +package org.hedgewars.hedgeroid.Datastructures; + +import java.io.File; +import java.io.FileNotFoundException; +import java.util.Comparator; + +import org.hedgewars.hedgeroid.Utils; + +import android.content.Context; +import android.widget.AdapterView.OnItemSelectedListener; + +/** + * Represents a map from the data directory + */ +public final class MapFile { + public static final String MISSION_PREFIX = "Mission: "; // TODO move text generation to UI to allow translation + public static final String MAP_DIRECTORY = "Maps"; + + public final String name; + public final boolean isMission; + + public MapFile(String name, boolean isMission) { + this.name = name; + this.isMission = isMission; + } + + /** + * @throws FileNotFoundException if the sdcard is not available. Does NOT throw if the requested map file does not exist. + */ + public static File getFileForMapname(Context ctx, String mapname) throws FileNotFoundException { + return new File(new File(Utils.getDataPathFile(ctx), MAP_DIRECTORY), mapname); + } + + public static final Comparator MISSIONS_FIRST_NAME_ORDER = new Comparator() { + public int compare(MapFile lhs, MapFile rhs) { + if(lhs.isMission != rhs.isMission) { + return lhs.isMission && !rhs.isMission ? -1 : 1; + } else { + return lhs.name.compareToIgnoreCase(rhs.name); + } + } + }; + + @Override + public String toString() { + return (isMission ? MISSION_PREFIX : "") + name; + } + + public File getPreviewFile(Context c) throws FileNotFoundException { + return new File(new File(new File(Utils.getDataPathFile(c), MAP_DIRECTORY), name), "preview.png"); + }; +} diff -r d70a5b0d1190 -r 0481bd74267c project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/MapRecipe.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/MapRecipe.java Sun Aug 12 22:46:23 2012 +0200 @@ -0,0 +1,80 @@ +/* + * Hedgewars for Android. An Android port of Hedgewars, a free turn based strategy game + * Copyright (c) 2011-2012 Richard Deurwaarder + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License + * + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +package org.hedgewars.hedgeroid.Datastructures; + +import org.hedgewars.hedgeroid.R; +import org.hedgewars.hedgeroid.frontlib.Frontlib; + +import android.content.res.Resources; + +public final class MapRecipe { + public static final String MAPNAME_REGULAR = "+rnd+"; + public static final String MAPNAME_MAZE = "+maze+"; + public static final String MAPNAME_DRAWN = "+drawn+"; + + public final int mapgen; // Frontlib.MAPGEN_xxx + public final int templateFilter; // Frontlib.TEMPLATEFILTER_xxx, only used when mapgen==MAPGEN_REGULAR + public final int mazeSize; // Frontlib.MAZE_SIZE_xxx, only used when mapgen==MAPGEN_MAZE + public final String name, seed, theme; + + private final byte[] drawData; // For drawn maps, can be null. + + public MapRecipe(int mapgen, int templateFilter, int mazeSize, String name, String seed, String theme, byte[] drawData) { + this.mapgen = mapgen; + this.templateFilter = templateFilter; + this.mazeSize = mazeSize; + this.name = name; + this.seed = seed; + this.theme = theme; + this.drawData = drawData==null ? null : drawData.clone(); + } + + public static MapRecipe makeMap(String name, String seed, String theme) { + return new MapRecipe(Frontlib.MAPGEN_NAMED, 0, 0, name, seed, theme, null); + } + + public static MapRecipe makeRandomMap(int templateFilter, String seed, String theme) { + return new MapRecipe(Frontlib.MAPGEN_REGULAR, templateFilter, 0, MAPNAME_REGULAR, seed, theme, null); + } + + public static MapRecipe makeRandomMaze(int mazeSize, String seed, String theme) { + return new MapRecipe(Frontlib.MAPGEN_MAZE, 0, mazeSize, MAPNAME_MAZE, seed, theme, null); + } + + public static MapRecipe makeDrawnMap(String seed, String theme, byte[] drawData) { + return new MapRecipe(Frontlib.MAPGEN_DRAWN, 0, 0, MAPNAME_DRAWN, seed, theme, drawData); + } + + public byte[] getDrawData() { + return drawData==null ? null : drawData.clone(); + } + + public static String formatMapName(Resources res, String map) { + if(map.charAt(0)=='+') { + if(map.equals(MAPNAME_REGULAR)) { + return res.getString(R.string.map_regular); + } else if(map.equals(MAPNAME_MAZE)) { + return res.getString(R.string.map_maze); + } else if(map.equals(MAPNAME_DRAWN)) { + return res.getString(R.string.map_drawn); + } + } + return map; + } +} diff -r d70a5b0d1190 -r 0481bd74267c project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/RoomlistRoom.java --- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/RoomlistRoom.java Sun Aug 12 22:37:57 2012 +0200 +++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/RoomlistRoom.java Sun Aug 12 22:46:23 2012 +0200 @@ -1,14 +1,8 @@ package org.hedgewars.hedgeroid.Datastructures; -import org.hedgewars.hedgeroid.R; - import android.content.res.Resources; public final class RoomlistRoom { - public static final String MAP_REGULAR = "+rnd+"; - public static final String MAP_MAZE = "+maze+"; - public static final String MAP_DRAWN = "+drawn+"; - public final String name, map, scheme, weapons, owner; public final int playerCount, teamCount; public final boolean inProgress; @@ -25,21 +19,8 @@ this.inProgress = inProgress; } - public static String formatMapName(Resources res, String map) { - if(map.charAt(0)=='+') { - if(map.equals(MAP_REGULAR)) { - return res.getString(R.string.map_regular); - } else if(map.equals(MAP_MAZE)) { - return res.getString(R.string.map_maze); - } else if(map.equals(MAP_DRAWN)) { - return res.getString(R.string.map_drawn); - } - } - return map; - } - public String formatMapName(Resources res) { - return formatMapName(res, map); + return MapRecipe.formatMapName(res, map); } @Override diff -r d70a5b0d1190 -r 0481bd74267c project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/Scheme.java --- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/Scheme.java Sun Aug 12 22:37:57 2012 +0200 +++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/Scheme.java Sun Aug 12 22:46:23 2012 +0200 @@ -38,13 +38,19 @@ } public int getHealth() { - return settings.get("health"); + Integer health = settings.get("health"); + return health==null ? 100 : health.intValue(); } - @Override + /*@Override public String toString() { return "Scheme [metascheme=" + metascheme + ", name=" + name + ", settings=" + settings + ", mods=" + mods + "]"; + }*/ + + @Override + public String toString() { + return name; // TODO change back once StartGameActivity does not need this anymore } public static final Comparator caseInsensitiveNameComparator = new Comparator() { diff -r d70a5b0d1190 -r 0481bd74267c project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/Schemes.java --- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/Schemes.java Sun Aug 12 22:37:57 2012 +0200 +++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/Schemes.java Sun Aug 12 22:46:23 2012 +0200 @@ -32,6 +32,12 @@ return new File(c.getFilesDir(), "schemes_builtin.ini"); } + public static Map loadAllSchemes(Context c) throws IOException { + Map result = loadUserSchemes(c); + result.putAll(loadBuiltinSchemes(c)); + return result; + } + public static Map loadUserSchemes(Context c) throws IOException { return loadSchemes(c, getUserSchemesFile(c)); } diff -r d70a5b0d1190 -r 0481bd74267c project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/Team.java --- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/Team.java Sun Aug 12 22:37:57 2012 +0200 +++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/Team.java Sun Aug 12 22:46:23 2012 +0200 @@ -22,26 +22,28 @@ import java.io.IOException; import java.util.ArrayList; import java.util.Collections; +import java.util.Comparator; import java.util.List; +import org.hedgewars.hedgeroid.Utils; import org.hedgewars.hedgeroid.EngineProtocol.PascalExports; import org.hedgewars.hedgeroid.frontlib.Flib; import org.hedgewars.hedgeroid.frontlib.Frontlib.TeamPtr; -import android.util.Log; +import android.content.Context; public final class Team { public static final String DIRECTORY_TEAMS = "teams"; - public static final int maxNumberOfHogs = PascalExports.HWgetMaxNumberOfHogs(); + public static final int HEDGEHOGS_PER_TEAM = Flib.INSTANCE.flib_get_hedgehogs_per_team(); public static final int maxNumberOfTeams = PascalExports.HWgetMaxNumberOfTeams(); public final String name, grave, flag, voice, fort; public final List hogs; public Team(String name, String grave, String flag, String voice, String fort, List hogs) { - if(hogs.size() != maxNumberOfHogs) { - throw new IllegalArgumentException("A team must consist of "+maxNumberOfHogs+" hogs."); + if(hogs.size() != HEDGEHOGS_PER_TEAM) { + throw new IllegalArgumentException("A team must consist of "+HEDGEHOGS_PER_TEAM+" hogs."); } this.name = name; this.grave = grave; @@ -52,7 +54,6 @@ } public void save(File f) throws IOException { - Log.d("Team", "saving to "+f.getAbsolutePath()); TeamPtr teamPtr = TeamPtr.createJavaOwned(this); if(Flib.INSTANCE.flib_team_to_ini(f.getAbsolutePath(), teamPtr) != 0) { throw new IOException("Error saving team "+name); @@ -69,11 +70,21 @@ return null; } } - + + public static File getTeamfileByName(Context c, String teamName) { + return new File(new File(c.getFilesDir(), DIRECTORY_TEAMS), Utils.replaceBadChars(teamName)+".hwt"); + } + @Override public String toString() { return "Team [name=" + name + ", grave=" + grave + ", flag=" + flag + ", voice=" + voice + ", fort=" + fort + ", hogs=" + hogs + "]"; } + + public static Comparator NAME_ORDER = new Comparator() { + public int compare(Team lhs, Team rhs) { + return lhs.name.compareToIgnoreCase(rhs.name); + } + }; } diff -r d70a5b0d1190 -r 0481bd74267c project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/TeamFile.java --- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/TeamFile.java Sun Aug 12 22:37:57 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,18 +0,0 @@ -package org.hedgewars.hedgeroid.Datastructures; - -import java.io.File; - -public final class TeamFile { - public final Team team; - public final File file; - - public TeamFile(Team team, File file) { - this.team = team; - this.file = file; - } - - @Override - public String toString() { - return "TeamFile [team=" + team + ", file=" + file + "]"; - } -} diff -r d70a5b0d1190 -r 0481bd74267c project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/TeamInGame.java --- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/TeamInGame.java Sun Aug 12 22:37:57 2012 +0200 +++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/TeamInGame.java Sun Aug 12 22:46:23 2012 +0200 @@ -1,5 +1,10 @@ package org.hedgewars.hedgeroid.Datastructures; +/** + * A team with per-game configuration. This is similar to the frontlib "team" structure, + * except that it does not include weaponset and initial health, which are handled on a + * per-game basis in the UI, but per-hog in the frontlib. + */ public final class TeamInGame { public final Team team; public final TeamIngameAttributes ingameAttribs; diff -r d70a5b0d1190 -r 0481bd74267c project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/TeamIngameAttributes.java --- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/TeamIngameAttributes.java Sun Aug 12 22:37:57 2012 +0200 +++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/TeamIngameAttributes.java Sun Aug 12 22:46:23 2012 +0200 @@ -3,19 +3,19 @@ import java.util.ArrayList; import java.util.Random; +import org.hedgewars.hedgeroid.frontlib.Flib; + public final class TeamIngameAttributes { - public static final int[] TEAM_COLORS = { - 0xd12b42, /* red */ - 0x4980c1, /* blue */ - 0x6ab530, /* green */ - 0xbc64c4, /* purple */ - 0xe76d14, /* orange */ - 0x3fb6e6, /* cyan */ - 0xe3e90c, /* yellow */ - 0x61d4ac, /* mint */ - 0xf1c3e1, /* pink */ - /* add new colors here */ - }; + public static final int DEFAULT_HOG_COUNT = 4; + public static final int[] TEAM_COLORS; + + static { + int[] teamColors = new int[Flib.INSTANCE.flib_get_teamcolor_count()]; + for(int i=0; i - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License - * - * 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA - */ - -package org.hedgewars.hedgeroid.Datastructures; - -import org.hedgewars.hedgeroid.EngineProtocol.PascalExports; - -public class Weapon implements Comparable{ - public static final String DIRECTORY_WEAPON = "weapons"; - public static final int maxWeapons = PascalExports.HWgetNumberOfWeapons(); - - private String name; - private String QT; - private String prob; - private String delay; - private String crate; - - public Weapon(String _name, String _QT, String _prob, String _delay, String _crate){ - name = _name; - - //Incase there's a newer ammoStore which is bigger we append with zeros - StringBuffer sb = new StringBuffer(); - while(_QT.length() + sb.length() < maxWeapons){ - sb.append('0'); - } - - QT = String.format("e%s %s%s", "ammloadt", _QT, sb); - prob = String.format("e%s %s%s", "ammprob", _prob, sb); - delay = String.format("e%s %s%s", "ammdelay", _delay, sb); - crate = String.format("e%s %s%s", "ammreinf", _crate, sb); - } - - public String toString(){ - return name; - } - - public int compareTo(Weapon another) { - return name.compareTo(another.name); - } -} diff -r d70a5b0d1190 -r 0481bd74267c project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/Weaponset.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/Weaponset.java Sun Aug 12 22:46:23 2012 +0200 @@ -0,0 +1,22 @@ +package org.hedgewars.hedgeroid.Datastructures; + +import org.hedgewars.hedgeroid.frontlib.Flib; + +public final class Weaponset { + public static final int WEAPONS_COUNT = Flib.INSTANCE.flib_get_weapons_count(); + + public final String name, loadout, crateProb, crateAmmo, delay; + + public Weaponset(String name, String loadout, String crateProb, String crateAmmo, String delay) { + this.name = name; + this.loadout = loadout; + this.crateProb = crateProb; + this.crateAmmo = crateAmmo; + this.delay = delay; + } + + @Override + public String toString() { + return name; // TODO use the generated one once StartGameActivity doesn't need this anymore + } +} diff -r d70a5b0d1190 -r 0481bd74267c project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/Weaponsets.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/Weaponsets.java Sun Aug 12 22:46:23 2012 +0200 @@ -0,0 +1,63 @@ +package org.hedgewars.hedgeroid.Datastructures; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import org.hedgewars.hedgeroid.frontlib.Flib; +import org.hedgewars.hedgeroid.frontlib.Frontlib.WeaponsetListPtr; + +import android.content.Context; + +public final class Weaponsets { + private Weaponsets() { + throw new AssertionError("This class is not meant to be instantiated"); + } + + public static File getUserWeaponsetsFile(Context c) { + return new File(c.getFilesDir(), "weapons_user.ini"); + } + + public static File getBuiltinWeaponsetsFile(Context c) { + return new File(c.getFilesDir(), "weapons_builtin.ini"); + } + + public static List loadAllWeaponsets(Context c) throws IOException { + List result = loadBuiltinWeaponsets(c); + result.addAll(loadUserWeaponsets(c)); + return result; + } + + public static List loadUserWeaponsets(Context c) throws IOException { + return loadWeaponsets(c, getUserWeaponsetsFile(c)); + } + + public static List loadBuiltinWeaponsets(Context c) throws IOException { + return loadWeaponsets(c, getBuiltinWeaponsetsFile(c)); + } + + public static List loadWeaponsets(Context c, File weaponsetFile) throws IOException { + if(!weaponsetFile.isFile()) { + // No file == no weaponsets, no error + return new ArrayList(); + } + WeaponsetListPtr weaponsetListPtr = null; + try { + weaponsetListPtr = Flib.INSTANCE.flib_weaponsetlist_from_ini(weaponsetFile.getAbsolutePath()); + if(weaponsetListPtr == null) { + throw new IOException("Unable to read weaponsets from "+weaponsetFile); + } + return weaponsetListPtr.deref(); + } finally { + if(weaponsetListPtr != null) { + Flib.INSTANCE.flib_weaponsetlist_destroy(weaponsetListPtr); + } + } + } + + public static void saveUserWeaponsets(Context c, List weaponsets) throws IOException { + WeaponsetListPtr ptr = WeaponsetListPtr.createJavaOwned(weaponsets); + Flib.INSTANCE.flib_weaponsetlist_to_ini(getUserWeaponsetsFile(c).getAbsolutePath(), ptr); + } +} diff -r d70a5b0d1190 -r 0481bd74267c project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Downloader/DownloadAssets.java --- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Downloader/DownloadAssets.java Sun Aug 12 22:37:57 2012 +0200 +++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Downloader/DownloadAssets.java Sun Aug 12 22:46:23 2012 +0200 @@ -7,10 +7,9 @@ import org.hedgewars.hedgeroid.MainActivity; import org.hedgewars.hedgeroid.R; import org.hedgewars.hedgeroid.Utils; -import org.hedgewars.hedgeroid.Datastructures.Scheme; import org.hedgewars.hedgeroid.Datastructures.Schemes; import org.hedgewars.hedgeroid.Datastructures.Team; -import org.hedgewars.hedgeroid.Datastructures.Weapon; +import org.hedgewars.hedgeroid.Datastructures.Weaponsets; import android.content.res.AssetManager; import android.os.AsyncTask; @@ -46,7 +45,7 @@ protected Boolean doInBackground(Object... params) { try { Utils.writeStreamToFile(act.getResources().openRawResource(R.raw.schemes_builtin), Schemes.getBuiltinSchemesFile(act)); - Utils.resRawToFilesDir(act, R.array.weapons, Weapon.DIRECTORY_WEAPON); + Utils.writeStreamToFile(act.getResources().openRawResource(R.raw.weapons_builtin), Weaponsets.getBuiltinWeaponsetsFile(act)); Utils.resRawToFilesDir(act, R.array.teams, Team.DIRECTORY_TEAMS); copyFileOrDir(act.getAssets(), Utils.getDataPathFile(act), "Data"); copyFileOrDir(act.getAssets(), new File(Utils.getCachePath(act), VERSION_FILENAME), VERSION_FILENAME); diff -r d70a5b0d1190 -r 0481bd74267c project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/EngineProtocol/GameConfig.java --- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/EngineProtocol/GameConfig.java Sun Aug 12 22:37:57 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,45 +0,0 @@ -/* - * Hedgewars for Android. An Android port of Hedgewars, a free turn based strategy game - * Copyright (c) 2011-2012 Richard Deurwaarder - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License - * - * 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA - */ - -package org.hedgewars.hedgeroid.EngineProtocol; - -import java.util.ArrayList; - -import org.hedgewars.hedgeroid.Datastructures.GameMode; -import org.hedgewars.hedgeroid.Datastructures.Map; -import org.hedgewars.hedgeroid.Datastructures.Scheme; -import org.hedgewars.hedgeroid.Datastructures.Team; -import org.hedgewars.hedgeroid.Datastructures.Weapon; - -public class GameConfig { - public GameMode mode = GameMode.MODE_LOCAL; - public Map map = null; - public String theme = null; - public Scheme scheme = null; - public Weapon weapon = null; - - public String style = null; - public String training = null; - public String seed = null; - - public ArrayList teams = new ArrayList(); - - public GameConfig(){ - - } -} diff -r d70a5b0d1190 -r 0481bd74267c project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/EngineProtocol/PascalExports.java --- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/EngineProtocol/PascalExports.java Sun Aug 12 22:37:57 2012 +0200 +++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/EngineProtocol/PascalExports.java Sun Aug 12 22:46:23 2012 +0200 @@ -31,10 +31,6 @@ System.loadLibrary("hwengine"); } - public static native int HWversionInfoNetProto(); - public static native String HWversionInfoVersion(); - public static native int HWgetNumberOfWeapons(); public static native int HWgetMaxNumberOfTeams(); - public static native int HWgetMaxNumberOfHogs(); public static native int HWterminate(boolean b); } diff -r d70a5b0d1190 -r 0481bd74267c project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/MainActivity.java --- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/MainActivity.java Sun Aug 12 22:37:57 2012 +0200 +++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/MainActivity.java Sun Aug 12 22:46:23 2012 +0200 @@ -24,6 +24,7 @@ import org.hedgewars.hedgeroid.Downloader.DownloadAssets; import org.hedgewars.hedgeroid.Downloader.DownloadListActivity; +import org.hedgewars.hedgeroid.frontlib.Flib; import org.hedgewars.hedgeroid.netplay.LobbyActivity; import org.hedgewars.hedgeroid.netplay.Netplay; import org.hedgewars.hedgeroid.netplay.Netplay.State; diff -r d70a5b0d1190 -r 0481bd74267c project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/SDLActivity.java --- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/SDLActivity.java Sun Aug 12 22:37:57 2012 +0200 +++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/SDLActivity.java Sun Aug 12 22:46:23 2012 +0200 @@ -6,13 +6,16 @@ import javax.microedition.khronos.egl.EGLDisplay; import javax.microedition.khronos.egl.EGLSurface; -import org.hedgewars.hedgeroid.EngineProtocol.GameConfig; +import org.hedgewars.hedgeroid.Datastructures.GameConfig; import org.hedgewars.hedgeroid.EngineProtocol.PascalExports; import org.hedgewars.hedgeroid.frontlib.Flib; import org.hedgewars.hedgeroid.frontlib.Frontlib.GameSetupPtr; import org.hedgewars.hedgeroid.frontlib.Frontlib.GameconnPtr; +import org.hedgewars.hedgeroid.frontlib.Frontlib.IntCallback; import org.hedgewars.hedgeroid.netplay.TickHandler; +import com.sun.jna.Pointer; + import android.app.Activity; import android.content.Context; import android.graphics.Canvas; @@ -424,7 +427,18 @@ private final int surfaceWidth, surfaceHeight; private final GameConfig config; - + private GameconnPtr conn; + HandlerThread thread = new HandlerThread("IPC thread"); + + private final IntCallback dccb = new IntCallback() { + public void callback(Pointer context, int arg1) { + Log.d("SDLMain", "Disconnected: "+arg1); + Flib.INSTANCE.flib_gameconn_destroy(conn); + conn = null; + thread.quit(); + } + }; + public SDLMain(int width, int height, GameConfig _config) { config = _config; surfaceWidth = width; @@ -433,35 +447,40 @@ public void run() { //Set up the IPC socket server to communicate with the engine - HandlerThread thread = new HandlerThread("IPC thread"); - thread.start(); // TODO ensure it gets stopped + final GameconnPtr conn = Flib.INSTANCE.flib_gameconn_create("Xeli", GameSetupPtr.createJavaOwned(config), false); if(conn == null) { throw new AssertionError(); } + Flib.INSTANCE.flib_gameconn_onDisconnect(conn, dccb, null); int port = Flib.INSTANCE.flib_gameconn_getport(conn); String path = Utils.getDataPath(SDLActivity.mSingleton);//This represents the data directory path = path.substring(0, path.length()-1);//remove the trailing '/' + thread.start(); // TODO ensure it gets stopped + new TickHandler(thread.getLooper(), 500, new Runnable() { + public void run() { + Log.d("SDLMain", "pre-tick"); + Flib.INSTANCE.flib_gameconn_tick(conn); + Log.d("SDLMain", "post-tick"); + } + }).start(); + + Log.d("SDLMain", "Starting engine"); // Runs SDL_main() with added parameters SDLActivity.nativeInit(new String[] { String.valueOf(port), String.valueOf(surfaceWidth), String.valueOf(surfaceHeight), "0", "en.txt", "xeli", "1", "1", "1", path, "" }); - - new TickHandler(thread.getLooper(), 50, new Runnable() { - public void run() { - Flib.INSTANCE.flib_gameconn_tick(conn); - } - }); + Log.d("SDLMain", "Engine stopped"); try { - thread.quit(); thread.join(); } catch (InterruptedException e) { throw new AssertionError(); } + Log.v("SDLMain", "thread joined"); Flib.INSTANCE.flib_gameconn_destroy(conn); - Log.v("SDL", "SDL thread terminated"); + Log.v("SDLMain", "SDL thread terminated"); } } diff -r d70a5b0d1190 -r 0481bd74267c project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/StartGameActivity.java --- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/StartGameActivity.java Sun Aug 12 22:37:57 2012 +0200 +++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/StartGameActivity.java Sun Aug 12 22:46:23 2012 +0200 @@ -19,19 +19,29 @@ package org.hedgewars.hedgeroid; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.UUID; + import org.hedgewars.hedgeroid.Datastructures.FrontendDataUtils; -import org.hedgewars.hedgeroid.Datastructures.Map; -import org.hedgewars.hedgeroid.Datastructures.Map.MapType; +import org.hedgewars.hedgeroid.Datastructures.GameConfig; +import org.hedgewars.hedgeroid.Datastructures.MapFile; +import org.hedgewars.hedgeroid.Datastructures.MapRecipe; import org.hedgewars.hedgeroid.Datastructures.Scheme; -import org.hedgewars.hedgeroid.Datastructures.Team; -import org.hedgewars.hedgeroid.Datastructures.Weapon; -import org.hedgewars.hedgeroid.EngineProtocol.GameConfig; +import org.hedgewars.hedgeroid.Datastructures.Schemes; +import org.hedgewars.hedgeroid.Datastructures.TeamInGame; +import org.hedgewars.hedgeroid.Datastructures.Weaponset; +import org.hedgewars.hedgeroid.Datastructures.Weaponsets; import android.app.Activity; import android.content.Intent; import android.graphics.drawable.Drawable; import android.os.Bundle; -import android.os.Parcelable; +import android.util.Log; import android.view.View; import android.view.View.OnClickListener; import android.widget.AdapterView; @@ -43,17 +53,16 @@ import android.widget.Toast; public class StartGameActivity extends Activity { - public static final int ACTIVITY_TEAM_SELECTOR = 0; - private GameConfig config = null; private ImageButton start, back, team; private Spinner maps, gameplay, gamescheme, weapons, themes; private ImageView themeIcon, mapPreview, teamCount; + + private List teams = new ArrayList(); public void onCreate(Bundle savedInstanceState){ super.onCreate(savedInstanceState); - config = new GameConfig(); setContentView(R.layout.starting_game); @@ -75,22 +84,35 @@ back.setOnClickListener(backClicker); team.setOnClickListener(teamClicker); - ArrayAdapter adapter = new ArrayAdapter(this, R.layout.listview_item, FrontendDataUtils.getMaps(this)); + List mapFiles; + try { + mapFiles = FrontendDataUtils.getMaps(this); + } catch (FileNotFoundException e) { + Log.e("StartGameActivity", e.getMessage(), e); + mapFiles = Collections.emptyList(); + } + ArrayAdapter adapter = new ArrayAdapter(this, R.layout.listview_item, mapFiles); adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); maps.setAdapter(adapter); maps.setOnItemSelectedListener(mapsClicker); - //set to first nonmap + //set to first nonmission for(int i = 0; i < adapter.getCount(); i++){ - if(((Map)adapter.getItem(i)).getType() == MapType.TYPE_DEFAULT){ + if(!((MapFile)adapter.getItem(i)).isMission){ maps.setSelection(i, false); break; } } - adapter = new ArrayAdapter(this, R.layout.listview_item, FrontendDataUtils.getGameplay(this)); + List gameStyles; + try { + gameStyles = FrontendDataUtils.getGameStyles(this); + } catch (FileNotFoundException e) { + Log.e("StartGameActivity", e.getMessage(), e); + gameStyles = Collections.emptyList(); + } + adapter = new ArrayAdapter(this, R.layout.listview_item, gameStyles); adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); gameplay.setAdapter(adapter); - gameplay.setOnItemSelectedListener(gameplayClicker); //set to first nonmap for(int i = 0; i < adapter.getCount(); i++){ if(((String)adapter.getItem(i)).equals("None")){ @@ -99,30 +121,48 @@ } } - adapter = new ArrayAdapter(this, R.layout.listview_item, FrontendDataUtils.getSchemes(this)); + List schemes; + try { + schemes = new ArrayList(Schemes.loadAllSchemes(this).values()); + } catch (IOException e) { + Log.e("StartGameActivity", e.getMessage(), e); + schemes = Collections.emptyList(); + } + adapter = new ArrayAdapter(this, R.layout.listview_item, schemes); adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); gamescheme.setAdapter(adapter); - gamescheme.setOnItemSelectedListener(schemeClicker); - //set to first nonmap for(int i = 0; i < adapter.getCount(); i++){ - if(((Scheme)adapter.getItem(i)).toString().equals("Default")){ + if(((Scheme)adapter.getItem(i)).name.equals("Default")){ gamescheme.setSelection(i, false); break; } } - - adapter = new ArrayAdapter(this, R.layout.listview_item, FrontendDataUtils.getWeapons(this)); + List weaponsets; + try { + weaponsets = Weaponsets.loadAllWeaponsets(this); + } catch(IOException e) { + Log.e("StartGameActivity", e.getMessage(), e); + weaponsets = Collections.emptyList(); + } + adapter = new ArrayAdapter(this, R.layout.listview_item, weaponsets); adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); weapons.setAdapter(adapter); - weapons.setOnItemSelectedListener(weaponClicker); for(int i = 0; i < adapter.getCount(); i++){ - if(((Weapon)adapter.getItem(i)).toString().equals("Crazy")){ + if(((Weaponset)adapter.getItem(i)).name.equals("Crazy")){ weapons.setSelection(i, false); break; } } - adapter = new ArrayAdapter(this, R.layout.listview_item, FrontendDataUtils.getThemes(this)); + + List themeList; + try { + themeList = FrontendDataUtils.getThemes(this); + } catch(FileNotFoundException e) { + Log.e("StartGameActivity", e.getMessage(), e); + themeList = Collections.emptyList(); + } + adapter = new ArrayAdapter(this, R.layout.listview_item, themeList); adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); themes.setAdapter(adapter); themes.setOnItemSelectedListener(themesClicker); @@ -130,7 +170,7 @@ private void startTeamsActivity(){ Intent i = new Intent(StartGameActivity.this, TeamSelectionActivity.class); - // TODO i.putParcelableArrayListExtra("teams", config.teams); + TeamSelectionActivity.activityParams = new ArrayList(teams); startActivityForResult(i, ACTIVITY_TEAM_SELECTOR); } @@ -138,12 +178,9 @@ switch(requestCode){ case ACTIVITY_TEAM_SELECTOR: if(resultCode == Activity.RESULT_OK){ - Parcelable[] parcelables = (Parcelable[])data.getParcelableArrayExtra("teams"); - config.teams.clear(); - for(Parcelable t : parcelables){ - // TODO config.teams.add((Team)t); - } - teamCount.getDrawable().setLevel(config.teams.size()); + teams = new ArrayList(TeamSelectionActivity.activityReturn); + TeamSelectionActivity.activityReturn = Collections.emptyList(); + teamCount.getDrawable().setLevel(teams.size()); } break; } @@ -156,7 +193,6 @@ String themeName = (String) arg0.getAdapter().getItem(position); Drawable themeIconDrawable = Drawable.createFromPath(Utils.getDataPath(StartGameActivity.this) + "Themes/" + themeName + "/icon@2X.png"); themeIcon.setImageDrawable(themeIconDrawable); - config.theme = themeName; } public void onNothingSelected(AdapterView arg0) { @@ -167,9 +203,13 @@ private OnItemSelectedListener mapsClicker = new OnItemSelectedListener(){ public void onItemSelected(AdapterView arg0, View view, int position,long rowId) { - Map map = (Map)arg0.getAdapter().getItem(position); - mapPreview.setImageDrawable(map.getDrawable()); - config.map = map; + MapFile map = (MapFile)arg0.getAdapter().getItem(position); + try { + File previewFile = map.getPreviewFile(getApplicationContext()); + mapPreview.setImageDrawable(Drawable.createFromPath(previewFile.getAbsolutePath())); + } catch (FileNotFoundException e) { + mapPreview.setImageDrawable(null); + } } public void onNothingSelected(AdapterView arg0) { @@ -177,38 +217,19 @@ }; - private OnItemSelectedListener weaponClicker = new OnItemSelectedListener(){ - public void onItemSelected(AdapterView arg0, View arg1, int arg2, long arg3) { - config.weapon = (Weapon)arg0.getAdapter().getItem(arg2); - } - public void onNothingSelected(AdapterView arg0) { - - } - }; - private OnItemSelectedListener schemeClicker = new OnItemSelectedListener(){ - public void onItemSelected(AdapterView arg0, View arg1, int arg2, long arg3) { - config.scheme = (Scheme)arg0.getAdapter().getItem(arg2); - } - public void onNothingSelected(AdapterView arg0) { - - } - }; - private OnItemSelectedListener gameplayClicker = new OnItemSelectedListener(){ - public void onItemSelected(AdapterView arg0, View arg1, int arg2, long arg3) { - //config = ()arg0.getAdapter().getItem(arg2); - } - public void onNothingSelected(AdapterView arg0) { - - } - }; - private OnClickListener startClicker = new OnClickListener(){ public void onClick(View v) { - if(config.teams.size() < 2){ - Toast.makeText(StartGameActivity.this, R.string.not_enough_teams, Toast.LENGTH_LONG).show(); + if(teams.size() < 2) { + Toast.makeText(getApplicationContext(), R.string.not_enough_teams, Toast.LENGTH_LONG).show(); startTeamsActivity(); } else { - SDLActivity.startConfig = config; + String style = (String)gameplay.getSelectedItem(); + Scheme scheme = (Scheme)gamescheme.getSelectedItem(); + String mapName = ((MapFile)maps.getSelectedItem()).name; + String theme = (String)themes.getSelectedItem(); + MapRecipe map = MapRecipe.makeMap(mapName, UUID.randomUUID().toString(), theme); + Weaponset weaponset = (Weaponset)weapons.getSelectedItem(); + SDLActivity.startConfig = new GameConfig(style, scheme, map, teams, weaponset); Intent i = new Intent(StartGameActivity.this, SDLActivity.class); startActivity(i); } diff -r d70a5b0d1190 -r 0481bd74267c project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/TeamCreatorActivity.java --- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/TeamCreatorActivity.java Sun Aug 12 22:37:57 2012 +0200 +++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/TeamCreatorActivity.java Sun Aug 12 22:46:23 2012 +0200 @@ -20,11 +20,13 @@ package org.hedgewars.hedgeroid; import java.io.File; +import java.io.FileNotFoundException; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.NoSuchElementException; import org.hedgewars.hedgeroid.Datastructures.FrontendDataUtils; import org.hedgewars.hedgeroid.Datastructures.Hog; @@ -37,7 +39,6 @@ import android.os.Bundle; import android.view.View; import android.view.View.OnClickListener; -import android.view.View.OnFocusChangeListener; import android.widget.AdapterView; import android.widget.AdapterView.OnItemSelectedListener; import android.widget.ArrayAdapter; @@ -49,23 +50,29 @@ import android.widget.ScrollView; import android.widget.SimpleAdapter; import android.widget.Spinner; +import android.widget.SpinnerAdapter; import android.widget.TextView; import android.widget.Toast; -public class TeamCreatorActivity extends Activity implements Runnable{ - +/** + * Edit or create a team. If a team should be edited, it is supplied in the extras + * as parameter oldTeamName. + */ +public class TeamCreatorActivity extends Activity implements Runnable { + public static final String PARAMETER_EXISTING_TEAMNAME = "existingTeamName"; + private TextView name; private Spinner difficulty, grave, flag, voice, fort; private ImageView imgFort; private ArrayList hogDice = new ArrayList(); private ArrayList hogHat = new ArrayList(); private ArrayList hogName = new ArrayList(); - private ImageButton back, save, voiceButton; + private ImageButton voiceButton; private ScrollView scroller; private MediaPlayer mp = null; - private boolean settingsChanged = false; - private boolean saved = false; - private String fileName = null; + private boolean initComplete = false; + + private String existingTeamName = null; private final List> flagsData = new ArrayList>(); private final List> typesData = new ArrayList>(); @@ -76,6 +83,16 @@ public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + initComplete = false; + + // Restore state and read parameters + if(savedInstanceState != null) { + existingTeamName = savedInstanceState.getString(PARAMETER_EXISTING_TEAMNAME); + } else { + existingTeamName = getIntent().getStringExtra(PARAMETER_EXISTING_TEAMNAME); + } + + // Set up view setContentView(R.layout.team_creation); name = (TextView) findViewById(R.id.txtName); @@ -87,15 +104,11 @@ imgFort = (ImageView) findViewById(R.id.imgFort); - back = (ImageButton) findViewById(R.id.btnBack); - save = (ImageButton) findViewById(R.id.btnSave); voiceButton = (ImageButton) findViewById(R.id.btnPlay); scroller = (ScrollView) findViewById(R.id.scroller); - save.setOnClickListener(saveClicker); - back.setOnClickListener(backClicker); - + // Wire view elements LinearLayout ll = (LinearLayout) findViewById(R.id.HogsContainer); for (int i = 0; i < ll.getChildCount(); i++) { RelativeLayout team_creation_entry = (RelativeLayout) ll.getChildAt(i); @@ -108,107 +121,116 @@ .findViewById(R.id.txtTeam1)); } - SimpleAdapter sa = new SimpleAdapter(this, gravesData, - R.layout.spinner_textimg_entry, new String[] { "txt", "img" }, - new int[] { R.id.spinner_txt, R.id.spinner_img }); - sa.setDropDownViewResource(R.layout.spinner_textimg_dropdown_entry); - sa.setViewBinder(viewBinder); - grave.setAdapter(sa); - grave.setOnFocusChangeListener(focusser); - - sa = new SimpleAdapter(this, flagsData, R.layout.spinner_textimg_entry, - new String[] { "txt", "img" }, new int[] { R.id.spinner_txt, - R.id.spinner_img }); - sa.setDropDownViewResource(R.layout.spinner_textimg_dropdown_entry); - sa.setViewBinder(viewBinder); - flag.setAdapter(sa); - flag.setOnFocusChangeListener(focusser); - - sa = new SimpleAdapter(this, typesData, R.layout.spinner_textimg_entry, - new String[] { "txt", "img" }, new int[] { R.id.spinner_txt, - R.id.spinner_img }); - sa.setDropDownViewResource(R.layout.spinner_textimg_dropdown_entry); - difficulty.setAdapter(sa); - difficulty.setOnFocusChangeListener(focusser); - - sa = new SimpleAdapter(this, hatsData, R.layout.spinner_textimg_entry, - new String[] { "txt", "img" }, new int[] { R.id.spinner_txt, - R.id.spinner_img }); - sa.setDropDownViewResource(R.layout.spinner_textimg_dropdown_entry); - sa.setViewBinder(viewBinder); + grave.setAdapter(createMapSpinnerAdapter(gravesData)); + flag.setAdapter(createMapSpinnerAdapter(flagsData)); + difficulty.setAdapter(createMapSpinnerAdapter(typesData)); + SpinnerAdapter hatAdapter = createMapSpinnerAdapter(hatsData); for (Spinner spin : hogHat) { - spin.setAdapter(sa); + spin.setAdapter(hatAdapter); } - ArrayAdapter adapter = new ArrayAdapter(this, R.layout.listview_item, voicesData); - adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); - voice.setAdapter(adapter); - voice.setOnFocusChangeListener(focusser); + + voice.setAdapter(createListSpinnerAdapter(voicesData)); voiceButton.setOnClickListener(voiceClicker); - adapter = new ArrayAdapter(this, R.layout.listview_item, fortsData); - adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); - fort.setAdapter(adapter); + fort.setAdapter(createListSpinnerAdapter(fortsData)); fort.setOnItemSelectedListener(fortSelector); - fort.setOnFocusChangeListener(focusser); new Thread(this).start(); } - public void run(){ - final ArrayList> gravesDataNew = FrontendDataUtils.getGraves(this); - this.runOnUiThread(new Runnable(){ - public void run() { - copy(gravesData, gravesDataNew); - ((SimpleAdapter)grave.getAdapter()).notifyDataSetChanged(); - } - }); - - final ArrayList> flagsDataNew = FrontendDataUtils.getFlags(this); - this.runOnUiThread(new Runnable(){ - public void run() { - copy(flagsData, flagsDataNew); - ((SimpleAdapter)flag.getAdapter()).notifyDataSetChanged(); - } - }); - - final ArrayList> typesDataNew = FrontendDataUtils.getTypes(this); - this.runOnUiThread(new Runnable(){ - public void run() { - copy(typesData, typesDataNew); - ((SimpleAdapter)difficulty.getAdapter()).notifyDataSetChanged(); - } - }); - - final ArrayList> hatsDataNew = FrontendDataUtils.getHats(this); - this.runOnUiThread(new Runnable(){ - public void run() { - copy(hatsData, hatsDataNew); - ((SimpleAdapter)hogHat.get(0).getAdapter()).notifyDataSetChanged(); - } - }); - - final ArrayList voicesDataNew = FrontendDataUtils.getVoices(this); - this.runOnUiThread(new Runnable(){ - public void run() { - copy(voicesData, voicesDataNew); - ((ArrayAdapter)voice.getAdapter()).notifyDataSetChanged(); - } - }); - - final ArrayList fortsDataNew = FrontendDataUtils.getForts(this); - this.runOnUiThread(new Runnable(){ - public void run() { - copy(fortsData, fortsDataNew); - ((ArrayAdapter)fort.getAdapter()).notifyDataSetChanged(); - } - }); + private SpinnerAdapter createMapSpinnerAdapter(List> data) { + SimpleAdapter sa = new SimpleAdapter(this, data, + R.layout.spinner_textimg_entry, new String[] { "txt", "img" }, + new int[] { R.id.spinner_txt, R.id.spinner_img }); + sa.setDropDownViewResource(R.layout.spinner_textimg_dropdown_entry); + sa.setViewBinder(viewBinder); + return sa; + } + + private SpinnerAdapter createListSpinnerAdapter(List data) { + ArrayAdapter adapter = new ArrayAdapter(this, R.layout.listview_item, data); + adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + return adapter; } - private static void copy(List dest, List src){ - for(T t: src) dest.add(t); + public void run(){ + try { + final ArrayList> gravesDataNew = FrontendDataUtils.getGraves(this); + runOnUiThread(new Runnable(){ + public void run() { + gravesData.addAll(gravesDataNew); + ((SimpleAdapter)grave.getAdapter()).notifyDataSetChanged(); + } + }); + + final ArrayList> flagsDataNew = FrontendDataUtils.getFlags(this); + runOnUiThread(new Runnable(){ + public void run() { + flagsData.addAll(flagsDataNew); + ((SimpleAdapter)flag.getAdapter()).notifyDataSetChanged(); + } + }); + + final ArrayList> typesDataNew = FrontendDataUtils.getTypes(this); + runOnUiThread(new Runnable(){ + public void run() { + typesData.addAll(typesDataNew); + ((SimpleAdapter)difficulty.getAdapter()).notifyDataSetChanged(); + } + }); + + final ArrayList> hatsDataNew = FrontendDataUtils.getHats(this); + runOnUiThread(new Runnable(){ + public void run() { + hatsData.addAll(hatsDataNew); + ((SimpleAdapter)hogHat.get(0).getAdapter()).notifyDataSetChanged(); + } + }); + + final ArrayList voicesDataNew = FrontendDataUtils.getVoices(this); + runOnUiThread(new Runnable(){ + public void run() { + voicesData.addAll(voicesDataNew); + ((ArrayAdapter)voice.getAdapter()).notifyDataSetChanged(); + } + }); + + final ArrayList fortsDataNew = FrontendDataUtils.getForts(this); + runOnUiThread(new Runnable(){ + public void run() { + fortsData.addAll(fortsDataNew); + ((ArrayAdapter)fort.getAdapter()).notifyDataSetChanged(); + } + }); + + if(existingTeamName!=null) { + final Team loadedTeam = Team.load(Team.getTeamfileByName(getApplicationContext(), existingTeamName)); + if(loadedTeam==null) { + existingTeamName = null; + } else { + runOnUiThread(new Runnable(){ + public void run() { + setTeamValues(loadedTeam); + } + }); + } + } + runOnUiThread(new Runnable(){ + public void run() { + initComplete = true; + } + }); + } catch(FileNotFoundException e) { + this.runOnUiThread(new Runnable(){ + public void run() { + Toast.makeText(getApplicationContext(), R.string.error_missing_sdcard_or_files, Toast.LENGTH_LONG).show(); + finish(); + } + }); + } } - + public void onDestroy() { super.onDestroy(); if (mp != null) { @@ -217,93 +239,88 @@ } } - private OnFocusChangeListener focusser = new OnFocusChangeListener() { - public void onFocusChange(View v, boolean hasFocus) { - settingsChanged = true; - } - - }; + @Override + protected void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + outState.putString(PARAMETER_EXISTING_TEAMNAME, existingTeamName); + } public void onBackPressed() { - onFinishing(); + if(initComplete) { + saveTeam(); + } + setResult(RESULT_OK); super.onBackPressed(); - } - private OnClickListener backClicker = new OnClickListener() { - public void onClick(View v) { - onFinishing(); - finish(); + private int getDifficultyLevelFromText(String text) { + if (text.equals(getString(R.string.human))) { + return 0; + } else if (text.equals(getString(R.string.bot5))) { + return 1; + } else if (text.equals(getString(R.string.bot4))) { + return 2; + } else if (text.equals(getString(R.string.bot3))) { + return 3; + } else if (text.equals(getString(R.string.bot2))) { + return 4; + } else { + return 5; + } + } + + private String getTxtFromDifficulty(int level) { + switch(level) { + case 0: return getString(R.string.human); + case 1: return getString(R.string.bot5); + case 2: return getString(R.string.bot4); + case 3: return getString(R.string.bot3); + case 4: return getString(R.string.bot2); + default: return getString(R.string.bot1); + } + } + + private void saveTeam() { + String teamName = name.getText().toString(); + String teamFlag = (String)((Map) flag.getSelectedItem()).get("txt"); + String teamFort = fort.getSelectedItem().toString(); + String teamGrave = (String)((Map) grave.getSelectedItem()).get("txt"); + String teamVoice = voice.getSelectedItem().toString(); + String levelString = (String)((Map) difficulty.getSelectedItem()).get("txt"); + int levelInt = getDifficultyLevelFromText(levelString); + + List hogs = new ArrayList(); + for (int i = 0; i < hogName.size(); i++) { + String name = hogName.get(i).getText().toString(); + String hat = ((Map) hogHat.get(i).getSelectedItem()).get("txt").toString(); + hogs.add(new Hog(name, hat, levelInt)); + } + + Team team = new Team(teamName, teamGrave, teamFlag, teamVoice, teamFort, hogs); + File teamsDir = new File(getFilesDir(), Team.DIRECTORY_TEAMS); + if (!teamsDir.exists()) teamsDir.mkdir(); + + File newFile = Team.getTeamfileByName(this, teamName); + File oldFile = null; + if(existingTeamName != null) { + oldFile = Team.getTeamfileByName(this, existingTeamName); + } + try { + team.save(newFile); + Toast.makeText(TeamCreatorActivity.this, R.string.saved, Toast.LENGTH_SHORT).show(); + // If the team was renamed, delete the old file. + if(oldFile != null && oldFile.isFile() && !oldFile.equals(newFile)) { + oldFile.delete(); + } + existingTeamName = teamName; + } catch(IOException e) { + Toast.makeText(getApplicationContext(), R.string.error_save_failed, Toast.LENGTH_SHORT).show(); } }; - private void onFinishing() { - if (settingsChanged) { - setResult(RESULT_OK); - } else { - setResult(RESULT_CANCELED); - } - } - - private OnClickListener saveClicker = new OnClickListener() { - public void onClick(View v) { - String teamName = name.getText().toString(); - String teamFlag = (String)((Map) flag.getSelectedItem()).get("txt"); - String teamFort = fort.getSelectedItem().toString(); - String teamGrave = (String)((Map) grave.getSelectedItem()).get("txt"); - String teamVoice = voice.getSelectedItem().toString(); - String levelString = (String)((Map) difficulty.getSelectedItem()).get("txt"); - int levelInt; - if (levelString.equals(getString(R.string.human))) { - levelInt = 0; - } else if (levelString.equals(getString(R.string.bot5))) { - levelInt = 1; - } else if (levelString.equals(getString(R.string.bot4))) { - levelInt = 2; - } else if (levelString.equals(getString(R.string.bot3))) { - levelInt = 3; - } else if (levelString.equals(getString(R.string.bot2))) { - levelInt = 4; - } else { - levelInt = 5; - } - - List hogs = new ArrayList(); - for (int i = 0; i < hogName.size(); i++) { - String name = hogName.get(i).getText().toString(); - String hat = ((Map) hogHat.get(i).getSelectedItem()).get("txt").toString(); - hogs.add(new Hog(name, hat, levelInt)); - } - - Team team = new Team(teamName, teamGrave, teamFlag, teamVoice, teamFort, hogs); - File teamsDir = new File(getFilesDir(), Team.DIRECTORY_TEAMS); - if (!teamsDir.exists()) teamsDir.mkdir(); - if(fileName == null){ - fileName = createNewFilename(Utils.replaceBadChars(team.name)); - } - try { - team.save(new File(teamsDir, fileName)); - Toast.makeText(TeamCreatorActivity.this, R.string.saved, Toast.LENGTH_SHORT).show(); - saved = true; - } catch(IOException e) { - Toast.makeText(getApplicationContext(), R.string.error_save_failed, Toast.LENGTH_SHORT).show(); - } - } - }; - - private String createNewFilename(String suggestedName){ - File f = new File(getFilesDir(), suggestedName); - if(f.exists()){ - return createNewFilename(suggestedName + (int)(Math.random()*10)); - } else { - return suggestedName + (int)(Math.random()*10); - } - } - private OnItemSelectedListener fortSelector = new OnItemSelectedListener() { public void onItemSelected(AdapterView arg0, View arg1, int position, long arg3) { - settingsChanged = true; String fortName = (String) arg0.getAdapter().getItem(position); Drawable fortIconDrawable = Drawable.createFromPath(Utils .getDataPath(TeamCreatorActivity.this) @@ -351,54 +368,48 @@ } }; + @SuppressWarnings("unchecked") private void setTeamValues(Team t){ - - if (t != null) { + if (t == null) { + return; + } + + try { name.setText(t.name); - int position = ((ArrayAdapter) voice.getAdapter()).getPosition(t.voice); - voice.setSelection(position); - - position = ((ArrayAdapter) fort.getAdapter()).getPosition(t.fort); - fort.setSelection(position); - - position = 0; - for (HashMap hashmap : typesData) { - if (t.hogs.get(0) != null && hashmap.get("txt").equals(t.hogs.get(0).level)) { - difficulty.setSelection(position); - break; - } - } - - position = 0; - for (HashMap hashmap : gravesData) { - if (hashmap.get("txt").equals(t.grave)) { - grave.setSelection(position); - break; - } - } - - position = 0; - for (HashMap hashmap : typesData) { - if (hashmap.get("txt").equals(t.flag)) { - flag.setSelection(position); - break; - } - } - - for (int i = 0; i < Team.maxNumberOfHogs; i++) { - position = 0; - for (HashMap hashmap : hatsData) { - if (hashmap.get("txt").equals(t.hogs.get(i).hat)) { - hogHat.get(i).setSelection(position); - } - } - + voice.setSelection(findPosition((ArrayAdapter) voice.getAdapter(), t.voice)); + fort.setSelection(findPosition((ArrayAdapter) fort.getAdapter(), t.fort)); + difficulty.setSelection(findPosition(typesData, getTxtFromDifficulty(t.hogs.get(0).level))); // TODO store actual difficulty int in typesData + grave.setSelection(findPosition(gravesData, t.grave)); + flag.setSelection(findPosition(flagsData, t.flag)); + + for (int i = 0; i < Team.HEDGEHOGS_PER_TEAM; i++) { + hogHat.get(i).setSelection(findPosition(hatsData, t.hogs.get(i).hat)); hogName.get(i).setText(t.hogs.get(i).name); } - //this.fileName = t.file; + } catch(NoSuchElementException e) { + Toast.makeText(getApplicationContext(), R.string.error_team_attribute_not_found, Toast.LENGTH_LONG).show(); + finish(); } } + int findPosition(ArrayAdapter adapter, String key) throws NoSuchElementException { + int position = adapter.getPosition(key); + if(position<0) { + throw new NoSuchElementException(); + } + return position; + } + + int findPosition(List> data, String txtValue) throws NoSuchElementException { + int position = 0; + for (Map map : data) { + if (map.get("txt").equals(txtValue)) { + return position; + } + position++; + } + throw new NoSuchElementException(); + } private SimpleAdapter.ViewBinder viewBinder = new SimpleAdapter.ViewBinder() { @@ -413,5 +424,4 @@ } } }; - } diff -r d70a5b0d1190 -r 0481bd74267c project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/TeamSelectionActivity.java --- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/TeamSelectionActivity.java Sun Aug 12 22:37:57 2012 +0200 +++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/TeamSelectionActivity.java Sun Aug 12 22:46:23 2012 +0200 @@ -19,23 +19,18 @@ package org.hedgewars.hedgeroid; -import java.io.File; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import org.hedgewars.hedgeroid.Datastructures.FrontendDataUtils; import org.hedgewars.hedgeroid.Datastructures.Team; -import org.hedgewars.hedgeroid.Datastructures.TeamFile; import org.hedgewars.hedgeroid.Datastructures.TeamInGame; import org.hedgewars.hedgeroid.Datastructures.TeamIngameAttributes; import android.app.Activity; -import android.content.Context; import android.content.Intent; import android.os.Bundle; -import android.os.Parcelable; -import android.util.Pair; import android.view.ContextMenu; import android.view.MenuItem; import android.view.View; @@ -46,7 +41,6 @@ import android.widget.ImageButton; import android.widget.ImageView; import android.widget.ListView; -import android.widget.RelativeLayout; import android.widget.SimpleAdapter; import android.widget.SimpleAdapter.ViewBinder; import android.widget.TextView; @@ -54,10 +48,10 @@ public class TeamSelectionActivity extends Activity implements Runnable{ private static final int ACTIVITY_TEAMCREATION = 0; - private static volatile List> activityParams; - private static volatile List> activityReturn; + public static volatile List activityParams; + public static volatile List activityReturn; - private ImageButton addTeam, back; + private ImageButton addTeam; private ListView availableTeams, selectedTeams; private List> availableTeamsList, selectedTeamsList; private TextView txtInfo; @@ -68,12 +62,10 @@ setContentView(R.layout.team_selector); addTeam = (ImageButton) findViewById(R.id.btnAdd); - back = (ImageButton) findViewById(R.id.btnBack); txtInfo = (TextView) findViewById(R.id.txtInfo); selectedTeams = (ListView) findViewById(R.id.selectedTeams); availableTeams = (ListView) findViewById(R.id.availableTeams); addTeam.setOnClickListener(addTeamClicker); - back.setOnClickListener(backClicker); availableTeamsList = new ArrayList>(); SimpleAdapter adapter = new SimpleAdapter(this, availableTeamsList, R.layout.team_selection_entry_simple, new String[]{"txt", "img"}, new int[]{R.id.txtName, R.id.imgDifficulty}); @@ -93,23 +85,33 @@ } public void run(){ - List> teamsList = getTeams(this);//teams from xml - ArrayList teamsStartGame = getIntent().getStringArrayListExtra("selectedTeamNames"); - - for(HashMap hashmap : teamsList){ + List teams = FrontendDataUtils.getTeams(this); + List existingTeams = activityParams; + final List newSelectedList = new ArrayList(); + final List newAvailableList = new ArrayList(); + + for(Team team : teams){ boolean added = false; - for(String teamName : teamsStartGame){ - if(((Team)hashmap.get("team")).name.equals(teamName)){ // add to available or add to selected - selectedTeamsList.add(hashmap); + for(TeamInGame existingTeam : existingTeams){ + if(team.name.equals(existingTeam.team.name)){ // add to available or add to selected + newSelectedList.add(new TeamInGame(team, existingTeam.ingameAttribs)); added = true; break; } } - if(!added) availableTeamsList.add(hashmap); + if(!added) newAvailableList.add(team); } this.runOnUiThread(new Runnable(){ public void run() { + availableTeamsList.clear(); + selectedTeamsList.clear(); + for(TeamInGame t : newSelectedList) { + selectedTeamsList.add(toMap(t)); + } + for(Team t : newAvailableList) { + availableTeamsList.add(toMap(t)); + } ((SimpleAdapter)selectedTeams.getAdapter()).notifyDataSetChanged(); ((SimpleAdapter)availableTeams.getAdapter()).notifyDataSetChanged(); } @@ -145,39 +147,36 @@ * Updates the list view when TeamCreationActivity is shutdown and the user returns to this point */ private void updateListViews(){ - unregisterForContextMenu(availableTeams); - availableTeamsList = getTeams(this); + List teams = FrontendDataUtils.getTeams(this); + availableTeamsList.clear(); + for(Team team : teams) { + availableTeamsList.add(toMap(team)); + } + ArrayList> toBeRemoved = new ArrayList>(); + ArrayList> toBeRemovedFromSelected = new ArrayList>(); for(HashMap hashmap : selectedTeamsList){ String name = (String)hashmap.get("txt"); - + boolean exists = false; for(HashMap hash : availableTeamsList){ if(name.equals((String)hash.get("txt"))){ toBeRemoved.add(hash); + exists = true; + break; } } + if(!exists) { + toBeRemovedFromSelected.add(hashmap); + } } for(HashMap hash: toBeRemoved) availableTeamsList.remove(hash); - - SimpleAdapter adapter = new SimpleAdapter(this, availableTeamsList, R.layout.team_selection_entry, new String[]{"txt", "img"}, new int[]{R.id.txtName, R.id.imgDifficulty}); - availableTeams.setAdapter(adapter); - registerForContextMenu(availableTeams); - availableTeams.setOnItemClickListener(availableClicker); - - + for(HashMap hash: toBeRemovedFromSelected) selectedTeamsList.remove(hash); + ((SimpleAdapter)selectedTeams.getAdapter()).notifyDataSetChanged(); + ((SimpleAdapter)availableTeams.getAdapter()).notifyDataSetChanged(); } - private void setTeamColor(int position, int color){ - View iv = ((RelativeLayout)selectedTeams.getChildAt(position)).findViewById(R.id.teamCount); - setTeamColor(iv, color); - } - private void setTeamColor(View iv, int color){ - iv.setBackgroundColor(0xFF000000 + color); - } - - private void setTeamHogCount(int position, int count){ - ImageView iv = (ImageView)((RelativeLayout)selectedTeams.getChildAt(position)).findViewById(R.id.teamCount); - setTeamHogCount(iv, count); + private void setTeamColor(View iv, int colorIndex){ + iv.setBackgroundColor(0xFF000000 + TeamIngameAttributes.TEAM_COLORS[colorIndex]); } private void setTeamHogCount(ImageView iv, int count){ @@ -227,13 +226,6 @@ } }; - private OnClickListener backClicker = new OnClickListener(){ - public void onClick(View v){ - returnTeams(); - finish(); - } - }; - private OnItemClickListener availableClicker = new OnItemClickListener(){ public void onItemClick(AdapterView arg0, View arg1, int position,long arg3) { selectAvailableTeamsItem(position); @@ -260,19 +252,19 @@ public boolean onContextItemSelected(MenuItem item){ AdapterView.AdapterContextMenuInfo menuInfo = (AdapterContextMenuInfo) item.getMenuInfo(); int position = menuInfo.position; - TeamFile teamfile = (TeamFile)availableTeamsList.get(position).get("teamfile"); + Team team = (Team)availableTeamsList.get(position).get("team"); switch(item.getItemId()){ case 0://select selectAvailableTeamsItem(position); return true; case 1://delete - teamfile.file.delete(); + Team.getTeamfileByName(getApplicationContext(), team.name).delete(); availableTeamsList.remove(position); ((SimpleAdapter)availableTeams.getAdapter()).notifyDataSetChanged(); return true; case 2://edit Intent i = new Intent(TeamSelectionActivity.this, TeamCreatorActivity.class); - i.putExtra("teamfile", teamfile.file); + i.putExtra(TeamCreatorActivity.PARAMETER_EXISTING_TEAMNAME, team.name); startActivityForResult(i, ACTIVITY_TEAMCREATION); return true; } @@ -281,14 +273,12 @@ private void selectAvailableTeamsItem(int position){ HashMap hash = (HashMap) availableTeamsList.get(position); - Team t = (Team)hash.get("team"); int[] illegalcolors = new int[selectedTeamsList.size()]; for(int i = 0; i < selectedTeamsList.size(); i++){ - illegalcolors[i] = ((Team)selectedTeamsList.get(i).get("team")).color; + illegalcolors[i] = (Integer)selectedTeamsList.get(i).get("color"); } - t.setRandomColor(illegalcolors); - hash.put("color", t.color); - hash.put("count", t.hogCount); + hash.put("color", TeamIngameAttributes.randomColorIndex(illegalcolors)); + hash.put("count", TeamIngameAttributes.DEFAULT_HOG_COUNT); selectedTeamsList.add(hash); availableTeamsList.remove(position); @@ -298,47 +288,36 @@ txtInfo.setText(String.format(getResources().getString(R.string.teams_info_template), selectedTeamsList.size())); } - private void returnTeams(){ - int teamsCount = selectedTeamsList.size(); - Intent i = new Intent(); - Parcelable[] teams = new Parcelable[teamsCount]; - for(int x = 0 ; x < teamsCount; x++){ - teams[x] = (Team)selectedTeamsList.get(x).get("team"); + private void returnTeams() { + List result = new ArrayList(); + for(HashMap item : selectedTeamsList) { + result.add(new TeamInGame((Team)item.get("team"), new TeamIngameAttributes("Player", (Integer)item.get("color"), (Integer)item.get("count"), false))); } - i.putExtra("teams", teams); - setResult(Activity.RESULT_OK, i); - - } - - private static List> getTeams(Context c){ - List> ret = new ArrayList>(); - List teamfiles = FrontendDataUtils.getTeamFiles(c); - for(TeamFile tf : teamfiles) { - ret.add(teamfileToMap(tf)); - } - return ret; + activityReturn = result; + setResult(Activity.RESULT_OK); } private static final int[] botlevelDrawables = new int[] { R.drawable.human, R.drawable.bot5, R.drawable.bot4, R.drawable.bot3, R.drawable.bot2, R.drawable.bot1 }; - - private static HashMap teamfileToMap(TeamFile tf){ - HashMap hashmap = new HashMap(); - Team t = tf.team; - hashmap.put("team", t); - hashmap.put("teamfile", tf); - hashmap.put("txt", t.name); - hashmap.put("color", t.color); - hashmap.put("count", t.hogCount); + private static HashMap toMap(Team t) { + HashMap map = new HashMap(); + map.put("team", t); + map.put("txt", t.name); int botlevel = t.hogs.get(0).level; if(botlevel<0 || botlevel>=botlevelDrawables.length) { - hashmap.put("img", R.drawable.bot1); + map.put("img", R.drawable.bot1); } else { - hashmap.put("img", botlevelDrawables[botlevel]); - } - return hashmap; - return null; + map.put("img", botlevelDrawables[botlevel]); + } + return map; + } + + private static HashMap toMap(TeamInGame t) { + HashMap map = toMap(t.team); + map.put("color", t.ingameAttribs.colorIndex); + map.put("count", t.ingameAttribs.hogCount); + return map; } } diff -r d70a5b0d1190 -r 0481bd74267c project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Utils.java --- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Utils.java Sun Aug 12 22:37:57 2012 +0200 +++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Utils.java Sun Aug 12 22:46:23 2012 +0200 @@ -54,7 +54,7 @@ * get the path to which we should download all the data files * @param c context * @return The directory - * @throws FileNotFoundException if external storage is not avaliable at the moment + * @throws FileNotFoundException if external storage is not available at the moment */ public static File getCachePath(Context c) throws FileNotFoundException { File cachePath = null; @@ -105,37 +105,19 @@ } /** - * Get files from dirName, dir name is relative to {@link getDownloadPath} - * @param dirName - * @param c context - * @return string of files - */ - public static String[] getFileNamesFromRelativeDir(Context c, String dirName){ - String prefix = getDataPath(c); - File f = new File(prefix + dirName); - - if(f.exists() && f.isDirectory()) return f.list(); - else{ - - Log.e("Utils::", "Couldn't find dir: " + dirName); - return new String[0]; - } - } - - /** * Return a File array with all the files from dirName * @param c * @param dirName * @return + * @throws FileNotFoundException If the sdcard is not available or the subdirectory "dirName" does not exist */ - public static File[] getFilesFromRelativeDir(Context c, String dirName){ - String prefix = getDataPath(c); - File f = new File(prefix + dirName); + public static File[] getFilesFromRelativeDir(Context c, String dirName) throws FileNotFoundException { + File f = new File(getDataPathFile(c), dirName); - if(f.exists() && f.isDirectory()) return f.listFiles(); - else { - Log.e("Utils::", "Dir not found: " + dirName); - return new File[0]; + if(f.isDirectory()) { + return f.listFiles(); + } else { + throw new FileNotFoundException("Directory "+dirName+" does not exist."); } } @@ -161,8 +143,9 @@ * @param path * @param fileSuffix * @return + * @throws FileNotFoundException If the sdcard is not available or the subdirectory "path" does not exist */ - public static List getDirsWithFileSuffix(Context c, String path, String fileSuffix){ + public static List getDirsWithFileSuffix(Context c, String path, String fileSuffix) throws FileNotFoundException{ File[] files = getFilesFromRelativeDir(c,path); ArrayList ret = new ArrayList(); @@ -174,16 +157,13 @@ /** * Get all files from directory dir which have the given suffix - * @param c - * @param dir - * @param suffix - * @param removeSuffix - * @return + * @throws FileNotFoundException If the sdcard is not available or the subdirectory "dir" does not exist */ - public static ArrayList getFilesFromDirWithSuffix(Context c, String dir, String suffix, boolean removeSuffix){ - String[] files = Utils.getFileNamesFromRelativeDir(c, dir); + public static ArrayList getFileNamesFromDirWithSuffix(Context c, String dir, String suffix, boolean removeSuffix) throws FileNotFoundException{ + File[] files = Utils.getFilesFromRelativeDir(c, dir); ArrayList ret = new ArrayList(); - for(String s : files){ + for(File file : files){ + String s = file.getName(); if(s.endsWith(suffix)){ if(removeSuffix) ret.add(s.substring(0, s.length()-suffix.length())); else ret.add(s); diff -r d70a5b0d1190 -r 0481bd74267c project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/frontlib/Frontlib.java --- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/frontlib/Frontlib.java Sun Aug 12 22:37:57 2012 +0200 +++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/frontlib/Frontlib.java Sun Aug 12 22:46:23 2012 +0200 @@ -1,4 +1,5 @@ package org.hedgewars.hedgeroid.frontlib; +import java.io.UnsupportedEncodingException; import java.nio.Buffer; import java.util.ArrayList; import java.util.HashMap; @@ -6,15 +7,17 @@ import java.util.Map; import org.hedgewars.hedgeroid.Datastructures.Hog; +import org.hedgewars.hedgeroid.Datastructures.MapRecipe; import org.hedgewars.hedgeroid.Datastructures.MetaScheme; import org.hedgewars.hedgeroid.Datastructures.MetaScheme.Mod; import org.hedgewars.hedgeroid.Datastructures.MetaScheme.Setting; +import org.hedgewars.hedgeroid.Datastructures.GameConfig; import org.hedgewars.hedgeroid.Datastructures.RoomlistRoom; import org.hedgewars.hedgeroid.Datastructures.Scheme; import org.hedgewars.hedgeroid.Datastructures.Team; import org.hedgewars.hedgeroid.Datastructures.TeamInGame; import org.hedgewars.hedgeroid.Datastructures.TeamIngameAttributes; -import org.hedgewars.hedgeroid.EngineProtocol.GameConfig; +import org.hedgewars.hedgeroid.Datastructures.Weaponset; import com.sun.jna.Callback; import com.sun.jna.Library; @@ -23,56 +26,126 @@ import com.sun.jna.Pointer; import com.sun.jna.PointerType; import com.sun.jna.Structure; +import com.sun.jna.ptr.IntByReference; +/** + * Here is an introduction to the most important aspects of the JNA code. + * + * This interface permits access to the Hedgewars frontend library (frontlib) + * from Java. Each function directly contained in the Frontlib interface + * represents a mapped C function. The Structure classes (ending in -Struct) are + * mappings of C structs, and the PointerType classes (ending in -Ptr) represent + * pointers to structs. + * + * Quick notes for USING these classes from outside this package: + * + * Usage should be fairly straightforward, but there are a few surprising + * gotchas. First, when you implement callbacks, YOU are responsible for + * ensuring that the callback objects are not garbage-collected while they might + * still be called! So make sure you keep them in member variables or similar, + * because Java will not know if there are still native references to them. + * + * When using Frontlib from outside its package, you only interact with structs + * via the PointerType classes. They allow you to get at the data of the struct + * with a function called deref(), which creates a plain normal Java object + * representing the data (e.g. SchemePtr.deref() will give you a Scheme object). + * + * Remember that you usually have to destroy structs that you receive from the + * library, because they are owned by the native code, not Java. For example, if + * you obtain a {@link MetaschemePtr} metaPtr using flib_metascheme_from_ini, + * you have to call flib_metascheme_release(metaPtr) after you are done using + * it. The recommended pattern for most cases is to call deref() on the pointer + * to get a Java object (that you can keep as long as you like), and then + * immediately destroy the struct if it needs destroying. To find out whether + * and how the struct needs to be destroyed, see the library's documentation of + * the function that you got the struct from. + * + * To pass new structs to the library, you can use the static createJavaOwned() + * function in each PointerType, which creates a new struct from the Java object + * you provide, and returns a pointer to that struct that you can pass to + * library functions. This new structure's memory is owned and managed by Java + * code, so do not destroy it with frontlib functions! + * + * There is a slight mismatch between the data model for the game setup. The + * frontlib supports setting initial health and weaponset per-hog, because the + * engine allows for that, but currently neither the networking protocol nor the + * PC frontend support this feature, so the Android version does not take + * advantage of it either and treats both as per-game settings. The initial + * health is contained in the game scheme, the weaponset is explicitly part of + * the GameConfig. When converting GameConfig to a native flib_gamesetup, both + * are automatically copied to all hogs in the game, and for the reverse + * conversion the weaponset of the first hog of the first team is used as the + * GameConfig weaponset. This means that GameConfig.weaponset will be null if + * there are no teams in the game. + * + * When starting a network game, you only need to query the GameSetupPtr from + * the netconn and use it to create the gameconn - this is preferable to using + * your own recreation of the game setup, because that way the same piece of + * code is used to determine the game setup on all platforms. + * + * The "context" parameter of the callbacks is never needed here because JNA + * generates function code for each callback object. Don't worry about it, just + * pass null for context and ignore the context parameter in the callbacks. + * + * Finally, the library functions are documented in the actual library, not + * here, so check the docs there to find out what exactly each function does! + * + * Notes about the structure of this class (for the next one who has to touch + * this...): + * + * Java/C interop is quite fiddly and error-prone, so as long as things work, + * try to stick to the established patterns. + * + * Structure types should always be hidden from the outside world, because they + * can be misused too easily. For example, if you get a Structure from the + * library, change a String value in there and pass it back, JNA will re-write + * that string using Java-owned memory without freeing the old native-owned + * string, which causes a memory leak and possibly a double-free or other Bad + * Things (tm). To avoid problems like this, Structure types are only used + * internally, to map existing structures to Java types (without modifying them) + * or to create brand-new, Java-owned structures. Both operations are exposed to + * the outside through the PointerType classes corresponding to the structures + * in question. + * + * Since all of the struct mapping happens in Java, it is never checked against + * the actual struct declarations in the library. That means strange things can + * start happening at runtime if the frontlib structs are modified without + * changing the mappings here to match. This also applies to the function + * signatures: JNA checks whether the functions actually exist when loading the + * library, but it has no way of knowing whether the signatures are correct. If + * the signatures here deviate from those in the frontlib, you might get stack + * corruption. + * + * In order to check at least the function signatures, take a look at the file + * extra/jnacontrol.c in the frontlib sources. You can validate whether the + * function signatures are still correct by copy-pasting them into jnaControl.c + * and compiling it against the frontlib headers. The typedefs and #defines in + * that file will make the compiler see the Java method signatures as C function + * declarations. Since the same functions are already declared in the frontlib + * headers, the compiler will give you errors if the signatures don't match. + */ public interface Frontlib extends Library { static final int NATIVE_INT_SIZE = 4; static final int NATIVE_BOOL_SIZE = 1; - static final int NETCONN_STATE_CONNECTING = 0; - static final int NETCONN_STATE_LOBBY = 1; - static final int NETCONN_STATE_ROOM = 2; - static final int NETCONN_STATE_INGAME = 3; - static final int NETCONN_STATE_DISCONNECTED = 10; - - static final int NETCONN_DISCONNECT_NORMAL = 0; - static final int NETCONN_DISCONNECT_SERVER_TOO_OLD = 1; - static final int NETCONN_DISCONNECT_AUTH_FAILED = 2; - static final int NETCONN_DISCONNECT_CONNLOST = 3; - static final int NETCONN_DISCONNECT_INTERNAL_ERROR = 100; - - static final int NETCONN_ROOMLEAVE_ABANDONED = 0; - static final int NETCONN_ROOMLEAVE_KICKED = 1; - - static final int NETCONN_MSG_TYPE_PLAYERINFO = 0; - static final int NETCONN_MSG_TYPE_SERVERMESSAGE = 1; - static final int NETCONN_MSG_TYPE_WARNING = 2; - static final int NETCONN_MSG_TYPE_ERROR = 3; - - static final int NETCONN_MAPCHANGE_FULL = 0; - static final int NETCONN_MAPCHANGE_MAP = 1; - static final int NETCONN_MAPCHANGE_MAPGEN = 2; - static final int NETCONN_MAPCHANGE_DRAWNMAP = 3; - static final int NETCONN_MAPCHANGE_MAZE_SIZE = 4; - static final int NETCONN_MAPCHANGE_TEMPLATE = 5; - static final int NETCONN_MAPCHANGE_THEME = 6; - static final int NETCONN_MAPCHANGE_SEED = 7; - - static final int GAME_END_FINISHED = 0; - static final int GAME_END_INTERRUPTED = 1; - static final int GAME_END_HALTED = 2; - static final int GAME_END_ERROR = 3; - - static final int HEDGEHOGS_PER_TEAM = 8; - public static class NetconnPtr extends PointerType { } public static class MapconnPtr extends PointerType { } public static class GameconnPtr extends PointerType { } - public static class MetaschemePtr extends PointerType { } + + // TODO avoid code duplication in the pointer types + public static class MetaschemePtr extends PointerType { + public MetaScheme deref() { + return deref(getPointer()); + } + + public static MetaScheme deref(Pointer p) { + MetaschemeStruct struct = new MetaschemeStruct(p); + struct.read(); + return struct.toMetaScheme(); + } + } public static class RoomArrayPtr extends PointerType { - /** - * Returns the (native-owned) rooms in this list - */ public RoomlistRoom[] getRooms(int count) { Pointer ptr = getPointer(); if(ptr == null) { @@ -88,65 +161,123 @@ } public static class RoomPtr extends PointerType { - public RoomPtr() { super(); } - public RoomPtr(Pointer ptr) { super(ptr); } - public RoomlistRoom deref() { return deref(getPointer()); } public static RoomlistRoom deref(Pointer p) { - RoomStruct r = new RoomStruct(p); - r.read(); - return new RoomlistRoom(r.name, r.map, r.scheme, r.weapons, r.owner, r.playerCount, r.teamCount, r.inProgress); + RoomStruct struct = new RoomStruct(p); + struct.read(); + return struct.toRoomlistRoom(); } } public static class TeamPtr extends PointerType { + private TeamStruct javaOwnedInstance; + public TeamInGame deref() { - return deref(getPointer()); + TeamStruct struct = new TeamStruct(getPointer()); + struct.read(); + return struct.toTeamInGame(); } - public static TeamInGame deref(Pointer p) { - TeamStruct ts = new TeamStruct(p); - ts.read(); - List hogs = new ArrayList(); - for(int i=0; i deref() { + WeaponsetListStruct struct = new WeaponsetListStruct(getPointer()); + struct.read(); + return struct.toWeaponsetList(); + } + + public static WeaponsetListPtr createJavaOwned(List list) { + WeaponsetListPtr result = new WeaponsetListPtr(); + result.javaOwnedInstance = new WeaponsetListStruct(); + result.javaOwnedInstance.fillFrom(list); + result.javaOwnedInstance.autoWrite(); + result.setPointer(result.javaOwnedInstance.getPointer()); + return result; + } + } + + public static class MapRecipePtr extends PointerType { + private MapRecipeStruct javaOwnedInstance; + + public MapRecipe deref() { + MapRecipeStruct struct = new MapRecipeStruct(getPointer()); + struct.read(); + return struct.toMapRecipe(); + } + + public static MapRecipePtr createJavaOwned(MapRecipe recipe) { + MapRecipePtr result = new MapRecipePtr(); + result.javaOwnedInstance = new MapRecipeStruct(); + result.javaOwnedInstance.fillFrom(recipe); + result.javaOwnedInstance.autoWrite(); + result.setPointer(result.javaOwnedInstance.getPointer()); + return result; + } + } + + public static class SchemePtr extends PointerType { + private SchemeStruct javaOwnedInstance; + + public Scheme deref() { + SchemeStruct struct = new SchemeStruct(getPointer()); + struct.read(); + return struct.toScheme(); + } + + public static SchemePtr createJavaOwned(Scheme scheme) { + SchemePtr result = new SchemePtr(); + result.javaOwnedInstance = new SchemeStruct(); + result.javaOwnedInstance.fillFrom(scheme); + result.javaOwnedInstance.autoWrite(); + result.setPointer(result.javaOwnedInstance.getPointer()); + return result; + } + } + public static class SchemelistPtr extends PointerType { private SchemelistStruct javaOwnedInstance; public List deref() { - return deref(getPointer()); - } - - public static List deref(Pointer p) { - SchemelistStruct sls = new SchemelistStruct(p); - sls.read(); - return sls.toSchemeList(); + SchemelistStruct struct = new SchemelistStruct(getPointer()); + struct.read(); + return struct.toSchemeList(); } public static SchemelistPtr createJavaOwned(List schemes) { @@ -160,11 +291,20 @@ } public static class GameSetupPtr extends PointerType { + private GameSetupStruct javaOwnedInstance; + + public GameConfig deref() { + GameSetupStruct struct = new GameSetupStruct(getPointer()); + struct.read(); + return struct.toGameConfig(); + } + public static GameSetupPtr createJavaOwned(GameConfig conf) { - GameSetupStruct gss = GameSetupStruct.from(conf); - gss.write(); GameSetupPtr result = new GameSetupPtr(); - result.setPointer(gss.getPointer()); + result.javaOwnedInstance = new GameSetupStruct(); + result.javaOwnedInstance.fillFrom(conf); + result.javaOwnedInstance.autoWrite(); + result.setPointer(result.javaOwnedInstance.getPointer()); return result; } } @@ -177,14 +317,14 @@ public HogStruct() { super(); setFieldOrder(FIELD_ORDER); } public HogStruct(Pointer ptr) { super(ptr); setFieldOrder(FIELD_ORDER); } - public static HogStruct from(Hog hog) { - HogStruct hs = new HogStruct(); - hs.difficulty = hog.level; - hs.hat = hog.hat; - hs.name = hog.name; - // TODO weaponset - // TODO initialHealth - return hs; + public void fillFrom(Hog hog) { + difficulty = hog.level; + hat = hog.hat; + name = hog.name; + } + + public Hog toHog() { + return new Hog(name, hat, difficulty); } public String name; @@ -198,7 +338,7 @@ public int difficulty; public int initialHealth; - public WeaponsetPtr weaponset; + public WeaponsetStruct.ByRef weaponset; } static class TeamStruct extends Structure { @@ -209,32 +349,55 @@ public TeamStruct() { super(); setFieldOrder(FIELD_ORDER); } public TeamStruct(Pointer ptr) { super(ptr); setFieldOrder(FIELD_ORDER); } - public static TeamStruct from(Team team, TeamIngameAttributes attrs) { - TeamStruct ts = new TeamStruct(); + public void fillFrom(Team team, TeamIngameAttributes attrs) { if(team != null) { - ts.name = team.name; - ts.grave = team.grave; - ts.flag = team.flag; - ts.voicepack = team.voice; - ts.fort = team.fort; - if(team.hogs.size() != HEDGEHOGS_PER_TEAM) { + name = team.name; + grave = team.grave; + flag = team.flag; + voicepack = team.voice; + fort = team.fort; + if(team.hogs.size() != Team.HEDGEHOGS_PER_TEAM) { throw new IllegalArgumentException(); } - for(int i=0; i hogList = new ArrayList(); + for(int i=0; i list) { + weaponsetCount = list.size(); + weaponsets = new WeaponsetPointerByReference(); + Structure[] structs = weaponsets.toArray(weaponsetCount); + + for(int i=0; i toWeaponsetList() { + if(weaponsetCount<=0) { + return new ArrayList(); + } else { + List list = new ArrayList(weaponsetCount); + Structure[] structs = weaponsets.toArray(weaponsetCount); + + for(int i=0; i0) { + buf = new byte[drawDataSize.intValue()]; + drawData.read(0, buf, 0, drawDataSize.intValue()); + } + return new MapRecipe(mapgen, templateFilter, mazeSize, name, seed, theme, buf); + } + public int mapgen; public String name; public String seed; public String theme; public Pointer drawData; - public int drawDataSize; + public NativeLong drawDataSize; public int templateFilter; public int mazeSize; } @@ -408,7 +701,7 @@ static class SchemeStruct extends Structure { public static class ByVal extends SchemeStruct implements Structure.ByValue {} public static class ByRef extends SchemeStruct implements Structure.ByReference {} - private static String[] FIELD_ORDER = new String[] {"_referenceCount", "meta", "name", "settings", "mod"}; + private static String[] FIELD_ORDER = new String[] {"meta", "name", "settings", "mod"}; public SchemeStruct() { super(); setFieldOrder(FIELD_ORDER); } public SchemeStruct(Pointer ptr) { super(ptr); setFieldOrder(FIELD_ORDER); } @@ -442,7 +735,6 @@ return new Scheme(metaScheme, name, settingsMap, modsMap); } - public int _referenceCount; public MetaschemeStruct.ByRef meta; public String name; public Pointer settings; @@ -469,6 +761,18 @@ public SchemelistStruct() { super(); setFieldOrder(FIELD_ORDER); } public SchemelistStruct(Pointer ptr) { super(ptr); setFieldOrder(FIELD_ORDER); } + public void fillFrom(List schemeList) { + schemeCount = schemeList.size(); + schemes = new SchemePointerByReference(); + Structure[] schemePtrStructs = schemes.toArray(schemeCount); + + for(int i=0; i schemeList) { - schemeCount = schemeList.size(); - schemes = new SchemePointerByReference(); - Structure[] schemePtrStructs = schemes.toArray(schemeCount); - - for(int i=0; i teamList, WeaponsetStruct.ByRef weaponset, int initialHealth) { + teamCount = teamList.size(); + teams = new TeamPointerByReference(); + Structure[] teamPtrStructs = teams.toArray(teamCount); + + for(int i=0; i toTeamInGameList() { + if(teamCount<=0) { + return new ArrayList(); + } else { + List result = new ArrayList(teamCount); + Structure[] structs = teams.toArray(teamCount); + + for(int i=0; i teams = teamlist != null ? teamlist.toTeamInGameList() : null; + + WeaponsetStruct weaponsetStruct = teamlist != null && teamlist.teamCount>0 ? teamlist.teams.team.hogs[0].weaponset : null; + Weaponset weaponset = weaponsetStruct != null ? weaponsetStruct.toWeaponset() : null; + return new GameConfig(script, scheme, mapRecipe, teams, weaponset); } public String script; @@ -544,6 +895,12 @@ public TeamlistStruct.ByRef teamlist; } + /* + * Callback interfaces. The context parameter is never needed here and + * should always be ignored. Be sure to keep a reference to each callback + * for as long as they might be called by native code, to avoid premature + * garbage collection. + */ public static interface VoidCallback extends Callback { void callback(Pointer context); } @@ -620,15 +977,50 @@ void callback(int level, String logMessage); } + // frontlib.h int flib_init(); void flib_quit(); + // hwconsts.h + int flib_get_teamcolor(int colorIndex); + int flib_get_teamcolor_count(); + int flib_get_hedgehogs_per_team(); + int flib_get_weapons_count(); + + // net/netconn.h + static final int NETCONN_STATE_CONNECTING = 0; + static final int NETCONN_STATE_LOBBY = 1; + static final int NETCONN_STATE_ROOM = 2; + static final int NETCONN_STATE_DISCONNECTED = 10; + + static final int NETCONN_DISCONNECT_NORMAL = 0; + static final int NETCONN_DISCONNECT_SERVER_TOO_OLD = 1; + static final int NETCONN_DISCONNECT_AUTH_FAILED = 2; + static final int NETCONN_DISCONNECT_CONNLOST = 3; + static final int NETCONN_DISCONNECT_INTERNAL_ERROR = 100; + + static final int NETCONN_ROOMLEAVE_ABANDONED = 0; + static final int NETCONN_ROOMLEAVE_KICKED = 1; + + static final int NETCONN_MSG_TYPE_PLAYERINFO = 0; + static final int NETCONN_MSG_TYPE_SERVERMESSAGE = 1; + static final int NETCONN_MSG_TYPE_WARNING = 2; + static final int NETCONN_MSG_TYPE_ERROR = 3; + + static final int NETCONN_MAPCHANGE_FULL = 0; + static final int NETCONN_MAPCHANGE_MAP = 1; + static final int NETCONN_MAPCHANGE_MAPGEN = 2; + static final int NETCONN_MAPCHANGE_DRAWNMAP = 3; + static final int NETCONN_MAPCHANGE_MAZE_SIZE = 4; + static final int NETCONN_MAPCHANGE_TEMPLATE = 5; + static final int NETCONN_MAPCHANGE_THEME = 6; + static final int NETCONN_MAPCHANGE_SEED = 7; + NetconnPtr flib_netconn_create(String playerName, MetaschemePtr meta, String dataDirPath, String host, int port); void flib_netconn_destroy(NetconnPtr conn); void flib_netconn_tick(NetconnPtr conn); boolean flib_netconn_is_chief(NetconnPtr conn); - boolean flib_netconn_is_in_room_context(NetconnPtr conn); String flib_netconn_get_playername(NetconnPtr conn); GameSetupPtr flib_netconn_create_gamesetup(NetconnPtr conn); int flib_netconn_send_quit(NetconnPtr conn, String quitmsg); @@ -644,7 +1036,7 @@ int flib_netconn_send_toggleReady(NetconnPtr conn); int flib_netconn_send_addTeam(NetconnPtr conn, TeamPtr team); int flib_netconn_send_removeTeam(NetconnPtr conn, String teamname); - int flib_netconn_send_engineMessage(NetconnPtr conn, Buffer message, NativeLong size); // TODO check if NativeLong==size_t + int flib_netconn_send_engineMessage(NetconnPtr conn, Buffer message, NativeLong size); int flib_netconn_send_teamHogCount(NetconnPtr conn, String teamname, int hogcount); int flib_netconn_send_teamColor(NetconnPtr conn, String teamname, int colorIndex); int flib_netconn_send_weaponset(NetconnPtr conn, WeaponsetPtr weaponset); @@ -702,7 +1094,12 @@ void flib_netconn_onAdminAccess(NetconnPtr conn, VoidCallback callback, Pointer context); void flib_netconn_onServerVar(NetconnPtr conn, StrStrCallback callback, Pointer context); - // Gameconn + // ipc/gameconn.h + static final int GAME_END_FINISHED = 0; + static final int GAME_END_INTERRUPTED = 1; + static final int GAME_END_HALTED = 2; + static final int GAME_END_ERROR = 3; + GameconnPtr flib_gameconn_create(String playerName, GameSetupPtr setup, boolean netgame); GameconnPtr flib_gameconn_create_playdemo(Buffer demo, NativeLong size); GameconnPtr flib_gameconn_create_loadgame(String playerName, Buffer save, NativeLong size); @@ -715,6 +1112,7 @@ int flib_gameconn_send_enginemsg(GameconnPtr conn, Buffer data, NativeLong len); int flib_gameconn_send_textmsg(GameconnPtr conn, int msgtype, String msg); int flib_gameconn_send_chatmsg(GameconnPtr conn, String playername, String msg); + int flib_gameconn_send_quit(GameconnPtr conn); void flib_gameconn_onConnect(GameconnPtr conn, VoidCallback callback, Pointer context); void flib_gameconn_onDisconnect(GameconnPtr conn, IntCallback callback, Pointer context); @@ -723,7 +1121,7 @@ void flib_gameconn_onGameRecorded(GameconnPtr conn, BytesBoolCallback callback, Pointer context); void flib_gameconn_onEngineMessage(GameconnPtr conn, BytesCallback callback, Pointer context); - // MapConn + // ipc/mapconn.h MapconnPtr flib_mapconn_create(MapRecipePtr mapdesc); void flib_mapconn_destroy(MapconnPtr conn); int flib_mapconn_getport(MapconnPtr conn); @@ -731,11 +1129,7 @@ void flib_mapconn_onFailure(MapconnPtr conn, StrCallback callback, Pointer context); void flib_mapconn_tick(MapconnPtr conn); - // GameSetup - void flib_gamesetup_destroy(GameSetupPtr gamesetup); - GameSetupPtr flib_gamesetup_copy(GameSetupPtr gamesetup); - - // MapRecipe + // model/map.h public static final int MAPGEN_REGULAR = 0; public static final int MAPGEN_MAZE = 1; public static final int MAPGEN_DRAWN = 2; @@ -754,33 +1148,28 @@ public static final int MAZE_SIZE_SMALL_ISLANDS = 3; public static final int MAZE_SIZE_MEDIUM_ISLANDS = 4; public static final int MAZE_SIZE_LARGE_ISLANDS = 5; - - MapRecipePtr flib_map_create_regular(String seed, String theme, int templateFilter); - MapRecipePtr flib_map_create_maze(String seed, String theme, int mazeSize); - MapRecipePtr flib_map_create_named(String seed, String name); - MapRecipePtr flib_map_create_drawn(String seed, String theme, Buffer drawData, NativeLong drawDataSize); - MapRecipePtr flib_map_copy(MapRecipePtr map); - MapRecipePtr flib_map_retain(MapRecipePtr map); - void flib_map_release(MapRecipePtr map); - - // Metascheme + + // model/scheme.h MetaschemePtr flib_metascheme_from_ini(String filename); MetaschemePtr flib_metascheme_retain(MetaschemePtr metainfo); void flib_metascheme_release(MetaschemePtr metainfo); - // Scheme lists + // model/schemelist.h SchemelistPtr flib_schemelist_from_ini(MetaschemePtr meta, String filename); int flib_schemelist_to_ini(String filename, SchemelistPtr list); void flib_schemelist_destroy(SchemelistPtr list); - // Team - void flib_team_destroy(TeamPtr team); + // model/team.h TeamPtr flib_team_from_ini(String filename); int flib_team_to_ini(String filename, TeamPtr team); - void flib_team_set_weaponset(TeamPtr team, WeaponsetPtr set); - void flib_team_set_health(TeamPtr team, int health); + void flib_team_destroy(TeamPtr team); - // Logging + // model/weapon.h + WeaponsetListPtr flib_weaponsetlist_from_ini(String filename); + int flib_weaponsetlist_to_ini(String filename, WeaponsetListPtr weaponsets); + void flib_weaponsetlist_destroy(WeaponsetListPtr list); + + // util/logging.h public static final int FLIB_LOGLEVEL_ALL = -100; public static final int FLIB_LOGLEVEL_DEBUG = -1; public static final int FLIB_LOGLEVEL_INFO = 0; diff -r d70a5b0d1190 -r 0481bd74267c project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/Netplay.java --- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/Netplay.java Sun Aug 12 22:37:57 2012 +0200 +++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/Netplay.java Sun Aug 12 22:46:23 2012 +0200 @@ -2,10 +2,13 @@ import java.io.File; import java.io.FileNotFoundException; +import java.util.Map; +import java.util.TreeMap; import org.hedgewars.hedgeroid.R; import org.hedgewars.hedgeroid.Utils; import org.hedgewars.hedgeroid.Datastructures.RoomlistRoom; +import org.hedgewars.hedgeroid.Datastructures.Team; import org.hedgewars.hedgeroid.Datastructures.TeamInGame; import org.hedgewars.hedgeroid.Datastructures.TeamIngameAttributes; import org.hedgewars.hedgeroid.frontlib.Flib; @@ -82,6 +85,7 @@ public final MessageLog lobbyChatlog; public final MessageLog roomChatlog; public final Teamlist roomTeamlist = new Teamlist(); + private final Map roomRequestedTeams = new TreeMap(); public Netplay(Context appContext) { this.appContext = appContext; @@ -133,6 +137,13 @@ public void sendCreateRoom(String name) { sendToNet(ThreadedNetConnection.ToNetHandler.MSG_SEND_CREATE_ROOM, name); } public void sendLeaveRoom(String message) { sendToNet(ThreadedNetConnection.ToNetHandler.MSG_SEND_LEAVE_ROOM, message); } public void sendKick(String player) { sendToNet(ThreadedNetConnection.ToNetHandler.MSG_SEND_KICK, player); } + public void sendAddTeam(Team newTeam) { + roomRequestedTeams.put(newTeam.name, newTeam); + sendToNet(ThreadedNetConnection.ToNetHandler.MSG_SEND_ADD_TEAM, newTeam); + } + public void sendRemoveTeam(String teamName) { sendToNet(ThreadedNetConnection.ToNetHandler.MSG_SEND_REMOVE_TEAM, teamName); } + public void sendTeamColorIndex(String teamName, int colorIndex) { sendToNet(ThreadedNetConnection.ToNetHandler.MSG_SEND_TEAM_COLOR_INDEX, colorIndex, teamName); } + public void sendTeamHogCount(String teamName, int hogCount) { sendToNet(ThreadedNetConnection.ToNetHandler.MSG_SEND_TEAM_HOG_COUNT, hogCount, teamName); } public void disconnect() { sendToNet(ThreadedNetConnection.ToNetHandler.MSG_DISCONNECT, "User Quit"); } @@ -202,18 +213,17 @@ } private boolean sendToNet(int what) { - if(connection != null) { - Handler handler = connection.toNetHandler; - return handler.sendMessage(handler.obtainMessage(what)); - } else { - return false; - } + return sendToNet(what, 0, null); } private boolean sendToNet(int what, Object obj) { + return sendToNet(what, 0, obj); + } + + private boolean sendToNet(int what, int arg1, Object obj) { if(connection != null) { Handler handler = connection.toNetHandler; - return handler.sendMessage(handler.obtainMessage(what, obj)); + return handler.sendMessage(handler.obtainMessage(what, arg1, 0, obj)); } else { return false; } @@ -338,6 +348,7 @@ roomChatlog.clear(); roomPlayerlist.clear(); roomTeamlist.clear(); + roomRequestedTeams.clear(); changeState(State.ROOM); chief = (Boolean)msg.obj; Intent intent = new Intent(ACTION_ENTERED_ROOM_FROM_LOBBY); @@ -358,7 +369,14 @@ break; } case MSG_TEAM_ADDED: { - roomTeamlist.addTeamWithNewId((TeamInGame)msg.obj); + Team newTeam = (Team)msg.obj; + TeamIngameAttributes attrs = new TeamIngameAttributes(playerName, roomTeamlist.getUnusedOrRandomColorIndex(), TeamIngameAttributes.DEFAULT_HOG_COUNT, false); + TeamInGame tig = new TeamInGame(newTeam, attrs); + roomTeamlist.addTeamWithNewId(tig); + if(chief) { + sendTeamColorIndex(newTeam.name, attrs.colorIndex); + sendTeamHogCount(newTeam.name, attrs.hogCount); + } break; } case MSG_TEAM_DELETED: { @@ -366,7 +384,18 @@ break; } case MSG_TEAM_ACCEPTED: { - // TODO depends: adding teams + Team requestedTeam = roomRequestedTeams.remove(msg.obj); + if(requestedTeam!=null) { + TeamIngameAttributes attrs = new TeamIngameAttributes(playerName, roomTeamlist.getUnusedOrRandomColorIndex(), TeamIngameAttributes.DEFAULT_HOG_COUNT, false); + TeamInGame tig = new TeamInGame(requestedTeam, attrs); + roomTeamlist.addTeamWithNewId(tig); + if(chief) { + sendTeamColorIndex(requestedTeam.name, attrs.colorIndex); + sendTeamHogCount(requestedTeam.name, attrs.hogCount); + } + } else { + Log.e("Netplay", "Got accepted message for team that was never requested."); + } break; } case MSG_TEAM_COLOR_CHANGED: { @@ -412,7 +441,7 @@ private final Context appContext; private final FromNetHandler fromNetHandler; private final TickHandler tickHandler; - + /** * conn can only be null while connecting (the first thing in the thread), and directly after disconnecting, * in the same message (the looper is shut down on disconnect, so there will be no messages after that). @@ -589,7 +618,7 @@ private final TeamCallback teamAddedCb = new TeamCallback() { public void callback(Pointer context, TeamPtr team) { - sendFromNet(FromNetHandler.MSG_TEAM_ADDED, team.deref()); + sendFromNet(FromNetHandler.MSG_TEAM_ADDED, team.deref().team); } }; @@ -674,8 +703,11 @@ public static final int MSG_SEND_CREATE_ROOM = 8; public static final int MSG_SEND_LEAVE_ROOM = 9; public static final int MSG_SEND_KICK = 10; - - public static final int MSG_DISCONNECT = 11; + public static final int MSG_SEND_ADD_TEAM = 11; + public static final int MSG_SEND_REMOVE_TEAM = 12; + public static final int MSG_DISCONNECT = 13; + public static final int MSG_SEND_TEAM_COLOR_INDEX = 14; + public static final int MSG_SEND_TEAM_HOG_COUNT = 15; public ToNetHandler(Looper looper) { super(looper); @@ -697,7 +729,7 @@ break; } case MSG_SEND_ROOMLIST_REQUEST: { - FLIB.flib_netconn_send_request_roomlist(conn); // TODO restrict to lobby state? + FLIB.flib_netconn_send_request_roomlist(conn); break; } case MSG_SEND_PLAYER_INFO_REQUEST: { @@ -732,11 +764,33 @@ FLIB.flib_netconn_send_kick(conn, (String)msg.obj); break; } + case MSG_SEND_ADD_TEAM: { + FLIB.flib_netconn_send_addTeam(conn, TeamPtr.createJavaOwned((Team)msg.obj)); + break; + } + case MSG_SEND_REMOVE_TEAM: { + if(FLIB.flib_netconn_send_removeTeam(conn, (String)msg.obj)==0) { + sendFromNet(FromNetHandler.MSG_TEAM_DELETED, msg.obj); + } + break; + } case MSG_DISCONNECT: { FLIB.flib_netconn_send_quit(conn, (String)msg.obj); shutdown(false, "User quit"); break; } + case MSG_SEND_TEAM_COLOR_INDEX: { + if(FLIB.flib_netconn_send_teamColor(conn, (String)msg.obj, msg.arg1)==0) { + sendFromNet(FromNetHandler.MSG_TEAM_COLOR_CHANGED, msg.arg1, msg.obj); + } + break; + } + case MSG_SEND_TEAM_HOG_COUNT: { + if(FLIB.flib_netconn_send_teamHogCount(conn, (String)msg.obj, msg.arg1)==0) { + sendFromNet(FromNetHandler.MSG_HOG_COUNT_CHANGED, msg.arg1, msg.obj); + } + break; + } default: { Log.e("ToNetHandler", "Unknown message type: "+msg.what); break; diff -r d70a5b0d1190 -r 0481bd74267c project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/RoomActivity.java --- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/RoomActivity.java Sun Aug 12 22:37:57 2012 +0200 +++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/RoomActivity.java Sun Aug 12 22:46:23 2012 +0200 @@ -1,6 +1,7 @@ package org.hedgewars.hedgeroid.netplay; import org.hedgewars.hedgeroid.R; +import org.hedgewars.hedgeroid.Datastructures.Team; import org.hedgewars.hedgeroid.netplay.Netplay.State; import org.hedgewars.hedgeroid.netplay.NetplayStateFragment.NetplayStateListener; @@ -10,7 +11,7 @@ import android.widget.TabHost; import android.widget.Toast; -public class RoomActivity extends FragmentActivity implements NetplayStateListener { +public class RoomActivity extends FragmentActivity implements NetplayStateListener, TeamAddDialog.Listener { private TabHost tabHost; private Netplay netplay; @@ -72,4 +73,8 @@ throw new IllegalStateException("Unknown connection state: "+newState); } } + + public void onTeamAddDialogSubmitted(Team newTeam) { + netplay.sendAddTeam(newTeam); + } } diff -r d70a5b0d1190 -r 0481bd74267c project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/RoomListAdapter.java --- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/RoomListAdapter.java Sun Aug 12 22:37:57 2012 +0200 +++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/RoomListAdapter.java Sun Aug 12 22:46:23 2012 +0200 @@ -39,7 +39,7 @@ private static CharSequence formatExtra(Resources res, RoomlistRoom room) { String ownermsg = res.getString(R.string.roomlist_owner, room.owner); - String mapmsg = res.getString(R.string.roomlist_map, RoomlistRoom.formatMapName(res, room.map)); + String mapmsg = res.getString(R.string.roomlist_map, room.formatMapName(res)); String scheme = room.scheme.equals(room.weapons) ? room.scheme : room.scheme + " / " + room.weapons; String schememsg = res.getString(R.string.roomlist_scheme, scheme); return ownermsg + ". " + mapmsg + ", " + schememsg; @@ -74,7 +74,7 @@ teamCountView.setText(String.valueOf(room.teamCount)); } ownerView.setText(room.owner); - mapView.setText(RoomlistRoom.formatMapName(context.getResources(), room.map)); + mapView.setText(room.formatMapName(context.getResources())); schemeView.setText(room.scheme); weaponView.setText(room.weapons); } else { diff -r d70a5b0d1190 -r 0481bd74267c project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/TeamAddDialog.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/TeamAddDialog.java Sun Aug 12 22:46:23 2012 +0200 @@ -0,0 +1,92 @@ +package org.hedgewars.hedgeroid.netplay; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +import org.hedgewars.hedgeroid.R; +import org.hedgewars.hedgeroid.Datastructures.FrontendDataUtils; +import org.hedgewars.hedgeroid.Datastructures.Team; + +import android.app.Activity; +import android.app.AlertDialog; +import android.app.Dialog; +import android.content.DialogInterface; +import android.content.DialogInterface.OnClickListener; +import android.os.Bundle; +import android.support.v4.app.DialogFragment; + +public class TeamAddDialog extends DialogFragment { + private static final String STATE_TEAMS_ALREADY_IN_GAME = "teamAlreadyInGame"; + private ArrayList teamsAlreadyInGame; + private List availableTeams; + private Listener listener; + + public static interface Listener { + void onTeamAddDialogSubmitted(Team newTeam); + } + + public TeamAddDialog() { + // Only for reflection-based instantiation by the framework + } + + TeamAddDialog(Collection teamsAlreadyInGame) { + this.teamsAlreadyInGame = new ArrayList(teamsAlreadyInGame); + } + + @Override + public void onAttach(Activity activity) { + super.onAttach(activity); + try { + listener = (Listener) activity; + } catch(ClassCastException e) { + throw new ClassCastException("Activity " + activity + " must implement TeamAddDialog.Listener to use TeamAddDialog."); + } + } + + @Override + public void onDetach() { + super.onDetach(); + listener = null; + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + if(savedInstanceState != null) { + teamsAlreadyInGame = savedInstanceState.getStringArrayList(STATE_TEAMS_ALREADY_IN_GAME); + } + availableTeams = new ArrayList(); + List teams = FrontendDataUtils.getTeams(getActivity()); + for(Team team : teams) { + if(!teamsAlreadyInGame.contains(team.name)) { + availableTeams.add(team); + } + } + Collections.sort(availableTeams, Team.NAME_ORDER); + } + + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); + builder.setTitle(R.string.dialog_addteam_title); + builder.setIcon(R.drawable.human); + String[] teamNames = new String[availableTeams.size()]; + for(int i=0; i> teams = getMap().values(); + int[] illegalColors = new int[teams.size()]; + int i=0; + for(Pair item : teams) { + illegalColors[i] = item.first.ingameAttribs.colorIndex; + i++; + } + return TeamIngameAttributes.randomColorIndex(illegalColors); + } } diff -r d70a5b0d1190 -r 0481bd74267c project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/TeamlistFragment.java --- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/TeamlistFragment.java Sun Aug 12 22:37:57 2012 +0200 +++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/TeamlistFragment.java Sun Aug 12 22:46:23 2012 +0200 @@ -1,17 +1,31 @@ package org.hedgewars.hedgeroid.netplay; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + import org.hedgewars.hedgeroid.R; +import org.hedgewars.hedgeroid.Datastructures.Team; +import org.hedgewars.hedgeroid.Datastructures.TeamInGame; +import android.database.DataSetObserver; import android.os.Bundle; import android.support.v4.app.ListFragment; +import android.util.Pair; import android.view.LayoutInflater; import android.view.View; +import android.view.View.OnClickListener; import android.view.ViewGroup; +import android.widget.AdapterView.OnItemClickListener; +import android.widget.AdapterView; +import android.widget.Button; -public class TeamlistFragment extends ListFragment { +public class TeamlistFragment extends ListFragment implements OnItemClickListener { private Netplay netplay; private TeamlistAdapter adapter; - + private Button addTeamButton; + private DataSetObserver teamlistObserver; + @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -24,12 +38,48 @@ @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - return inflater.inflate(R.layout.fragment_teamlist, container, false); + View v = inflater.inflate(R.layout.fragment_teamlist, container, false); + addTeamButton = (Button)v.findViewById(R.id.addTeamButton); + addTeamButton.setOnClickListener(new OnClickListener() { + public void onClick(View v) { + new TeamAddDialog(getCurrentTeamNames()).show(getFragmentManager(), "team_add_dialog"); + } + }); + + teamlistObserver = new DataSetObserver() { + @Override + public void onChanged() { + addTeamButton.setEnabled(netplay.roomTeamlist.getMap().size() < Team.maxNumberOfTeams); + } + }; + netplay.roomTeamlist.registerObserver(teamlistObserver); + teamlistObserver.onChanged(); + + return v; } @Override public void onDestroy() { super.onDestroy(); adapter.invalidate(); + netplay.roomTeamlist.unregisterObserver(teamlistObserver); + } + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + getListView().setOnItemClickListener(this); + } + + private Collection getCurrentTeamNames() { + List names = new ArrayList(); + for(Pair teamWithId : netplay.roomTeamlist.getMap().values()) { + names.add(teamWithId.first.team.name); + } + return names; + } + + public void onItemClick(AdapterView parent, View view, int position, long id) { + netplay.sendRemoveTeam(adapter.getItem(position).team.name); } }