Libraryless. Compilation Failed (104211L/700K).
package utils; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.lang.reflect.Array; import java.net.MalformedURLException; import java.net.URL; import java.util.Arrays; import p.Resettable; import w.InputStreamSugar; /** * Some utilities that emulate C stlib methods or provide convenient functions * to do repetitive system and memory related stuff. * * @author Maes */ public final class C2JUtils { static public final char[] strcpy(char[] s1, final char[] s2) { for (int i = 0; i < Math.min(s1.length, s2.length); i++) { s1[i] = s2[i]; } return s1; } static public final char[] strcpy(char[] s1, final char[] s2, int off, int len) { for (int i = 0; i < len; i++) { s1[i] = s2[i + off]; } return s1; } static public final char[] strcpy(char[] s1, final char[] s2, int off) { for (int i = 0; i < Math.min(s1.length, s2.length - off); i++) { s1[i] = s2[i + off]; } return s1; } static public final char[] strcpy(char[] s1, String s2) { for (int i = 0; i < Math.min(s1.length, s2.length()); i++) { s1[i] = s2.charAt(i); } return s1; } /** Return a byte[] array from the string's chars, * ANDed to the lowest 8 bits. * * @param str * @return */ public static final byte[] toByteArray(String str) { byte[] retour = new byte[str.length()]; for (int i = 0; i < str.length(); i++) { retour[i] = (byte) (str.charAt(i) & 0xFF); } return retour; } /** * Finds index of first element of array matching key. Useful whenever an * "indexOf" property is required or you encounter the C-ism [pointer- * array_base] used to find array indices in O(1) time. However, since this * is just a dumb unsorted search, running time is O(n), so use this method * only sparingly and in scenarios where it won't occur very frequently * -once per level is probably OK-, but watch out for nested loops, and * cache the result whenever possible. Consider adding an index or ID type * of field to the searched type if you require to use this property too * often. * * @param array * @param key * @return */ public static int indexOf(Object[] array, Object key) { for (int i = 0; i < array.length; i++) { if (array[i] == key) { return i; } } return -1; } /** * Emulates C-style "string comparison". "Strings" are considered * null-terminated, and comparison is performed only up to the smaller of * the two. * * @param s1 * @param s2 * @return */ static public final boolean strcmp(char[] s1, final char[] s2) { boolean match = true; for (int i = 0; i < Math.min(s1.length, s2.length); i++) { if (s1[i] != s2[i]) { match = false; break; } } return match; } static public final boolean strcmp(char[] s1, String s2) { return strcmp(s1, s2.toCharArray()); } /** * C-like string length (null termination). * * @param s1 * @return */ static public final int strlen(char[] s1) { if (s1 == null) return 0; int len = 0; while (s1[len++] > 0) { if (len >= s1.length) break; } return len - 1; } /** * Return a new String based on C-like null termination. * * @param s * @return */ static public final String nullTerminatedString(char[] s) { if (s == null) return ""; int len = 0; while (s[len++] > 0) { if (len >= s.length) break; } return new String(s, 0, len - 1); } /** * Automatically "initializes" arrays of objects with their default * constuctor. It's better than doing it by hand, IMO. If you have a better * way, be my guest. * * @param os * @param c * @throws Exception * @throws */ public static final <T> void initArrayOfObjects(T[] os, Class<T> c) { try { for (int i = 0; i < os.length; i++) { os[i] = c.newInstance(); } } catch (Exception e) { e.printStackTrace(); System.err.println("Failure to allocate " + os.length + " objects of class" + c.getName() + "!"); System.exit(-1); } } /** * Automatically "initializes" arrays of objects with their default * constuctor. It's better than doing it by hand, IMO. If you have a better * way, be my guest. * * @param os * @throws Exception * @throws */ public static final <T> void initArrayOfObjects(T[] os) { Class<T> c = (Class<T>) os.getClass().getComponentType(); try { for (int i = 0; i < os.length; i++) { os[i] = (T) c.newInstance(); } } catch (Exception e) { e.printStackTrace(); System.err.println("Failure to allocate " + os.length + " objects of class " + c.getName() + "!"); System.exit(-1); } } /** * Uses reflection to automatically create and initialize an array of * objects of the specified class. Does not require casting on "reception". * * @param <T> * @param c * @param num * @return * @return */ public static final <T> T[] createArrayOfObjects(Class<T> c, int num) { T[] os = null; os=getNewArray(c,num); try { for (int i = 0; i < os.length; i++) { os[i] = (T) c.newInstance(); } } catch (Exception e) { e.printStackTrace(); System.err.println("Failure to instantiate " + os.length + " objects of class " + c.getName() + "!"); System.exit(-1); } return os; } /** * Uses reflection to automatically create and initialize an array of * objects of the specified class. Does not require casting on "reception". * Requires an instance of the desired class. This allows getting around * determining the runtime type of parametrized types. * * * @param <T> * @param instance An instance of a particular class. * @param num * @return * @return */ @SuppressWarnings("unchecked") public static final <T> T[] createArrayOfObjects(T instance, int num) { T[] os = null; Class<T> c=(Class<T>) instance.getClass(); os=getNewArray(c,num); try { for (int i = 0; i < os.length; i++) { os[i] = (T) c.newInstance(); } } catch (Exception e) { e.printStackTrace(); System.err.println("Failure to instantiate " + os.length + " objects of class " + c.getName() + "!"); System.exit(-1); } return os; } /** * Automatically "initializes" arrays of objects with their default * constuctor. It's better than doing it by hand, IMO. If you have a better * way, be my guest. * * @param os * @param startpos inclusive * @param endpos non-inclusive * @throws Exception * @throws */ public static final<T> void initArrayOfObjects(T[] os, int startpos, int endpos) { @SuppressWarnings("unchecked") Class<T> c = (Class<T>) os.getClass().getComponentType(); try { for (int i = startpos; i < endpos; i++) { os[i] = c.newInstance(); } } catch (Exception e) { e.printStackTrace(); System.err.println("Failure to allocate " + os.length + " objects of class " + c.getName() + "!"); System.exit(-1); } } /** This method gets eventually inlined, becoming very fast */ public static final int toUnsignedByte(byte b) { return (0x000000FF & b); } // Optimized array-fill methods designed to operate like C's memset. public static final void memset(boolean[] array, boolean value, int len) { if (len > 0) array[0] = value; for (int i = 1; i < len; i += i) { System.arraycopy(array, 0, array, i, ((len - i) < i) ? (len - i) : i); } } public static final void memset(char[] array, char value, int len) { if (len > 0) array[0] = value; for (int i = 1; i < len; i += i) { System.arraycopy(array, 0, array, i, ((len - i) < i) ? (len - i) : i); } } public static final void memset(int[] array, int value, int len) { if (len > 0) array[0] = value; for (int i = 1; i < len; i += i) { System.arraycopy(array, 0, array, i, ((len - i) < i) ? (len - i) : i); } } public static final void memset(short[] array, short value, int len) { if (len > 0) array[0] = value; for (int i = 1; i < len; i += i) { System.arraycopy(array, 0, array, i, ((len - i) < i) ? (len - i) : i); } } public static final long unsigned(int num) { return 0xFFFFFFFFL & num; } public static final char unsigned(short num) { return (char) num; } /** * Convenient alias for System.arraycopy(src, 0, dest, 0, length); * * @param dest * @param src * @param length */ public static final void memcpy(Object dest, Object src, int length) { System.arraycopy(src, 0, dest, 0, length); } public static final boolean testReadAccess(String URI) { InputStream in=null; // This is bullshit. if (URI == null) return false; if (URI.length() == 0) return false; try { in=new FileInputStream(URI); } catch (Exception e) { // Not a file... URL u; try { u = new URL(URI); } catch (MalformedURLException e1) { return false; } try { in=u.openConnection().getInputStream(); } catch (IOException e1) { return false; } } if (in!=null) { try { in.close(); } catch (IOException e) { } return true; } // All is well. Go on... return true; } public static final boolean testWriteAccess(String URI) { OutputStream out=null; // This is bullshit. if (URI == null) return false; if (URI.length() == 0) return false; try { out=new FileOutputStream(URI); } catch (Exception e) { // Not a file... URL u; try { u = new URL(URI); } catch (MalformedURLException e1) { return false; } try { out=u.openConnection().getOutputStream(); } catch (IOException e1) { return false; } } if (out!=null) { try { out.close(); } catch (IOException e) { } return true; } // All is well. Go on... return true; } /** * Returns true if flags are included in arg. Synonymous with (flags & * arg)!=0 * * @param flags * @param arg * @return */ public static final boolean flags(int flags, int arg) { return ((flags & arg) != 0); } public static final boolean flags(long flags, long arg) { return ((flags & arg) != 0); } /** * Returns 1 for true and 0 for false. Useful, given the amount of * "arithmetic" logical functions in legacy code. Synonymous with * (expr?1:0); * * @param flags * @param arg * @return */ public static final int eval(boolean expr) { return (expr ? 1 : 0); } /** * Returns 1 for non-null and 0 for null objects. Useful, given the amount * of "existential" logical functions in legacy code. Synonymous with * (expr!=null); * * @param flags * @param arg * @return */ public static final boolean eval(Object expr) { return (expr != null); } /** * Returns true for expr!=0, false otherwise. * * @param flags * @param arg * @return */ public static final boolean eval(int expr) { return expr != 0; } /** * Returns true for expr!=0, false otherwise. * * @param flags * @param arg * @return */ public static final boolean eval(long expr) { return expr != 0; } public static final void resetAll(Resettable[] r) { for (int i = 0; i < r.length; i++) { r[i].reset(); } } /** * Useful for unquoting strings, since StringTokenizer won't do it for us. * Returns null upon any failure. * * @param s * @param c * @return */ public static final String unquote(String s, char c) { int firstq = s.indexOf(c); int lastq = s.lastIndexOf(c); // Indexes valid? if (isQuoted(s,c)) return s.substring(firstq + 1, lastq); return null; } public static final boolean isQuoted(String s, char c) { int q1 = s.indexOf(c); int q2 = s.lastIndexOf(c); char c1,c2; // Indexes valid? if (q1 != -1 && q2 != -1) { if (q1 < q2) { c1=s.charAt(q1); c2=s.charAt(q2); return (c1==c2); } } return false; } public static final String unquoteIfQuoted(String s, char c) { String tmp = unquote(s, c); if (tmp != null) return tmp; return s; } /** Return either 0 or a hashcode * * @param o */ public static final int pointer(Object o) { if (o == null) return 0; else return o.hashCode(); } public static final boolean checkForExtension(String filename, String ext) { // Null filenames satisfy null extensions. if ((filename == null || filename.isEmpty()) && (ext == null || ext.isEmpty())) return true; String separator = System.getProperty("file.separator"); // Remove the path upto the filename. int lastSeparatorIndex = filename.lastIndexOf(separator); if (lastSeparatorIndex != -1) { filename = filename.substring(lastSeparatorIndex + 1); } String realext = null; // Get extension separator. It SHOULD be . on all platforms, right? int pos = filename.lastIndexOf('.'); if (pos >= 0 && pos <= filename.length() - 2) { // Extension present // Null comparator on valid extension if (ext == null || ext.isEmpty()) return false; realext = filename.substring(pos + 1); return realext.compareToIgnoreCase(ext) == 0; } else // No extension, and null/empty comparator if (ext == null || ext.isEmpty()) return true; // No extension, and non-null/nonempty comparator. return false; } /** Return the filename without extension, and stripped * of the path. * * @param s * @return */ public static final String removeExtension(String s) { String separator = System.getProperty("file.separator"); String filename; // Remove the path upto the filename. int lastSeparatorIndex = s.lastIndexOf(separator); if (lastSeparatorIndex == -1) { filename = s; } else { filename = s.substring(lastSeparatorIndex + 1); } // Remove the extension. int extensionIndex = filename.lastIndexOf("."); if (extensionIndex == -1) return filename; return filename.substring(0, extensionIndex); } /** * This method is supposed to return the "name" part of a filename. It was * intended to return length-limited (max 8 chars) strings to use as lump * indicators. There's normally no need to enforce this behavior, as there's * nothing preventing the engine from INTERNALLY using lump names with >8 * chars. However, just to be sure... * * @param path * @param limit Set to any value >0 to enforce a length limit * @param whole keep extension if set to true * @return */ public static final String extractFileBase(String path, int limit, boolean whole) { if (path==null) return path; int src = path.length() - 1; String separator = System.getProperty("file.separator"); src = path.lastIndexOf(separator)+1; if (src < 0) // No separator src = 0; int len = path.lastIndexOf('.'); if (whole || len<0 ) len=path.length()-src; // No extension. else len-= src; // copy UP to the specific number of characters, or all if (limit > 0) len = Math.min(limit, len); return path.substring(src, src + len); } /** Maes: File intead of "inthandle" */ public static long filelength(File handle) { try { return handle.length(); } catch (Exception e) { System.err.println("Error fstating"); return -1; } } @SuppressWarnings("unchecked") public static final <T> T[] resize(T[] oldarray, int newsize) { if (oldarray[0]!=null) return resize(oldarray[0],oldarray,newsize); T cls=null; try { cls = (T) oldarray.getClass().getComponentType().newInstance(); return resize(cls,oldarray,newsize); } catch (Exception e) { System.err.println("Cannot autodetect type in resizeArray.\n"); return null; } } /** Generic array resizing method. Calls Arrays.copyOf but then also * uses initArrayOfObject for the "abundant" elements. * * @param <T> * @param instance * @param oldarray * @param newsize * @return */ public static final <T> T[] resize(T instance,T[] oldarray, int newsize) { // Hmm... nope. if (newsize<=oldarray.length) return oldarray; // Copy old array with built-in stuff. T[] tmp =Arrays.copyOf(oldarray,newsize); // Init the null portions as well C2JUtils.initArrayOfObjects(tmp, oldarray.length,tmp.length); System.out.printf("Old array of type %s resized. New capacity: %d\n" ,instance.getClass(),newsize); return tmp; } /** Resize an array without autoinitialization. Same as Arrays.copyOf(..), just * prints a message. * * @param <T> * @param oldarray * @param newsize * @return */ public static final <T> T[] resizeNoAutoInit(T[] oldarray, int newsize) { // For non-autoinit types, this is enough. T[] tmp =Arrays.copyOf(oldarray,newsize); System.out.printf("Old array of type %s resized without auto-init. New capacity: %d\n" ,tmp.getClass().getComponentType(),newsize); return tmp; } @SuppressWarnings("unchecked") public final static<T> T[] getNewArray(T instance,int size){ T[] os=null; Class<T> c=(Class<T>) instance.getClass(); try { os = (T[]) Array.newInstance(c, size); } catch (Exception e) { e.printStackTrace(); System.err.println("Failure to allocate " + size + " objects of class " + c.getName() + "!"); System.exit(-1); } return os; } public final static<T> T[] getNewArray(int size,T instance){ @SuppressWarnings("unchecked") Class<T> c=(Class<T>) instance.getClass(); return getNewArray(c,size); } @SuppressWarnings("unchecked") public final static<T> T[] getNewArray(Class<T> c,int size){ T[] os=null; try { os = (T[]) Array.newInstance(c, size); } catch (Exception e) { e.printStackTrace(); System.err.println("Failure to allocate " + size + " objects of class " + c.getName() + "!"); System.exit(-1); } return os; } /** * Try to guess whether a URI represents a local file, a network any of the * above but zipped. Returns * * @param URI * @return an int with flags set according to InputStreamSugar */ public static int guessResourceType(String URI) { int result = 0; InputStream in = null; // This is bullshit. if (URI == null || URI.length() == 0) return InputStreamSugar.BAD_URI; try { in = new FileInputStream(new File(URI)); // It's a file result |= InputStreamSugar.FILE; } catch (Exception e) { // Not a file... URL u; try { u = new URL(URI); } catch (MalformedURLException e1) { return InputStreamSugar.BAD_URI; } try { in = u.openConnection().getInputStream(); result |= InputStreamSugar.NETWORK_FILE; } catch (IOException e1) { return InputStreamSugar.BAD_URI; } } // Try guessing if it's a ZIP file. A bit lame, really // TODO: add proper validation, and maybe MIME type checking // for network streams, for cases that we can't really // tell from extension alone. if (checkForExtension(URI, "zip")) { result |= InputStreamSugar.ZIP_FILE; } try { in.close(); } catch (IOException e) { } // All is well. Go on... return result; } } package utils; /** Half-assed way of finding the OS we're running under, shamelessly * ripped from: * * http://www.mkyong.com/java/how-to-detect-os-in-java-systemgetpropertyosname/ * . * This is required, as some things in AWT don't work exactly consistently cross-OS * (AWT frame size is the first thing that goes wrong, but also mouse grabbing * behavior). * * TODO: replace with Apache Commons library? * * @author velktron * */ public class OSValidator{ public static boolean isWindows(){ String os = System.getProperty("os.name").toLowerCase(); //windows return (os.indexOf( "win" ) >= 0); } public static boolean isMac(){ String os = System.getProperty("os.name").toLowerCase(); //Mac return (os.indexOf( "mac" ) >= 0); } public static boolean isUnix(){ String os = System.getProperty("os.name").toLowerCase(); //linux or unix return (os.indexOf( "nix") >=0 || os.indexOf( "nux") >=0); } public static boolean isUnknown(){ return (!isWindows() && !isUnix() && !isMac()); } } package doom; import defines.skill_t; /** Groups functions formerly in d_game, * in case you want to provide a different implementation */ public interface IDoomGame { void ExitLevel (); void WorldDone (); boolean CheckDemoStatus(); /** Can be called by the startup code or M_Responder. A normal game starts at map 1, but a warp test can start elsewhere */ public void DeferedInitNew (skill_t skill, int episode, int map); /** Can be called by the startup code or M_Responder, calls P_SetupLevel or W_EnterWorld. */ public void LoadGame (String name); /** Called by M_Responder. */ public void SaveGame (int slot, String description); /** Takes a screenshot *NOW* * */ public void ScreenShot() ; public void StartTitle(); public gameaction_t getGameAction(); public void setGameAction(gameaction_t ga); // public void PlayerReborn(int player); void DeathMatchSpawnPlayer(int playernum); } package doom; public interface NetConsts { public static int NCMD_EXIT= 0x80000000; public static int NCMD_RETRANSMIT =0x40000000; public static int NCMD_SETUP =0x20000000; public static int NCMD_KILL = 0x10000000; // kill game public static int NCMD_CHECKSUM = 0x0fffffff; public static int DOOMCOM_ID = 0x12345678; //Networking and tick handling related. Moved to DEFINES //protected static int BACKUPTICS = 12; // command_t public static short CMD_SEND = 1; public static short CMD_GET = 2; } package doom; import n.DoomSystemNetworking; import f.EndLevel; import f.Finale; import f.Wiper; import hu.HU; import i.IDiskDrawer; import i.IDoomSystem; import i.DoomVideoInterface; import m.IDoomMenu; import m.IRandom; import m.IVariablesManager; import p.AbstractLevelLoader; import p.Actions; import rr.ISpriteManager; import rr.Renderer; import rr.TextureManager; import s.IDoomSound; import s.IMusic; import s.ISoundDriver; import st.AbstractStatusBar; import timing.ITicker; import v.DoomVideoRenderer; import w.IWadLoader; import automap.IAutoMap; /** Since a lot of stuff requires shared/global access to * the WadLoader, the Renderer, the Video system etc. and * we're trying to depart from the global/static mentality, * a common sharing is required. Ideally, this would be a perfect * example of where multiple inheritance could be adopted, since most * stuff needs to share this status anyway. The next best thing is * to have local references of any used fields in the classes that use them. * * About generics: T refers to the type of the graphics resources, and is * currently byte[], as all graphics resources are 8-bit indexed. There are * no plans that this will change anytime soon. Some classes should allow * different types in theory, but it would be too complex and pointless to * make everything fully compliant at the moment. * * V refers to the type of DISPLAY, and can be 8-bit (byte[]), 16-bit (short[] * for HiColor and lesser modes such as ARGB4444, etc.), and, in the future, * int[] (truecolor). * * The general approach is sharing as much code as possible between different * implementations (e.g. rendering code), and only specialize methods/classes when * the abstraction of generics isn't enough (typically, when you have to assign * directly to primitive arrays or deal with primitive method signatures). * * Classes that have specialized code for indexed and hicolor modes should be top-level * classes in their package, and contain two nested, static, extending classes called * Indexed and HiColor e.g. new MyClass.Indexed() and new MyClass.HiColor(), while any common * code should reside in MyClass. * * @author velktron * */ public class DoomContext<T,V>{ public DoomMain<T,V> DM; public IDoomGame DG; public IWadLoader W; public IRandom RND; public IDoomSystem I; public IDoomSound S; public ISoundDriver ISND; public IMusic IMUS; public DoomVideoInterface<V> VI; public AbstractStatusBar ST; public DoomVideoRenderer<T,V> V; public DoomSystemNetworking DNI; public IDoomGameNetworking DGN; public AbstractLevelLoader LL; public IDoomMenu M; public Actions P; public Renderer<T,V> R; public HU HU; public IAutoMap<T,V> AM; public Finale<T> F; public EndLevel<T,V> WI; public Wiper<T,V> WIPE; public TextureManager<T> TM; public ISpriteManager SM; public ICommandLineManager CM; public ITicker TICK; public IDiskDrawer DD; public IVariablesManager VM; } package doom; import java.util.Hashtable; import utils.C2JUtils; import defines.GameMode_t; public class DoomVersions { public final static Hashtable<String,GameMode_t> VersionChecker=new Hashtable<String,GameMode_t>(); static { VersionChecker.put("doom.wad",GameMode_t.registered); VersionChecker.put("doom2.wad",GameMode_t.commercial); VersionChecker.put("udoom.wad",GameMode_t.retail); VersionChecker.put("tnt.wad",GameMode_t.pack_tnt); VersionChecker.put("plutonia.wad",GameMode_t.pack_plut); VersionChecker.put("doom1.wad",GameMode_t.shareware); VersionChecker.put("xbla.wad",GameMode_t.pack_xbla); } public DoomVersions(){ } /** Try all versions in given doommwaddir * */ public void tryThemAll(String doomwaddir){ // Commercial. doom2wad = (doomwaddir+ "/doom2.wad"); // Retail. doomuwad = (doomwaddir+ "/doomu.wad"); // Registered. doomwad = (doomwaddir+ "/doom.wad"); // Shareware. doom1wad = (doomwaddir+ "/doom1.wad"); // Bug, dear Shawn. // Insufficient malloc, caused spurious realloc errors. plutoniawad = (doomwaddir+ "/plutonia.wad"); tntwad = (doomwaddir+ "/tnt.wad"); xblawad = (doomwaddir+ "/xbla.wad"); // French stuff. doom2fwad=(doomwaddir+ "/doom2f.wad"); } public String doom1wad, doomwad, doomuwad, doom2wad, doom2fwad, plutoniawad, tntwad, xblawad; /** Try only one IWAD. * * @param iwad * @return */ public GameMode_t tryOnlyOne(String iwad, String doomwaddir) { // Is it a known and valid version? GameMode_t tmp=VersionChecker.get(iwad.toLowerCase()); if (tmp!=null) { // Can we read it? if (C2JUtils.testReadAccess(doomwaddir+iwad)) return tmp; // Yes, so communicate the gamemode back. } // It's either invalid or we can't read it. // Fuck that. return null; } } package doom; /** Meant to provide a more lightweight alternative to Java's serialization model, * specifically for the purpose of sending * Objects implementing this can return references to one same byte array, with minimal * overhead. Since it's for send-only purposes, it won't matter if it's modified. * * * But don't use it in lieu of CacheableDoomObject! * * @author admin * */ public interface IDatagramSerializable { /** Packs object into a byte array suitable to send over * datagram networks. Typically, objects cache this array * for later use, and is availabe through cached() * * @return */ public byte[] pack(); /** Packs object into a byte array suitable to send over * datagram networks. The array is supplied externally * (good for daisy-chaining stuff into a single packet). * * @return */ public void pack(byte[] buf, int offset); /** Deserializes an object from a given byte buffer. * Only the first (sizeof) bytes will be used, dependant * on each object's implementation. Will NOT also copy * the byte[] caches. */ public void unpack(byte[] buf); /** Deserializes an object from a given byte buffer. * Only the first (sizeof) bytes will be used, starting * from a specified offset, dependant on each object's * implementation. */ public void unpack(byte[] buf, int offset); /** Only use this if you are 100% sure that the object's content * won't have changed since the last call of pack(). * * @return Should return the underlying byte[] array directly. */ public byte[] cached(); } package doom; // Event structure. public class event_t { public event_t(){ } public event_t(event_t t){ this.type = t.type; this.data1 = t.data1; this.data2 = t.data2; this.data3 = t.data3; } public event_t(evtype_t type, int data) { this.type = type; this.data1 = data; } public event_t(char c) { this.type = evtype_t.ev_keydown; this.data1 = c; } public event_t(evtype_t type, int data1, int data2, int data3) { this.type = type; this.data1 = data1; this.data2 = data2; this.data3 = data3; } public void setFrom(event_t t){ this.type = t.type; this.data1 = t.data1; this.data2 = t.data2; this.data3 = t.data3; } public evtype_t type; /** keys / mouse/joystick buttons, or wheel rotation amount */ public int data1; /** mouse/joystick x move */ public int data2; /** mouse/joystick y move */ public int data3; }; package doom; import static data.Defines.*; import static g.Keys.*; import static data.Limits.*; import java.io.OutputStreamWriter; import automap.IAutoMap; import m.IUseVariables; import m.IVariablesManager; import m.Settings; import p.mobj_t; import rr.Renderer; import utils.C2JUtils; import v.DoomVideoRenderer; import data.mapthing_t; import defines.*; import demo.IDoomDemo; import f.Finale; import f.Wiper; /** * We need globally shared data structures, for defining the global state * variables. MAES: in pure OO style, this should be a global "Doom state" * object to be passed along various modules. No ugly globals here!!! Now, some * of the variables that appear here were actually defined in separate modules. * Pretty much, whatever needs to be shared with other modules was placed here, * either as a local definition, or as an extern share. The very least, I'll * document where everything is supposed to come from/reside. */ public abstract class DoomStatus<T,V> extends DoomContext<T,V> implements IUseVariables { public static final int BGCOLOR= 7; public static final int FGCOLOR =8; public static int RESENDCOUNT =10; public static int PL_DRONE =0x80; // bit flag in doomdata->player public String[] wadfiles=new String[MAXWADFILES]; boolean drone; /** Command line parametersm, actually defined in d_main.c */ public boolean nomonsters; // checkparm of -nomonsters public boolean respawnparm; // checkparm of -respawn public boolean fastparm; // checkparm of -fast public boolean devparm; // DEBUG: launched with -devparm // MAES: declared as "extern", shared with Menu.java public boolean inhelpscreens; boolean advancedemo; /////////// Local to doomstat.c //////////// // TODO: hide those behind getters /** Game Mode - identify IWAD as shareware, retail etc. * This is now hidden behind getters so some cases like plutonia * etc. can be handled more cleanly. * */ private GameMode_t gamemode; public void setGameMode(GameMode_t mode){ this.gamemode=mode; } public GameMode_t getGameMode(){ return gamemode; } public boolean isShareware(){ return (gamemode== GameMode_t.shareware); } /** Commercial means Doom 2, Plutonia, TNT, and possibly others like XBLA. * * @return */ public boolean isCommercial(){ return (gamemode== GameMode_t.commercial || gamemode== GameMode_t.pack_plut || gamemode== GameMode_t.pack_tnt || gamemode== GameMode_t.pack_xbla); } /** Retail means Ultimate. * * @return */ public boolean isRetail(){ return (gamemode== GameMode_t.retail ); } /** Registered is a subset of Ultimate * * @return */ public boolean isRegistered(){ return (gamemode== GameMode_t.registered || gamemode== GameMode_t.retail ); } public GameMission_t gamemission; /** Set if homebrew PWAD stuff has been added. */ public boolean modifiedgame; /** Language. */ public Language_t language; // /////////// Normally found in d_main.c /////////////// // Selected skill type, map etc. /** Defaults for menu, methinks. */ public skill_t startskill; public int startepisode; public int startmap; public boolean autostart; /** Selected by user */ public skill_t gameskill; public int gameepisode; public int gamemap; /** Nightmare mode flag, single player. */ public boolean respawnmonsters; /** Netgame? Only true if >1 player. */ public boolean netgame; /** * Flag: true only if started as net deathmatch. An enum might handle * altdeath/cooperative better. Use altdeath for the "2" value */ public boolean deathmatch; /** Use this instead of "deathmatch=2" which is bullshit. */ public boolean altdeath; //////////// STUFF SHARED WITH THE RENDERER /////////////// // ------------------------- // Status flags for refresh. // public boolean nodrawers; public boolean noblit; public boolean viewactive; // Player taking events, and displaying. public int consoleplayer; public int displayplayer; // Depending on view size - no status bar? // Note that there is no way to disable the // status bar explicitely. public boolean statusbaractive; public boolean automapactive; // In AutoMap mode? public boolean menuactive; // Menu overlayed? public boolean paused; // Game Pause? // ------------------------- // Internal parameters for sound rendering. // These have been taken from the DOS version, // but are not (yet) supported with Linux // (e.g. no sound volume adjustment with menu. // These are not used, but should be (menu). // From m_menu.c: // Sound FX volume has default, 0 - 15 // Music volume has default, 0 - 15 // These are multiplied by 8. /** maximum volume for sound */ public int snd_SfxVolume; /** maximum volume for music */ public int snd_MusicVolume; /** Maximum number of sound channels */ public int numChannels; // Current music/sfx card - index useless // w/o a reference LUT in a sound module. // Ideally, this would use indices found // in: /usr/include/linux/soundcard.h public int snd_MusicDevice; public int snd_SfxDevice; // Config file? Same disclaimer as above. public int snd_DesiredMusicDevice; public int snd_DesiredSfxDevice; // ------------------------------------- // Scores, rating. // Statistics on a given map, for intermission. // public int totalkills; public int totalitems; public int totalsecret; /** TNTHOM "cheat" for flashing HOM-detecting BG */ public boolean flashing_hom; // Added for prBoom+ code public int totallive; // Timer, for scores. public int levelstarttic; // gametic at level start public int leveltime; // tics in game play for par // -------------------------------------- // DEMO playback/recording related stuff. // No demo, there is a human player in charge? // Disable save/end game? public boolean usergame; // ? public boolean demoplayback; public boolean demorecording; // Quit after playing a demo from cmdline. public boolean singledemo; /** Set this to GS_DEMOSCREEN upon init, else it will be null*/ public gamestate_t gamestate=gamestate_t.GS_DEMOSCREEN; // ----------------------------- // Internal parameters, fixed. // These are set by the engine, and not changed // according to user inputs. Partly load from // WAD, partly set at startup time. public int gametic; // Bookkeeping on players - state. public player_t[] players; // Alive? Disconnected? public boolean[] playeringame = new boolean[MAXPLAYERS]; public mapthing_t[] deathmatchstarts = new mapthing_t[MAX_DM_STARTS]; /** pointer into deathmatchstarts */ public int deathmatch_p; /** Player spawn spots. */ public mapthing_t[] playerstarts = new mapthing_t[MAXPLAYERS]; /** Intermission stats. Parameters for world map / intermission. */ public wbstartstruct_t wminfo; /** LUT of ammunition limits for each kind. This doubles with BackPack powerup item. NOTE: this "maxammo" is treated like a global. */ public final static int[] maxammo = {200, 50, 300, 50}; // ----------------------------------------- // Internal parameters, used for engine. // // File handling stuff. public OutputStreamWriter debugfile; // if true, load all graphics at level load public boolean precache; // wipegamestate can be set to -1 // to force a wipe on the next draw // wipegamestate can be set to -1 to force a wipe on the next draw public gamestate_t wipegamestate = gamestate_t.GS_DEMOSCREEN; public int mouseSensitivity=15; // debug flag to cancel adaptiveness // Set to true during timedemos. public boolean singletics=false; /* A "fastdemo" is a demo with a clock that tics as * fast as possible, yet it maintains adaptiveness and doesn't * try to render everything at all costs. */ protected boolean fastdemo; protected boolean normaldemo; protected String loaddemo; public int bodyqueslot; // Needed to store the number of the dummy sky flat. // Used for rendering, // as well as tracking projectiles etc. //public int skyflatnum; // TODO: Netgame stuff (buffers and pointers, i.e. indices). // TODO: This is ??? public doomcom_t doomcom; // TODO: This points inside doomcom. public doomdata_t netbuffer; public ticcmd_t[] localcmds = new ticcmd_t[BACKUPTICS]; public int rndindex; public int maketic; public int[] nettics = new int[MAXNETNODES]; public ticcmd_t[][] netcmds;// [MAXPLAYERS][BACKUPTICS]; /** MAES: this WAS NOT in the original. * Remember to call it! */ protected void initNetGameStuff() { //this.netbuffer = new doomdata_t(); this.doomcom = new doomcom_t(); this.netcmds = new ticcmd_t[MAXPLAYERS][BACKUPTICS]; C2JUtils.initArrayOfObjects(localcmds); for (int i=0;i<MAXPLAYERS;i++){ C2JUtils.initArrayOfObjects(netcmds[i]); } } public abstract void Init(); // Fields used for selecting variable BPP implementations. protected abstract Finale<T> selectFinale(); protected abstract DoomVideoRenderer<T,V> selectVideoRenderer(); protected abstract Renderer<T,V> selectRenderer(); protected abstract Wiper<T,V> selectWiper(); protected abstract IAutoMap<T,V> selectAutoMap(); // MAES: Fields specific to DoomGame. A lot of them were // duplicated/externalized // in d_game.c and d_game.h, so it makes sense adopting a more unified // approach. protected gameaction_t gameaction=gameaction_t.ga_nothing; public boolean sendpause; // send a pause event next tic protected boolean sendsave; // send a save event next tic protected int starttime; protected boolean timingdemo; // if true, exit with report on completion public boolean getPaused() { return paused; } public void setPaused(boolean paused) { this.paused = paused; } // ////////// DEMO SPECIFIC STUFF///////////// protected String demoname; protected boolean netdemo; //protected IDemoTicCmd[] demobuffer; protected IDoomDemo demobuffer; /** pointers */ // USELESS protected int demo_p; // USELESS protected int demoend; protected short[][] consistancy = new short[MAXPLAYERS][BACKUPTICS]; protected byte[] savebuffer; /* TODO Proper reconfigurable controls. Defaults hardcoded for now. T3h h4x, d00d. */ public int key_right=KEY_RIGHTARROW; public int key_left=KEY_LEFTARROW; public int key_up='w'; public int key_down='a'; public int key_strafeleft='s'; public int key_straferight='d'; public int key_fire=KEY_CTRL; public int key_use=' '; public int key_strafe=KEY_ALT; public int key_speed=KEY_SHIFT; // Heretic stuff public int key_lookup=KEY_PGUP; public int key_lookdown=KEY_PGDN; public int key_lookcenter=KEY_END; public int mousebfire=0; public int mousebstrafe=1; public int mousebforward=2; public int joybfire; public int joybstrafe; public int joybuse; public int joybspeed; /** Cancel vertical mouse movement by default */ protected boolean novert=true; protected int MAXPLMOVE() { return forwardmove[1]; } protected static final int TURBOTHRESHOLD = 0x32; /** fixed_t */ protected final int[] forwardmove = { 0x19, 0x32 }; // + slow turn protected final int[] sidemove = { 0x18, 0x28 }; protected final int[] angleturn = { 640, 1280, 320 }; protected static final int SLOWTURNTICS = 6; protected static final int NUMKEYS = 256; protected boolean[] gamekeydown = new boolean[NUMKEYS]; protected boolean keysCleared; public boolean alwaysrun; protected int turnheld; // for accelerative turning protected int lookheld; // for accelerative looking? protected boolean[] mousearray = new boolean[4]; /** This is an alias for mousearray [1+i] */ protected boolean mousebuttons(int i) { return mousearray[1 + i]; // allow [-1] } protected void mousebuttons(int i, boolean value) { mousearray[1 + i] = value; // allow [-1] } protected void mousebuttons(int i, int value) { mousearray[1 + i] = value != 0; // allow [-1] } /** mouse values are used once */ protected int mousex, mousey; protected int dclicktime; protected int dclickstate; protected int dclicks; protected int dclicktime2, dclickstate2, dclicks2; /** joystick values are repeated */ protected int joyxmove, joyymove; protected boolean[] joyarray = new boolean[5]; protected boolean joybuttons(int i) { return joyarray[1 + i]; // allow [-1] } protected void joybuttons(int i, boolean value) { joyarray[1 + i] = value; // allow [-1] } protected void joybuttons(int i, int value) { joyarray[1 + i] = value != 0; // allow [-1] } protected int savegameslot; protected String savedescription; protected static final int BODYQUESIZE = 32; protected mobj_t[] bodyque = new mobj_t[BODYQUESIZE]; public String statcopy; // for statistics driver /** Not documented/used in linuxdoom. I supposed it could be used to * ignore mouse input? */ public boolean use_mouse,use_joystick; /** More prBoom+ stuff. Used mostly for code uhm..reuse, rather * than to actually change the way stuff works. * */ public static int compatibility_level; public DoomStatus(){ players = new player_t[MAXPLAYERS]; C2JUtils.initArrayOfObjects(players); this.wminfo=new wbstartstruct_t(); initNetGameStuff(); } @Override public void registerVariableManager(IVariablesManager manager) { this.VM=manager; } @Override public void update() { this.snd_SfxVolume=VM.getSetting(Settings.sfx_volume).getInteger(); this.snd_MusicVolume=VM.getSetting(Settings.music_volume).getInteger(); this.alwaysrun=VM.getSetting(Settings.alwaysrun).getBoolean(); // Keys... this.key_right=VM.getSetting(Settings.key_right).getChar(); this.key_left=VM.getSetting(Settings.key_left).getChar(); this.key_up=VM.getSetting(Settings.key_up).getChar(); this.key_down=VM.getSetting(Settings.key_down).getChar(); this.key_strafeleft=VM.getSetting(Settings.key_strafeleft).getChar(); this.key_straferight=VM.getSetting(Settings.key_straferight).getChar(); this.key_fire=VM.getSetting(Settings.key_fire).getChar(); this.key_use=VM.getSetting(Settings.key_use).getChar(); this.key_strafe=VM.getSetting(Settings.key_strafe).getChar(); this.key_speed=VM.getSetting(Settings.key_speed).getChar(); // Mouse buttons this.use_mouse=VM.getSetting(Settings.use_mouse).getBoolean(); this.mousebfire=VM.getSetting(Settings.mouseb_fire).getInteger(); this.mousebstrafe=VM.getSetting(Settings.mouseb_strafe).getInteger(); this.mousebforward=VM.getSetting(Settings.mouseb_forward).getInteger(); // Joystick this.use_joystick=VM.getSetting(Settings.use_joystick).getBoolean(); this.joybfire=VM.getSetting(Settings.joyb_fire).getInteger(); this.joybstrafe=VM.getSetting(Settings.joyb_strafe).getInteger(); this.joybuse=VM.getSetting(Settings.joyb_use).getInteger(); this.joybspeed=VM.getSetting(Settings.joyb_speed).getInteger(); // Sound this.numChannels=VM.getSetting(Settings.snd_channels).getInteger(); // Video...so you should wait until video renderer is active. this.V.setUsegamma(VM.getSetting(Settings.usegamma).getInteger()); // These should really be handled by the menu. this.M.setShowMessages(VM.getSetting(Settings.show_messages).getBoolean()); this.M.setScreenBlocks(VM.getSetting(Settings.screenblocks).getInteger()); // These should be handled by the HU for (int i=0;i<=9;i++){ String chatmacro=String.format("chatmacro%d",i); this.HU.setChatMacro(i,VM.getSetting(chatmacro).toString()); } } @Override public void commit() { VM.putSetting(Settings.sfx_volume,this.snd_SfxVolume); VM.putSetting(Settings.music_volume,this.snd_MusicVolume); VM.putSetting(Settings.alwaysrun, this.alwaysrun); // Keys... VM.putSetting(Settings.key_right,this.key_right); VM.putSetting(Settings.key_left,this.key_left); VM.putSetting(Settings.key_up,this.key_up); VM.putSetting(Settings.key_down,this.key_down); VM.putSetting(Settings.key_strafeleft,this.key_strafeleft); VM.putSetting(Settings.key_straferight,this.key_straferight); VM.putSetting(Settings.key_fire,this.key_fire); VM.putSetting(Settings.key_use,this.key_use); VM.putSetting(Settings.key_strafe,this.key_strafe); VM.putSetting(Settings.key_speed,this.key_speed); // Mouse buttons VM.putSetting(Settings.use_mouse,this.use_mouse); VM.putSetting(Settings.mouseb_fire,this.mousebfire); VM.putSetting(Settings.mouseb_strafe,this.mousebstrafe); VM.putSetting(Settings.mouseb_forward,this.mousebforward); // Joystick VM.putSetting(Settings.use_joystick,this.use_joystick); VM.putSetting(Settings.joyb_fire,this.joybfire); VM.putSetting(Settings.joyb_strafe,this.joybstrafe); VM.putSetting(Settings.joyb_use,this.joybuse); VM.putSetting(Settings.joyb_speed,this.joybspeed); // Sound VM.putSetting(Settings.snd_channels,this.numChannels); // Video... VM.putSetting(Settings.usegamma,V.getUsegamma()); // These should really be handled by the menu. VM.putSetting(Settings.show_messages,this.M.getShowMessages()); VM.putSetting(Settings.screenblocks,this.M.getScreenBlocks()); // These should be handled by the HU for (int i=0;i<=9;i++){ String chatmacro=String.format("chatmacro%d",i); VM.putSetting(chatmacro,this.HU.chat_macros[i]); } } } // $Log: DoomStatus.java,v $ // Revision 1.36 2012/11/06 16:04:58 velktron // Variables manager less tightly integrated. // // Revision 1.35 2012/09/24 17:16:22 velktron // Massive merge between HiColor and HEAD. There's no difference from now on, and development continues on HEAD. // // Revision 1.34.2.3 2012/09/24 16:58:06 velktron // TrueColor, Generics. // // Revision 1.34.2.2 2012/09/20 14:25:13 velktron // Unified DOOM!!! // // Revision 1.34.2.1 2012/09/17 16:06:52 velktron // Now handling updates of all variables, though those specific to some subsystems should probably be moved??? // // Revision 1.34 2011/11/01 23:48:10 velktron // Added tnthom stuff. // // Revision 1.33 2011/10/24 02:11:27 velktron // Stream compliancy // // Revision 1.32 2011/10/07 16:01:16 velktron // Added freelook stuff, using Keys. // // Revision 1.31 2011/09/27 16:01:41 velktron // -complevel_t // // Revision 1.30 2011/09/27 15:54:51 velktron // Added some more prBoom+ stuff. // // Revision 1.29 2011/07/28 17:07:04 velktron // Added always run hack. // // Revision 1.28 2011/07/16 10:57:50 velktron // Merged finnw's changes for enabling polling of ?_LOCK keys. // // Revision 1.27 2011/06/14 20:59:47 velktron // Channel settings now read from default.cfg. Changes in sound creation order. // // Revision 1.26 2011/06/04 11:04:25 velktron // Fixed registered/ultimate identification. // // Revision 1.25 2011/06/01 17:35:56 velktron // Techdemo v1.4a level. Default novert and experimental mochaevents interface. // // Revision 1.24 2011/06/01 00:37:58 velktron // Changed default keys to WASD. // // Revision 1.23 2011/05/31 21:45:51 velktron // Added XBLA version as explicitly supported. // // Revision 1.22 2011/05/30 15:50:42 velktron // Changed to work with new Abstract classes // // Revision 1.21 2011/05/26 17:52:11 velktron // Now using ICommandLineManager // // Revision 1.20 2011/05/26 13:39:52 velktron // Now using ICommandLineManager // // Revision 1.19 2011/05/25 17:56:52 velktron // Introduced some fixes for mousebuttons etc. // // Revision 1.18 2011/05/24 17:44:37 velktron // usemouse added for defaults // package doom; import i.DiskDrawer; import i.DoomStatusAware; import i.DoomSystem; import i.DoomVideoInterface; import i.Strings; import java.io.BufferedInputStream; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStreamWriter; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import n.DummyNetworkDriver; import static data.dstrings.*; import p.Actions; import p.BoomLevelLoader; import p.mobj_t; import automap.IAutoMap; import automap.Map; import awt.AWTDoom; import f.EndLevel; import f.Finale; import f.Wiper; import hu.HU; import m.Menu; import m.MenuMisc; import m.DoomRandom; import m.Settings; import m.VarsManager; import static doom.NetConsts.*; import static doom.englsh.*; import data.Tables; import data.dstrings; import data.mapthing_t; import data.mobjtype_t; import defines.*; import demo.IDemoTicCmd; import demo.IDoomDemo; import demo.VanillaDoomDemo; import demo.VanillaTiccmd; import data.sounds.musicenum_t; import data.sounds.sfxenum_t; import static data.Defines.BACKUPTICS; import static g.Keys.*; import static data.Defines.NORMALUNIX; import static data.Defines.PU_STATIC; import static data.Defines.VERSION; import rr.Renderer; import rr.SimpleTextureManager; import rr.SpriteManager; import rr.UnifiedRenderer; import rr.ViewVars; import rr.subsector_t; import rr.parallel.ParallelRenderer; import rr.parallel.ParallelRenderer2; import s.AbstractDoomAudio; import s.ClassicDoomSoundDriver; import s.ClipSFXModule; import s.DavidMusicModule; import s.DavidSFXModule; import s.DummyMusic; import s.DummySFX; import s.DummySoundDriver; import s.SpeakerDoomSoundDriver; import s.SuperDoomSoundDriver; //import s.SpeakerDoomSoundDriver; import savegame.IDoomSaveGame; import savegame.IDoomSaveGameHeader; import savegame.VanillaDSG; import savegame.VanillaDSGHeader; import st.StatusBar; import timing.FastTicker; import timing.ITicker; import timing.MilliTicker; import timing.NanoTicker; import utils.C2JUtils; import v.BufferedRenderer; import v.BufferedRenderer16; import v.BufferedRenderer32; import v.ColorTint; import v.DoomVideoRenderer; import v.GammaTables; import v.IVideoScale; import v.IVideoScaleAware; import v.PaletteGenerator; //import v.VideoScaleInfo; import v.VisualSettings; import w.DoomBuffer; import w.WadLoader; import static data.Defines.*; import static data.Limits.*; import static data.Tables.*; import static data.dstrings.SAVEGAMENAME; import static data.info.mobjinfo; import static data.info.states; import static m.fixed_t.FRACBITS; import static m.fixed_t.MAPFRACUNIT; import static utils.C2JUtils.*; // Emacs style mode select -*- C++ -*- //----------------------------------------------------------------------------- // // $Id: DoomMain.java,v 1.110 2016/06/06 22:21:24 velktron Exp $ // // Copyright (C) 1993-1996 by id Software, Inc. // // 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; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // DOOM main program (D_DoomMain) and game loop (D_DoomLoop), // plus functions to determine game mode (shareware, registered), // parse command line parameters, configure game parameters (turbo), // and call the startup functions. // // In Mocha Doom, this was unified with d_game and doomstat.c // //----------------------------------------------------------------------------- public abstract class DoomMain<T,V> extends DoomStatus<T,V> implements IDoomGameNetworking, IDoomGame, IDoom, IVideoScaleAware{ public static final String rcsid = "$Id: DoomMain.java,v 1.110 2016/06/06 22:21:24 velktron Exp $"; // // EVENT HANDLING // // Events are asynchronous inputs generally generated by the game user. // Events can be discarded if no responder claims them // public event_t[] events=new event_t[MAXEVENTS]; public int eventhead; public int eventtail; /** * D_PostEvent * Called by the I/O functions when input is detected */ public void PostEvent (event_t ev) { // TODO create a pool of reusable messages? // NEVERMIND we can use the original system. events[eventhead].setFrom(ev); eventhead = (++eventhead)&(MAXEVENTS-1); } /** * D_ProcessEvents * Send all the events of the given timestamp down the responder chain */ public void ProcessEvents () { event_t ev; // IF STORE DEMO, DO NOT ACCEPT INPUT if ( ( isCommercial() ) && (W.CheckNumForName("MAP01")<0) ) return; for ( ; eventtail != eventhead ; eventtail = (++eventtail)&(MAXEVENTS-1) ) { ev = events[eventtail]; if (M.Responder (ev)){ //epool.checkIn(ev); continue; // menu ate the event } Responder (ev); // We're done with it, return it to the pool. //epool.checkIn(ev); } } // "static" to Display, don't move. private boolean viewactivestate = false; private boolean menuactivestate = false; private boolean inhelpscreensstate = false; private boolean fullscreen = false; private gamestate_t oldgamestate = gamestate_t.GS_MINUS_ONE; private int borderdrawcount; /** * D_Display * draw current display, possibly wiping it from the previous * @throws IOException */ public void Display () throws IOException { int nowtime; int tics; int wipestart; int y; boolean done; boolean wipe; boolean redrawsbar; if (nodrawers) return; // for comparative timing / profiling redrawsbar = false; // change the view size if needed if (R.getSetSizeNeeded()) { R.ExecuteSetViewSize (); oldgamestate = gamestate_t.GS_MINUS_ONE; // force background redraw borderdrawcount = 3; } // save the current screen if about to wipe if (wipe=(gamestate != wipegamestate)) { wipe = true; WIPE.StartScreen(0, 0, SCREENWIDTH, SCREENHEIGHT); } else wipe = false; if (gamestate == gamestate_t.GS_LEVEL && eval(gametic)) HU.Erase(); // do buffered drawing switch (gamestate) { case GS_LEVEL: if (!eval(gametic)) break; if (automapactive) AM.Drawer (); if (wipe || (!R.isFullHeight() && fullscreen) || (inhelpscreensstate && !inhelpscreens) || (DD.justDoneReading())) redrawsbar = true; // just put away the help screen ST.Drawer (R.isFullHeight(), redrawsbar ); fullscreen = R.isFullHeight(); break; case GS_INTERMISSION: WI.Drawer (); break; case GS_FINALE: F.Drawer (); break; case GS_DEMOSCREEN: PageDrawer (); break; } // draw the view directly if (gamestate == gamestate_t.GS_LEVEL && !automapactive && eval(gametic)){ if (flashing_hom){ V.FillRect(gametic%256,0,view.getViewWindowX(),view.getViewWindowY(), view.getScaledViewWidth(),view.getScaledViewHeight()); } R.RenderPlayerView (players[displayplayer]); } // Automap was active, update only HU. if (gamestate == gamestate_t.GS_LEVEL && eval(gametic)) HU.Drawer (); // clean up border stuff if (gamestate != oldgamestate && gamestate != gamestate_t.GS_LEVEL) VI.SetPalette (0); // see if the border needs to be initially drawn if (gamestate == gamestate_t.GS_LEVEL && oldgamestate != gamestate_t.GS_LEVEL) { viewactivestate = false; // view was not active R.FillBackScreen (); // draw the pattern into the back screen } // see if the border needs to be updated to the screen if (gamestate == gamestate_t.GS_LEVEL && !automapactive && !R.isFullScreen()) { if (menuactive || menuactivestate || !viewactivestate) borderdrawcount = 3; if (eval(borderdrawcount)) { R.DrawViewBorder (); // erase old menu stuff borderdrawcount--; } } menuactivestate = menuactive; viewactivestate = viewactive; inhelpscreensstate = inhelpscreens; oldgamestate = wipegamestate = gamestate; // draw pause pic if (paused) { if (automapactive) y = 4; else y = view.getViewWindowY()+4; V.DrawPatchDirect(view.getViewWindowX()+(view.getScaledViewWidth()-68)/2, y,0,W.CachePatchName ("M_PAUSE", PU_CACHE)); } // menus go directly to the screen M.Drawer (); // menu is drawn even on top of everything NetUpdate (); // send out any new accumulation // Disk access goes after everything. DD.Drawer(); // normal update if (!wipe) { //System.out.print("Tick "+DM.gametic+"\t"); //System.out.print(DM.players[0]); VI.FinishUpdate (); // page flip or blit buffer return; } // wipe update. At this point, AT LEAST one frame of the game must have been // rendered for this to work. 22/5/2011: Fixed a vexing bug with the wiper. // Jesus Christ with a Super Shotgun! WIPE.EndScreen(0, 0, SCREENWIDTH, SCREENHEIGHT); wipestart = TICK.GetTime () - 1; do { do { nowtime = TICK.GetTime (); tics = nowtime - wipestart; } while (tics==0); // Wait until a single tic has passed. wipestart = nowtime; done = WIPE.ScreenWipe(Wiper.wipe.Melt.ordinal() , 0, 0, SCREENWIDTH, SCREENHEIGHT, tics); ISND.UpdateSound(); ISND.SubmitSound(); // update sounds after one wipe tic. VI.UpdateNoBlit (); M.Drawer (); // menu is drawn even on top of wipes VI.FinishUpdate (); // page flip or blit buffer } while (!done); } /** * D-DoomLoop() * Not a globally visible function, * just included for source reference, * called by D_DoomMain, never exits. * Manages timing and IO, * calls all ?_Responder, ?_Ticker, and ?_Drawer, * calls I_GetTime, I_StartFrame, and I_StartTic * @throws IOException */ public void DoomLoop () throws IOException { if (demorecording) BeginRecording (); if (eval(CM.CheckParm ("-debugfile"))) { String filename="debug"+consoleplayer+".txt"; System.out.println("debug output to: "+filename); try { debugfile = new OutputStreamWriter(new FileOutputStream(filename)); } catch (FileNotFoundException e) { System.err.println("Couldn't open debugfile. Now, that sucks some putrid shit out of John Romero's asshole!"); e.printStackTrace(); } } view=R.getView(); while (true) { // frame syncronous IO operations VI.StartFrame (); // process one or more tics if (singletics) { VI.StartTic (); ProcessEvents (); BuildTiccmd (netcmds[consoleplayer][maketic%BACKUPTICS]); if (advancedemo) DoAdvanceDemo (); M.Ticker (); Ticker (); gametic++; maketic++; } else { DGN.TryRunTics (); // will run at least one tic (in NET) } S.UpdateSounds (players[consoleplayer].mo);// move positional sounds // Update display, next frame, with current state. Display (); //#ifndef SNDSERV // Sound mixing for the buffer is snychronous. ISND.UpdateSound(); //#endif // Synchronous sound output is explicitly called. //#ifndef SNDINTR // Update sound output. ISND.SubmitSound(); //#endif } } // To keep an "eye" on the renderer. protected ViewVars view; // // DEMO LOOP // int demosequence; int pagetic; String pagename; /** * D_PageTicker * Handles timing for warped projection */ public final void PageTicker () { if (--pagetic < 0) AdvanceDemo (); } /** * D_PageDrawer */ public final void PageDrawer () { // FIXME: this check wasn't necessary in vanilla, since pagename was // guaranteed(?) not to be null or had a safe default value. if (pagename != null) V.DrawPatchSolidScaled (0,0, SAFE_SCALE, SAFE_SCALE,0,W.CachePatchName(pagename, PU_CACHE)); } /** * D_AdvanceDemo * Called after each demo or intro demosequence finishes */ public void AdvanceDemo () { advancedemo = true; } // // This cycles through the demo sequences. // FIXME - version dependant demo numbers? // public void DoAdvanceDemo () { players[consoleplayer].playerstate = PST_LIVE; // not reborn advancedemo = false; usergame = false; // no save / end game here paused = false; gameaction = gameaction_t.ga_nothing; if ( isRetail()) // Allows access to a 4th demo. demosequence = (demosequence+1)%7; else demosequence = (demosequence+1)%6; switch (demosequence) { case 0: if ( isCommercial() ) pagetic = 35 * 11; else pagetic = 170; gamestate = gamestate_t.GS_DEMOSCREEN; if (W.CheckNumForName("TITLEPIC")!=-1){ pagename = "TITLEPIC"; } else { if (W.CheckNumForName("DMENUPIC")!=-1){ pagename = "DMENUPIC"; } } if ( isCommercial() ) S.StartMusic(musicenum_t.mus_dm2ttl); else S.StartMusic (musicenum_t.mus_intro); break; case 1: DeferedPlayDemo ("demo1"); break; case 2: pagetic = 200; gamestate = gamestate_t.GS_DEMOSCREEN; pagename = "CREDIT"; break; case 3: DeferedPlayDemo ("demo2"); break; case 4: gamestate = gamestate_t.GS_DEMOSCREEN; if ( isCommercial()) { pagetic = 35 * 11; pagename = "TITLEPIC"; S.StartMusic(musicenum_t.mus_dm2ttl); } else { pagetic = 200; if ( isRetail() ) pagename = "CREDIT"; else pagename = "HELP1"; } break; case 5: DeferedPlayDemo ("demo3"); break; // THE DEFINITIVE DOOM Special Edition demo case 6: DeferedPlayDemo ("demo4"); break; } } /** * D_StartTitle */ public void StartTitle () { gameaction = gameaction_t.ga_nothing; demosequence = -1; AdvanceDemo (); } // printtitle for every printed line StringBuffer title=new StringBuffer(); /** * D_AddFile * * Adds file to the end of the wadfiles[] list. * Quite crude, we could use a listarray instead. * * @param file */ void AddFile (String file) { int numwadfiles; String newfile; for (numwadfiles = 0 ; eval(wadfiles[numwadfiles]) ; numwadfiles++) ; newfile = new String(file); wadfiles[numwadfiles] = newfile; } /** * IdentifyVersion * Checks availability of IWAD files by name, * to determine whether registered/commercial features * should be executed (notably loading PWAD's). */ public String IdentifyVersion () { String home; String doomwaddir; DoomVersions vcheck=new DoomVersions(); // By default. language=Language_t.english; // First, check for -iwad parameter. // If valid, then it trumps all others. int p; p=CM.CheckParm("-iwad"); if (eval(p)) { System.out.println("-iwad specified. Will be used with priority\n"); String test=CM.getArgv(p+1); // It might be quoted. test=C2JUtils.unquoteIfQuoted(test,'"'); String separator=System.getProperty("file.separator"); doomwaddir=test.substring(0, 1+test.lastIndexOf(separator)); String iwad=test.substring( 1+test.lastIndexOf(separator)); GameMode_t attempt=vcheck.tryOnlyOne(iwad,doomwaddir); // Note: at this point we can't distinguish between "doom" retail // and "doom" ultimate yet. if (attempt!=null) { AddFile(doomwaddir+iwad); this.setGameMode(attempt); return (doomwaddir+iwad); } } else { // Unix-like checking. Might come in handy sometimes. // This should ALWAYS be activated, else doomwaddir etc. won't be defined. doomwaddir = System.getenv("DOOMWADDIR"); if (doomwaddir!=null){ System.out.println("DOOMWADDIR found. Will be used with priority\n"); } home = System.getenv("HOME"); if (NORMALUNIX){ if (!eval(home)) I.Error("Please set $HOME to your home directory"); } Settings.basedefault=home+"/.doomrc"; // None found, using current. if (!eval(doomwaddir)) doomwaddir = "."; vcheck.tryThemAll(doomwaddir); } // MAES: Interesting. I didn't know of that :-o if (eval(CM.CheckParm ("-shdev"))) { setGameMode(GameMode_t.shareware); devparm = true; AddFile (dstrings.DEVDATA+"doom1.wad"); AddFile (dstrings.DEVMAPS+"data_se/texture1.lmp"); AddFile (dstrings.DEVMAPS+"data_se/pnames.lmp"); Settings.basedefault=dstrings.DEVDATA+"default.cfg"; return (dstrings.DEVDATA+"doom1.wad"); } if (eval(CM.CheckParm ("-regdev"))) { setGameMode(GameMode_t.registered); devparm = true; AddFile (dstrings.DEVDATA+"doom.wad"); AddFile (dstrings.DEVMAPS+"data_se/texture1.lmp"); AddFile (dstrings.DEVMAPS+"data_se/texture2.lmp"); AddFile (dstrings.DEVMAPS+"data_se/pnames.lmp"); Settings.basedefault=dstrings.DEVDATA+"default.cfg"; return (dstrings.DEVDATA+"doom.wad"); } if (eval(CM.CheckParm ("-comdev"))) { setGameMode(GameMode_t.commercial); devparm = true; /* I don't bother if(plutonia) D_AddFile (DEVDATA"plutonia.wad"); else if(tnt) D_AddFile (DEVDATA"tnt.wad"); else*/ AddFile (dstrings.DEVDATA+"doom2.wad"); AddFile (dstrings.DEVMAPS+"cdata/texture1.lmp"); AddFile (dstrings.DEVMAPS+"cdata/pnames.lmp"); Settings.basedefault=dstrings.DEVDATA+"default.cfg"; return (dstrings.DEVDATA+"doom2.wad"); } if ( testReadAccess(vcheck.doom2fwad)) { setGameMode(GameMode_t.commercial); // C'est ridicule! // Let's handle languages in config files, okay? language = Language_t.french; System.out.println("French version\n"); AddFile (vcheck.doom2fwad); return vcheck.doom2fwad; } if ( testReadAccess(vcheck.doom2wad)) { setGameMode(GameMode_t.commercial); AddFile (vcheck.doom2wad); return vcheck.doom2wad; } if ( testReadAccess (vcheck.plutoniawad) ) { setGameMode(GameMode_t.pack_plut); AddFile (vcheck.plutoniawad); return vcheck.plutoniawad; } if ( testReadAccess ( vcheck.tntwad) ) { setGameMode(GameMode_t.pack_tnt); AddFile (vcheck.tntwad); return vcheck.tntwad; } if ( testReadAccess ( vcheck.tntwad) ) { setGameMode(GameMode_t.pack_xbla); AddFile (vcheck.xblawad); return vcheck.xblawad; } if ( testReadAccess (vcheck.doomuwad) ) { // TODO auto-detect ultimate Doom even from doom.wad // Maes: this is done later on. setGameMode(GameMode_t.retail); AddFile (vcheck.doomuwad); return vcheck.doomuwad; } if ( testReadAccess (vcheck.doomwad) ) { setGameMode(GameMode_t.registered); AddFile (vcheck.doomwad); return vcheck.doomwad; } if ( testReadAccess (vcheck.doom1wad) ) { setGameMode(GameMode_t.shareware); AddFile (vcheck.doom1wad); return vcheck.doom1wad; } // MAES: Maybe we should add FreeDoom here later. System.out.println("Game mode indeterminate.\n"); setGameMode(GameMode_t.indetermined); return null; // We don't abort. Let's see what the PWAD contains. //exit(1); //I_Error ("Game mode indeterminate\n"); } /** * D_DoomMain * * Completes the job started by Init, which only created the * instances of the various stuff and registered the "status aware" * stuff. Here everything priority-critical is * called and created in more detail. * * */ public void Start() throws IOException { int p; StringBuffer file=new StringBuffer(); // TODO: This may modify the command line by appending more stuff // from an external file. Since it can affect other stuff too, // maybe it should be outside of Start() and into i.Main ? CM.FindResponseFile (); String iwadfilename=IdentifyVersion (); // Sets unbuffered output in C. Not needed here. setbuf (stdout, NULL); modifiedgame = false; nomonsters = eval(CM.CheckParm ("-nomonsters")); respawnparm = eval(CM.CheckParm ("-respawn")); fastparm = eval(CM.CheckParm ("-fast")); devparm = eval(CM.CheckParm ("-devparm")); if (eval(CM.CheckParm ("-altdeath"))) //deathmatch = 2; altdeath=true; else if (eval(CM.CheckParm ("-deathmatch"))) deathmatch = true; // MAES: Check for Ultimate Doom in "doom.wad" filename. WadLoader tmpwad=new WadLoader(); try { tmpwad.InitFile(iwadfilename,true); } catch (Exception e2) { // TODO Auto-generated catch block e2.printStackTrace(); } // Check using a reloadable hack. CheckForUltimateDoom(tmpwad); // MAES: better extract a method for this. GenerateTitle(); // Print ticker info. It has already been set at Init() though. if (eval(CM.CheckParm("-millis"))){ System.out.println("ITicker: Using millisecond accuracy timer."); } else if (eval(CM.CheckParm("-fasttic"))){ System.out.println("ITicker: Using fastest possible timer."); } else { System.out.println("ITicker: Using nanosecond accuracy timer."); } System.out.println(title.toString()); if (devparm) System.out.println(D_DEVSTR); // Running from CDROM? if (eval(CM.CheckParm("-cdrom"))) { System.out.println(D_CDROM); //System.get("c:\\doomdata",0); System.out.println (Settings.basedefault+"c:/doomdata/default.cfg"); } // turbo option if ( eval(p=CM.CheckParm ("-turbo")) ) { int scale = 200; //int forwardmove[2]; // int sidemove[2]; if (p<CM.getArgc()-1) scale = Integer.parseInt(CM.getArgv(p+1)); if (scale < 10) scale = 10; if (scale > 400) scale = 400; System.out.println("turbo scale: "+scale); forwardmove[0] = forwardmove[0]*scale/100; forwardmove[1] = forwardmove[1]*scale/100; sidemove[0] = sidemove[0]*scale/100; sidemove[1] = sidemove[1]*scale/100; } // add any files specified on the command line with -file wadfile // to the wad list // // convenience hack to allow -wart e m to add a wad file // prepend a tilde to the filename so wadfile will be reloadable p = CM.CheckParm ("-wart"); if (eval(p)) { char[] tmp=CM.getArgv(p).toCharArray(); tmp[4]= 'p';// big hack, change to -warp CM.setArgv(p,new String(tmp)); GameMode_t gamemode=getGameMode(); // Map name handling. switch (gamemode ) { case shareware: case retail: case registered: file.append("~"); file.append(DEVMAPS); file.append(String.format("E%cM%c.wad", CM.getArgv(p+1), CM.getArgv(p+2))); file.append(String.format("Warping to Episode %s, Map %s.\n", CM.getArgv(p+1),CM.getArgv(p+2))); break; case commercial: default: p = Integer.parseInt(CM.getArgv(p+1)); if (p<10){ file.append("~"); file.append(DEVMAPS); file.append(String.format("cdata/map0%d.wad", p)); } else { file.append("~"); file.append(DEVMAPS); file.append(String.format("cdata/map%d.wad", p)); } break; } AddFile (file.toString()); } p = CM.CheckParm ("-file"); if (eval(p)) { // the parms after p are wadfile/lump names, // until end of parms or another - preceded parm modifiedgame = true; // homebrew levels // MAES 1/6/2011: Avoid + to avoid clashing with +map while (++p != CM.getArgc() && CM.getArgv(p).charAt(0) != '-' && CM.getArgv(p).charAt(0) != '+') AddFile (C2JUtils.unquoteIfQuoted(CM.getArgv(p),'"')); } p = CM.CheckParm ("-playdemo"); if (eval(p)) normaldemo=true; else { p=CM.CheckParm ("-fastdemo"); if (eval(p)){ System.out.println("Fastdemo mode. Boundless clock!"); fastdemo=true; this.TICK=new FastTicker(); } else if (!eval(p)) { p = CM.CheckParm ("-timedemo"); if (eval(p)) singletics=true; } } // If any of the previous succeeded, try grabbing the filename. if ((normaldemo||fastdemo||singletics) && p < CM.getArgc()-1) { loaddemo=CM.getArgv(p+1); AddFile (loaddemo+".lmp"); System.out.printf("Playing demo %s.lmp.\n",loaddemo); autostart=true; } // Subsequent uses of loaddemo use only the lump name. loaddemo=C2JUtils.extractFileBase(loaddemo,0,true); // get skill / episode / map from parms // FIXME: should get them FROM THE DEMO itself. startskill = skill_t.sk_medium; startepisode = 1; startmap = 1; //autostart = false; p = CM.CheckParm ("-novert"); if (eval(p) && p < CM.getArgc()-1) { novert=!(CM.getArgv(p+1).toLowerCase().compareTo("disable")==0); if (!novert) System.out.println("-novert ENABLED (default)"); else System.out.println("-novert DISABLED. Hope you know what you're doing..."); } p = CM.CheckParm ("-skill"); if (eval(p) && p < CM.getArgc()-1) { startskill = skill_t.values()[CM.getArgv(p+1).charAt(0)-'1']; autostart = true; } p = CM.CheckParm ("-episode"); if (eval(p) && p < CM.getArgc()-1) { startepisode = CM.getArgv(p+1).charAt(0)-'0'; startmap = 1; autostart = true; } p = CM.CheckParm ("-timer"); if (eval(p) && p < CM.getArgc()-1 && deathmatch) { int time; time = Integer.parseInt(CM.getArgv(p+1)); System.out.print("Levels will end after "+time+" minute"); if (time>1) System.out.print("s"); System.out.print(".\n"); } // OK, and exactly how is this enforced? p = CM.CheckParm ("-avg"); if (eval(p) && p < CM.getArgc()-1 && deathmatch) System.out.print("Austin Virtual Gaming: Levels will end after 20 minutes\n"); // MAES 31/5/2011: added support for +map variation. p = CM.CheckParm ("-warp"); if (eval(p) && p < CM.getArgc()-1) { if (isCommercial()) startmap = Integer.parseInt(CM.getArgv(p+1)); else { int eval=11; try { eval=Integer.parseInt(CM.getArgv(p+1)); } catch (Exception e){ // swallow exception. No warp. } if (eval>99) eval%=100; if (eval<10) { startepisode = 1; startmap = 1; } else { startepisode = eval/10; startmap = eval%10; } } autostart = true; } // Maes: 1/6/2011 Added +map support p = CM.CheckParm ("+map"); if (eval(p)) { if (isCommercial()) { startmap = parseAsMapXX(CM.getArgv(p+1)); if (startmap!=-1){ autostart = true; } } else { int eval=parseAsExMx(CM.getArgv(p+1)); if (eval!=-1){ startepisode = Math.max(1,eval/10); startmap = Math.max(1,eval%10); autostart = true; } } } // init subsystems System.out.print("V_Init: allocate screens.\n"); V.Init (); System.out.print("Z_Init: Init zone memory allocation daemon. \n"); // DUMMY: Z_Init (); System.out.print("W_Init: Init WADfiles.\n"); try { W.InitMultipleFiles(wadfiles,true); } catch (Exception e1) { // TODO Auto-generated catch block e1.printStackTrace(); } // System.out.print("VI_Init: set colormaps.\n"); // MAES: FIX for incomplete palette lumps such as those in EGADOOM. // Generate the palette programmatically _anyway_ byte[] pal=PaletteGenerator.generatePalette(PaletteGenerator.playpal, 256, ColorTint.tints); // Copy over the one you read from disk... int pallump=W.GetNumForName("PLAYPAL"); byte[] tmppal=W.CacheLumpNumAsRawBytes(pallump, PU_STATIC); if (tmppal!=null) System.arraycopy(tmppal, 0, pal, 0, pal.length); V.createPalettes(pal, GammaTables.gammatables, 14, 256, 3, 5); W.InjectLumpNum(pallump,new DoomBuffer(ByteBuffer.wrap(pal))); // set it, create it, but don't make it visible yet. VI=selectVideoInterface(); VI.InitGraphics(); // MAES: Before we begin calling the various Init() stuff, // we need to make sure that objects that support the IVideoScaleAware // interface get set and initialized. initializeVideoScaleStuff(); // MAES: The status bar needs this update because it can "force" // the video renderer to assign it a scratchpad screen (Screen 4). // Since we're at it, let's update everything, it's easy! this.updateStatusHolders(this); // Update variables and stuff NOW. this.update(); // Check for -file in shareware CheckForPWADSInShareware(); // Iff additonal PWAD files are used, printmodified banner if (modifiedgame) // Generate WAD loading alert. Abort upon denial. if (!I.GenerateAlert(Strings.MODIFIED_GAME_TITLE,Strings.MODIFIED_GAME_DIALOG)) { W.CloseAllHandles(); System.exit(-2); } // Check and printwhich version is executed. switch ( getGameMode() ) { case shareware: case indetermined: System.out.print("===========================================================================\n"); System.out.print(" Shareware!\n"); System.out.print("===========================================================================\n"); break; case registered: case retail: case commercial: case pack_tnt: case pack_plut: case pack_xbla: System.out.print("===========================================================================\n"); System.out.print(" Commercial product - do not distribute!\n"); System.out.print(" Please report software piracy to the SPA: 1-800-388-PIR8\n"); System.out.print("===========================================================================\n"); break; default: // Ouch. break; } System.out.print("Tables.InitTables: Init trigonometric LUTs.\n"); Tables.InitTables(); System.out.print("M_Init: Init miscellaneous info.\n"); M.Init (); System.out.print("R_Init: Init DOOM refresh daemon - "); R.Init (); System.out.print("AM_Init: Init Automap colors - "); AM.Init (); // Called here to set up configurable colors. System.out.print("\nP_Init: Init Playloop state.\n"); P.Init (); System.out.print("I_Init: Setting up machine state.\n"); I.Init (); System.out.print("D_CheckNetGame: Checking network game status.\n"); CheckNetGame (); System.out.print("S_Init: Setting up sound.\n"); // Sound "drivers" before the game sound controller. if (CM.CheckParmBool("-nomusic") || CM.CheckParmBool("-nosound")) this.IMUS=new DummyMusic(); else this.IMUS=new DavidMusicModule(); if (CM.CheckParmBool("-nosfx") || CM.CheckParmBool("-nosound")) this.ISND=new DummySFX(); else { // Switch between possible sound drivers. // Crudish. if (CM.CheckParmBool("-audiolines")) this.ISND= new DavidSFXModule(this,numChannels); else // PC Speaker emulation if (CM.CheckParmBool("-speakersound")) this.ISND= new SpeakerDoomSoundDriver(this,numChannels); else if (CM.CheckParmBool("-clipsound")) this.ISND= new ClipSFXModule(this,numChannels); else // This is the default if (CM.CheckParmBool("-classicsound")) this.ISND= new ClassicDoomSoundDriver(this,numChannels); else // This is the default this.ISND= new SuperDoomSoundDriver(this,numChannels); } // Check for sound init failure and revert to dummy if (!ISND.InitSound()){ System.err.println ("S_InitSound: failed. Reverting to dummy...\n"); this.ISND=new DummySFX(); } if (!(CM.CheckParmBool("-nosound") || (ISND instanceof DummySFX)))// Obviously, nomusic && nosfx = nosound. this.S=new AbstractDoomAudio(this,numChannels); else // Saves a lot of distance calculations, // if we're not to output any sound at all. // TODO: create a Dummy that can handle music alone. this.S=new DummySoundDriver(); IMUS.InitMusic(); S.Init (snd_SfxVolume *8, snd_MusicVolume *8 ); // Hook audio to users. this.updateStatusHolders(this); System.out.print("HU_Init: Setting up heads up display.\n"); HU.Init(); System.out.print("ST_Init: Init status bar.\n"); ST.Init (); // check for a driver that wants intermission stats p = CM.CheckParm ("-statcopy"); if (eval(p) && p<CM.getArgc()-1) { // TODO: this should be chained to a logger //statcopy = CM.getArgv(p+1); System.out.print("External statistics registered.\n"); } // start the apropriate game based on parms p = CM.CheckParm ("-record"); if (eval(p) && p < CM.getArgc()-1) { RecordDemo (CM.getArgv(p+1)); autostart = true; } // NOW it's safe to init the disk reader. DD.Init(); // MAES: at this point everything should be set and initialized, so it's // time to make the players aware of the general status of Doom. //_D_ gonna try to initialize here, because it is needed to play a demo for (int i=0;i<MAXPLAYERS;i++){ players[i].updateStatus(this); } //p = CM.CheckParm ("-timedemo"); if (singletics) { TimeDemo (loaddemo); autostart = true; DoomLoop (); // never returns } p = CM.CheckParm ("-loadgame"); if (eval(p) && p < CM.getArgc()-1) { file.delete(0, file.length()); if (eval(CM.CheckParm("-cdrom"))){ file.append("c:\\doomdata\\"); file.append(SAVEGAMENAME); file.append("%c.dsg"); file.append(CM.getArgv(p+1).charAt(0)); } else { file.append(String.format("%s%c.dsg",SAVEGAMENAME, CM.getArgv(p+1).charAt(0))); } LoadGame(file.toString()); } if ( gameaction != gameaction_t.ga_loadgame ) { if (autostart || netgame) InitNew (startskill, startepisode, startmap); else StartTitle (); // start up intro loop } if (fastdemo||normaldemo) { singledemo = true; // quit after one demo if (fastdemo) timingdemo=true; InitNew (startskill, startepisode, startmap); gamestate=gamestate_t.GS_DEMOSCREEN; DeferedPlayDemo (loaddemo); DoomLoop (); // never returns } DoomLoop (); // never returns } protected abstract DoomVideoInterface<V> selectVideoInterface(); protected int parseAsMapXX(String argv) { if (argv.length()!=5) return -1; // Nah. if (argv.toLowerCase().lastIndexOf("map")!=0) return -1; // Meh. int map; try { map=Integer.parseInt(argv.substring(3)); } catch (NumberFormatException e){ return -1; // eww } return map; } protected int parseAsExMx(String argv) { if (argv.length()!=4) return -1; // Nah. if (argv.toLowerCase().lastIndexOf("e")!=0) return -1; // Meh. if (argv.toLowerCase().lastIndexOf("m")!=2) return -1; // Meh. int episode,mission; try { episode=Integer.parseInt(argv.substring(1,2)); mission=Integer.parseInt(argv.substring(3,4)); } catch (NumberFormatException e){ return -1; // eww } return episode*10+mission; } List<IVideoScaleAware> videoScaleChildren; public void initializeVideoScaleStuff() { videoScaleChildren=new ArrayList<IVideoScaleAware>(); // The automap... videoScaleChildren.add(this.AM); // The finale... videoScaleChildren.add(this.F); // The wiper... videoScaleChildren.add(this.WIPE); // The heads up... videoScaleChildren.add(this.HU); // The menu... videoScaleChildren.add(this.M); // The renderer (also has dependent children!) videoScaleChildren.add(this.R); // The Status Bar videoScaleChildren.add(this.ST); // Even the video renderer needs some initialization? videoScaleChildren.add(this.V); // wiper videoScaleChildren.add(this.WI); // disk drawer videoScaleChildren.add(this.DD); for(IVideoScaleAware i:videoScaleChildren){ if (i!=null){ i.setVideoScale(this.vs); i.initScaling(); } } } /** * */ protected void CheckForPWADSInShareware() { if (modifiedgame) { // These are the lumps that will be checked in IWAD, // if any one is not present, execution will be aborted. String[] name= { "e2m1","e2m2","e2m3","e2m4","e2m5","e2m6","e2m7","e2m8","e2m9", "e3m1","e3m3","e3m3","e3m4","e3m5","e3m6","e3m7","e3m8","e3m9", "dphoof","bfgga0","heada1","cybra1","spida1d1" }; int i; // Oh yes I can. if ( isShareware()) System.out.println("\nYou cannot -file with the shareware version. Register!"); // Check for fake IWAD with right name, // but w/o all the lumps of the registered version. if (isRegistered()) for (i = 0;i < 23; i++) if (W.CheckNumForName(name[i].toUpperCase())<0) I.Error("\nThis is not the registered version: "+name[i]); } } /** Check whether the "doom.wad" we actually loaded * is ultimate Doom's, by checking if it contains * e4m1 - e4m9. * */ protected void CheckForUltimateDoom(WadLoader W) { if (isRegistered()) { // These are the lumps that will be checked in IWAD, // if any one is not present, execution will be aborted. String[] lumps= { "e4m1","e4m2","e4m3","e4m4","e4m5","e4m6","e4m7","e4m8","e4m9" }; // Check for fake IWAD with right name, // but w/o all the lumps of the registered version. if (!CheckForLumps(lumps,W)) return; // Checks passed, so we can set the mode to Ultimate setGameMode(GameMode_t.retail); } } /** Check if ALL of the lumps exist. * * @param name * @return */ protected boolean CheckForLumps(String[] name, WadLoader W) { for (int i = 0;i < name.length; i++) if (W.CheckNumForName(name[i].toUpperCase())<0) { // Even one is missing? Not OK. return false; } return true; } /** * */ protected void GenerateTitle() { switch ( getGameMode() ) { case retail: title.append(" "); title.append("The Ultimate DOOM Startup v"); title.append(VERSION/100); title.append("."); title.append(VERSION%100); title.append(" "); break; case shareware: title.append(" "); title.append("DOOM Shareware Startup v"); title.append(VERSION/100); title.append("."); title.append(VERSION%100); title.append(" "); break; case registered: title.append(" "); title.append("DOOM Registered Startup v"); title.append(VERSION/100); title.append("."); title.append(VERSION%100); title.append(" "); break; case commercial: title.append(" "); title.append("DOOM 2: Hell on Earth v"); title.append(VERSION/100); title.append("."); title.append(VERSION%100); title.append(" "); break; case pack_plut: title.append(" "); title.append("DOOM 2: Plutonia Experiment v"); title.append(VERSION/100); title.append("."); title.append(VERSION%100); title.append(" "); break; case pack_tnt: title.append(" "); title.append("DOOM 2: TNT - Evilution v"); title.append(VERSION/100); title.append("."); title.append(VERSION%100); title.append(" "); break; case pack_xbla: title.append(" "); title.append("DOOM 2: No Rest for the Living v"); title.append(VERSION/100); title.append("."); title.append(VERSION%100); title.append(" "); break; default: title.append(" "); title.append("Public DOOM - v"); title.append(VERSION/100); title.append("."); title.append(VERSION%100); title.append(" "); break; } } // Used in BuildTiccmd. protected ticcmd_t base=new ticcmd_t(); /** * G_BuildTiccmd * Builds a ticcmd from all of the available inputs * or reads it from the demo buffer. * If recording a demo, write it out . * * The CURRENT event to process is written to the various * gamekeydown etc. arrays by the Responder method. * So look there for any fuckups in constructing them. * */ private void BuildTiccmd (ticcmd_t cmd) { int i; boolean strafe; boolean bstrafe; int speed, tspeed,lspeed; int forward; int side; int look; //base = I_BaseTiccmd (); // empty, or external driver // memcpy (cmd,base,sizeof(*cmd)); base.copyTo(cmd); cmd.consistancy = consistancy[consoleplayer][maketic%BACKUPTICS]; strafe = gamekeydown[key_strafe] || mousebuttons(mousebstrafe) || joybuttons(joybstrafe); speed = ((gamekeydown[key_speed]^alwaysrun) || joybuttons(joybspeed))?1:0; forward = side = look= 0; // use two stage accelerative turning // on the keyboard and joystick if (joyxmove < 0 || joyxmove > 0 || gamekeydown[key_right] || gamekeydown[key_left]) turnheld += ticdup; else turnheld = 0; if (turnheld < SLOWTURNTICS) tspeed = 2; // slow turn else tspeed = speed; if(gamekeydown[key_lookdown] || gamekeydown[key_lookup]) { lookheld += ticdup; } else { lookheld = 0; } if(lookheld < SLOWTURNTICS) { lspeed = 1; } else { lspeed = 2; } // let movement keys cancel each other out if (strafe) { if (gamekeydown[key_right]) { // fprintf(stderr, "strafe right\n"); side += sidemove[speed]; } if (gamekeydown[key_left]) { // fprintf(stderr, "strafe left\n"); side -= sidemove[speed]; } if (joyxmove > 0) side += sidemove[speed]; if (joyxmove < 0) side -= sidemove[speed]; } else { if (gamekeydown[key_right]) cmd.angleturn -= angleturn[tspeed]; if (gamekeydown[key_left]) cmd.angleturn += angleturn[tspeed]; if (joyxmove > 0) cmd.angleturn -= angleturn[tspeed]; if (joyxmove < 0) cmd.angleturn += angleturn[tspeed]; } if (gamekeydown[key_up]) { //System.err.print("up\n"); forward += forwardmove[speed]; } if (gamekeydown[key_down]) { //System.err.print("down\n"); forward -= forwardmove[speed]; } if (joyymove < 0) forward += forwardmove[speed]; if (joyymove > 0) forward -= forwardmove[speed]; if (gamekeydown[key_straferight]) side += sidemove[speed]; if (gamekeydown[key_strafeleft]) side -= sidemove[speed]; // Look up/down/center keys if(gamekeydown[key_lookup]) { System.err.print("Look up\n"); look = lspeed; } if(gamekeydown[key_lookdown]) { System.err.print("Look down\n"); look = -lspeed; } if(gamekeydown[key_lookcenter]) { System.err.print("Center look\n"); look = TOCENTER; } // buttons cmd.chatchar = HU.dequeueChatChar(); if (gamekeydown[key_fire] || mousebuttons(mousebfire) || joybuttons(joybfire)) cmd.buttons |= BT_ATTACK; if (gamekeydown[key_use] || joybuttons(joybuse) ) { cmd.buttons |= BT_USE; // clear double clicks if hit use button dclicks = 0; } // chainsaw overrides for (i=0 ; i<NUMWEAPONS-1 ; i++) if (gamekeydown['1'+i]) { //System.out.println("Attempting weapon change (building ticcmd)"); cmd.buttons |= BT_CHANGE; cmd.buttons |= i<<BT_WEAPONSHIFT; break; } // mouse if (mousebuttons(mousebforward)) forward += forwardmove[speed]; // forward double click (operator precedence? && over > if (mousebuttons(mousebforward) != (dclickstate!=0) && (dclicktime > 1) ) { dclickstate = mousebuttons(mousebforward)?1:0; if (dclickstate!=0) dclicks++; if (dclicks == 2) { cmd.buttons |= BT_USE; dclicks = 0; } else dclicktime = 0; } else { dclicktime += ticdup; if (dclicktime > 20) { dclicks = 0; dclickstate = 0; } } // strafe double click bstrafe = mousebuttons(mousebstrafe) || joybuttons(joybstrafe); if ((bstrafe != (dclickstate2!=0)) && dclicktime2 > 1 ) { dclickstate2 = bstrafe?1:0; if (dclickstate2!=0) dclicks2++; if (dclicks2 == 2) { cmd.buttons |= BT_USE; dclicks2 = 0; } else dclicktime2 = 0; } else { dclicktime2 += ticdup; if (dclicktime2 > 20) { dclicks2 = 0; dclickstate2 = 0; } } // By default, no vertical mouse movement if (!novert) forward += mousey; if (strafe) side += mousex*2; else cmd.angleturn -= mousex*0x8; mousex = mousey = 0; if (forward > MAXPLMOVE()) forward = MAXPLMOVE(); else if (forward < -MAXPLMOVE()) forward = -MAXPLMOVE(); if (side > MAXPLMOVE()) side = MAXPLMOVE(); else if (side < -MAXPLMOVE()) side = -MAXPLMOVE(); cmd.forwardmove += forward; cmd.sidemove += side; if(players[consoleplayer].playerstate == PST_LIVE) { if(look < 0) { look += 16; } cmd.lookfly = (char) look; } // special buttons if (sendpause) { sendpause = false; cmd.buttons = BT_SPECIAL | BTS_PAUSE; } if (sendsave) { sendsave = false; cmd.buttons = (char) (BT_SPECIAL | BTS_SAVEGAME | (savegameslot<<BTS_SAVESHIFT)); } } // // G_DoLoadLevel // //extern gamestate_t wipegamestate; public boolean DoLoadLevel () { int i; // Set the sky map. // First thing, we have a dummy sky texture name, // a flat. The data is in the WAD only because // we look for an actual index, instead of simply // setting one. TM.setSkyFlatNum(TM.FlatNumForName ( SKYFLATNAME )); // DOOM determines the sky texture to be used // depending on the current episode, and the game version. if (isCommercial() || ( gamemission == GameMission_t.pack_tnt ) || ( gamemission == GameMission_t.pack_plut ) ) { TM.setSkyTexture(TM.TextureNumForName ("SKY3")); if (gamemap < 12) TM.setSkyTexture(TM.TextureNumForName ("SKY1")); else if (gamemap < 21) TM.setSkyTexture(TM.TextureNumForName ("SKY2")); } levelstarttic = gametic; // for time calculation if (wipegamestate == gamestate_t.GS_LEVEL) wipegamestate = gamestate_t.GS_MINUS_ONE; // force a wipe gamestate = gamestate_t.GS_LEVEL; for (i=0 ; i<MAXPLAYERS ; i++) { if (playeringame[i] && players[i].playerstate == PST_DEAD) players[i].playerstate = PST_REBORN; // I don't give a shit if it's not super-duper optimal. Arrays.fill(players[i].frags, 0); } try { LL.SetupLevel (gameepisode, gamemap, 0, gameskill); } catch (Exception e){ e.printStackTrace(); // Failure loading level. return false; } displayplayer = consoleplayer; // view the guy you are playing gameaction = gameaction_t.ga_nothing; //Z_CheckHeap (); // clear cmd building stuff Arrays.fill(gamekeydown, false); keysCleared = true; joyxmove = joyymove = 0; mousex = mousey = 0; sendpause = sendsave = paused = false; Arrays.fill (mousearray, false); Arrays.fill(joyarray, false); // killough 5/13/98: in case netdemo has consoleplayer other than green ST.Start(); HU.Start(); // killough: make -timedemo work on multilevel demos // Move to end of function to minimize noise -- killough 2/22/98: if (timingdemo) { if (first) { starttime = RealTime.GetTime(); first=false; } } // Try reclaiming some memory from limit-expanded buffers. R.resetLimits(); return true; } protected boolean first=true; // Maes: needed because a caps lock down signal is issued // as soon as the app gains focus. This causes lock keys to respond. // With this hack, they are ignored (?) the first time this happens. public boolean justfocused=true; /** * G_Responder * Get info needed to make ticcmd_ts for the players. */ public boolean Responder (event_t ev) { // allow spy mode changes even during the demo if (gamestate == gamestate_t.GS_LEVEL && ev.type == evtype_t.ev_keydown && ev.data1 == KEY_F12 && (singledemo || !deathmatch) ) { // spy mode do { displayplayer++; if (displayplayer == MAXPLAYERS) displayplayer = 0; } while (!playeringame[displayplayer] && displayplayer != consoleplayer); return true; } // any other key pops up menu if in demos if (gameaction == gameaction_t.ga_nothing && !singledemo && (demoplayback || gamestate == gamestate_t.GS_DEMOSCREEN) ) { if (ev.type == evtype_t.ev_keydown || (ev.type == evtype_t.ev_mouse && ev.data1!=0) || (ev.type == evtype_t.ev_joystick && ev.data1!=0) ) { M.StartControlPanel (); return true; } return false; } if (gamestate == gamestate_t.GS_LEVEL) { if (devparm && ev.type == evtype_t.ev_keydown && ev.data1 == ';') { DeathMatchSpawnPlayer (0); return true; } //automapactive=true; if (HU.Responder (ev)) return true; // chat ate the event if (ST.Responder (ev)) return true; // status window ate it if (AM.Responder (ev)) return true; // automap ate it } if (gamestate == gamestate_t.GS_FINALE) { if (F.Responder (ev)) return true; // finale ate the event } switch (ev.type) { case ev_clear: // PAINFULLY and FORCEFULLY clear the buttons. Arrays.fill(gamekeydown, false); keysCleared = true; return false; // Nothing more to do here. case ev_keydown: if (ev.data1 == KEY_PAUSE) { sendpause = true; return true; } /* CAPS lock will only go through as a keyup event if (ev.data1 == KEY_CAPSLOCK) { if (justfocused) justfocused=false; else // If caps are turned on, turn autorun on anyway. if (!alwaysrun) { alwaysrun=true; players[consoleplayer].message=String.format("Always run: %s",alwaysrun); } return true; } */ if (ev.data1 <NUMKEYS) gamekeydown[ev.data1] = true; return true; // eat key down events case ev_keyup: if (ev.data1 == KEY_CAPSLOCK) { if (justfocused) justfocused=false; else { // Just toggle it. It's too hard to read the state. alwaysrun=!alwaysrun; players[consoleplayer].message=String.format("Always run: %s",alwaysrun); } } if (ev.data1 <NUMKEYS) gamekeydown[ev.data1] = false; return false; // always let key up events filter down case ev_mouse: // Ignore them at the responder level if (use_mouse){ mousebuttons(0, ev.data1 & 1); mousebuttons(1, ev.data1 & 2); mousebuttons(2, ev.data1 & 4); mousex = ev.data2*(mouseSensitivity+5)/10; mousey = ev.data3*(mouseSensitivity+5)/10; } return true; // eat events case ev_joystick: if (use_joystick){ joybuttons(0, ev.data1 & 1); joybuttons(1, ev.data1 & 2); joybuttons(2,ev.data1 & 4); joybuttons(3,ev.data1 & 8); joyxmove = ev.data2; joyymove = ev.data3; } return true; // eat events default: break; } return false; } private final String turbomessage="is turbo!"; /** * G_Ticker * Make ticcmd_ts for the players. */ public void Ticker () { int i; int buf; ticcmd_t cmd; // do player reborns if needed for (i=0 ; i<MAXPLAYERS ; i++) if (playeringame[i] && players[i].playerstate == PST_REBORN) DoReborn (i); // do things to change the game state while (gameaction != gameaction_t.ga_nothing) { switch (gameaction) { case ga_loadlevel: DoLoadLevel (); break; case ga_newgame: DoNewGame (); break; case ga_loadgame: DoLoadGame (); break; case ga_savegame: DoSaveGame (); break; case ga_playdemo: DoPlayDemo (); break; case ga_completed: DoCompleted (); break; case ga_victory: F.StartFinale (); break; case ga_worlddone: DoWorldDone (); break; case ga_screenshot: ScreenShot (); gameaction = gameaction_t.ga_nothing; break; case ga_nothing: break; } } // get commands, check consistancy, // and build new consistancy check buf = (gametic/ticdup)%BACKUPTICS; for (i=0 ; i<MAXPLAYERS ; i++) { if (playeringame[i]) { cmd = players[i].cmd; //System.out.println("Current command:"+cmd); //memcpy (cmd, &netcmds[i][buf], sizeof(ticcmd_t)); netcmds[i][buf].copyTo(cmd); // MAES: this is where actual demo commands are being issued or created! // Essentially, a demo is a sequence of stored ticcmd_t with a header. // Knowing that, it's possible to objectify it. if (demoplayback) ReadDemoTiccmd (cmd); if (demorecording) WriteDemoTiccmd (cmd); // check for turbo cheats if (cmd.forwardmove > TURBOTHRESHOLD && ((gametic&31)==0) && ((gametic>>5)&3) == i ) { //extern char *player_names[4]; //sprintf (turbomessage, "%s is turbo!",player_names[i]); players[consoleplayer].message = hu.HU.player_names[i]+turbomessage; } if (netgame && !netdemo && (gametic%ticdup)==0 ) { if (gametic > BACKUPTICS && consistancy[i][buf] != cmd.consistancy) { I.Error ("consistency failure (%d should be %d)", cmd.consistancy, consistancy[i][buf]); } if (players[i].mo!=null) consistancy[i][buf] = (short) players[i].mo.x; else consistancy[i][buf] = (short) RND.getIndex(); } } } // check for special buttons for (i=0 ; i<MAXPLAYERS ; i++) { if (playeringame[i]) { if ((players[i].cmd.buttons & BT_SPECIAL)!=0) { switch (players[i].cmd.buttons & BT_SPECIALMASK) { case BTS_PAUSE: // MAES: fixed stupid ^pause bug. paused = !paused; if (paused) S.PauseSound (); else S.ResumeSound (); break; case BTS_SAVEGAME: if (savedescription==null) savedescription=new String( "NET GAME"); savegameslot = (players[i].cmd.buttons & BTS_SAVEMASK)>>BTS_SAVESHIFT; gameaction = gameaction_t.ga_savegame; break; } } } } // do main actions switch (gamestate) { case GS_LEVEL: P.Ticker (); ST.Ticker (); AM.Ticker (); HU.Ticker (); break; case GS_INTERMISSION: WI.Ticker (); break; case GS_FINALE: F.Ticker (); break; case GS_DEMOSCREEN: PageTicker (); break; } } // // PLAYER STRUCTURE FUNCTIONS // also see P_SpawnPlayer in P_Things // /** * G_InitPlayer * Called at the start. * Called by the game initialization functions. * * MAES: looks like dead code. It's never called. * */ protected void InitPlayer (int player) { player_t p; // set up the saved info p = players[player]; // clear everything else to defaults p.PlayerReborn (); } // // G_CheckSpot // Returns false if the player cannot be respawned // at the given mapthing_t spot // because something is occupying it // //void P_SpawnPlayer (mapthing_t* mthing); private boolean CheckSpot ( int playernum, mapthing_t mthing ) { int x,y; // fixed_t subsector_t ss; int an; // angle mobj_t mo; if (players[playernum].mo==null) { // first spawn of level, before corpses for (int i=0 ; i<playernum ; i++) if (players[i].mo.x == mthing.x << FRACBITS && players[i].mo.y == mthing.y << FRACBITS) return false; return true; } x = mthing.x << FRACBITS; y = mthing.y << FRACBITS; if (!P.CheckPosition (players[playernum].mo, x, y) ) return false; // flush an old corpse if needed if (bodyqueslot >= BODYQUESIZE) P.RemoveMobj (bodyque[bodyqueslot%BODYQUESIZE]); bodyque[bodyqueslot%BODYQUESIZE] = players[playernum].mo; bodyqueslot++; // spawn a teleport fog ss = LL.PointInSubsector (x,y); // Angles stored in things are supposed to be "sanitized" against rollovers. an = (int) (( ANG45 * (mthing.angle/45) ) >>> ANGLETOFINESHIFT); mo = P.SpawnMobj (x+20*finecosine[an], y+20*finesine[an] , ss.sector.floorheight , mobjtype_t.MT_TFOG); if (players[consoleplayer].viewz != 1) ; S.StartSound (mo, sfxenum_t.sfx_telept); // don't start sound on first frame return true; } // // G_DeathMatchSpawnPlayer // Spawns a player at one of the random death match spots // called at level load and each death // @Override public void DeathMatchSpawnPlayer (int playernum) { int i,j; int selections; selections = deathmatch_p; if (selections < 4) I.Error ("Only %d deathmatch spots, 4 required", selections); for (j=0 ; j<20 ; j++) { i = RND.P_Random() % selections; if (CheckSpot (playernum, deathmatchstarts[i]) ) { deathmatchstarts[i].type = (short) (playernum+1); P.SpawnPlayer (deathmatchstarts[i]); return; } } // no good spot, so the player will probably get stuck // MAES: seriously, fuck him. P.SpawnPlayer (playerstarts[playernum]); } // // G_DoReborn // public void DoReborn (int playernum) { int i; if (!netgame) { // reload the level from scratch gameaction = gameaction_t.ga_loadlevel; } else { // respawn at the start // first dissasociate the corpse players[playernum].mo.player = null; // spawn at random spot if in death match if (deathmatch) { DeathMatchSpawnPlayer (playernum); return; } if (CheckSpot (playernum, playerstarts[playernum]) ) { P.SpawnPlayer (playerstarts[playernum]); return; } // try to spawn at one of the other players spots for (i=0 ; i<MAXPLAYERS ; i++) { if (CheckSpot (playernum, playerstarts[i]) ) { playerstarts[i].type = (short) (playernum+1); // fake as other player P.SpawnPlayer (playerstarts[i]); playerstarts[i].type = (short) (i+1); // restore return; } // he's going to be inside something. Too bad. // MAES: Yeah, they're like, fuck him. } P.SpawnPlayer (playerstarts[playernum]); } } /** DOOM Par Times [4][10] */ final int[][] pars = { {0}, {0,30,75,120,90,165,180,180,30,165}, {0,90,90,90,120,90,360,240,30,170}, {0,90,45,90,150,90,90,165,30,135} }; /** DOOM II Par Times */ final int[] cpars = { 30,90,120,120,90,150,120,120,270,90, // 1-10 210,150,150,150,210,150,420,150,210,150, // 11-20 240,150,180,150,150,300,330,420,300,180, // 21-30 120,30 // 31-32 }; // // G_DoCompleted // boolean secretexit; public final void ExitLevel () { secretexit = false; gameaction = gameaction_t.ga_completed; } // Here's for the german edition. public void SecretExitLevel () { // IF NO WOLF3D LEVELS, NO SECRET EXIT! if ( isCommercial() && (W.CheckNumForName("MAP31")<0)) secretexit = false; else secretexit = true; gameaction = gameaction_t.ga_completed; } protected void DoCompleted () { int i; gameaction = gameaction_t.ga_nothing; for (i=0 ; i<MAXPLAYERS ; i++) if (playeringame[i]) players[i].PlayerFinishLevel (); // take away cards and stuff if (automapactive) AM.Stop (); if ( !isCommercial()) switch(gamemap) { case 8: // MAES: end of episode gameaction = gameaction_t.ga_victory; return; case 9: // MAES: end of secret level for (i=0 ; i<MAXPLAYERS ; i++) players[i].didsecret = true; break; } /* Hmmm - why? MAES: Clearly redundant. if ( (gamemap == 8) && (!isCommercial()) ) { // victory gameaction = gameaction_t.ga_victory; return; } if ( (gamemap == 9) && !isCommercial() ) { // exit secret level for (i=0 ; i<MAXPLAYERS ; i++) players[i].didsecret = true; } */ wminfo.didsecret = players[consoleplayer].didsecret; wminfo.epsd = gameepisode -1; wminfo.last = gamemap -1; // wminfo.next is 0 biased, unlike gamemap if ( isCommercial()) { if (secretexit) switch(gamemap) { case 15: wminfo.next = 30; break; case 31: wminfo.next = 31; break; } else switch(gamemap) { case 31: case 32: wminfo.next = 15; break; default: wminfo.next = gamemap; } } else { if (secretexit) wminfo.next = 8; // go to secret level else if (gamemap == 9) { // returning from secret level switch (gameepisode) { case 1: wminfo.next = 3; break; case 2: wminfo.next = 5; break; case 3: wminfo.next = 6; break; case 4: wminfo.next = 2; break; } } else wminfo.next = gamemap; // go to next level } wminfo.maxkills = totalkills; wminfo.maxitems = totalitems; wminfo.maxsecret = totalsecret; wminfo.maxfrags = 0; if ( isCommercial() ) wminfo.partime = 35*cpars[gamemap-1]; else if (gameepisode >= pars.length) wminfo.partime = 0; else wminfo.partime = 35*pars[gameepisode][gamemap]; wminfo.pnum = consoleplayer; for (i=0 ; i<MAXPLAYERS ; i++) { wminfo.plyr[i].in = playeringame[i]; wminfo.plyr[i].skills = players[i].killcount; wminfo.plyr[i].sitems = players[i].itemcount; wminfo.plyr[i].ssecret = players[i].secretcount; wminfo.plyr[i].stime = leveltime; C2JUtils.memcpy (wminfo.plyr[i].frags, players[i].frags , wminfo.plyr[i].frags.length); } gamestate = gamestate_t.GS_INTERMISSION; viewactive = false; automapactive = false; if (statcopy!=null) C2JUtils.memcpy (statcopy, wminfo,1); WI.Start (wminfo); } /** * G_WorldDone */ public void WorldDone () { gameaction = gameaction_t.ga_worlddone; if (secretexit) players[consoleplayer].didsecret = true; if ( isCommercial() ) { switch (gamemap) { case 15: case 31: if (!secretexit) break; case 6: case 11: case 20: case 30: F.StartFinale (); break; } } } public void DoWorldDone () { gamestate = gamestate_t.GS_LEVEL; gamemap = wminfo.next+1; DoLoadLevel (); gameaction = gameaction_t.ga_nothing; viewactive = true; } // // G_InitFromSavegame // Can be called by the startup code or the menu task. // //extern boolean setsizeneeded; //void R_ExecuteSetViewSize (void); String savename; public void LoadGame (String name) { savename=new String(name); gameaction = gameaction_t.ga_loadgame; } /** This is fugly. Making a "savegame object" will make at least certain comparisons * easier, and avoid writing code twice. */ protected void DoLoadGame () { try{ int i; StringBuffer vcheck=new StringBuffer(); VanillaDSGHeader header=new VanillaDSGHeader(); IDoomSaveGame dsg=new VanillaDSG(); dsg.updateStatus(this.DM); gameaction = gameaction_t.ga_nothing; DataInputStream f=new DataInputStream(new BufferedInputStream(new FileInputStream(savename))); header.read(f); f.close(); // skip the description field vcheck.append("version "); vcheck.append(VERSION); if (vcheck.toString().compareTo(header.getVersion())!=0) { f.close(); return; // bad version } // Ok so far, reopen stream. f=new DataInputStream(new BufferedInputStream(new FileInputStream(savename))); gameskill = header.getGameskill(); gameepisode = header.getGameepisode(); gamemap = header.getGamemap(); for (i=0 ; i<MAXPLAYERS ; i++) playeringame[i] = header.getPlayeringame()[i]; // load a base level InitNew (gameskill, gameepisode, gamemap); if (gameaction == gameaction_t.ga_failure) { // failure to load. Abort. f.close(); return; } gameaction = gameaction_t.ga_nothing; // get the times leveltime = header.getLeveltime(); // dearchive all the modifications boolean ok=dsg.doLoad(f); f.close(); // MAES: this will cause a forced exit. // The problem is that the status will have already been altered // (perhaps VERY badly) so it makes no sense to progress. // If you want it bullet-proof, you could implement // a "tentative loading" subsystem, which will only alter the game // if everything works out without errors. But who cares :-p if (!ok) I.Error("Bad savegame"); // done //Z_Free (savebuffer); if (R.getSetSizeNeeded()) R.ExecuteSetViewSize (); // draw the pattern into the back screen R.FillBackScreen (); } catch (Exception e){ e.printStackTrace(); } } // // G_SaveGame // Called by the menu task. // Description is a 24 byte text string // public void SaveGame ( int slot, String description ) { savegameslot = slot; savedescription=new String(description); sendsave = true; } protected void DoSaveGame () { try{ String name; //char[] name2=new char[VERSIONSIZE]; String description; StringBuffer build=new StringBuffer(); IDoomSaveGameHeader header=new VanillaDSGHeader(); IDoomSaveGame dsg=new VanillaDSG(); dsg.updateStatus(this.DM); if (eval(CM.CheckParm("-cdrom"))) { build.append("c:\\doomdata\\"); build.append(SAVEGAMENAME); build.append("%d.dsg"); } else { build.append(SAVEGAMENAME); build.append("%d.dsg"); } name=String.format(build.toString(), savegameslot); description = savedescription; header.setName(description); header.setVersion(String.format("version %d",VERSION)); header.setGameskill(gameskill); header.setGameepisode(gameepisode); header.setGamemap(gamemap); header.setPlayeringame(playeringame); header.setLeveltime(leveltime); dsg.setHeader(header); // Try opening a save file. No intermediate buffer (performance?) DataOutputStream f=new DataOutputStream(new FileOutputStream(name)); boolean ok=dsg.doSave(f); f.close(); } catch (Exception e){ e.printStackTrace(); } // Saving is not as destructive as loading. gameaction = gameaction_t.ga_nothing; savedescription = ""; players[consoleplayer].message = GGSAVED; // draw the pattern into the back screen R.FillBackScreen (); } skill_t d_skill; int d_episode; int d_map; public void DeferedInitNew ( skill_t skill, int episode, int map) { d_skill = skill; d_episode = episode; d_map = map; gameaction = gameaction_t.ga_newgame; } public void DoNewGame () { demoplayback = false; netdemo = false; netgame = false; deathmatch = false; playeringame[1] = playeringame[2] = playeringame[3] = false; respawnparm = false; fastparm = false; nomonsters = false; consoleplayer = 0; InitNew (d_skill, d_episode, d_map); gameaction = gameaction_t.ga_nothing; } /** * G_InitNew * Can be called by the startup code or the menu task, * consoleplayer, displayplayer, playeringame[] should be set. */ public void InitNew ( skill_t skill, int episode, int map ) { int i; if (paused) { paused = false; S.ResumeSound (); } if (skill.ordinal() > skill_t.sk_nightmare.ordinal()) skill = skill_t.sk_nightmare; // This was quite messy with SPECIAL and commented parts. // Supposedly hacks to make the latest edition work. // It might not work properly. if (episode < 1) episode = 1; if ( isRetail() ) { if (episode > 4) episode = 4; } else if ( isShareware() ) { if (episode > 1) episode = 1; // only start episode 1 on shareware } else { if (episode > 3) episode = 3; } if (map < 1) map = 1; if ( (map > 9) && ( !isCommercial())) map = 9; RND.ClearRandom (); if (skill == skill_t.sk_nightmare || respawnparm ) respawnmonsters = true; else respawnmonsters = false; // If on nightmare/fast monsters make everything MOAR pimp. if (fastparm || (skill == skill_t.sk_nightmare && gameskill != skill_t.sk_nightmare) ) { for (i=statenum_t.S_SARG_RUN1.ordinal() ; i<=statenum_t.S_SARG_PAIN2.ordinal() ; i++) states[i].tics >>= 1; mobjinfo[mobjtype_t.MT_BRUISERSHOT.ordinal()].speed = 20*MAPFRACUNIT; mobjinfo[mobjtype_t.MT_HEADSHOT.ordinal()].speed = 20*MAPFRACUNIT; mobjinfo[mobjtype_t.MT_TROOPSHOT.ordinal()].speed = 20*MAPFRACUNIT; } else if (skill != skill_t.sk_nightmare && gameskill == skill_t.sk_nightmare) { for (i=statenum_t.S_SARG_RUN1.ordinal() ; i<=statenum_t.S_SARG_PAIN2.ordinal() ; i++) states[i].tics <<= 1; mobjinfo[mobjtype_t.MT_BRUISERSHOT.ordinal()].speed = 15*MAPFRACUNIT; mobjinfo[mobjtype_t.MT_HEADSHOT.ordinal()].speed = 10*MAPFRACUNIT; mobjinfo[mobjtype_t.MT_TROOPSHOT.ordinal()].speed = 10*MAPFRACUNIT; } // force players to be initialized upon first level load for (i=0 ; i<MAXPLAYERS ; i++) players[i].playerstate = PST_REBORN; usergame = true; // will be set false if a demo paused = false; demoplayback = false; automapactive = false; viewactive = true; gameepisode = episode; gamemap = map; gameskill = skill; viewactive = true; // set the sky map for the episode if ( isCommercial()) { TM.setSkyTexture(TM.TextureNumForName ("SKY3")); if (gamemap < 12) TM.setSkyTexture(TM.TextureNumForName ("SKY1")); else if (gamemap < 21) TM.setSkyTexture(TM.TextureNumForName ("SKY2")); } else switch (episode) { case 1: TM.setSkyTexture(TM.TextureNumForName ("SKY1")); break; case 2: TM.setSkyTexture(TM.TextureNumForName ("SKY2")); break; case 3: TM.setSkyTexture(TM.TextureNumForName ("SKY3")); break; case 4: // Special Edition sky TM.setSkyTexture(TM.TextureNumForName ("SKY4")); break; } if (!DoLoadLevel ()) levelLoadFailure(); } protected void levelLoadFailure(){ boolean endgame=I.GenerateAlert(Strings.LEVEL_FAILURE_TITLE, Strings.LEVEL_FAILURE_CAUSE); if (endgame){ // Initiate endgame gameaction=gameaction_t.ga_failure; gamestate = gamestate_t.GS_DEMOSCREEN; M.ClearMenus(); StartTitle(); } else { // Shutdown immediately. I.Quit(); } } // // DEMO RECORDING // public void ReadDemoTiccmd (ticcmd_t cmd) { IDemoTicCmd democmd=demobuffer.getNextTic(); if (democmd == null) { // end of demo data stream CheckDemoStatus (); // Force status resetting this.demobuffer.resetDemo(); return; } democmd.decode(cmd); } public void WriteDemoTiccmd (ticcmd_t cmd) { if (gamekeydown['q']) // press q to end demo recording CheckDemoStatus (); IDemoTicCmd reccmd=new VanillaTiccmd(); reccmd.encode(cmd); demobuffer.putTic(reccmd); // MAES: Useless, we can't run out of space anymore (at least not in theory). /* demobuffer[demo_p++] = cmd.forwardmove; demobuffer[demo_p++] = cmd.sidemove; demobuffer[demo_p++] = (byte) ((cmd.angleturn+128)>>8); demobuffer[demo_p++] = (byte) cmd.buttons; demo_p -= 4; if (demo_p > demoend - 16) { // no more space CheckDemoStatus (); return; } */ //ReadDemoTiccmd (cmd); // make SURE it is exactly the same // MAES: this is NOT the way to do in Mocha, because we are not manipulating // the demo index directly anymore. Instead, decode what we have just saved. reccmd.decode(cmd); } /** * G_RecordDemo */ public void RecordDemo (String name) { StringBuffer buf=new StringBuffer(); usergame = false; buf.append(name); buf.append(".lmp"); demoname=buf.toString(); demobuffer = new VanillaDoomDemo(); demorecording = true; } public void BeginRecording () { demobuffer.setVersion(VERSION); demobuffer.setSkill(gameskill); demobuffer.setEpisode(gameepisode); demobuffer.setMap(gamemap); demobuffer.setDeathmatch(deathmatch); demobuffer.setRespawnparm(respawnparm); demobuffer.setFastparm(fastparm); demobuffer.setNomonsters(nomonsters); demobuffer.setConsoleplayer(consoleplayer); demobuffer.setPlayeringame(playeringame); } String defdemoname; /** * G_PlayDemo */ public void DeferedPlayDemo (String name) { defdemoname = name; gameaction = gameaction_t.ga_playdemo; } public void DoPlayDemo () { skill_t skill; boolean fail=false; int i, episode, map; gameaction = gameaction_t.ga_nothing; // MAES: Yeah, it's OO all the way now, baby ;-) try { demobuffer = (IDoomDemo) W.CacheLumpName(defdemoname.toUpperCase(), PU_STATIC,VanillaDoomDemo.class); } catch (Exception e){ fail=true; } fail=(demobuffer.getSkill()==null); if (fail || demobuffer.getVersion()!= VERSION) { System.err.println("Demo is from a different game version!\n"); System.err.println("Version code read: "+demobuffer.getVersion()); gameaction = gameaction_t.ga_nothing; return; } skill = demobuffer.getSkill(); episode = demobuffer.getEpisode(); map = demobuffer.getMap(); deathmatch = demobuffer.isDeathmatch(); respawnparm = demobuffer.isRespawnparm(); fastparm = demobuffer.isFastparm(); nomonsters = demobuffer.isNomonsters(); consoleplayer = demobuffer.getConsoleplayer(); // Do this, otherwise previously loaded demos will be stuck at their end. demobuffer.resetDemo(); boolean[] pigs=demobuffer.getPlayeringame(); for (i=0 ; i<MAXPLAYERS ; i++) playeringame[i] = pigs[i]; if (playeringame[1]) { netgame = true; netdemo = true; } // don't spend a lot of time in loadlevel precache = false; InitNew (skill, episode, map); precache = true; usergame = false; demoplayback = true; } // // G_TimeDemo // public void TimeDemo (String name) { nodrawers = CM.CheckParm ("-nodraw")!=0; noblit = CM.CheckParm ("-noblit")!=0; timingdemo = true; singletics = true; defdemoname = name; gameaction = gameaction_t.ga_playdemo; } /** G_CheckDemoStatus * * Called after a death or level completion to allow demos to be cleaned up * Returns true if a new demo loop action will take place * */ public boolean CheckDemoStatus () { int endtime; if (timingdemo) { endtime = RealTime.GetTime (); // killough -- added fps information and made it work for longer demos: long realtics=endtime-starttime; this.commit(); VM.SaveDefaults(VM.getDefaultFile()); I.Error ("timed %d gametics in %d realtics = %f frames per second",gametic , realtics, gametic*(double)(TICRATE)/realtics); } if (demoplayback) { if (singledemo) I.Quit (); // Z_ChangeTag (demobuffer, PU_CACHE); demoplayback = false; netdemo = false; netgame = false; deathmatch = false; playeringame[1] = playeringame[2] = playeringame[3] = false; respawnparm = false; fastparm = false; nomonsters = false; consoleplayer = 0; AdvanceDemo (); return true; } if (demorecording) { //demobuffer[demo_p++] = (byte) DEMOMARKER; MenuMisc.WriteFile(demoname, demobuffer); //Z_Free (demobuffer); demorecording = false; I.Error ("Demo %s recorded",demoname); } return false; } /** This should always be available for real timing */ protected ITicker RealTime; public DoomMain(){ // Init game status... super(); C2JUtils.initArrayOfObjects(events,event_t.class); this.I=new DoomSystem(); gamestate=gamestate_t.GS_DEMOSCREEN; this.RealTime=new MilliTicker(); } protected void updateStatusHolders(DoomStatus<T,V> DS){ for (DoomStatusAware dsa : status_holders){ dsa.updateStatus(DS); } } /** A list with objects that do hold status and need to be aware of * it. */ protected List<DoomStatusAware> status_holders; /* Since it's so intimately tied, it's less troublesome to merge the "main" and "network" * code. * */ /** To be initialized by the DoomNetworkingInterface via a setter */ //private doomcom_t doomcom; //private doomdata_t netbuffer; // points inside doomcom protected StringBuilder sb=new StringBuilder(); // // NETWORKING // // gametic is the tic about to (or currently being) run // maketic is the tick that hasn't had control made for it yet // nettics[] has the maketics for all players // // a gametic cannot be run until nettics[] > gametic for all players // //ticcmd_t[] localcmds= new ticcmd_t[BACKUPTICS]; //ticcmd_t [][] netcmds=new ticcmd_t [MAXPLAYERS][BACKUPTICS]; int[] nettics=new int[MAXNETNODES]; boolean[] nodeingame=new boolean[MAXNETNODES]; // set false as nodes leave game boolean[] remoteresend=new boolean[MAXNETNODES]; // set when local needs tics int[] resendto=new int[MAXNETNODES]; // set when remote needs tics int[] resendcount=new int[MAXNETNODES]; int[] nodeforplayer=new int[MAXPLAYERS]; int maketic; int lastnettic; int skiptics; protected int ticdup; public int getTicdup() { return ticdup; } public void setTicdup(int ticdup) { this.ticdup = ticdup; } int maxsend; // BACKUPTICS/(2*ticdup)-1; //void D_ProcessEvents (void); //void G_BuildTiccmd (ticcmd_t *cmd); //void D_DoAdvanceDemo (void); // _D_ boolean reboundpacket = false; doomdata_t reboundstore = new doomdata_t(); // // //123 /** MAES: interesting. After testing it was found to return the following size: * (8*(netbuffer.numtics+1)); */ int NetbufferSize () { // return (int)(((doomdata_t)0).cmds[netbuffer.numtics]); return (8*(netbuffer.numtics+1)); } protected long NetbufferChecksum () { long c; int i,l; c = 0x1234567L; // FIXME -endianess? if (NORMALUNIX) return 0; // byte order problems /* Here it was trying to get the length of a doomdata_t struct up to retransmit from. * l = (NetbufferSize () - (int)&(((doomdata_t *)0)->retransmitfrom))/4; * (int)&(((doomdata_t *)0)->retransmitfrom) evaluates to "4" * Therefore, l= (netbuffersize - 4)/4 * */ l = (NetbufferSize () - 4)/4; for (i=0 ; i<l ; i++) // TODO: checksum would be better computer in the netbuffer itself. // The C code actually takes all fields into account. c += 0;// TODO: (netbuffer->retransmitfrom)[i] * (i+1); return c & NCMD_CHECKSUM; } // // // protected int ExpandTics (int low) { int delta; delta = low - (maketic&0xff); if (delta >= -64 && delta <= 64) return (maketic&~0xff) + low; if (delta > 64) return (maketic&~0xff) - 256 + low; if (delta < -64) return (maketic&~0xff) + 256 + low; I.Error ("ExpandTics: strange value %d at maketic %d",low,maketic); return 0; } /** * HSendPacket * * Will send out a packet to all involved parties. A special * case is the rebound storage, which acts as a local "echo" * which is then picked up by the host itself. This is * necessary to simulate a 1-node network. * @throws IOException * */ void HSendPacket (int node, int flags ) { netbuffer.checksum = (int) (NetbufferChecksum () | flags); // Local node's comms are sent to rebound packet, which is // then picked up again. THIS IS VITAL FOR SINGLE-PLAYER // SPEED THROTTLING TOO, AS IT RELIES ON NETWORK ACKS/BUSY // WAITING. if (node==0) { // _D_ reboundstore.copyFrom(netbuffer); reboundpacket = true; return; } if (DM.demoplayback) return; if (!DM.netgame) I.Error ("Tried to transmit to another node"); doomcom.command = CMD_SEND; doomcom.remotenode = (short) node; doomcom.datalength = (short) NetbufferSize (); if (DM.debugfile!=null) { int i; int realretrans; if (flags(netbuffer.checksum , NCMD_RETRANSMIT)) realretrans = ExpandTics (netbuffer.retransmitfrom); else realretrans = -1; logger(debugfile,"send ("+ExpandTics(netbuffer.starttic)+", "+netbuffer.numtics + ", R "+ realretrans+ "["+ doomcom.datalength+"]"); for (i=0 ; i<doomcom.datalength ; i++) // TODO: get a serialized string representation. logger(debugfile,netbuffer.toString()+"\n"); } // This should execute a "send" command for the current stuff in doomcom. DNI.NetCmd (); } // // HGetPacket // Returns false if no packet is waiting // private boolean HGetPacket () { // Fugly way of "clearing" the buffer. sb.setLength(0); if (reboundpacket) { // FIXME: MAES: this looks like a struct copy netbuffer.copyFrom(reboundstore); doomcom.remotenode = 0; reboundpacket = false; return true; } // If not actually a netgame (e.g. single player, demo) return. if (!DM.netgame) return false; if (DM.demoplayback) return false; doomcom.command = CMD_GET; DNI.NetCmd (); // Invalid node? if (doomcom.remotenode == -1) return false; if (doomcom.datalength != NetbufferSize ()) { if (eval(debugfile)) logger(debugfile,"bad packet length "+doomcom.datalength+"\n"); return false; } if (NetbufferChecksum () != (netbuffer.checksum&NCMD_CHECKSUM) ) { if (eval(debugfile)) logger(debugfile,"bad packet checksum\n"); return false; } if (eval(debugfile)) { int realretrans; int i; if (flags(netbuffer.checksum , NCMD_SETUP)) logger(debugfile,"setup packet\n"); else { if (flags(netbuffer.checksum , NCMD_RETRANSMIT)) realretrans = ExpandTics (netbuffer.retransmitfrom); else realretrans = -1; sb.append("get "); sb.append(doomcom.remotenode); sb.append(" = ("); sb.append(ExpandTics(netbuffer.starttic)); sb.append(" + "); sb.append(netbuffer.numtics); sb.append(", R "); sb.append(realretrans); sb.append(")["); sb.append(doomcom.datalength); sb.append("]"); logger(debugfile,sb.toString()); // Trick: force update of internal buffer. netbuffer.pack(); /* TODO: Could it be actually writing stuff beyond the boundaries of a single doomdata object? * A doomcom object has a lot of header info, and a single "raw" data placeholder, which by now * should be inside netbuffer....right? * * */ try{ for (i=0 ; i<doomcom.datalength ; i++) { debugfile.write(Integer.toHexString(netbuffer.cached()[i])); debugfile.write('\n'); } } catch( IOException e){ // "Drown" IOExceptions here. } } } return true; } //// GetPackets StringBuilder exitmsg=new StringBuilder(80); public void GetPackets () { int netconsole; int netnode; ticcmd_t src, dest; int realend; int realstart; while ( HGetPacket() ) { if (flags(netbuffer.checksum , NCMD_SETUP)) continue; // extra setup packet netconsole = netbuffer.player & ~PL_DRONE; netnode = doomcom.remotenode; // to save bytes, only the low byte of tic numbers are sent // Figure out what the rest of the bytes are realstart = ExpandTics (netbuffer.starttic); realend = (realstart+netbuffer.numtics); // check for exiting the game if (flags(netbuffer.checksum , NCMD_EXIT)) { if (!nodeingame[netnode]) continue; nodeingame[netnode] = false; playeringame[netconsole] = false; exitmsg.insert(0, "Player 1 left the game"); exitmsg.setCharAt(7,(char) (exitmsg.charAt(7)+netconsole)); players[consoleplayer].message = exitmsg.toString(); if (demorecording) DM.CheckDemoStatus (); continue; } // check for a remote game kill if (flags(netbuffer.checksum , NCMD_KILL)) I.Error ("Killed by network driver"); nodeforplayer[netconsole] = netnode; // check for retransmit request if ( resendcount[netnode] <= 0 && flags(netbuffer.checksum , NCMD_RETRANSMIT) ) { resendto[netnode] = ExpandTics(netbuffer.retransmitfrom); if (eval(debugfile)){ sb.setLength(0); sb.append("retransmit from "); sb.append(resendto[netnode]); sb.append('\n'); logger(debugfile,sb.toString()); resendcount[netnode] = RESENDCOUNT; } } else resendcount[netnode]--; // check for out of order / duplicated packet if (realend == nettics[netnode]) continue; if (realend < nettics[netnode]) { if (eval(debugfile)){ sb.setLength(0); sb.append("out of order packet ("); sb.append(realstart); sb.append(" + "); sb.append(netbuffer.numtics); sb.append(")\n"); logger(debugfile,sb.toString()); } continue; } // check for a missed packet if (realstart > nettics[netnode]) { // stop processing until the other system resends the missed tics if (eval(debugfile)) { sb.setLength(0); sb.append("missed tics from "); sb.append(netnode); sb.append(" ("); sb.append(realstart); sb.append(" - "); sb.append(nettics[netnode]); sb.append(")\n"); logger(debugfile,sb.toString()); } remoteresend[netnode] = true; continue; } // update command store from the packet { int start; remoteresend[netnode] = false; start = nettics[netnode] - realstart; src = netbuffer.cmds[start]; while (nettics[netnode] < realend) { dest = netcmds[netconsole][nettics[netnode]%BACKUPTICS]; nettics[netnode]++; // MAES: this is a struct copy. src.copyTo(dest); // Advance src start++; //_D_: had to add this (see linuxdoom source). That fixed that damn consistency failure!!! if (start < netbuffer.cmds.length) src = netbuffer.cmds[start]; } } } } protected void logger(OutputStreamWriter debugfile, String string) { try { debugfile.write(string); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } int gametime; @Override public void NetUpdate () { int nowtime; int newtics; int i,j; int realstart; int gameticdiv; // check time nowtime = TICK.GetTime ()/ticdup; newtics = nowtime - gametime; gametime = nowtime; if (newtics <= 0) // nothing new to update { // listen for other packets GetPackets (); return; } else { if (skiptics <= newtics) { newtics -= skiptics; skiptics = 0; } else { skiptics -= newtics; newtics = 0; } netbuffer.player = (byte) consoleplayer; // build new ticcmds for console player gameticdiv = gametic/ticdup; for (i=0 ; i<newtics ; i++) { VI.StartTic (); ProcessEvents (); if (maketic - gameticdiv >= BACKUPTICS/2-1) break; // can't hold any more //System.out.printf ("mk:%d ",maketic); BuildTiccmd (localcmds[maketic%BACKUPTICS]); maketic++; } if (singletics) return; // singletic update is syncronous // send the packet to the other nodes for (i=0 ; i<doomcom.numnodes ; i++) if (nodeingame[i]) { netbuffer.starttic = (byte) (realstart = resendto[i]); netbuffer.numtics = (byte) (maketic - realstart); if (netbuffer.numtics > BACKUPTICS) I.Error ("NetUpdate: netbuffer.numtics > BACKUPTICS"); resendto[i] = maketic - doomcom.extratics; for (j=0 ; j< netbuffer.numtics ; j++) localcmds[(realstart+j)%BACKUPTICS].copyTo(netbuffer.cmds[j]); // MAES: one of _D_ fixes. //netbuffer.cmds[j] = localcmds[(realstart+j)%BACKUPTICS]; if (remoteresend[i]) { netbuffer.retransmitfrom = (byte) nettics[i]; HSendPacket (i, NCMD_RETRANSMIT); } else { netbuffer.retransmitfrom = 0; HSendPacket (i, 0); } } GetPackets(); } } // // CheckAbort // private void CheckAbort () { event_t ev; int stoptic; stoptic = TICK.GetTime () + 2; while (TICK.GetTime() < stoptic) VI.StartTic (); VI.StartTic (); for ( ; eventtail != eventhead ; eventtail = (++eventtail)&(MAXEVENTS-1) ) { ev = events[eventtail]; if (ev.type == evtype_t.ev_keydown && ev.data1 == KEY_ESCAPE) I.Error ("Network game synchronization aborted."); } } boolean[] gotinfo=new boolean[MAXNETNODES]; /** * D_ArbitrateNetStart * @throws IOException * * */ public void ArbitrateNetStart () throws IOException { int i; autostart = true; // Clear it up... Arrays.fill(gotinfo,false); if (doomcom.consoleplayer!=0) { // listen for setup info from key player System.out.println("listening for network start info...\n"); while (true) { CheckAbort (); if (!HGetPacket ()) continue; if (flags(netbuffer.checksum , NCMD_SETUP)) { if (netbuffer.player != VERSION) I.Error ("Different DOOM versions cannot play a net game!"); startskill = skill_t.values()[netbuffer.retransmitfrom & 15]; // Deathmatch if (((netbuffer.retransmitfrom & 0xc0) >> 6)==1) deathmatch = true; else // Cooperative if (((netbuffer.retransmitfrom & 0xc0) >> 6)==2) altdeath = true; nomonsters = (netbuffer.retransmitfrom & 0x20) > 0; respawnparm = (netbuffer.retransmitfrom & 0x10) > 0; startmap = netbuffer.starttic & 0x3f; startepisode = netbuffer.starttic >> 6; return; } } } else { // key player, send the setup info System.out.println("sending network start info...\n"); do { CheckAbort (); for (i=0 ; i<doomcom.numnodes ; i++) { netbuffer.retransmitfrom = (byte) startskill.ordinal(); if (deathmatch) netbuffer.retransmitfrom |= (1<<6); else if (altdeath) netbuffer.retransmitfrom |= (2<<6); if (nomonsters) netbuffer.retransmitfrom |= 0x20; if (respawnparm) netbuffer.retransmitfrom |= 0x10; netbuffer.starttic = (byte) (startepisode * 64 + startmap); netbuffer.player = VERSION; netbuffer.numtics = 0; HSendPacket (i, NCMD_SETUP); } //#if 1 for(i = 10 ; (i>0) && HGetPacket(); --i) { if((netbuffer.player&0x7f) < MAXNETNODES) gotinfo[netbuffer.player&0x7f] = true; } /* while (HGetPacket ()) { gotinfo[netbuffer.player&0x7f] = true; } */ for (i=1 ; i<doomcom.numnodes ; i++) if (!gotinfo[i]) break; } while (i < doomcom.numnodes); } } // // D_CheckNetGame // Works out player numbers among the net participants // private void CheckNetGame () throws IOException { int i; for (i=0 ; i<MAXNETNODES ; i++) { nodeingame[i] = false; nettics[i] = 0; remoteresend[i] = false; // set when local needs tics resendto[i] = 0; // which tic to start sending } // I_InitNetwork sets doomcom and netgame DNI.InitNetwork (); if (doomcom.id != DOOMCOM_ID) I.Error ("Doomcom buffer invalid!"); // Maes: This is the only place where netbuffer is definitively set to something netbuffer = doomcom.data; consoleplayer = displayplayer = doomcom.consoleplayer; if (netgame) ArbitrateNetStart (); System.out.printf ("startskill %s deathmatch: %s startmap: %d startepisode: %d\n", startskill.toString(), Boolean.toString(deathmatch), startmap, startepisode); // read values out of doomcom ticdup = doomcom.ticdup; // MAES: ticdup must not be zero at this point. Obvious, no? maxsend = BACKUPTICS/(2*ticdup)-1; if (maxsend<1) maxsend = 1; for (i=0 ; i<doomcom.numplayers ; i++) playeringame[i] = true; for (i=0 ; i<doomcom.numnodes ; i++) nodeingame[i] = true; System.out.printf ("player %d of %d (%d nodes)\n",(consoleplayer+1), doomcom.numplayers, doomcom.numnodes); } // // D_QuitNetGame // Called before quitting to leave a net game // without hanging the other players // @Override public void QuitNetGame () throws IOException { int i, j; if (eval(debugfile)) try { debugfile.close(); } catch (IOException e) { e.printStackTrace(); } if (!netgame || !usergame || consoleplayer == -1 || demoplayback) return; // send a bunch of packets for security netbuffer.player = (byte) consoleplayer; netbuffer.numtics = 0; for (i=0 ; i<4 ; i++) { for (j=1 ; j<doomcom.numnodes ; j++) if (nodeingame[j]) HSendPacket (j, NCMD_EXIT); I.WaitVBL (1); } } // // TryRunTics // int[] frametics=new int[4]; int frameon; boolean[] frameskip=new boolean[4]; int oldnettics; int oldentertics; @Override public void TryRunTics () throws IOException { int i; int lowtic; int entertic; int realtics; int availabletics; int counts; int numplaying; // get real tics entertic = TICK.GetTime ()/ticdup; realtics = entertic - oldentertics; oldentertics = entertic; //System.out.printf("Entertic %d, realtics %d, oldentertics %d\n",entertic,realtics,oldentertics); // get available tics NetUpdate (); lowtic = MAXINT; numplaying = 0; for (i=0 ; i<doomcom.numnodes ; i++) { if (nodeingame[i]) { numplaying++; if (nettics[i] < lowtic) lowtic = nettics[i]; } } availabletics = lowtic - gametic/ticdup; // decide how many tics to run if (realtics < availabletics-1) counts = realtics+1; else if (realtics < availabletics) counts = realtics; else counts = availabletics; if (counts < 1) counts = 1; frameon++; if (eval(debugfile)){ sb.setLength(0); sb.append( "=======real: "); sb.append(realtics); sb.append(" avail: "); sb.append(availabletics); sb.append(" game: "); sb.append(counts); sb.append("\n"); debugfile.write(sb.toString()); } if (!demoplayback) { // ideally nettics[0] should be 1 - 3 tics above lowtic // if we are consistantly slower, speed up time for (i=0 ; i<MAXPLAYERS ; i++) if (playeringame[i]) break; if (consoleplayer == i) { // the key player does not adapt } else { if (nettics[0] <= nettics[nodeforplayer[i]]) { gametime--; System.out.print("-"); } frameskip[frameon&3] = oldnettics > nettics[nodeforplayer[i]]; oldnettics = nettics[0]; if (frameskip[0] && frameskip[1] && frameskip[2] && frameskip[3]) { skiptics = 1; System.out.print("+"); } } }// demoplayback // wait for new tics if needed while (lowtic < gametic/ticdup + counts) { NetUpdate (); lowtic = MAXINT; // Finds the node with the lowest number of tics. for (i=0 ; i<doomcom.numnodes ; i++) if (nodeingame[i] && nettics[i] < lowtic) lowtic = nettics[i]; if (lowtic < gametic/ticdup) I.Error ("TryRunTics: lowtic < gametic"); // don't stay in here forever -- give the menu a chance to work int time=TICK.GetTime(); if (time/ticdup - entertic >= 20) { M.Ticker (); return; } } // run the count * ticdup dics while (counts-->0) { for (i=0 ; i<ticdup ; i++) { if (gametic/ticdup > lowtic) I.Error ("gametic>lowtic"); if (advancedemo) DM.DoAdvanceDemo (); M.Ticker (); Ticker (); gametic++; // modify command for duplicated tics if (i != ticdup-1) { ticcmd_t cmd; int buf; int j; buf = (gametic/ticdup)%BACKUPTICS; for (j=0 ; j<MAXPLAYERS ; j++) { cmd = netcmds[j][buf]; cmd.chatchar = 0; if (flags(cmd.buttons , BT_SPECIAL)) cmd.buttons = 0; } } } NetUpdate (); // check for new console commands } } @Override public doomcom_t getDoomCom() { return this.doomcom; } @Override public void setDoomCom(doomcom_t doomcom) { this.doomcom=doomcom; } @Override public void setGameAction(gameaction_t action) { this.gameaction=action; } @Override public gameaction_t getGameAction() { return this.gameaction; } ////////////////////////////VIDEO SCALE STUFF //////////////////////////////// protected int SCREENWIDTH; protected int SCREENHEIGHT; protected int SAFE_SCALE; protected IVideoScale vs; @Override public void setVideoScale(IVideoScale vs) { this.vs=vs; } @Override public void initScaling() { this.SCREENHEIGHT=vs.getScreenHeight(); this.SCREENWIDTH=vs.getScreenWidth(); this.SAFE_SCALE=vs.getSafeScaling(); } public void setCommandLineArgs(ICommandLineManager cM) { this.CM=cM; } public boolean shouldPollLockingKeys() { if (keysCleared) { keysCleared = false; return true; } return false; } /** * Since this is a fully OO implementation, we need a way to create * the instances of the Refresh daemon, the Playloop, the Wadloader * etc. which however are now completely independent of each other * (well, ALMOST), and are typically only passed context when * instantiated. * * If you instantiate one too early, it will have null context. * * The trick is to construct objects in the correct order. Some of * them have Init() methods which are NOT yet safe to call. * */ public void Init(){ // The various objects that need to "sense" the global status // end up here. This allows one-call updates. status_holders=new ArrayList<DoomStatusAware>(); // Doommain is both "main" and handles most of the game status. this.DM=this; this.DG = this; this.DGN=this; // DoomMain also handles its own Game Networking. // Set ticker. It is a shared status object, but not a holder itself. if (eval(CM.CheckParm("-millis"))){ TICK=new MilliTicker(); } else if (eval(CM.CheckParm("-fasttic"))){ TICK=new FastTicker(); } else { TICK=new NanoTicker(); } // Network "driver" status_holders.add((DoomStatusAware) (this.DNI=new DummyNetworkDriver(this))); // Random number generator, but we can have others too. this.RND=new DoomRandom(); // Sound can be left until later, in Start this.W=new WadLoader(this.I); // The wadloader is a "weak" status holder. status_holders.add(this.WIPE=selectWiper()); // Then the menu... status_holders.add(this.HU=new HU(this)); status_holders.add(this.M=new Menu(this)); status_holders.add(this.LL=new BoomLevelLoader(this)); // This will set R. this.R= selectRenderer(); status_holders.add(this.R); status_holders.add((this.P=new Actions(this))); status_holders.add(this.ST=new StatusBar(this)); status_holders.add(this.AM=selectAutoMap()); // Call Init later. // Let the renderer pick its own. It makes linking easier. this.TM=R.getTextureManager(); this.SM=R.getSpriteManager(); //_D_: well, for EndLevel and Finale to work, they need to be instanciated somewhere! // it seems to fit perfectly here status_holders.add(this.WI = new EndLevel(this)); status_holders.add(this.F = selectFinale()); // TODO: find out if we have requests for a specific resolution, // and try honouring them as closely as possible. // 23/5/2011: Experimental dynamic resolution subsystem vs=VisualSettings.parse(CM); // Initializing actually sets drawing positions, constants, // etc. for main. Children will be set later in Start(). this.initScaling(); this.V=selectVideoRenderer(); status_holders.add((DoomStatusAware) this.I); status_holders.add((DoomStatusAware) this.V); // Disk access visualizer status_holders.add((DoomStatusAware) (this.DD=new DiskDrawer(this,DiskDrawer.STDISK))); updateStatusHolders(this); } public static final class HiColor extends DoomMain<byte[], short[]>{ public HiColor(){ super(); } protected IAutoMap<byte[], short[]> selectAutoMap() { return new Map.HiColor(this); } protected final Finale<byte[]> selectFinale(){ return new Finale<byte[]>(this); } protected final DoomVideoRenderer<byte[],short[]> selectVideoRenderer(){ return new BufferedRenderer16(SCREENWIDTH,SCREENHEIGHT); } protected final DoomVideoInterface<short[]> selectVideoInterface(){ return new AWTDoom.HiColor(this,V); } protected final Wiper<byte[],short[]> selectWiper(){ return new Wiper.HiColor(this); } protected final Renderer<byte[],short[]> selectRenderer() { // Serial or parallel renderer (serial is default, but can be forced) if (eval(CM.CheckParm("-serialrenderer"))){ return new UnifiedRenderer.HiColor(this); } else // Parallel. Either with default values (2,1) or user-specified. if (CM.CheckParmBool("-parallelrenderer")||CM.CheckParmBool("-parallelrenderer2")){ int p = CM.CheckParm ("-parallelrenderer"); if (p<1) p=CM.CheckParm("-parallelrenderer2"); if (p < CM.getArgc()-1) { // Next THREE args must be numbers. int walls=1, floors=1,masked=2; startmap = Integer.parseInt(CM.getArgv(p+1)); // Try parsing walls. try { walls=Integer.parseInt(CM.getArgv(p+1)); } catch (Exception e){ // OK, move on anyway. } // Try parsing floors. If wall succeeded, but floors // not, it will default to 1. try { floors=Integer.parseInt(CM.getArgv(p+2)); } catch (Exception e){ // OK, move on anyway. } try { masked=Integer.parseInt(CM.getArgv(p+3)); } catch (Exception e){ // OK, move on anyway. } // In the worst case, we will use the defaults. if (CM.CheckParmBool("-parallelrenderer")) // TODO: temporarily disabled return new ParallelRenderer.HiColor(this,walls,floors,masked); // return (Renderer<byte[], short[]>) new ParallelRenderer(this,walls,floors,masked); // else // return (Renderer<byte[], short[]>) new ParallelRenderer2(this,walls,floors,masked); } } else { // Force serial return new UnifiedRenderer.HiColor(this); } return null; } /** * M_Screenshot * * Currently saves PCX screenshots, and only in devparm. * Very oldschool ;-) * * TODO: add non-devparm hotkey for screenshots, sequential screenshot * messages, option to save as either PCX or PNG. Also, request * current palette from VI (otherwise gamma settings and palette effects * don't show up). * */ public void ScreenShot () { int i; short[] linear; String format=new String("DOOM%d%d%d%d.png"); String lbmname = null; // munge planar buffer to linear linear = (short[]) V.getScreen(DoomVideoRenderer.SCREEN_WS); VI.ReadScreen (linear); // find a file name to save it to int[] digit=new int[4]; for (i=0 ; i<=9999 ; i++) { digit[0] = ((i/1000 )%10); digit[1] = ((i/100)%10); digit[2] = ((i/10)%10); digit[3] = (i%10); lbmname=String.format(format, digit[0],digit[1],digit[2],digit[3]); if (!C2JUtils.testReadAccess(lbmname)) break; // file doesn't exist } if (i==10000) I.Error ("M_ScreenShot: Couldn't create a PNG"); // save the pcx file MenuMisc.WritePNGfile (lbmname, linear, SCREENWIDTH, SCREENHEIGHT); players[consoleplayer].message = "screen shot"; } } public static final class Indexed extends DoomMain<byte[], byte[]>{ public Indexed(){ super(); } protected IAutoMap<byte[], byte[]> selectAutoMap() { return new Map.Indexed(this); } protected final Finale<byte[]> selectFinale(){ return new Finale<byte[]>(this); } protected final DoomVideoRenderer<byte[],byte[]> selectVideoRenderer(){ return new BufferedRenderer(SCREENWIDTH,SCREENHEIGHT); } protected final DoomVideoInterface<byte[]> selectVideoInterface(){ return new AWTDoom.Indexed(this,V); } protected final Wiper<byte[],byte[]> selectWiper(){ return new Wiper.Indexed(this); } protected final Renderer<byte[],byte[]> selectRenderer() { // Serial or parallel renderer (serial is default, but can be forced) if (eval(CM.CheckParm("-serialrenderer"))){ return new UnifiedRenderer.Indexed(this); } else // Parallel. Either with default values (2,1) or user-specified. if (CM.CheckParmBool("-parallelrenderer")||CM.CheckParmBool("-parallelrenderer2")){ int p = CM.CheckParm ("-parallelrenderer"); if (p<1) p=CM.CheckParm("-parallelrenderer2"); if (p < CM.getArgc()-1) { // Next THREE args must be numbers. int walls=1, floors=1,masked=2; startmap = Integer.parseInt(CM.getArgv(p+1)); // Try parsing walls. try { walls=Integer.parseInt(CM.getArgv(p+1)); } catch (Exception e){ // OK, move on anyway. } // Try parsing floors. If wall succeeded, but floors // not, it will default to 1. try { floors=Integer.parseInt(CM.getArgv(p+2)); } catch (Exception e){ // OK, move on anyway. } try { masked=Integer.parseInt(CM.getArgv(p+3)); } catch (Exception e){ // OK, move on anyway. } // In the worst case, we will use the defaults. if (CM.CheckParmBool("-parallelrenderer")) // TODO: temporarily disabled //return new UnifiedRenderer.Indexed(this); return new ParallelRenderer.Indexed(this,walls,floors,masked); //else // return new ParallelRenderer2(this,walls,floors,masked); } } else { // Force serial return new UnifiedRenderer.Indexed(this); } return null; } /** * M_Screenshot * * Currently saves PCX screenshots, and only in devparm. * Very oldschool ;-) * * TODO: add non-devparm hotkey for screenshots, sequential screenshot * messages, option to save as either PCX or PNG. Also, request * current palette from VI (otherwise gamma settings and palette effects * don't show up). * */ public void ScreenShot () { int i; byte[] linear; String format=new String("DOOM%d%d%d%d.png"); String lbmname = null; // munge planar buffer to linear linear = (byte[]) V.getScreen(DoomVideoRenderer.SCREEN_WS); VI.ReadScreen (linear); // find a file name to save it to int[] digit=new int[4]; for (i=0 ; i<=9999 ; i++) { digit[0] = ((i/1000 )%10); digit[1] = ((i/100)%10); digit[2] = ((i/10)%10); digit[3] = (i%10); lbmname=String.format(format, digit[0],digit[1],digit[2],digit[3]); if (!C2JUtils.testReadAccess(lbmname)) break; // file doesn't exist } if (i==10000) I.Error ("M_ScreenShot: Couldn't create a PNG"); // save the pcx file MenuMisc.WritePNGfile(lbmname, linear, SCREENWIDTH, SCREENHEIGHT,V.getPalette()); players[consoleplayer].message = "screen shot"; } } public static final class TrueColor extends DoomMain<byte[], int[]>{ public TrueColor(){ super(); } protected IAutoMap<byte[], int[]> selectAutoMap() { return new Map.TrueColor(this); } protected final Finale<byte[]> selectFinale(){ return new Finale<byte[]>(this); } protected final DoomVideoRenderer<byte[],int[]> selectVideoRenderer(){ return new BufferedRenderer32(SCREENWIDTH,SCREENHEIGHT); } protected final DoomVideoInterface<int[]> selectVideoInterface(){ return new AWTDoom.TrueColor(this,V); } protected final Wiper<byte[],int[]> selectWiper(){ return new Wiper.TrueColor(this); } protected final Renderer<byte[],int[]> selectRenderer() { // Serial or parallel renderer (serial is default, but can be forced) if (eval(CM.CheckParm("-serialrenderer"))){ return new UnifiedRenderer.TrueColor(this); } else // Parallel. Either with default values (2,1) or user-specified. if (CM.CheckParmBool("-parallelrenderer")||CM.CheckParmBool("-parallelrenderer2")){ int p = CM.CheckParm ("-parallelrenderer"); if (p<1) p=CM.CheckParm("-parallelrenderer2"); if (p < CM.getArgc()-1) { // Next THREE args must be numbers. int walls=1, floors=1,masked=2; startmap = Integer.parseInt(CM.getArgv(p+1)); // Try parsing walls. try { walls=Integer.parseInt(CM.getArgv(p+1)); } catch (Exception e){ // OK, move on anyway. } // Try parsing floors. If wall succeeded, but floors // not, it will default to 1. try { floors=Integer.parseInt(CM.getArgv(p+2)); } catch (Exception e){ // OK, move on anyway. } try { masked=Integer.parseInt(CM.getArgv(p+3)); } catch (Exception e){ // OK, move on anyway. } // In the worst case, we will use the defaults. if (CM.CheckParmBool("-parallelrenderer")) // TODO: temporarily disabled return new ParallelRenderer.TrueColor(this,walls,floors,masked); // return (Renderer<byte[], short[]>) new ParallelRenderer(this,walls,floors,masked); // else // return (Renderer<byte[], short[]>) new ParallelRenderer2(this,walls,floors,masked); } } else { // Force serial return new UnifiedRenderer.TrueColor(this); } return null; } /** * M_Screenshot * * Currently saves PCX screenshots, and only in devparm. * Very oldschool ;-) * * TODO: add non-devparm hotkey for screenshots, sequential screenshot * messages, option to save as either PCX or PNG. Also, request * current palette from VI (otherwise gamma settings and palette effects * don't show up). * */ public void ScreenShot () { int i; int[] linear; String format=new String("DOOM%d%d%d%d.png"); String lbmname = null; // munge planar buffer to linear linear = (int[]) V.getScreen(DoomVideoRenderer.SCREEN_WS); VI.ReadScreen (linear); // find a file name to save it to [...]
full source download show line numbers debug dex old transpilations
Travelled to 15 computer(s): aoiabmzegqzx, bhatertpkbcr, cbybwowwnfue, cfunsshuasjs, ddnzoavkxhuk, gwrvuhgaqvyk, ishqpsrjomds, lpdgvwnxivlt, mqqgnosmbjvj, onxytkatvevr, pyentgdyhuwx, pzhvpgtvlbxg, tslmcundralx, tvejysmllsmz, vouqrxazstgt
No comments. add comment
Snippet ID: | #1005016 |
Snippet name: | Mocha Doom Single Source (include) |
Eternal ID of this version: | #1005016/1 |
Text MD5: | dc89d3a9538a824122a98f8aabc8c0fe |
Transpilation MD5: | 73ca03f98e9862a606ae662c56a197f1 |
Author: | stefan |
Category: | javax / games |
Type: | JavaX source code |
Public (visible to everyone): | Yes |
Archived (hidden from active list): | No |
Created/modified: | 2016-09-28 22:00:15 |
Source code size: | 3225853 bytes / 104211 lines |
Pitched / IR pitched: | No / No |
Views / Downloads: | 11838 / 21636 |
Referenced in: | #1005029 - Mocha Doom Single Source With Translator |