17 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA |
17 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA |
18 */ |
18 */ |
19 |
19 |
20 package org.hedgewars.hedgeroid.Datastructures; |
20 package org.hedgewars.hedgeroid.Datastructures; |
21 |
21 |
|
22 import java.util.Collections; |
|
23 import java.util.Comparator; |
|
24 import java.util.HashMap; |
22 import java.util.Map; |
25 import java.util.Map; |
23 |
26 |
24 import java.io.File; |
27 public final class Scheme { |
25 import java.io.FilenameFilter; |
28 public final MetaScheme metascheme; |
26 import java.io.IOException; |
29 public final String name; |
27 import java.util.ArrayList; |
30 public final Map<String, Integer> settings; |
28 import java.util.Arrays; |
31 public final Map<String, Boolean> mods; |
29 import java.util.List; |
|
30 import java.util.Map.Entry; |
|
31 import java.util.TreeMap; |
|
32 |
|
33 import org.hedgewars.hedgeroid.EngineProtocol.EngineProtocolNetwork; |
|
34 import org.ini4j.Ini; |
|
35 import org.ini4j.InvalidFileFormatException; |
|
36 import org.ini4j.Profile.Section; |
|
37 |
|
38 import android.content.Context; |
|
39 import android.os.Parcel; |
|
40 import android.os.Parcelable; |
|
41 import android.util.Log; |
|
42 |
|
43 public class Scheme implements Parcelable, Comparable<Scheme>{ |
|
44 public static final String DIRECTORY_SCHEME = "schemes"; |
|
45 private static final Map<String, BasicSettingMeta> basicSettingsMeta = new TreeMap<String, BasicSettingMeta>(); |
|
46 private static final Map<String, GameModMeta> gameModsMeta = new TreeMap<String, GameModMeta>(); |
|
47 |
|
48 private final String name; |
|
49 private final int gamemod; |
|
50 private final Map<String, Integer> basic = new TreeMap<String, Integer>(); |
|
51 |
32 |
52 public Scheme(String _name, Map<String, Integer> _basic, int _gamemod) { |
33 public Scheme(MetaScheme metascheme, String name, Map<String, Integer> settings, Map<String, Boolean> mods) { |
53 name = _name; |
34 this.metascheme = metascheme; |
54 gamemod = _gamemod; |
35 this.name = name; |
55 basic.putAll(_basic); |
36 this.settings = Collections.unmodifiableMap(new HashMap<String, Integer>(settings)); |
|
37 this.mods = Collections.unmodifiableMap(new HashMap<String, Boolean>(mods)); |
56 } |
38 } |
57 |
39 |
58 public Scheme(Parcel in){ |
|
59 name = in.readString(); |
|
60 gamemod = in.readInt(); |
|
61 in.readMap(basic, Integer.class.getClassLoader()); |
|
62 } |
|
63 |
|
64 public int getHealth() { |
40 public int getHealth() { |
65 return basic.get("InitialHealth"); |
41 return settings.get("health"); |
66 } |
|
67 |
|
68 public void sendToEngine(EngineProtocolNetwork epn) throws IOException{ |
|
69 epn.sendToEngine(String.format("e$gmflags %d", gamemod)); |
|
70 |
|
71 for(Map.Entry<String, Integer> entry : basic.entrySet()) { |
|
72 BasicSettingMeta basicflag = basicSettingsMeta.get(entry.getKey()); |
|
73 |
|
74 //Health is a special case, it doesn't need to be send |
|
75 //to the engine yet, we'll do that with the other HH info |
|
76 if(!basicflag.command.equals("inithealth")){ |
|
77 epn.sendToEngine(String.format("%s %d", basicflag.command, entry.getValue())); |
|
78 } |
|
79 } |
|
80 } |
|
81 |
|
82 public String toString(){ |
|
83 return name; |
|
84 } |
|
85 |
|
86 public static List<Scheme> getSchemes(Context c) throws IllegalArgumentException { |
|
87 File schemeDir = new File(c.getFilesDir(), DIRECTORY_SCHEME); |
|
88 File[] files = schemeDir.listFiles(new FilenameFilter() { |
|
89 public boolean accept(File dir, String filename) { |
|
90 return filename.toLowerCase().startsWith("scheme_"); |
|
91 } |
|
92 }); |
|
93 if(files == null) files = new File[0]; |
|
94 Arrays.sort(files); |
|
95 List<Scheme> schemes = new ArrayList<Scheme>(); |
|
96 |
|
97 for(File file : files) { |
|
98 try { |
|
99 Ini ini = new Ini(file); |
|
100 |
|
101 String name = ini.get("Scheme", "name"); |
|
102 if(name==null) { |
|
103 name = file.getName(); |
|
104 } |
|
105 Section basicSettingsSection = ini.get("BasicSettings"); |
|
106 Section gameModsSection = ini.get("GameMods"); |
|
107 if(basicSettingsSection == null || gameModsSection == null) { |
|
108 Log.e(Scheme.class.getCanonicalName(), "Scheme file "+file+" is missing the BasicSettings or GameMods section - skipping."); |
|
109 continue; |
|
110 } |
|
111 |
|
112 Map<String, Integer> basicSettings = new TreeMap<String, Integer>(); |
|
113 for(Entry<String, BasicSettingMeta> entry : basicSettingsMeta.entrySet()) { |
|
114 String key = entry.getKey(); |
|
115 BasicSettingMeta settingMeta = entry.getValue(); |
|
116 Integer value = null; |
|
117 if(basicSettingsSection.containsKey(key)) { |
|
118 try { |
|
119 value = Integer.valueOf(basicSettingsSection.get(key)); |
|
120 } catch (NumberFormatException e) { |
|
121 // ignore |
|
122 } |
|
123 } |
|
124 |
|
125 if(value==null) { |
|
126 Log.w(Scheme.class.getCanonicalName(), "Scheme file "+file+" setting "+key+" is missing or invalid, using default."); |
|
127 value = settingMeta.def; |
|
128 } |
|
129 |
|
130 if(settingMeta.checkOverMax) { |
|
131 value = Math.min(value, settingMeta.max); |
|
132 } |
|
133 if(settingMeta.times1000) { |
|
134 value *= 1000; |
|
135 } |
|
136 |
|
137 basicSettings.put(key, value); |
|
138 } |
|
139 |
|
140 int gamemods = 0; |
|
141 for(Entry<String, GameModMeta> entry : gameModsMeta.entrySet()) { |
|
142 String key = entry.getKey(); |
|
143 GameModMeta modMeta = entry.getValue(); |
|
144 if(Boolean.parseBoolean(gameModsSection.get(key))) { |
|
145 gamemods |= (1 << modMeta.bitmaskIndex); |
|
146 } |
|
147 } |
|
148 |
|
149 schemes.add(new Scheme(name, basicSettings, gamemods)); |
|
150 } catch (InvalidFileFormatException e) { |
|
151 throw new RuntimeException(e); |
|
152 } catch (IOException e) { |
|
153 throw new RuntimeException(e); |
|
154 } |
|
155 } |
|
156 return schemes; |
|
157 } |
|
158 |
|
159 /** |
|
160 * This method will parse the basic flags from a prespecified ini file. |
|
161 * In the future we could use one provided by the Data folder. |
|
162 */ |
|
163 public static void parseConfiguration(Context c) { |
|
164 File schemeDir = new File(c.getFilesDir(), DIRECTORY_SCHEME); |
|
165 File settingsFile = new File(schemeDir, "basicsettings"); |
|
166 File gameModsFile = new File(schemeDir, "gamemods"); |
|
167 |
|
168 try { |
|
169 Ini ini = new Ini(settingsFile); |
|
170 for(Entry<String, Section> sectionEntry : ini.entrySet()) { |
|
171 basicSettingsMeta.put(sectionEntry.getKey(), new BasicSettingMeta(sectionEntry.getValue())); |
|
172 } |
|
173 |
|
174 ini = new Ini(gameModsFile); |
|
175 for(Entry<String, Section> sectionEntry : ini.entrySet()) { |
|
176 gameModsMeta.put(sectionEntry.getKey(), new GameModMeta(sectionEntry.getValue())); |
|
177 } |
|
178 } catch (InvalidFileFormatException e) { |
|
179 throw new RuntimeException(e); |
|
180 } catch (IOException e) { |
|
181 throw new RuntimeException(e); |
|
182 } |
|
183 } |
|
184 |
|
185 public int describeContents() { |
|
186 return 0; |
|
187 } |
|
188 |
|
189 public void writeToParcel(Parcel dest, int flags) { |
|
190 dest.writeString(name); |
|
191 dest.writeInt(gamemod); |
|
192 dest.writeMap(basic); |
|
193 } |
|
194 |
|
195 public static final Parcelable.Creator<Scheme> CREATOR = new Parcelable.Creator<Scheme>() { |
|
196 public Scheme createFromParcel(Parcel source) { |
|
197 return new Scheme(source); |
|
198 } |
|
199 public Scheme[] newArray(int size) { |
|
200 return new Scheme[size]; |
|
201 } |
|
202 |
|
203 }; |
|
204 |
|
205 public int compareTo(Scheme another) { |
|
206 return name.compareTo(another.name); |
|
207 } |
|
208 } |
|
209 |
|
210 class BasicSettingMeta { |
|
211 final String command; |
|
212 final String title; |
|
213 final int def; |
|
214 final int min; |
|
215 final int max; |
|
216 final boolean times1000; |
|
217 final boolean checkOverMax; |
|
218 |
|
219 public BasicSettingMeta(Ini.Section section) { |
|
220 command = getRequired(section, "command"); |
|
221 title = section.get("title", ""); |
|
222 def = Integer.parseInt(getRequired(section, "default")); |
|
223 min = Integer.parseInt(getRequired(section, "min")); |
|
224 max = Integer.parseInt(getRequired(section, "max")); |
|
225 times1000 = Boolean.parseBoolean(section.get("times1000", "false")); |
|
226 checkOverMax = Boolean.parseBoolean(section.get("checkOverMax", "false")); |
|
227 } |
|
228 |
|
229 private String getRequired(Ini.Section section, String key) { |
|
230 String result = section.get(key); |
|
231 if(result==null) { |
|
232 throw new IllegalArgumentException("basicsettings.ini, section "+section.getName()+" is missing required setting "+key+"."); |
|
233 } |
|
234 return result; |
|
235 } |
42 } |
236 |
43 |
237 @Override |
44 @Override |
238 public String toString() { |
45 public String toString() { |
239 return String |
46 return "Scheme [metascheme=" + metascheme + ", name=" + name |
240 .format("BasicSettingMeta [command=%s, title=%s, def=%s, min=%s, max=%s, times1000=%s, checkOverMax=%s]", |
47 + ", settings=" + settings + ", mods=" + mods + "]"; |
241 command, title, def, min, max, times1000, checkOverMax); |
|
242 } |
|
243 } |
|
244 |
|
245 // TODO: Extend with additional metadata |
|
246 class GameModMeta { |
|
247 final int bitmaskIndex; |
|
248 |
|
249 public GameModMeta(Ini.Section section) { |
|
250 bitmaskIndex = Integer.parseInt(getRequired(section, "bitmaskIndex")); |
|
251 } |
48 } |
252 |
49 |
253 private String getRequired(Ini.Section section, String key) { |
50 public static final Comparator<Scheme> caseInsensitiveNameComparator = new Comparator<Scheme>() { |
254 String result = section.get(key); |
51 public int compare(Scheme lhs, Scheme rhs) { |
255 if(result==null) { |
52 return lhs.name.compareToIgnoreCase(rhs.name); |
256 throw new IllegalArgumentException("gamemods.ini, section "+section.getName()+" is missing required setting "+key+"."); |
|
257 } |
53 } |
258 return result; |
54 }; |
259 } |
|
260 |
|
261 @Override |
|
262 public String toString() { |
|
263 return String.format("GameModMeta [bitmaskIndex=%s]", bitmaskIndex); |
|
264 } |
|
265 } |
55 } |