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 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"; } } } //$Log: DoomMain.java,v $ //Revision 1.110 2016/06/06 22:21:24 velktron //Use coalescing // //Revision 1.109 2012/11/06 16:04:58 velktron //Variables manager less tightly integrated. // //Revision 1.108 2012/11/05 17:25:29 velktron //Fixed tinting system according to SodaHolic's advice. // //Revision 1.107 2012/09/27 16:53:46 velktron //Stupid brokeness prevented -loadgame from working. // //Revision 1.106 2012/09/26 23:15:20 velktron //Parallel renderer restored...sort of. // //Revision 1.105 2012/09/26 15:54:22 velktron //Spritemanager is set up by renderer. // //Revision 1.104 2012/09/24 22:36:49 velktron //Fixed HOM detection. // //Revision 1.103 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.101.2.11 2012/09/24 16:58:06 velktron //TrueColor, Generics. // //Revision 1.101.2.10 2012/09/21 16:17:25 velktron //More generic. // //Revision 1.101.2.9 2012/09/20 14:25:13 velktron //Unified DOOM!!! // package doom; /** A shiny new and enterprisey (yeah right) interface for * command-line handling. No more argv/argc nastiness! * * @author velktron * */ public interface ICommandLineManager { public abstract String getArgv(int index); public abstract int getArgc(); /** * M_CheckParm Checks for the given parameter in the program's command line * arguments. Returns the argument number (1 to argc-1) or 0 if not present * * OK, now WHY ON EARTH was this be defined in m_menu.c? * * MAES: this needs to be modified for Java, or else bump myargc one element up. * */ public abstract int CheckParm(String check); void FindResponseFile(); public abstract void setArgv(int index, String string); boolean CheckParmBool(String check); } package doom; import defines.*; // // PSPRITE ACTIONS for waepons. // This struct controls the weapon animations. // // Each entry is: // ammo/amunition type // upstate // downstate // readystate // atkstate, i.e. attack/fire/hit frame // flashstate, muzzle flash // public class weaponinfo_t { /* public weaponinfo_t(ammotype_t ammo, int upstate, int downstate, int readystate, int atkstate, int flashstate) { super(); this.ammo = ammo; this.upstate = upstate; this.downstate = downstate; this.readystate = readystate; this.atkstate = atkstate; this.flashstate = flashstate; }*/ public ammotype_t ammo; public weaponinfo_t(ammotype_t ammo, statenum_t upstate, statenum_t downstate, statenum_t readystate, statenum_t atkstate, statenum_t flashstate) { super(); this.ammo = ammo; this.upstate = upstate; this.downstate = downstate; this.readystate = readystate; this.atkstate = atkstate; this.flashstate = flashstate; } public statenum_t upstate; public statenum_t downstate; public statenum_t readystate; public statenum_t atkstate; public statenum_t flashstate; /* public int upstate; public int downstate; public int readystate; public int atkstate; public int flashstate; */ } package doom; /** The possible events according to Doom */ public enum evtype_t { ev_null, ev_keydown, ev_keyup, ev_mouse, ev_joystick, ev_mousewheel, // extension ev_clear // Forcibly clear all button input (e.g. when losing focus) }; package doom; /** Mocha Doom uses the think_t type as an ENUM for available action functions. This * makes enumerations etc. easy to keep track of. * * @author velktron * */ public enum think_t { /** Here's your "decent NOP" function */ NOP, A_Light0(2), A_WeaponReady(2), A_Lower(2), A_Raise(2), A_Punch(2), A_ReFire(2), A_FirePistol(2), A_Light1(2), A_FireShotgun(2), A_Light2(2), A_FireShotgun2(2), A_CheckReload(2), A_OpenShotgun2(2), A_LoadShotgun2(2), A_CloseShotgun2(2), A_FireCGun(2), A_GunFlash(2), A_FireMissile(2), A_Saw(2), A_FirePlasma(2), A_BFGsound(2), A_FireBFG(2), A_BFGSpray(1), A_Explode(1), A_Pain(1), A_PlayerScream(1), A_Fall(1), A_XScream(1), A_Look(1), A_Chase(1), A_FaceTarget(1), A_PosAttack(1), A_Scream(1), A_SPosAttack(1), A_VileChase(1), A_VileStart(1), A_VileTarget(1), A_VileAttack(1), A_StartFire(1), A_Fire(1), A_FireCrackle(1), A_Tracer(1), A_SkelWhoosh(1), //Historically, "think_t" is yet another //function pointer to a routine to handle //an actor. // //Experimental stuff. //To compile this as "ANSI C with classes" //we will need to handle the various //action functions cleanly. // //typedef void (*actionf_v)(); //typedef void (*actionf_p1)( void* ); //typedef void (*actionf_p2)( void*, void* ); /*typedef union { actionf_p1 acp1; actionf_v acv; actionf_p2 acp2; } actionf_t; */ A_SkelFist(1), A_SkelMissile(1), A_FatRaise(1), A_FatAttack1(1), A_FatAttack2(1), A_FatAttack3(1), A_BossDeath(1), A_CPosAttack(1), A_CPosRefire(1), A_TroopAttack(1), A_SargAttack(1), A_HeadAttack(1), A_BruisAttack(1), A_SkullAttack(1), A_Metal(1), A_SpidRefire(1), A_BabyMetal(1), A_BspiAttack(1), A_Hoof(1), A_CyberAttack(1), A_PainAttack(1), A_PainDie(1), A_KeenDie(1), A_BrainPain(1), A_BrainScream(1), A_BrainDie(1), A_BrainAwake(1), A_BrainSpit(1), A_SpawnSound(1), A_SpawnFly(1), A_BrainExplode(1), P_MobjThinker(1), T_FireFlicker(1), T_LightFlash(1), T_StrobeFlash(1), T_Glow(1), T_MoveCeiling(1), T_MoveFloor(1), T_VerticalDoor(1), T_PlatRaise(1), T_SlidingDoor(1), // The following are dummies that exist only for demo sync debugging DeathMatchSpawnPlayer, PlayerInSpecialSector, SpawnLightFlash, SpawnStrobeFlash, ExplodeMissile, CheckMissileRange, DoPlat, CheckMissileSpawn, DamageMobj, KillMobj, NewChaseDir, P_GunShot, PIT_ChangeSector, PIT_CheckThing, TryWalk, SpawnBlood, SpawnMapThing, SpawnMobj, SpawnMissile, SpawnPuff; think_t(){ type=0; // Default, but Doom has no "type 0" functions! } think_t(int type){ this.type=type; } /** 0 for void, 1 for acp1, 2 for acp2 */ public int getType() { return type; } private int type; public String ToString(){ return this.name()+" Type: "+type; } public static final int acpv=0; public static final int acp1=1; public static final int acp2=2; } package doom; import java.io.IOException; /** Doom is actually tied to its networking module. * Therefore, no matter where and how you implement it, these functions * need to be callable from within many modules. * * This is the so called "game networking" which is internal and game-specific, * and not system networking which deals with the low level sockets and packet * stuff. You'll need DoomSystemNetworking for that one. * * @author Velktron * */ public interface IDoomGameNetworking { public void TryRunTics() throws IOException; /** * NetUpdate * Builds ticcmds for console player, * sends out a packet * @throws IOException */ public void NetUpdate (); public doomcom_t getDoomCom(); public void setDoomCom(doomcom_t doomcom); public int getTicdup(); public void setTicdup(int ticdup); } package doom; import i.DoomStatusAware; import i.IDoomSystem; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.Arrays; import m.IRandom; import data.Tables; import data.state_t; import defines.*; import data.sounds.sfxenum_t; import static data.Defines.*; import static data.Tables.*; import static m.fixed_t.*; import static data.info.*; import static p.mobj_t.*; import p.Actions; import p.mobj_t; import p.pspdef_t; import rr.LightsAndColors; import rr.Renderer; import rr.sector_t; import s.IDoomSound; import utils.C2JUtils; import w.DoomBuffer; import w.DoomIO; import w.IPackableDoomObject; import w.IReadableDoomObject; import static utils.C2JUtils.*; import static data.Limits.*; import static doom.items.weaponinfo; import static p.mobj_t.MF_SHADOW; /** * Extended player object info: player_t The player data structure depends on a * number of other structs: items (internal inventory), animation states * (closely tied to the sprites used to represent them, unfortunately). * * #include "d_items.h" * #include "p_pspr.h" * * In addition, the player is just a special * case of the generic moving object/actor. * NOTE: this doesn't mean it needs to extend it, although it would be * possible. * * #include "p_mobj.h" * * Finally, for odd reasons, the player input is buffered within * the player data struct, as commands per game tick. * * #include "d_ticcmd.h" */ public class player_t /*extends mobj_t */ implements Cloneable ,DoomStatusAware, IReadableDoomObject, IPackableDoomObject { /** Probably doomguy needs to know what the fuck is going on */ private DoomStatus DS; private IDoomGame DG; private Actions P; private Renderer<?,?> R; private IRandom RND; private IDoomSystem I; private IDoomSound S; /* Fugly hack to "reset" the player. Not worth the fugliness. public static player_t nullplayer; static { nullplayer = new player_t(); } */ public player_t() { powers = new int[NUMPOWERS]; frags = new int[MAXPLAYERS]; ammo = new int[NUMAMMO]; //maxammo = new int[NUMAMMO]; maxammo= new int[NUMAMMO]; cards = new boolean[card_t.NUMCARDS.ordinal()]; weaponowned = new boolean[NUMWEAPONS]; psprites = new pspdef_t[NUMPSPRITES]; C2JUtils.initArrayOfObjects(psprites); this.mo=new mobj_t(); // If a player doesn't reference himself through his object, he will have an existential crisis. this.mo.player=this; readyweapon=weapontype_t.wp_fist; this.cmd=new ticcmd_t(); //weaponinfo=new weaponinfo_t(); } public final static int CF_NOCLIP = 1; // No damage, no health loss. public final static int CF_GODMODE = 2; public final static int CF_NOMOMENTUM = 4; // Not really a cheat, just a debug aid. /** The "mobj state" of the player is stored here, even though he "inherits" * all mobj_t properties (except being a thinker). However, for good or bad, * his mobj properties are modified by accessing player.mo */ public mobj_t mo; /** playerstate_t */ public int playerstate; public ticcmd_t cmd; /** * Determine POV, including viewpoint bobbing during movement. (fixed_t) * Focal origin above r.z */ public int viewz; /** (fixed_t) Base height above floor for viewz. */ public int viewheight; /** (fixed_t) Bob/squat speed. */ public int deltaviewheight; /** (fixed_t) bounded/scaled total momentum. */ public int bob; // Heretic stuff public int flyheight; public int lookdir; public boolean centering; /** * This is only used between levels, mo->health is used during levels. * CORRECTION: this is also used by the automap widget. * MAES: fugly hax, as even passing "Integers" won't work, as they are immutable. * Fuck that, I'm doing it the fugly MPI Java way! */ public int[] health = new int[1]; /** has to be passed around :-( */ public int[] armorpoints = new int[1]; /** Armor type is 0-2. */ public int armortype; /** Power ups. invinc and invis are tic counters. */ public int[] powers; public boolean[] cards; public boolean backpack; // Frags, kills of other players. public int[] frags; public weapontype_t readyweapon; // Is wp_nochange if not changing. public weapontype_t pendingweapon; public boolean[] weaponowned; public int[] ammo; public int[] maxammo; /** True if button down last tic. */ public boolean attackdown; public boolean usedown; // Bit flags, for cheats and debug. // See cheat_t, above. public int cheats; // Refired shots are less accurate. public int refire; // For intermission stats. public int killcount; public int itemcount; public int secretcount; // Hint messages. public String message; // For screen flashing (red or bright). public int damagecount; public int bonuscount; // Who did damage (NULL for floors/ceilings). public mobj_t attacker; // So gun flashes light up areas. public int extralight; /** * Current PLAYPAL, ??? can be set to REDCOLORMAP for pain, etc. MAES: "int" * my ass. It's yet another pointer alias into colormaps. Ergo, array and * pointer. */ // public byte[] fixedcolormap; public int fixedcolormap; // Player skin colorshift, // 0-3 for which color to draw player. public int colormap; // TODO: Overlay view sprites (gun, etc). public pspdef_t[] psprites; // True if secret level has been done. public boolean didsecret; /** It's probably faster to clone the null player */ public void reset() { Arrays.fill(this.ammo, 0); Arrays.fill(this.armorpoints, 0); Arrays.fill(this.cards, false); Arrays.fill(this.frags, 0); Arrays.fill(this.health, 0); Arrays.fill(this.maxammo, 0); Arrays.fill(this.powers, 0); Arrays.fill(this.weaponowned, false); //Arrays.fill(this.psprites, null); this.cheats=0; // Forgot to clear up cheats flag... this.armortype = 0; this.attackdown = false; this.attacker = null; this.backpack = false; this.bob = 0; } @Override public player_t clone() throws CloneNotSupportedException { return (player_t) super.clone(); } /** 16 pixels of bob */ private static int MAXBOB = 0x100000; /** * P_Thrust Moves the given origin along a given angle. * * @param angle * (angle_t) * @param move * (fixed_t) */ public void Thrust(long angle, int move) { mo.momx += FixedMul(move, finecosine(angle)); mo.momy += FixedMul(move, finesine( angle)); } protected final static int PLAYERTHRUST=2048/TIC_MUL; /** * P_MovePlayer */ public void MovePlayer() { ticcmd_t cmd = this.cmd; mo.angle += (cmd.angleturn << 16); mo.angle&=BITS32; // Do not let the player control movement // if not onground. onground = (mo.z <= mo.floorz); if (cmd.forwardmove != 0 && onground) Thrust(mo.angle, cmd.forwardmove * PLAYERTHRUST); if (cmd.sidemove != 0 && onground) Thrust((mo.angle - ANG90)&BITS32, cmd.sidemove * PLAYERTHRUST); if ((cmd.forwardmove != 0 || cmd.sidemove != 0) && mo.state == states[statenum_t.S_PLAY.ordinal()]) { this.mo.SetMobjState(statenum_t.S_PLAY_RUN1); } // Freelook code ripped off Heretic. Sieg heil! int look = cmd.lookfly&15; if(look > 7) { look -= 16; } if(look!=0) { if(look == TOCENTER) { centering = true; } else { lookdir += 5*look; if(lookdir > 90 || lookdir < -110) { lookdir -= 5*look; } } } // Centering is done over several tics if(centering) { if(lookdir > 0) { lookdir -= 8; } else if(lookdir < 0) { lookdir += 8; } if(Math.abs(lookdir) < 8) { lookdir = 0; centering = false; } } /* Flight stuff from Heretic fly = cmd.lookfly>>4; if(fly > 7) { fly -= 16; } if(fly && player->powers[pw_flight]) { if(fly != TOCENTER) { player->flyheight = fly*2; if(!(player->mo->flags2&MF2_FLY)) { player->mo->flags2 |= MF2_FLY; player->mo->flags |= MF_NOGRAVITY; } } else { player->mo->flags2 &= ~MF2_FLY; player->mo->flags &= ~MF_NOGRAVITY; } } else if(fly > 0) { P_PlayerUseArtifact(player, arti_fly); } if(player->mo->flags2&MF2_FLY) { player->mo->momz = player->flyheight*FRACUNIT; if(player->flyheight) { player->flyheight /= 2; } } */ } // // GET STUFF // // a weapon is found with two clip loads, // a big item has five clip loads public static final int[] clipammo = { 10, 4, 20, 1 }; /** * P_GiveAmmo Num is the number of clip loads, not the individual count (0= * 1/2 clip). * * @return false if the ammo can't be picked up at all * @param ammo * intended to be ammotype_t. */ public boolean GiveAmmo(ammotype_t amm, int num) { int oldammo; int ammo = amm.ordinal(); if (ammo == ammotype_t.am_noammo.ordinal()) return false; if (ammo < 0 || ammo > NUMAMMO) I.Error("P_GiveAmmo: bad type %i", ammo); if (this.ammo[ammo] == maxammo[ammo]) return false; if (num != 0) num *= clipammo[ammo]; else num = clipammo[ammo] / 2; if (DS.gameskill == skill_t.sk_baby ||DS.gameskill == skill_t.sk_nightmare) { // give double ammo in trainer mode, // you'll need in nightmare num <<= 1; } oldammo = this.ammo[ammo]; this.ammo[ammo] += num; if (this.ammo[ammo] > maxammo[ammo]) this.ammo[ammo] = maxammo[ammo]; // If non zero ammo, // don't change up weapons, // player was lower on purpose. if (oldammo != 0) return true; // We were down to zero, // so select a new weapon. // Preferences are not user selectable. switch (ammotype_t.values()[ammo]) { case am_clip: if (readyweapon == weapontype_t.wp_fist) { if (weaponowned[weapontype_t.wp_chaingun.ordinal()]) pendingweapon = weapontype_t.wp_chaingun; else pendingweapon = weapontype_t.wp_pistol; } break; case am_shell: if (readyweapon == weapontype_t.wp_fist || readyweapon == weapontype_t.wp_pistol) { if (weaponowned[weapontype_t.wp_shotgun.ordinal()]) pendingweapon = weapontype_t.wp_shotgun; } break; case am_cell: if (readyweapon == weapontype_t.wp_fist || readyweapon == weapontype_t.wp_pistol) { if (weaponowned[weapontype_t.wp_plasma.ordinal()]) pendingweapon = weapontype_t.wp_plasma; } break; case am_misl: if (readyweapon == weapontype_t.wp_fist) { if (weaponowned[weapontype_t.wp_missile.ordinal()]) pendingweapon = weapontype_t.wp_missile; } default: break; } return true; } public static final int BONUSADD = 6; /** * P_GiveWeapon * The weapon name may have a MF_DROPPED flag ored in. */ public boolean GiveWeapon(weapontype_t weapn, boolean dropped) { boolean gaveammo; boolean gaveweapon; int weapon = weapn.ordinal(); if (DS.netgame && (DS.deathmatch != true) // ???? was "2" && !dropped) { // leave placed weapons forever on net games if (weaponowned[weapon]) return false; bonuscount += BONUSADD; weaponowned[weapon] = true; if (DS.deathmatch) GiveAmmo(weaponinfo[weapon].ammo, 5); else GiveAmmo(weaponinfo[weapon].ammo, 2); pendingweapon = weapn; if (this ==DS.players[DS.consoleplayer]) S.StartSound (null, sfxenum_t.sfx_wpnup); return false; } if (weaponinfo[weapon].ammo != ammotype_t.am_noammo) { // give one clip with a dropped weapon, // two clips with a found weapon if (dropped) gaveammo = GiveAmmo(weaponinfo[weapon].ammo, 1); else gaveammo = GiveAmmo(weaponinfo[weapon].ammo, 2); } else gaveammo = false; if (weaponowned[weapon]) gaveweapon = false; else { gaveweapon = true; weaponowned[weapon] = true; pendingweapon = weapn; } return (gaveweapon || gaveammo); } /** * P_GiveBody Returns false if the body isn't needed at all */ public boolean GiveBody(int num) { if (this.health[0] >= MAXHEALTH) return false; health[0] += num; if (health[0] > MAXHEALTH) health[0] = MAXHEALTH; mo.health = health[0]; return true; } /** * P_GiveArmor Returns false if the armor is worse than the current armor. */ public boolean GiveArmor(int armortype) { int hits; hits = armortype * 100; if (armorpoints[0] >= hits) return false; // don't pick up this.armortype = armortype; armorpoints[0] = hits; return true; } /** * P_GiveCard */ public void GiveCard(card_t crd) { int card = crd.ordinal(); if (cards[card]) return; bonuscount = BONUSADD; cards[card] = true; } // // P_GivePower // public boolean GivePower( int /* powertype_t */power) // MAES: // I // didn't // change // this! { if (power == pw_invulnerability) { powers[power] = INVULNTICS; return true; } if (power == pw_invisibility) { powers[power] = INVISTICS; mo.flags |= MF_SHADOW; return true; } if (power == pw_infrared) { powers[power] = INFRATICS; return true; } if (power == pw_ironfeet) { powers[power] = IRONTICS; return true; } if (power == pw_strength) { GiveBody(100); powers[power] = 1; return true; } if (powers[power] != 0) return false; // already got it powers[power] = 1; return true; } /** * G_PlayerFinishLevel * Called when a player completes a level. */ public final void PlayerFinishLevel () { Arrays.fill(powers, 0); Arrays.fill(cards,false); mo.flags &= ~mobj_t.MF_SHADOW; // cancel invisibility extralight = 0; // cancel gun flashes fixedcolormap = 0; // cancel ir gogles damagecount = 0; // no palette changes bonuscount = 0; lookdir = 0; // From heretic } /** * P_PlayerInSpecialSector * Called every tic frame * that the player origin is in a special sector */ protected void PlayerInSpecialSector () { sector_t sector; sector = mo.subsector.sector; // Falling, not all the way down yet? if (mo.z != sector.floorheight) return; // Has hitten ground. switch (sector.special) { case 5: // HELLSLIME DAMAGE if (powers[pw_ironfeet]==0) if (!flags(DS.leveltime,0x1f)) P.DamageMobj (mo,null, null, 10); break; case 7: // NUKAGE DAMAGE if (powers[pw_ironfeet]==0) if (!flags(DS.leveltime,0x1f)) P.DamageMobj (mo, null, null, 5); break; case 16: // SUPER HELLSLIME DAMAGE case 4: // STROBE HURT if (!eval(powers[pw_ironfeet]) || (RND.P_Random()<5) ) { if (!flags(DS.leveltime,0x1f)) P.DamageMobj (mo, null, null, 20); } break; case 9: // SECRET SECTOR secretcount++; sector.special = 0; break; case 11: // EXIT SUPER DAMAGE! (for E1M8 finale) cheats &= ~CF_GODMODE; if (!flags(DS.leveltime,0x1f)) P.DamageMobj (mo, null, null, 20); if (health[0] <= 10) DG.ExitLevel(); break; default: I.Error ("P_PlayerInSpecialSector: unknown special %d", sector.special); break; }; } // Index of the special effects (INVUL inverse) map. public static final int INVERSECOLORMAP =LightsAndColors.LIGHTLEVELS; // //P_CalcHeight //Calculate the walking / running height adjustment // public void CalcHeight () { int angle; int bob; // fixed // Regular movement bobbing // (needs to be calculated for gun swing // even if not on ground) // OPTIMIZE: tablify angle // Note: a LUT allows for effects // like a ramp with low health. this.bob = FixedMul (mo.momx, mo.momx) + FixedMul (mo.momy,mo.momy); this.bob >>= 2; if (this.bob>MAXBOB) this.bob = MAXBOB; if (flags(cheats ,CF_NOMOMENTUM) || !onground) { viewz = mo.z + VIEWHEIGHT; if (viewz > mo.ceilingz-4*FRACUNIT) viewz = mo.ceilingz-4*FRACUNIT; viewz = mo.z + viewheight; return; } angle = (FINEANGLES/20*DS.leveltime)&FINEMASK; bob = FixedMul ( this.bob/2, finesine[angle]); // move viewheight if (playerstate == PST_LIVE) { viewheight += deltaviewheight; if (viewheight > VIEWHEIGHT) { viewheight = VIEWHEIGHT; deltaviewheight = 0; } if (viewheight < VIEWHEIGHT/2) { viewheight = VIEWHEIGHT/2; if (deltaviewheight <= 0) deltaviewheight = 1; } if (deltaviewheight!=0) { deltaviewheight += FRACUNIT/4; if (deltaviewheight==0) deltaviewheight = 1; } } viewz = mo.z + viewheight + bob; if (viewz > mo.ceilingz-4*FRACUNIT) viewz = mo.ceilingz-4*FRACUNIT; } private static final long ANG5 = (ANG90/18); /** * P_DeathThink * Fall on your face when dying. * Decrease POV height to floor height. * * DOOMGUY IS SO AWESOME THAT HE THINKS EVEN WHEN DEAD!!! * */ public void DeathThink () { long angle; //angle_t long delta; MovePsprites (); // fall to the ground if (viewheight > 6*FRACUNIT) viewheight -= FRACUNIT; if (viewheight < 6*FRACUNIT) viewheight = 6*FRACUNIT; deltaviewheight = 0; onground = (mo.z <= mo.floorz); CalcHeight (); if (attacker!=null && attacker != mo) { angle = R.PointToAngle2 (mo.x, mo.y, attacker.x, attacker.y); delta = Tables.addAngles(angle, - mo.angle); if (delta < ANG5 || delta > -ANG5) { // Looking at killer, // so fade damage flash down. mo.angle = angle; if (damagecount!=0) damagecount--; } else if (delta < ANG180) mo.angle += ANG5; else mo.angle -= ANG5; } else if (damagecount!=0) damagecount--; if (flags(cmd.buttons ,BT_USE)) playerstate = PST_REBORN; } // // P_MovePsprites // Called every tic by player thinking routine. // public void MovePsprites () { pspdef_t psp; @SuppressWarnings("unused") // Shut up compiler state_t state=null; for (int i=0 ; i<NUMPSPRITES ; i++) { psp = psprites[i]; // a null state means not active if ( (state = psp.state)!=null ) { // drop tic count and possibly change state // a -1 tic count never changes if (psp.tics != -1) { psp.tics--; if (!eval(psp.tics)) this.SetPsprite (i, psp.state.nextstate); } } } psprites[ps_flash].sx = psprites[ps_weapon].sx; psprites[ps_flash].sy = psprites[ps_weapon].sy; } /** /* P_SetPsprite */ public void SetPsprite ( int position, statenum_t newstate ) { pspdef_t psp; state_t state; psp = psprites[position]; do { if (newstate==null) { // object removed itself psp.state = null; break; } state = states[newstate.ordinal()]; psp.state = state; psp.tics = (int) state.tics; // could be 0 if (state.misc1!=0) { // coordinate set psp.sx = (int) (state.misc1 << FRACBITS); psp.sy = (int) (state.misc2 << FRACBITS); } // Call action routine. // Modified handling. if (state.acp2!=null) { state.acp2.invoke(this, psp); if (psp.state==null) break; } newstate = psp.state.nextstate; } while (psp.tics==0); // an initial state of 0 could cycle through } /** Accessory method to identify which "doomguy" we are. * Because we can't use the [target.player-players] syntax * in order to get an array index, in Java. * * If -1 is returned, then we have existential problems. * */ public int identify(){ if (id>=0) return id; int i; // Let's assume that we know jack. for (i=0;i<DS.players.length;i++) if (this==DS.players[i]) break; return id=i; } private int id=-1; private boolean onground; /* psprnum_t enum */ public static int ps_weapon=0, ps_flash=1, NUMPSPRITES=2; public static int LOWERSPEED = MAPFRACUNIT*6; public static int RAISESPEED = MAPFRACUNIT*6; public static int WEAPONBOTTOM =128*FRACUNIT; public static int WEAPONTOP =32*FRACUNIT; // plasma cells for a bfg attack private static int BFGCELLS =40; /* P_SetPsprite public void SetPsprite ( player_t player, int position, statenum_t newstate ) { pspdef_t psp; state_t state; psp = psprites[position]; do { if (newstate==null) { // object removed itself psp.state = null; break; } state = states[newstate.ordinal()]; psp.state = state; psp.tics = (int) state.tics; // could be 0 if (state.misc1!=0) { // coordinate set psp.sx = (int) (state.misc1 << FRACBITS); psp.sy = (int) (state.misc2 << FRACBITS); } // Call action routine. // Modified handling. if (state.action.getType()==acp2) { P.A.dispatch(state.action,this, psp); if (psp.state==null) break; } newstate = psp.state.nextstate; } while (psp.tics==0); // an initial state of 0 could cycle through } */ /** fixed_t */ int swingx, swingy; /**P_CalcSwing * * @param player */ public void CalcSwing (player_t player) { int swing; // fixed_t int angle; // OPTIMIZE: tablify this. // A LUT would allow for different modes, // and add flexibility. swing = this.bob; angle = (FINEANGLES/70*DS.leveltime)&FINEMASK; swingx = FixedMul ( swing, finesine[angle]); angle = (FINEANGLES/70*DS.leveltime+FINEANGLES/2)&FINEMASK; swingy = -FixedMul ( swingx, finesine[angle]); } // // P_BringUpWeapon // Starts bringing the pending weapon up // from the bottom of the screen. // Uses player // public void BringUpWeapon () { statenum_t newstate=statenum_t.S_NULL; if (pendingweapon == weapontype_t.wp_nochange) pendingweapon = readyweapon; if (pendingweapon == weapontype_t.wp_chainsaw) S.StartSound (mo, sfxenum_t.sfx_sawup); newstate = weaponinfo[pendingweapon.ordinal()].upstate; pendingweapon = weapontype_t.wp_nochange; psprites[ps_weapon].sy = WEAPONBOTTOM; this.SetPsprite ( ps_weapon, newstate); } /** * P_CheckAmmo * Returns true if there is enough ammo to shoot. * If not, selects the next weapon to use. */ public boolean CheckAmmo () { ammotype_t ammo; int count; ammo = weaponinfo[readyweapon.ordinal()].ammo; // Minimal amount for one shot varies. if (readyweapon == weapontype_t.wp_bfg) count = BFGCELLS; else if (readyweapon == weapontype_t.wp_supershotgun) count = 2; // Double barrel. else count = 1; // Regular. // Some do not need ammunition anyway. // Return if current ammunition sufficient. if (ammo == ammotype_t.am_noammo || this.ammo[ammo.ordinal()] >= count) return true; // Out of ammo, pick a weapon to change to. // Preferences are set here. do { if (weaponowned[weapontype_t.wp_plasma.ordinal()] && (this.ammo[ammotype_t.am_cell.ordinal()]!=0) && !DS.isShareware() ) { pendingweapon = weapontype_t.wp_plasma; } else if (weaponowned[weapontype_t.wp_supershotgun.ordinal()] && this.ammo[ammotype_t.am_shell.ordinal()]>2 && DS.isCommercial() ) { pendingweapon = weapontype_t.wp_supershotgun; } else if (weaponowned[weapontype_t.wp_chaingun.ordinal()] && this.ammo[ammotype_t.am_clip.ordinal()]!=0) { pendingweapon = weapontype_t.wp_chaingun; } else if (weaponowned[weapontype_t.wp_shotgun.ordinal()] && this.ammo[ammotype_t.am_shell.ordinal()]!=0) { pendingweapon = weapontype_t.wp_shotgun; } else if (this.ammo[ammotype_t.am_clip.ordinal()]!=0) { pendingweapon = weapontype_t.wp_pistol; } else if (weaponowned[weapontype_t.wp_chainsaw.ordinal()]) { pendingweapon = weapontype_t.wp_chainsaw; } else if (weaponowned[weapontype_t.wp_missile.ordinal()] && this.ammo[ammotype_t.am_misl.ordinal()]!=0) { pendingweapon = weapontype_t.wp_missile; } else if (weaponowned[weapontype_t.wp_bfg.ordinal()] && this.ammo[ammotype_t.am_cell.ordinal()]>40 && !DS.isShareware() ) { pendingweapon = weapontype_t.wp_bfg; } else { // If everything fails. pendingweapon = weapontype_t.wp_fist; } } while (pendingweapon == weapontype_t.wp_nochange); // Now set appropriate weapon overlay. this.SetPsprite ( ps_weapon, weaponinfo[readyweapon.ordinal()].downstate); return false; } /** * P_DropWeapon * Player died, so put the weapon away. */ public void DropWeapon () { this.SetPsprite ( ps_weapon, weaponinfo[readyweapon.ordinal()].downstate); } /** * P_SetupPsprites * Called at start of level for each */ public void SetupPsprites () { int i; // remove all psprites for (i=0 ; i<NUMPSPRITES ; i++) psprites[i].state = null; // spawn the gun pendingweapon = readyweapon; BringUpWeapon (); } /** * P_PlayerThink */ public void PlayerThink (player_t player) { ticcmd_t cmd; weapontype_t newweapon; // fixme: do this in the cheat code if (flags(player.cheats , player_t.CF_NOCLIP)) player.mo.flags |= MF_NOCLIP; else player.mo.flags &= ~MF_NOCLIP; // chain saw run forward cmd = player.cmd; if (flags(player.mo.flags , MF_JUSTATTACKED)) { cmd.angleturn = 0; cmd.forwardmove = (0xc800/512); cmd.sidemove = 0; player.mo.flags &= ~MF_JUSTATTACKED; } if (player.playerstate == PST_DEAD) { player.DeathThink (); return; } // Move around. // Reactiontime is used to prevent movement // for a bit after a teleport. if (eval(player.mo.reactiontime)) player.mo.reactiontime--; else player.MovePlayer (); player.CalcHeight (); if (eval(player.mo.subsector.sector.special)) player.PlayerInSpecialSector (); // Check for weapon change. // A special event has no other buttons. if (flags(cmd.buttons , BT_SPECIAL)) cmd.buttons = 0; if (flags(cmd.buttons , BT_CHANGE)) { // The actual changing of the weapon is done // when the weapon psprite can do it // (read: not in the middle of an attack). // System.out.println("Weapon change detected, attempting to perform"); newweapon = weapontype_t.values()[(cmd.buttons&BT_WEAPONMASK)>>BT_WEAPONSHIFT]; // If chainsaw is available, it won't change back to the fist // unless player also has berserk. if (newweapon == weapontype_t.wp_fist && player.weaponowned[weapontype_t.wp_chainsaw.ordinal()] && !(player.readyweapon == weapontype_t.wp_chainsaw && eval(player.powers[pw_strength]))) { newweapon = weapontype_t.wp_chainsaw; } // Will switch between SG and SSG in Doom 2. if ( DS.isCommercial() && newweapon == weapontype_t.wp_shotgun && player.weaponowned[weapontype_t.wp_supershotgun.ordinal()] && player.readyweapon != weapontype_t.wp_supershotgun) { newweapon = weapontype_t.wp_supershotgun; } if (player.weaponowned[newweapon.ordinal()] && newweapon != player.readyweapon) { // Do not go to plasma or BFG in shareware, // even if cheated. if ((newweapon != weapontype_t.wp_plasma && newweapon != weapontype_t.wp_bfg) || !DS.isShareware() ) { player.pendingweapon = newweapon; } } } // check for use if (flags(cmd.buttons , BT_USE)) { if (!player.usedown) { P.UseLines (player); player.usedown = true; } } else player.usedown = false; // cycle psprites player.MovePsprites (); // Counters, time dependent power ups. // Strength counts up to diminish fade. if (eval(player.powers[pw_strength])) player.powers[pw_strength]++; if (eval(player.powers[pw_invulnerability])) player.powers[pw_invulnerability]--; if (eval(player.powers[pw_invisibility])) if (! eval(--player.powers[pw_invisibility]) ) player.mo.flags &= ~MF_SHADOW; if (eval(player.powers[pw_infrared])) player.powers[pw_infrared]--; if (eval(player.powers[pw_ironfeet])) player.powers[pw_ironfeet]--; if (eval(player.damagecount)) player.damagecount--; if (eval(player.bonuscount)) player.bonuscount--; // Handling colormaps. if (eval(player.powers[pw_invulnerability])) { if (player.powers[pw_invulnerability] > 4*32 || flags(player.powers[pw_invulnerability],8) ) player.fixedcolormap = player_t.INVERSECOLORMAP; else player.fixedcolormap = 0; } else if (eval(player.powers[pw_infrared])) { if (player.powers[pw_infrared] > 4*32 || flags(player.powers[pw_infrared],8) ) { // almost full bright player.fixedcolormap = 1; } else player.fixedcolormap = 0; } else player.fixedcolormap = 0; } /** * G_PlayerReborn * Called after a player dies * almost everything is cleared and initialized * * */ public void PlayerReborn () { int i; int[] frags=new int [MAXPLAYERS]; int killcount; int itemcount; int secretcount; // System.arraycopy(players[player].frags, 0, frags, 0, frags.length); // We save the player's frags here... C2JUtils.memcpy (frags,this.frags,frags.length); killcount = this.killcount; itemcount = this.itemcount; secretcount = this.secretcount; //MAES: we need to simulate an erasure, possibly without making // a new object.memset (p, 0, sizeof(*p)); //players[player]=(player_t) player_t.nullplayer.clone(); // players[player]=new player_t(); this.reset(); // And we copy the old frags into the "new" player. C2JUtils.memcpy(this.frags, frags, this.frags.length); this.killcount = killcount; this.itemcount = itemcount; this.secretcount = secretcount; usedown = attackdown = true; // don't do anything immediately playerstate = PST_LIVE; health[0] = MAXHEALTH; readyweapon = pendingweapon = weapontype_t.wp_pistol; weaponowned[weapontype_t.wp_fist.ordinal()] = true; weaponowned[weapontype_t.wp_pistol.ordinal()] = true; ammo[ammotype_t.am_clip.ordinal()] = 50; lookdir = 0; // From Heretic for (i=0 ; i<NUMAMMO ; i++) this.maxammo[i] = DoomStatus.maxammo[i]; } /** Called by Actions ticker */ public void PlayerThink() { PlayerThink(this); } @Override public void updateStatus(DoomStatus DS) { this.DS=DS; this.DG=DS.DG; this.P=DS.P; this.R=DS.R; this.RND=DS.RND; this.I=DS.I; this.S=DS.S; } public String toString(){ sb.setLength(0); sb.append("player"); sb.append(" momx "); sb.append(this.mo.momx); sb.append(" momy "); sb.append(this.mo.momy); sb.append(" x "); sb.append(this.mo.x); sb.append(" y "); sb.append(this.mo.y); return sb.toString(); } private static StringBuilder sb=new StringBuilder(); public void read(DataInputStream f) throws IOException{ // Careful when loading/saving: // A player only carries a pointer to a mobj, which is "saved" // but later discarded at load time, at least in vanilla. In any case, // it has the size of a 32-bit integer, so make sure you skip it. // TODO: OK, so vanilla's monsters lost "state" when saved, including non-Doomguy // infighting. Did they "remember" Doomguy too? // ANSWER: they didn't. // The player is special in that it unambigously allows identifying // its own map object in an absolute way. Once we identify // at least one (e.g. object #45 is pointer 0x43545345) then, since // map objects are stored in a nice serialized order. this.p_mobj= DoomIO.readLEInt(f); // player mobj pointer this.playerstate=DoomIO.readLEInt(f); this.cmd.read(f); this.viewz=DoomIO.readLEInt(f); this.viewheight= DoomIO.readLEInt(f); this.deltaviewheight= DoomIO.readLEInt(f); this.bob=DoomIO.readLEInt(f); this.health[0]=DoomIO.readLEInt(f); this.armorpoints[0]=DoomIO.readLEInt(f); this.armortype=DoomIO.readLEInt(f); DoomIO.readIntArray(f,this.powers, ByteOrder.LITTLE_ENDIAN); DoomIO.readBooleanIntArray(f,this.cards); this.backpack=DoomIO.readIntBoolean(f); DoomIO.readIntArray(f,frags, ByteOrder.LITTLE_ENDIAN); this.readyweapon=weapontype_t.values()[DoomIO.readLEInt(f)]; this.pendingweapon=weapontype_t.values()[DoomIO.readLEInt(f)]; DoomIO.readBooleanIntArray(f,this.weaponowned); DoomIO.readIntArray(f,ammo,ByteOrder.LITTLE_ENDIAN); DoomIO.readIntArray(f,maxammo,ByteOrder.LITTLE_ENDIAN); // Read these as "int booleans" this.attackdown=DoomIO.readIntBoolean(f); this.usedown=DoomIO.readIntBoolean(f); this.cheats=DoomIO.readLEInt(f); this.refire=DoomIO.readLEInt(f); // For intermission stats. this.killcount=DoomIO.readLEInt(f); this.itemcount=DoomIO.readLEInt(f); this.secretcount=DoomIO.readLEInt(f); // Hint messages. f.skipBytes(4); // For screen flashing (red or bright). this.damagecount=DoomIO.readLEInt(f); this.bonuscount=DoomIO.readLEInt(f); // Who did damage (NULL for floors/ceilings). // TODO: must be properly denormalized before saving/loading f.skipBytes(4); // TODO: waste a read for attacker mobj. // So gun flashes light up areas. this.extralight=DoomIO.readLEInt(f); // Current PLAYPAL, ??? // can be set to REDCOLORMAP for pain, etc. this.fixedcolormap=DoomIO.readLEInt(f); this.colormap=DoomIO.readLEInt(f); // PSPDEF _is_ readable. for (pspdef_t p: this.psprites) p.read(f); this.didsecret=DoomIO.readIntBoolean(f); // Total size should be 280 bytes. } public void write(DataOutputStream f) throws IOException{ // It's much more convenient to pre-buffer, since // we'll be writing all Little Endian stuff. ByteBuffer b=ByteBuffer.allocate(280); this.pack(b); // Total size should be 280 bytes. // Write everything nicely and at once. f.write(b.array()); } // Used to disambiguate between objects public int p_mobj; @Override public void pack(ByteBuffer buf) throws IOException { ByteOrder bo=ByteOrder.LITTLE_ENDIAN; buf.order(bo); // The player is special in that it unambiguously allows identifying // its own map object in an absolute way. Once we identify // at least one (e.g. object #45 is pointer 0x43545345) then, since // map objects are stored in a nice serialized order by using // their next/prev pointers, you can reconstruct their // relationships a posteriori. // Store our own hashcode or "pointer" if you wish. buf.putInt(pointer(mo)); buf.putInt(playerstate); cmd.pack(buf); buf.putInt(viewz); buf.putInt(viewheight); buf.putInt(deltaviewheight); buf.putInt(bob); buf.putInt(health[0]); buf.putInt(armorpoints[0]); buf.putInt(armortype); DoomBuffer.putIntArray(buf,this.powers,this.powers.length,bo); DoomBuffer.putBooleanIntArray(buf,this.cards,this.cards.length, bo); DoomBuffer.putBooleanInt(buf,backpack,bo); DoomBuffer.putIntArray(buf,this.frags,this.frags.length,bo); buf.putInt(readyweapon.ordinal()); buf.putInt(pendingweapon.ordinal()); DoomBuffer.putBooleanIntArray(buf,this.weaponowned,this.weaponowned.length, bo); DoomBuffer.putIntArray(buf,this.ammo,this.ammo.length, bo); DoomBuffer.putIntArray(buf,this.maxammo,this.maxammo.length, bo); // Read these as "int booleans" DoomBuffer.putBooleanInt(buf,attackdown,bo); DoomBuffer.putBooleanInt(buf,usedown,bo); buf.putInt(cheats); buf.putInt(refire); // For intermission stats. buf.putInt(this.killcount); buf.putInt(this.itemcount); buf.putInt(this.secretcount); // Hint messages. buf.putInt(0); // For screen flashing (red or bright). buf.putInt(this.damagecount); buf.putInt(this.bonuscount); // Who did damage (NULL for floors/ceilings). // TODO: must be properly denormalized before saving/loading buf.putInt(pointer(attacker)); // So gun flashes light up areas. buf.putInt(this.extralight); // Current PLAYPAL, ??? // can be set to REDCOLORMAP for pain, etc. buf.putInt(this.fixedcolormap); buf.putInt(this.colormap); // PSPDEF _is_ readable. for (pspdef_t p: this.psprites) p.pack(buf); buf.putInt(this.didsecret?1:0); } } package doom; /** killough 8/29/98: threads of thinkers, for more efficient searches * cph 2002/01/13: for consistency with the main thinker list, keep objects * pending deletion on a class list too */ public enum th_class { th_delete, th_misc, th_friends, th_enemies, th_all; public static final int NUMTHCLASS=th_class.values().length; } package doom; import java.nio.ByteBuffer; import utils.C2JUtils; import w.DoomBuffer; public class doomdata_t implements IDatagramSerializable { public static final int DOOMDATALEN=8+data.Defines.BACKUPTICS*ticcmd_t.TICCMDLEN; // High bit is retransmit request. /** MAES: was "unsigned" */ public int checksum; /* CAREFUL!!! Those "bytes" are actually unsigned * */ /** Only valid if NCMD_RETRANSMIT. */ public byte retransmitfrom; public byte starttic; public byte player; public byte numtics; public ticcmd_t[] cmds; public doomdata_t(){ cmds=new ticcmd_t[data.Defines.BACKUPTICS]; C2JUtils.initArrayOfObjects(cmds); // Enough space for its own header + the ticcmds; buffer=new byte[DOOMDATALEN]; // This "pegs" the ByteBuffer to this particular array. // Separate updates are not necessary. bbuf=ByteBuffer.wrap(buffer); } // Used for datagram serialization. private byte[] buffer; private ByteBuffer bbuf; @Override public byte[] pack() { bbuf.rewind(); // Why making it harder? bbuf.putInt(checksum); bbuf.put(retransmitfrom); bbuf.put(starttic); bbuf.put(player); bbuf.put(numtics); // FIXME: it's probably more efficient to use System.arraycopy ? // Or are the packets too small anyway? At most we'll be sending "doomdata_t's" for (int i=0;i<cmds.length;i++){ bbuf.put(cmds[i].pack()); } return bbuf.array(); } @Override public void pack(byte[] buf, int offset) { // No need to make it harder...just pack it and slap it in. byte[] tmp=this.pack(); System.arraycopy(tmp, 0, buf, offset, tmp.length); } @Override public void unpack(byte[] buf) { unpack(buf,0); } @Override public void unpack(byte[] buf, int offset) { checksum=DoomBuffer.getBEInt(buf); offset=+4; retransmitfrom=buf[offset++]; starttic=buf[offset++]; player=buf[offset++]; numtics=buf[offset++]; for (int i=0;i<cmds.length;i++){ cmds[i].unpack(buf,offset); offset+=ticcmd_t.TICCMDLEN; } } public void selfUnpack(){ unpack(this.buffer); } public void copyFrom(doomdata_t source) { this.checksum=source.checksum; this.numtics=source.numtics; this.player=source.player; this.retransmitfrom=source.retransmitfrom; this.starttic=source.starttic; // MAES: this was buggy as hell, and didn't work at all, which // in turn prevented other subsystems such as speed throttling and // networking to work. // // This should be enough to alter the ByteBuffer too. //System.arraycopy(source.cached(), 0, this.buffer, 0, DOOMDATALEN); // This should set all fields //selfUnpack(); } @Override public byte[] cached(){ return this.buffer; } StringBuilder sb=new StringBuilder(); public String toString(){ sb.setLength(0); sb.append("doomdata_t "); sb.append(retransmitfrom); sb.append(" starttic "); sb.append(starttic); sb.append(" player "); sb.append(player); sb.append(" numtics "); sb.append(numtics); return sb.toString(); } } package doom; // Emacs style mode select -*- C++ -*- //----------------------------------------------------------------------------- // // $Id: net.java,v 1.5 2011/02/11 00:11:13 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. // // $Log: net.java,v $ // Revision 1.5 2011/02/11 00:11:13 velktron // A MUCH needed update to v1.3. // // Revision 1.1 2010/06/30 08:58:50 velktron // Let's see if this stuff will finally commit.... // // // Most stuff is still being worked on. For a good place to start and get an idea of what is being done, I suggest checking out the "testers" package. // // Revision 1.1 2010/06/29 11:07:34 velktron // Release often, release early they say... // // Commiting ALL stuff done so far. A lot of stuff is still broken/incomplete, and there's still mixed C code in there. I suggest you load everything up in Eclpise and see what gives from there. // // A good place to start is the testers/ directory, where you can get an idea of how a few of the implemented stuff works. // // // DESCRIPTION: // DOOM Network game communication and protocol, // all OS independend parts. // //----------------------------------------------------------------------------- //static const char rcsid[] = "$Id: net.java,v 1.5 2011/02/11 00:11:13 velktron Exp $"; //#include "m_menu.h" //#include "i_system.h" //#include "i_video.h" //#include "i_net.h" //#include "g_game.h" import static data.Defines.*; import data.doomstat.*; // //Network play related stuff. //There is a data struct that stores network //communication related stuff, and another //one that defines the actual packets to //be transmitted. // public class net{ protected static int NCMD_EXIT= 0x80000000; protected static int NCMD_RETRANSMIT =0x40000000; protected static int NCMD_SETUP =0x20000000; protected static int NCMD_KILL = 0x10000000; // kill game protected static int NCMD_CHECKSUM = 0x0fffffff; protected static int DOOMCOM_ID = 0x12345678; //Max computers/players in a game. protected static int MAXNETNODES = 8; //Networking and tick handling related. protected static int BACKUPTICS = 12; // commant_t protected static int CMD_SEND = 1; protected static int CMD_GET = 2; doomcom_t doomcom; doomdata_t netbuffer; // points inside doomcom // // 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 // public static int RESENDCOUNT =10; public static int PL_DRONE =0x80; // bit flag in doomdata->player 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; int ticdup; int maxsend; // BACKUPTICS/(2*ticdup)-1 //void D_ProcessEvents (void); //void G_BuildTiccmd (ticcmd_t *cmd); //void D_DoAdvanceDemo (void); boolean reboundpacket; doomdata_t reboundstore; // // //123 /** MAES: interesting. After testing it was found to return the following size: * */ int NetbufferSize () { // return (int)(((doomdata_t)0).cmds[netbuffer.numtics]); return (8*(netbuffer.numtics+1)); } } /* //1 // Checksum // unsigned NetbufferChecksum (void) { unsigned c; int i,l; c = 0x1234567; // FIXME -endianess? #ifdef NORMALUNIX return 0; // byte order problems #endif l = (NetbufferSize () - (int)&(((doomdata_t *)0)->retransmitfrom))/4; for (i=0 ; i<l ; i++) c += ((unsigned *)&netbuffer->retransmitfrom)[i] * (i+1); return c & NCMD_CHECKSUM; } // // // 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 %i at maketic %i",low,maketic); return 0; } // // HSendPacket // void HSendPacket (int node, int flags ) { netbuffer->checksum = NetbufferChecksum () | flags; if (!node) { reboundstore = *netbuffer; reboundpacket = true; return; } if (demoplayback) return; if (!netgame) I_Error ("Tried to transmit to another node"); doomcom->command = CMD_SEND; doomcom->remotenode = node; doomcom->datalength = NetbufferSize (); if (debugfile) { int i; int realretrans; if (netbuffer->checksum & NCMD_RETRANSMIT) realretrans = ExpandTics (netbuffer->retransmitfrom); else realretrans = -1; fprintf (debugfile,"send (%i + %i, R %i) [%i] ", ExpandTics(netbuffer->starttic), netbuffer->numtics, realretrans, doomcom->datalength); for (i=0 ; i<doomcom->datalength ; i++) fprintf (debugfile,"%i ",((byte *)netbuffer)[i]); fprintf (debugfile,"\n"); } I_NetCmd (); } // // HGetPacket // Returns false if no packet is waiting // boolean HGetPacket (void) { if (reboundpacket) { *netbuffer = reboundstore; doomcom->remotenode = 0; reboundpacket = false; return true; } if (!netgame) return false; if (demoplayback) return false; doomcom->command = CMD_GET; I_NetCmd (); if (doomcom->remotenode == -1) return false; if (doomcom->datalength != NetbufferSize ()) { if (debugfile) fprintf (debugfile,"bad packet length %i\n",doomcom->datalength); return false; } if (NetbufferChecksum () != (netbuffer->checksum&NCMD_CHECKSUM) ) { if (debugfile) fprintf (debugfile,"bad packet checksum\n"); return false; } if (debugfile) { int realretrans; int i; if (netbuffer->checksum & NCMD_SETUP) fprintf (debugfile,"setup packet\n"); else { if (netbuffer->checksum & NCMD_RETRANSMIT) realretrans = ExpandTics (netbuffer->retransmitfrom); else realretrans = -1; fprintf (debugfile,"get %i = (%i + %i, R %i)[%i] ", doomcom->remotenode, ExpandTics(netbuffer->starttic), netbuffer->numtics, realretrans, doomcom->datalength); for (i=0 ; i<doomcom->datalength ; i++) fprintf (debugfile,"%i ",((byte *)netbuffer)[i]); fprintf (debugfile,"\n"); } } return true; } // // GetPackets // char exitmsg[80]; void GetPackets (void) { int netconsole; int netnode; ticcmd_t *src, *dest; int realend; int realstart; while ( HGetPacket() ) { if (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 (netbuffer->checksum & NCMD_EXIT) { if (!nodeingame[netnode]) continue; nodeingame[netnode] = false; playeringame[netconsole] = false; strcpy (exitmsg, "Player 1 left the game"); exitmsg[7] += netconsole; players[consoleplayer].message = exitmsg; if (demorecording) G_CheckDemoStatus (); continue; } // check for a remote game kill if (netbuffer->checksum & NCMD_KILL) I_Error ("Killed by network driver"); nodeforplayer[netconsole] = netnode; // check for retransmit request if ( resendcount[netnode] <= 0 && (netbuffer->checksum & NCMD_RETRANSMIT) ) { resendto[netnode] = ExpandTics(netbuffer->retransmitfrom); if (debugfile) fprintf (debugfile,"retransmit from %i\n", resendto[netnode]); resendcount[netnode] = RESENDCOUNT; } else resendcount[netnode]--; // check for out of order / duplicated packet if (realend == nettics[netnode]) continue; if (realend < nettics[netnode]) { if (debugfile) fprintf (debugfile, "out of order packet (%i + %i)\n" , realstart,netbuffer->numtics); continue; } // check for a missed packet if (realstart > nettics[netnode]) { // stop processing until the other system resends the missed tics if (debugfile) fprintf (debugfile, "missed tics from %i (%i - %i)\n", netnode, realstart, nettics[netnode]); 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]++; *dest = *src; src++; } } } } // // NetUpdate // Builds ticcmds for console player, // sends out a packet // int gametime; void NetUpdate (void) { int nowtime; int newtics; int i,j; int realstart; int gameticdiv; // check time nowtime = I_GetTime ()/ticdup; newtics = nowtime - gametime; gametime = nowtime; if (newtics <= 0) // nothing new to update goto listen; if (skiptics <= newtics) { newtics -= skiptics; skiptics = 0; } else { skiptics -= newtics; newtics = 0; } netbuffer->player = consoleplayer; // build new ticcmds for console player gameticdiv = gametic/ticdup; for (i=0 ; i<newtics ; i++) { I_StartTic (); D_ProcessEvents (); if (maketic - gameticdiv >= BACKUPTICS/2-1) break; // can't hold any more //printf ("mk:%i ",maketic); G_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 = realstart = resendto[i]; netbuffer->numtics = maketic - realstart; if (netbuffer->numtics > BACKUPTICS) I_Error ("NetUpdate: netbuffer->numtics > BACKUPTICS"); resendto[i] = maketic - doomcom->extratics; for (j=0 ; j< netbuffer->numtics ; j++) netbuffer->cmds[j] = localcmds[(realstart+j)%BACKUPTICS]; if (remoteresend[i]) { netbuffer->retransmitfrom = nettics[i]; HSendPacket (i, NCMD_RETRANSMIT); } else { netbuffer->retransmitfrom = 0; HSendPacket (i, 0); } } // listen for other packets listen: GetPackets (); } // // CheckAbort // void CheckAbort (void) { event_t *ev; int stoptic; stoptic = I_GetTime () + 2; while (I_GetTime() < stoptic) I_StartTic (); I_StartTic (); for ( ; eventtail != eventhead ; eventtail = (++eventtail)&(MAXEVENTS-1) ) { ev = &events[eventtail]; if (ev->type == ev_keydown && ev->data1 == KEY_ESCAPE) I_Error ("Network game synchronization aborted."); } } // // D_ArbitrateNetStart // void D_ArbitrateNetStart (void) { int i; boolean gotinfo[MAXNETNODES]; autostart = true; memset (gotinfo,0,sizeof(gotinfo)); if (doomcom->consoleplayer) { // listen for setup info from key player printf ("listening for network start info...\n"); while (1) { CheckAbort (); if (!HGetPacket ()) continue; if (netbuffer->checksum & NCMD_SETUP) { if (netbuffer->player != VERSION) I_Error ("Different DOOM versions cannot play a net game!"); startskill = netbuffer->retransmitfrom & 15; deathmatch = (netbuffer->retransmitfrom & 0xc0) >> 6; 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 printf ("sending network start info...\n"); do { CheckAbort (); for (i=0 ; i<doomcom->numnodes ; i++) { netbuffer->retransmitfrom = startskill; if (deathmatch) netbuffer->retransmitfrom |= (deathmatch<<6); if (nomonsters) netbuffer->retransmitfrom |= 0x20; if (respawnparm) netbuffer->retransmitfrom |= 0x10; netbuffer->starttic = startepisode * 64 + startmap; netbuffer->player = VERSION; netbuffer->numtics = 0; HSendPacket (i, NCMD_SETUP); } #if 1 for(i = 10 ; i && HGetPacket(); --i) { if((netbuffer->player&0x7f) < MAXNETNODES) gotinfo[netbuffer->player&0x7f] = true; } #else while (HGetPacket ()) { gotinfo[netbuffer->player&0x7f] = true; } #endif 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 // extern int viewangleoffset; void D_CheckNetGame (void) { 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 I_InitNetwork (); if (doomcom->id != DOOMCOM_ID) I_Error ("Doomcom buffer invalid!"); netbuffer = &doomcom->data; consoleplayer = displayplayer = doomcom->consoleplayer; if (netgame) D_ArbitrateNetStart (); printf ("startskill %i deathmatch: %i startmap: %i startepisode: %i\n", startskill, deathmatch, startmap, startepisode); // read values out of doomcom ticdup = doomcom->ticdup; 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; printf ("player %i of %i (%i nodes)\n", consoleplayer+1, doomcom->numplayers, doomcom->numnodes); } // // D_QuitNetGame // Called before quitting to leave a net game // without hanging the other players // void D_QuitNetGame (void) { int i, j; if (debugfile) fclose (debugfile); if (!netgame || !usergame || consoleplayer == -1 || demoplayback) return; // send a bunch of packets for security netbuffer->player = 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[4]; int frameon; int frameskip[4]; int oldnettics; extern boolean advancedemo; public static void TryRunTics () { int i; int lowtic; int entertic; static int oldentertics; int realtics; int availabletics; int counts; int numplaying; // get real tics entertic = I_GetTime ()/ticdup; realtics = entertic - oldentertics; oldentertics = entertic; // 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 (debugfile) fprintf (debugfile, "=======real: %i avail: %i game: %i\n", realtics, availabletics,counts); 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--; // printf ("-"); } frameskip[frameon&3] = (oldnettics > nettics[nodeforplayer[i]]); oldnettics = nettics[0]; if (frameskip[0] && frameskip[1] && frameskip[2] && frameskip[3]) { skiptics = 1; // printf ("+"); } } }// demoplayback // wait for new tics if needed while (lowtic < gametic/ticdup + counts) { NetUpdate (); lowtic = MAXINT; 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 if (I_GetTime ()/ticdup - entertic >= 20) { M_Ticker (); return; } } // run the count * ticdup dics while (counts--) { for (i=0 ; i<ticdup ; i++) { if (gametic/ticdup > lowtic) I_Error ("gametic>lowtic"); if (advancedemo) D_DoAdvanceDemo (); M_Ticker (); G_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 (cmd->buttons & BT_SPECIAL) cmd->buttons = 0; } } } NetUpdate (); // check for new console commands } } }*/ package doom; public class doomcom_t { public doomcom_t(){ this.data=new doomdata_t(); } // Supposed to be DOOMCOM_ID? // Maes: was "long", but they intend 32-bit "int" here. Hurray for C's consistency! public int id; // DOOM executes an int to execute commands. public short intnum; // Communication between DOOM and the driver. // Is CMD_SEND or CMD_GET. public short command; // Is dest for send, set by get (-1 = no packet). public short remotenode; // Number of bytes in doomdata to be sent public short datalength; // Info common to all nodes. // Console is allways node 0. public short numnodes; // Flag: 1 = no duplication, 2-5 = dup for slow nets. public short ticdup; // Flag: 1 = send a backup tic in every packet. public short extratics; // Flag: 1 = deathmatch. public short deathmatch; // Flag: -1 = new game, 0-5 = load savegame public short savegame; public short episode; // 1-3 public short map; // 1-9 public short skill; // 1-5 // Info specific to this node. public short consoleplayer; public short numplayers; // These are related to the 3-display mode, // in which two drones looking left and right // were used to render two additional views // on two additional computers. // Probably not operational anymore. // 1 = left, 0 = center, -1 = right public short angleoffset; // 1 = drone public short drone; // The packet data to be sent. public doomdata_t data; } package doom; public class pic_t { public pic_t(byte width, byte height, byte data) { super(); this.width = width; this.height = height; this.data = data; } public byte width; public byte height; public byte data; } package doom; /** The defined weapons, * including a marker indicating * user has not changed weapon. */ public enum weapontype_t { wp_fist, wp_pistol, wp_shotgun, wp_chaingun, wp_missile, wp_plasma, wp_bfg, wp_chainsaw, wp_supershotgun, NUMWEAPONS, // No pending weapon change. wp_nochange; public String toString(){ return this.name(); } } package doom; import static utils.C2JUtils.eval; import java.io.BufferedInputStream; import java.io.DataInputStream; import java.io.FileInputStream; import java.util.ArrayList; import java.util.HashMap; import java.util.StringTokenizer; import w.DoomIO; /** * A class to handle the command-line args in an unified manner, and without * them being tied to DoomMain or DoomStatus. * * * @author velktron * */ public class CommandLine implements ICommandLineManager { /* What bullshit, those are supposed to carry over from the very first main */ protected int myargc; protected String[] myargv; public CommandLine(String[] argv){ // Bump argcount up by 1 to maintain CheckParm behavior // In C, argument 0 is the executable itself, and most // of Doom's code is tailored to reflect that, with 0 // meaning "not found" with regards to a parameter. // It's easier to change this here. // TODO: this is really bad practice, eliminate once a // cvar system is in place. myargv=new String[argv.length+1]; System.arraycopy(argv, 0, myargv, 1, argv.length); myargc=argv.length+1; cvars=new HashMap<String, Object>(); } /* (non-Javadoc) * @see doom.ICommandLineManager#getArgv(int) */ @Override public String getArgv(int index){ if (index>myargc) return null; else return myargv[index]; } /* (non-Javadoc) * @see doom.ICommandLineManager#getArgc() */ @Override public int getArgc(){ return myargc; } /* (non-Javadoc) * @see doom.ICommandLineManager#CheckParm(java.lang.String) */ @Override public int CheckParm(String check) { int i; for (i = 1; i < myargc; i++) { if (check.compareToIgnoreCase(myargv[i]) == 0) return i; } return 0; } @Override public boolean CheckParmBool(String check) { int i; for (i = 1; i < myargc; i++) { if (check.compareToIgnoreCase(myargv[i]) == 0) return true; } return false; } /** * Find a Response File * * Not very well documented, but Doom apparently could use a sort of * script file with command line arguments inside, if you prepend @ to * the command-like argument itself. The arguments themselves could * be separated by any sort of whitespace or ASCII characters exceeding "z" * in value. * * E.g. doom @crap * * would load a file named "crap". * * Now, the original function is crap for several reasons: for one, * it will bomb if more than 100 arguments <i>total</i> are formed. * Memory allocation will also fail because the tokenizer used only * stops at file size limit, not at maximum parsed arguments limit * (MACARGVS = 100). * * This is the wiki's entry: * * doom @<response> * This parameter tells the Doom engine to read from a response file, * a text file that may store additional command line parameters. * The file may have any name that is valid to the system, optionally * with an extension. The parameters are typed as in the command line * (-episode 2, for example), but one per line, where up to 100 lines * may be used. The additional parameters may be disabled for later * use by placing a vertical bar (the | character) between the * prefixing dash (-) and the rest of the parameter name. * * */ @Override public void FindResponseFile () { try{ for (int i = 1;i < getArgc();i++) if (getArgv(i).charAt(0)=='@') { DataInputStream handle; // save o int size; int indexinfile; char[] infile=null; char[] file=null; // Fuck that, we're doing it properly. ArrayList<String> parsedargs=new ArrayList<String>(); ArrayList<String> moreargs=new ArrayList<String>(); String firstargv; // READ THE RESPONSE FILE INTO MEMORY handle = new DataInputStream(new BufferedInputStream(new FileInputStream(myargv[i].substring(1)))); if (!eval(handle)) { System.out.print ("\nNo such response file!"); System.exit(1); } System.out.println("Found response file "+myargv[i].substring(1)); size = (int) handle.available(); file=new char[size]; DoomIO.readNonUnicodeCharArray(handle, file,size); handle.close(); // Save first argument. firstargv = myargv[0]; // KEEP ALL CMDLINE ARGS FOLLOWING @RESPONSEFILE ARG // This saves the old references. for (int k = i+1; k < myargc; k++) moreargs.add(myargv[k]); infile = file; indexinfile = 0; indexinfile++; // SKIP PAST ARGV[0] (KEEP IT) // HMM? StringBuffer build=new StringBuffer(); /* MAES: the code here looked like some primitive tokenizer. that assigned C-strings to memory locations. Instead, we'll tokenize the file input correctly here. */ StringTokenizer tk=new StringTokenizer(String.copyValueOf(infile)); //myargv = new String[tk.countTokens()+argc]; parsedargs.add(firstargv); while(tk.hasMoreTokens()) { parsedargs.add(tk.nextToken()); } // Append the other args to the end. parsedargs.addAll(moreargs); /* NOW the original myargv is reset, but the old values still survive in * the listarray.*/ myargv= new String[parsedargs.size()]; myargv=parsedargs.toArray(myargv); myargc = myargv.length; // DISPLAY ARGS System.out.println(myargc+" command-line args:"); for (int k=0;k<myargc;k++) System.out.println(myargv[k]); // Stops at the first one. Pity, because we could do funky recursive stuff with that :-p break; } } catch (Exception e){ e.printStackTrace(); } } @Override public void setArgv(int index, String string) { this.myargv[index]=string; } private HashMap<String, Object> cvars; public boolean cvarExists(String name){ return cvars.containsKey(name); } public Object removeCvar(String name){ return cvars.remove(name); } public void putCvar(String name, int value){ cvars.put(name, value); } public void putCvar(String name, double value){ cvars.put(name, value); } public void putCvar(String name, String value){ cvars.put(name, value); } public void putCvar(String name, boolean value){ cvars.put(name, value); } public void putCvar(String name, String[] value){ cvars.put(name, value); } public Integer getInt(String name){ Object stuff=cvars.get(name); if (stuff !=null){ if (stuff instanceof Integer){ return (Integer) stuff; } } return null; } public Double getFloat(String name){ Object stuff=cvars.get(name); if (stuff !=null){ if (stuff instanceof Double){ return (Double) stuff; } } return null; } public String getString(String name){ Object stuff=cvars.get(name); if (stuff !=null){ if (stuff instanceof String){ return (String) stuff; } } return null; } public String[] getStringArray(String name){ Object stuff=cvars.get(name); if (stuff !=null){ if (stuff instanceof String[]){ return (String[]) stuff; } } return null; } public Boolean getBoolean(String name){ Object stuff=cvars.get(name); if (stuff !=null){ if (stuff instanceof Boolean){ return (Boolean) stuff; } } return null; } public <K> void setCvar(String name, K value){ if (cvars.containsKey(name)){ cvars.put(name, value); } } public static String checkParameterCouple(String check, String[] myargv) { int found=-1; for (int i = 0; i < myargv.length; i++) { if (check.compareToIgnoreCase(myargv[i]) == 0){ found=i; // found. Break on first. break; } } // Found, and there's room to spare for one more? if ((found>=0)&&(found<myargv.length-1)){ if (myargv[found+1]!=null){ // Not null, not empty and not a parameter switch if ((myargv[found+1].length()>=1) && (myargv[found+1].charAt(0)!='-')) return myargv[found+1]; } } // Well duh. return null; } /** Is a parameter based on an prefix identifier e.g. '-' * * @param what * @param identifier * @return */ public static boolean isParameter(String what, char identifier){ if (what!=null && what.length()>-0){ return (what.charAt(0)!=identifier); } return false; } public static int parameterMultipleValues(String check, String[] myargv) { int found=-1; // It's not even a valid parameter name if (!isParameter(check,'-')) return -1; // Does it exist? if ((found=checkParm(check,myargv))==-1) return found; // Found, and there are still some to spare int rest=myargv.length-found-1; int count=0; for (int i=found+1;i<myargv.length;i++){ if (isParameter(myargv[i],'-')) break; else count++; // not a parameter, count up } // Well duh. return count; } public static int checkParm(String check, String[] params) { int i; for (i = 0; i < params.length; i++) { if (check.compareToIgnoreCase(params[i]) == 0) return i; } return -1; } } package doom; import java.io.DataInputStream; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import static utils.C2JUtils.pointer; import w.CacheableDoomObject; import w.IPackableDoomObject; import w.IReadableDoomObject; import p.ActionType1; import p.ActionType2; import p.ActionTypeSS; public class thinker_t implements CacheableDoomObject,IReadableDoomObject,IPackableDoomObject{ public thinker_t prev; public thinker_t next; public think_t function; /* killough 8/29/98: we maintain thinkers in several equivalence classes, * according to various criteria, so as to allow quicker searches. */ /** Next, previous thinkers in same class */ public thinker_t cnext, cprev; // Thinkers can either have one parameter of type (mobj_t), // Or otherwise be sector specials, flickering lights etc. // Those are atypical and need special handling. public ActionType1 acp1; public ActionType2 acp2; public ActionTypeSS acpss; /** extra fields, to use when archiving/unarchiving for * identification. Also in blocklinks, etc. */ public int id,previd, nextid,functionid; @Override public void read(DataInputStream f) throws IOException { readbuffer.position(0); readbuffer.order(ByteOrder.LITTLE_ENDIAN); f.read(readbuffer.array()); unpack(readbuffer); } /** This adds 12 bytes */ @Override public void pack(ByteBuffer b) throws IOException { // It's possible to reconstruct even by hashcodes. // As for the function, that should be implied by the mobj_t type. b.order(ByteOrder.LITTLE_ENDIAN); b.putInt(pointer(prev)); b.putInt(pointer(next)); b.putInt(pointer(function)); //System.out.printf("Packed thinker %d %d %d\n",pointer(prev),pointer(next),pointer(function)); } @Override public void unpack(ByteBuffer b) throws IOException { // We are supposed to archive pointers to other thinkers, // but they are rather useless once on disk. b.order(ByteOrder.LITTLE_ENDIAN); previd=b.getInt(); nextid=b.getInt(); functionid=b.getInt(); //System.out.printf("Unpacked thinker %d %d %d\n",pointer(previd),pointer(nextid),pointer(functionid)); } private static ByteBuffer readbuffer=ByteBuffer.allocate(12); } package doom; // Emacs style mode select -*- C++ -*- //----------------------------------------------------------------------------- // // $Id: items.java,v 1.3 2010/12/20 17:15:08 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. // // $Log: items.java,v $ // Revision 1.3 2010/12/20 17:15:08 velktron // Made the renderer more OO -> TextureManager and other changes as well. // // Revision 1.2 2010/08/19 23:14:49 velktron // Automap // // Revision 1.1 2010/06/30 08:58:50 velktron // Let's see if this stuff will finally commit.... // // // Most stuff is still being worked on. For a good place to start and get an idea of what is being done, I suggest checking out the "testers" package. // // Revision 1.1 2010/06/29 11:07:34 velktron // Release often, release early they say... // // Commiting ALL stuff done so far. A lot of stuff is still broken/incomplete, and there's still mixed C code in there. I suggest you load everything up in Eclpise and see what gives from there. // // A good place to start is the testers/ directory, where you can get an idea of how a few of the implemented stuff works. // // // DESCRIPTION: // //----------------------------------------------------------------------------- import defines.*; public class items{ public static weaponinfo_t[] weaponinfo= { new weaponinfo_t( // fist ammotype_t.am_noammo, statenum_t.S_PUNCHUP, statenum_t.S_PUNCHDOWN, statenum_t.S_PUNCH, statenum_t.S_PUNCH1, statenum_t.S_NULL ), new weaponinfo_t( // pistol ammotype_t.am_clip, statenum_t.S_PISTOLUP, statenum_t.S_PISTOLDOWN, statenum_t.S_PISTOL, statenum_t.S_PISTOL1, statenum_t.S_PISTOLFLASH ), new weaponinfo_t( // shotgun ammotype_t.am_shell, statenum_t.S_SGUNUP, statenum_t.S_SGUNDOWN, statenum_t.S_SGUN, statenum_t.S_SGUN1, statenum_t.S_SGUNFLASH1 ), new weaponinfo_t( // chaingun ammotype_t.am_clip, statenum_t.S_CHAINUP, statenum_t.S_CHAINDOWN, statenum_t.S_CHAIN, statenum_t.S_CHAIN1, statenum_t.S_CHAINFLASH1 ), new weaponinfo_t( // missile launcher ammotype_t.am_misl, statenum_t.S_MISSILEUP, statenum_t.S_MISSILEDOWN, statenum_t.S_MISSILE, statenum_t.S_MISSILE1, statenum_t.S_MISSILEFLASH1 ), new weaponinfo_t( // plasma rifle ammotype_t.am_cell, statenum_t.S_PLASMAUP, statenum_t.S_PLASMADOWN, statenum_t.S_PLASMA, statenum_t.S_PLASMA1, statenum_t.S_PLASMAFLASH1 ), new weaponinfo_t( // bfg 9000 ammotype_t.am_cell, statenum_t.S_BFGUP, statenum_t.S_BFGDOWN, statenum_t.S_BFG, statenum_t.S_BFG1, statenum_t.S_BFGFLASH1 ), new weaponinfo_t( // chainsaw ammotype_t.am_noammo, statenum_t.S_SAWUP, statenum_t.S_SAWDOWN, statenum_t.S_SAW, statenum_t.S_SAW1, statenum_t.S_NULL ), new weaponinfo_t( // super shotgun ammotype_t.am_shell, statenum_t.S_DSGUNUP, statenum_t.S_DSGUNDOWN, statenum_t.S_DSGUN, statenum_t.S_DSGUN1, statenum_t.S_DSGUNFLASH1 ) }; } package doom; public enum gameaction_t { ga_nothing, ga_loadlevel, ga_newgame, ga_loadgame, ga_savegame, ga_playdemo, ga_completed, ga_victory, ga_worlddone, ga_screenshot, ga_failure // HACK: communicate failures silently } package doom; // Emacs style mode select -*- C++ -*- //----------------------------------------------------------------------------- // // $Id: englsh.java,v 1.5 2011/05/31 21:46:20 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: // Printed strings for translation. // English language support (default). // //----------------------------------------------------------------------------- // // Printed strings for translation // // // D_Main.C // public class englsh{ public final static String D_DEVSTR="Development mode ON.\n"; public final static String D_CDROM="CD-ROM Version: default.cfg from c:\\doomdata\n"; // // M_Menu.C // public final static String PRESSKEY ="press a key."; public final static String PRESSYN ="press y or n."; public final static String QUITMSG="are you sure you want to\nquit this great game?"; public final static String LOADNET ="you can't do load while in a net game!\n\n"+PRESSKEY; public final static String QLOADNET="you can't quickload during a netgame!\n\n"+PRESSKEY; public final static String QSAVESPOT="you haven't picked a quicksave slot yet!\n\n"+PRESSKEY; public final static String SAVEDEAD ="you can't save if you aren't playing!\n\n"+PRESSKEY; public final static String QSPROMPT ="quicksave over your game named\n\n'%s'?\n\n"+PRESSYN; public final static String QLPROMPT="do you want to quickload the game named\n\n'%s'?\n\n"+PRESSYN; public final static String NEWGAME = "you can't start a new game\nwhile in a network game.\n\n"+PRESSKEY; public final static String NIGHTMARE ="are you sure? this skill level\nisn't even remotely fair.\n\n"+PRESSYN; public final static String SWSTRING = "this is the shareware version of doom.\n\nyou need to order the entire trilogy.\n\n"+PRESSKEY; public final static String MSGOFF="Messages OFF"; public final static String MSGON ="Messages ON"; public final static String NETEND="you can't end a netgame!\n\n"+PRESSKEY; public final static String ENDGAME="are you sure you want to end the game?\n\n"+PRESSYN; public final static String DOSY ="(press y to quit)"; public final static String DETAILHI="High detail"; public final static String DETAILLO="Low detail"; public final static String GAMMALVL0="Gamma correction OFF"; public final static String GAMMALVL1="Gamma correction level 1"; public final static String GAMMALVL2="Gamma correction level 2"; public final static String GAMMALVL3="Gamma correction level 3"; public final static String GAMMALVL4="Gamma correction level 4"; public final static String EMPTYSTRING="empty slot"; // // P_inter.C // public final static String GOTARMOR="Picked up the armor."; public final static String GOTMEGA="Picked up the MegaArmor!"; public final static String GOTHTHBONUS="Picked up a health bonus."; public final static String GOTARMBONUS="Picked up an armor bonus."; public final static String GOTSTIM="Picked up a stimpack."; public final static String GOTMEDINEED="Picked up a medikit that you REALLY need!"; public final static String GOTMEDIKIT="Picked up a medikit."; public final static String GOTSUPER="Supercharge!"; public final static String GOTBLUECARD="Picked up a blue keycard."; public final static String GOTYELWCARD="Picked up a yellow keycard."; public final static String GOTREDCARD="Picked up a red keycard."; public final static String GOTBLUESKUL="Picked up a blue skull key."; public final static String GOTYELWSKUL="Picked up a yellow skull key."; public final static String GOTREDSKULL="Picked up a red skull key."; public final static String GOTINVUL="Invulnerability!"; public final static String GOTBERSERK="Berserk!"; public final static String GOTINVIS="Partial Invisibility"; public final static String GOTSUIT="Radiation Shielding Suit"; public final static String GOTMAP="Computer Area Map"; public final static String GOTVISOR="Light Amplification Visor"; public final static String GOTMSPHERE="MegaSphere!"; public final static String GOTCLIP="Picked up a clip."; public final static String GOTCLIPBOX="Picked up a box of bullets."; public final static String GOTROCKET="Picked up a rocket."; public final static String GOTROCKBOX="Picked up a box of rockets."; public final static String GOTCELL="Picked up an energy cell."; public final static String GOTCELLBOX="Picked up an energy cell pack."; public final static String GOTSHELLS="Picked up 4 shotgun shells."; public final static String GOTSHELLBOX="Picked up a box of shotgun shells."; public final static String GOTBACKPACK="Picked up a backpack full of ammo!"; public final static String GOTBFG9000="You got the BFG9000! Oh, yes."; public final static String GOTCHAINGUN="You got the chaingun!"; public final static String GOTCHAINSAW="A chainsaw! Find some meat!"; public final static String GOTLAUNCHER="You got the rocket launcher!"; public final static String GOTPLASMA="You got the plasma gun!"; public final static String GOTSHOTGUN="You got the shotgun!"; public final static String GOTSHOTGUN2="You got the super shotgun!"; // // P_Doors.C // public final static String PD_BLUEO="You need a blue key to activate this object"; public final static String PD_REDO="You need a red key to activate this object"; public final static String PD_YELLOWO="You need a yellow key to activate this object"; public final static String PD_BLUEK="You need a blue key to open this door"; public final static String PD_REDK="You need a red key to open this door"; public final static String PD_YELLOWK="You need a yellow key to open this door"; // // G_game.C // public final static String GGSAVED="game saved."; // // HU_stuff.C // public final static String HUSTR_MSGU="[Message unsent]"; public final static String HUSTR_E1M1="E1M1: Hangar"; public final static String HUSTR_E1M2="E1M2: Nuclear Plant"; public final static String HUSTR_E1M3="E1M3: Toxin Refinery"; public final static String HUSTR_E1M4="E1M4: Command Control"; public final static String HUSTR_E1M5="E1M5: Phobos Lab"; public final static String HUSTR_E1M6="E1M6: Central Processing"; public final static String HUSTR_E1M7="E1M7: Computer Station"; public final static String HUSTR_E1M8="E1M8: Phobos Anomaly"; public final static String HUSTR_E1M9="E1M9: Military Base"; public final static String HUSTR_E2M1="E2M1: Deimos Anomaly"; public final static String HUSTR_E2M2="E2M2: Containment Area"; public final static String HUSTR_E2M3="E2M3: Refinery"; public final static String HUSTR_E2M4="E2M4: Deimos Lab"; public final static String HUSTR_E2M5="E2M5: Command Center"; public final static String HUSTR_E2M6="E2M6: Halls of the Damned"; public final static String HUSTR_E2M7="E2M7: Spawning Vats"; public final static String HUSTR_E2M8="E2M8: Tower of Babel"; public final static String HUSTR_E2M9="E2M9: Fortress of Mystery"; public final static String HUSTR_E3M1="E3M1: Hell Keep"; public final static String HUSTR_E3M2="E3M2: Slough of Despair"; public final static String HUSTR_E3M3="E3M3: Pandemonium"; public final static String HUSTR_E3M4="E3M4: House of Pain"; public final static String HUSTR_E3M5="E3M5: Unholy Cathedral"; public final static String HUSTR_E3M6="E3M6: Mt. Erebus"; public final static String HUSTR_E3M7="E3M7: Limbo"; public final static String HUSTR_E3M8="E3M8: Dis"; public final static String HUSTR_E3M9="E3M9: Warrens"; public final static String HUSTR_E4M1="E4M1: Hell Beneath"; public final static String HUSTR_E4M2="E4M2: Perfect Hatred"; public final static String HUSTR_E4M3="E4M3: Sever The Wicked"; public final static String HUSTR_E4M4="E4M4: Unruly Evil"; public final static String HUSTR_E4M5="E4M5: They Will Repent"; public final static String HUSTR_E4M6="E4M6: Against Thee Wickedly"; public final static String HUSTR_E4M7="E4M7: And Hell Followed"; public final static String HUSTR_E4M8="E4M8: Unto The Cruel"; public final static String HUSTR_E4M9="E4M9: Fear"; public final static String HUSTR_1="level 1: entryway"; public final static String HUSTR_2="level 2: underhalls"; public final static String HUSTR_3="level 3: the gantlet"; public final static String HUSTR_4="level 4: the focus"; public final static String HUSTR_5="level 5: the waste tunnels"; public final static String HUSTR_6="level 6: the crusher"; public final static String HUSTR_7="level 7: dead simple"; public final static String HUSTR_8="level 8: tricks and traps"; public final static String HUSTR_9="level 9: the pit"; public final static String HUSTR_10="level 10: refueling base"; public final static String HUSTR_11="level 11: 'o' of destruction!"; public final static String HUSTR_12="level 12: the factory"; public final static String HUSTR_13="level 13: downtown"; public final static String HUSTR_14="level 14: the inmost dens"; public final static String HUSTR_15="level 15: industrial zone"; public final static String HUSTR_16="level 16: suburbs"; public final static String HUSTR_17="level 17: tenements"; public final static String HUSTR_18="level 18: the courtyard"; public final static String HUSTR_19="level 19: the citadel"; public final static String HUSTR_20="level 20: gotcha!"; public final static String HUSTR_21="level 21: nirvana"; public final static String HUSTR_22="level 22: the catacombs"; public final static String HUSTR_23="level 23: barrels o' fun"; public final static String HUSTR_24="level 24: the chasm"; public final static String HUSTR_25="level 25: bloodfalls"; public final static String HUSTR_26="level 26: the abandoned mines"; public final static String HUSTR_27="level 27: monster condo"; public final static String HUSTR_28="level 28: the spirit world"; public final static String HUSTR_29="level 29: the living end"; public final static String HUSTR_30="level 30: icon of sin"; public final static String HUSTR_31="level 31: wolfenstein"; public final static String HUSTR_32="level 32: grosse"; public final static String HUSTR_33="level 33: betray"; public final static String PHUSTR_1="level 1: congo"; public final static String PHUSTR_2="level 2: well of souls"; public final static String PHUSTR_3="level 3: aztec"; public final static String PHUSTR_4="level 4: caged"; public final static String PHUSTR_5="level 5: ghost town"; public final static String PHUSTR_6="level 6: baron's lair"; public final static String PHUSTR_7="level 7: caughtyard"; public final static String PHUSTR_8="level 8: realm"; public final static String PHUSTR_9="level 9: abattoire"; public final static String PHUSTR_10="level 10: onslaught"; public final static String PHUSTR_11="level 11: hunted"; public final static String PHUSTR_12="level 12: speed"; public final static String PHUSTR_13="level 13: the crypt"; public final static String PHUSTR_14="level 14: genesis"; public final static String PHUSTR_15="level 15: the twilight"; public final static String PHUSTR_16="level 16: the omen"; public final static String PHUSTR_17="level 17: compound"; public final static String PHUSTR_18="level 18: neurosphere"; public final static String PHUSTR_19="level 19: nme"; public final static String PHUSTR_20="level 20: the death domain"; public final static String PHUSTR_21="level 21: slayer"; public final static String PHUSTR_22="level 22: impossible mission"; public final static String PHUSTR_23="level 23: tombstone"; public final static String PHUSTR_24="level 24: the final frontier"; public final static String PHUSTR_25="level 25: the temple of darkness"; public final static String PHUSTR_26="level 26: bunker"; public final static String PHUSTR_27="level 27: anti-christ"; public final static String PHUSTR_28="level 28: the sewers"; public final static String PHUSTR_29="level 29: odyssey of noises"; public final static String PHUSTR_30="level 30: the gateway of hell"; public final static String PHUSTR_31="level 31: cyberden"; public final static String PHUSTR_32="level 32: go 2 it"; public final static String THUSTR_1="level 1: system control"; public final static String THUSTR_2="level 2: human bbq"; public final static String THUSTR_3="level 3: power control"; public final static String THUSTR_4="level 4: wormhole"; public final static String THUSTR_5="level 5: hanger"; public final static String THUSTR_6="level 6: open season"; public final static String THUSTR_7="level 7: prison"; public final static String THUSTR_8="level 8: metal"; public final static String THUSTR_9="level 9: stronghold"; public final static String THUSTR_10="level 10: redemption"; public final static String THUSTR_11="level 11: storage facility"; public final static String THUSTR_12="level 12: crater"; public final static String THUSTR_13="level 13: nukage processing"; public final static String THUSTR_14="level 14: steel works"; public final static String THUSTR_15="level 15: dead zone"; public final static String THUSTR_16="level 16: deepest reaches"; public final static String THUSTR_17="level 17: processing area"; public final static String THUSTR_18="level 18: mill"; public final static String THUSTR_19="level 19: shipping/respawning"; public final static String THUSTR_20="level 20: central processing"; public final static String THUSTR_21="level 21: administration center"; public final static String THUSTR_22="level 22: habitat"; public final static String THUSTR_23="level 23: lunar mining project"; public final static String THUSTR_24="level 24: quarry"; public final static String THUSTR_25="level 25: baron's den"; public final static String THUSTR_26="level 26: ballistyx"; public final static String THUSTR_27="level 27: mount pain"; public final static String THUSTR_28="level 28: heck"; public final static String THUSTR_29="level 29: river styx"; public final static String THUSTR_30="level 30: last call"; public final static String THUSTR_31="level 31: pharaoh"; public final static String THUSTR_32="level 32: caribbean"; public final static String HUSTR_CHATMACRO1="I'm ready to kick butt!"; public final static String HUSTR_CHATMACRO2="I'm OK."; public final static String HUSTR_CHATMACRO3="I'm not looking too good!"; public final static String HUSTR_CHATMACRO4="Help!"; public final static String HUSTR_CHATMACRO5="You suck!"; public final static String HUSTR_CHATMACRO6="Next time, scumbag..."; public final static String HUSTR_CHATMACRO7="Come here!"; public final static String HUSTR_CHATMACRO8="I'll take care of it."; public final static String HUSTR_CHATMACRO9="Yes"; public final static String HUSTR_CHATMACRO0="No"; public final static String HUSTR_TALKTOSELF1="You mumble to yourself"; public final static String HUSTR_TALKTOSELF2="Who's there?"; public final static String HUSTR_TALKTOSELF3="You scare yourself"; public final static String HUSTR_TALKTOSELF4="You start to rave"; public final static String HUSTR_TALKTOSELF5="You've lost it..."; public final static String HUSTR_MESSAGESENT="[Message Sent]"; // The following should NOT be changed unless it seems // just AWFULLY necessary public final static String HUSTR_PLRGREEN="Green: "; public final static String HUSTR_PLRINDIGO="Indigo: "; public final static String HUSTR_PLRBROWN="Brown: "; public final static String HUSTR_PLRRED ="Red: "; public final static char HUSTR_KEYGREEN = 'g'; public final static char HUSTR_KEYINDIGO ='i'; public final static char HUSTR_KEYBROWN = 'b'; public final static char HUSTR_KEYRED = 'r'; // // AM_map.C // public final static String AMSTR_FOLLOWON="Follow Mode ON"; public final static String AMSTR_FOLLOWOFF="Follow Mode OFF"; public final static String AMSTR_GRIDON="Grid ON"; public final static String AMSTR_GRIDOFF="Grid OFF"; public final static String AMSTR_MARKEDSPOT="Marked Spot"; public final static String AMSTR_MARKSCLEARED="All Marks Cleared"; // // ST_stuff.C // public final static String STSTR_MUS ="Music Change"; public final static String STSTR_NOMUS ="IMPOSSIBLE SELECTION"; public final static String STSTR_DQDON ="Degreelessness Mode On"; public final static String STSTR_DQDOFF="Degreelessness Mode Off"; public final static String STSTR_KFAADDED="Very Happy Ammo Added"; public final static String STSTR_FAADDED="Ammo (no keys) Added"; public final static String STSTR_NCON ="No Clipping Mode ON"; public final static String STSTR_NCOFF ="No Clipping Mode OFF"; public final static String STSTR_BEHOLD="inVuln, Str, Inviso, Rad, Allmap, or Lite-amp"; public final static String STSTR_BEHOLDX="Power-up Toggled"; public final static String STSTR_CHOPPERS="... doesn't suck - GM"; public final static String STSTR_CLEV ="Changing Level..."; // // F_Finale.C // public final static String E1TEXT =( "Once you beat the big badasses and\n"+ "clean out the moon base you're supposed\n"+ "to win, aren't you? Aren't you? Where's\n"+ "your fat reward and ticket home? What\n"+ "the hell is this? It's not supposed to\n"+ "end this way!\n"+ "\n" + "It stinks like rotten meat, but looks\n"+ "like the lost Deimos base. Looks like\n"+ "you're stuck on The Shores of Hell.\n"+ "The only way out is through.\n"+ "\n"+ "To continue the DOOM experience, play\n"+ "The Shores of Hell and its amazing\n"+ "sequel, Inferno!\n"); public final static String E2TEXT =( "You've done it! The hideous cyber-\n"+ "demon lord that ruled the lost Deimos\n"+ "moon base has been slain and you\n"+ "are triumphant! But ... where are\n"+ "you? You clamber to the edge of the\n"+ "moon and look down to see the awful\n"+ "truth.\n" + "\n"+ "Deimos floats above Hell itself!\n"+ "You've never heard of anyone escaping\n"+ "from Hell, but you'll make the bastards\n"+ "sorry they ever heard of you! Quickly,\n"+ "you rappel down to the surface of\n"+ "Hell.\n"+ "\n" + "Now, it's on to the final chapter of\n"+ "DOOM! -- Inferno."); public final static String E3TEXT =( "The loathsome spiderdemon that\n"+ "masterminded the invasion of the moon\n"+ "bases and caused so much death has had\n"+ "its ass kicked for all time.\n"+ "\n"+ "A hidden doorway opens and you enter.\n"+ "You've proven too tough for Hell to\n"+ "contain, and now Hell at last plays\n"+ "fair -- for you emerge from the door\n"+ "to see the green fields of Earth!\n"+ "Home at last.\n" + "\n"+ "You wonder what's been happening on\n"+ "Earth while you were battling evil\n"+ "unleashed. It's good that no Hell-\n"+ "spawn could have come through that\n"+ "door with you ..."); public final static String E4TEXT =( "the spider mastermind must have sent forth\n"+ "its legions of hellspawn before your\n"+ "final confrontation with that terrible\n"+ "beast from hell. but you stepped forward\n"+ "and brought forth eternal damnation and\n"+ "suffering upon the horde as a true hero\n"+ "would in the face of something so evil.\n"+ "\n"+ "besides, someone was gonna pay for what\n"+ "happened to daisy, your pet rabbit.\n"+ "\n"+ "but now, you see spread before you more\n"+ "potential pain and gibbitude as a nation\n"+ "of demons run amok among our cities.\n"+ "\n"+ "next stop, hell on earth!"); // after level 6, put this: public final static String C1TEXT =( "YOU HAVE ENTERED DEEPLY INTO THE INFESTED\n" + "STARPORT. BUT SOMETHING IS WRONG. THE\n" + "MONSTERS HAVE BROUGHT THEIR OWN REALITY\n" + "WITH THEM, AND THE STARPORT'S TECHNOLOGY\n" + "IS BEING SUBVERTED BY THEIR PRESENCE.\n" + "\n"+ "AHEAD, YOU SEE AN OUTPOST OF HELL, A\n" + "FORTIFIED ZONE. IF YOU CAN GET PAST IT,\n" + "YOU CAN PENETRATE INTO THE HAUNTED HEART\n" + "OF THE STARBASE AND FIND THE CONTROLLING\n" + "SWITCH WHICH HOLDS EARTH'S POPULATION\n" + "HOSTAGE."); // After level 11, put this: public final static String C2TEXT =( "YOU HAVE WON! YOUR VICTORY HAS ENABLED\n" + "HUMANKIND TO EVACUATE EARTH AND ESCAPE\n"+ "THE NIGHTMARE. NOW YOU ARE THE ONLY\n"+ "HUMAN LEFT ON THE FACE OF THE PLANET.\n"+ "CANNIBAL MUTATIONS, CARNIVOROUS ALIENS,\n"+ "AND EVIL SPIRITS ARE YOUR ONLY NEIGHBORS.\n"+ "YOU SIT BACK AND WAIT FOR DEATH, CONTENT\n"+ "THAT YOU HAVE SAVED YOUR SPECIES.\n"+ "\n"+ "BUT THEN, EARTH CONTROL BEAMS DOWN A\n"+ "MESSAGE FROM SPACE: \"SENSORS HAVE LOCATED\n"+ "THE SOURCE OF THE ALIEN INVASION. IF YOU\n"+ "GO THERE, YOU MAY BE ABLE TO BLOCK THEIR\n"+ "ENTRY. THE ALIEN BASE IS IN THE HEART OF\n"+ "YOUR OWN HOME CITY, NOT FAR FROM THE\n"+ "STARPORT.\" SLOWLY AND PAINFULLY YOU GET\n"+ "UP AND RETURN TO THE FRAY."); // After level 20, put this: public final static String C3TEXT =( "YOU ARE AT THE CORRUPT HEART OF THE CITY,\n"+ "SURROUNDED BY THE CORPSES OF YOUR ENEMIES.\n"+ "YOU SEE NO WAY TO DESTROY THE CREATURES'\n"+ "ENTRYWAY ON THIS SIDE, SO YOU CLENCH YOUR\n"+ "TEETH AND PLUNGE THROUGH IT.\n"+ "\n"+ "THERE MUST BE A WAY TO CLOSE IT ON THE\n"+ "OTHER SIDE. WHAT DO YOU CARE IF YOU'VE\n"+ "GOT TO GO THROUGH HELL TO GET TO IT?"); // After level 29, put this: public final static String C4TEXT =( "THE HORRENDOUS VISAGE OF THE BIGGEST\n"+ "DEMON YOU'VE EVER SEEN CRUMBLES BEFORE\n"+ "YOU, AFTER YOU PUMP YOUR ROCKETS INTO\n"+ "HIS EXPOSED BRAIN. THE MONSTER SHRIVELS\n"+ "UP AND DIES, ITS THRASHING LIMBS\n"+ "DEVASTATING UNTOLD MILES OF HELL'S\n"+ "SURFACE.\n"+ "\n"+ "YOU'VE DONE IT. THE INVASION IS OVER.\n"+ "EARTH IS SAVED. HELL IS A WRECK. YOU\n"+ "WONDER WHERE BAD FOLKS WILL GO WHEN THEY\n"+ "DIE, NOW. WIPING THE SWEAT FROM YOUR\n"+ "FOREHEAD YOU BEGIN THE LONG TREK BACK\n"+ "HOME. REBUILDING EARTH OUGHT TO BE A\n"+ "LOT MORE FUN THAN RUINING IT WAS.\n"); // Before level 31, put this: public final static String C5TEXT =( "CONGRATULATIONS, YOU'VE FOUND THE SECRET\n"+ "LEVEL! LOOKS LIKE IT'S BEEN BUILT BY\n"+ "HUMANS, RATHER THAN DEMONS. YOU WONDER\n"+ "WHO THE INMATES OF THIS CORNER OF HELL\n"+ "WILL BE."); // Before level 32, put this: public final static String C6TEXT =( "CONGRATULATIONS, YOU'VE FOUND THE\n"+ "SUPER SECRET LEVEL! YOU'D BETTER\n"+ "BLAZE THROUGH THIS ONE!\n"); // after map 06 public final static String P1TEXT =( "You gloat over the steaming carcass of the\n"+ "Guardian. With its death, you've wrested\n"+ "the Accelerator from the stinking claws\n"+ "of Hell. You relax and glance around the\n"+ "room. Damn! There was supposed to be at\n"+ "least one working prototype, but you can't\n"+ "see it. The demons must have taken it.\n"+ "\n"+ "You must find the prototype, or all your\n"+ "struggles will have been wasted. Keep\n"+ "moving, keep fighting, keep killing.\n"+ "Oh yes, keep living, too."); // after map 11 public final static String P2TEXT =( "Even the deadly Arch-Vile labyrinth could\n"+ "not stop you, and you've gotten to the\n"+ "prototype Accelerator which is soon\n"+ "efficiently and permanently deactivated.\n"+ "\n"+ "You're good at that kind of thing."); // after map 20 public final static String P3TEXT =( "You've bashed and battered your way into\n"+ "the heart of the devil-hive. Time for a\n"+ "Search-and-Destroy mission, aimed at the\n"+ "Gatekeeper, whose foul offspring is\n"+ "cascading to Earth. Yeah, he's bad. But\n"+ "you know who's worse!\n"+ "\n"+ "Grinning evilly, you check your gear, and\n"+ "get ready to give the bastard a little Hell\n"+ "of your own making!"); // after map 30 public final static String P4TEXT =( "The Gatekeeper's evil face is splattered\n"+ "all over the place. As its tattered corpse\n"+ "collapses, an inverted Gate forms and\n"+ "sucks down the shards of the last\n"+ "prototype Accelerator, not to mention the\n"+ "few remaining demons. You're done. Hell\n"+ "has gone back to pounding bad dead folks \n"+ "instead of good live ones. Remember to\n"+ "tell your grandkids to put a rocket\n"+ "launcher in your coffin. If you go to Hell\n"+ "when you die, you'll need it for some\n"+ "final cleaning-up ..."); // before map 31 public final static String P5TEXT =( "You've found the second-hardest level we\n"+ "got. Hope you have a saved game a level or\n"+ "two previous. If not, be prepared to die\n"+ "aplenty. For master marines only."); // before map 32 public final static String P6TEXT =( "Betcha wondered just what WAS the hardest\n"+ "level we had ready for ya? Now you know.\n"+ "No one gets out alive."); public final static String T1TEXT =( "You've fought your way out of the infested\n"+ "experimental labs. It seems that UAC has\n"+ "once again gulped it down. With their\n"+ "high turnover, it must be hard for poor\n"+ "old UAC to buy corporate health insurance\n"+ "nowadays..\n"+ "\n"+ "Ahead lies the military complex, now\n"+ "swarming with diseased horrors hot to get\n"+ "their teeth into you. With luck, the\n"+ "complex still has some warlike ordnance\n"+ "laying around."); public final static String T2TEXT =( "You hear the grinding of heavy machinery\n"+ "ahead. You sure hope they're not stamping\n"+ "out new hellspawn, but you're ready to\n"+ "ream out a whole herd if you have to.\n"+ "They might be planning a blood feast, but\n"+ "you feel about as mean as two thousand\n"+ "maniacs packed into one mad killer.\n"+ "\n"+ "You don't plan to go down easy."); public final static String T3TEXT =( "The vista opening ahead looks real damn\n"+ "familiar. Smells familiar, too -- like\n"+ "fried excrement. You didn't like this\n"+ "place before, and you sure as hell ain't\n"+ "planning to like it now. The more you\n"+ "brood on it, the madder you get.\n"+ "Hefting your gun, an evil grin trickles\n"+ "onto your face. Time to take some names."); public final static String T4TEXT =( "Suddenly, all is silent, from one horizon\n"+ "to the other. The agonizing echo of Hell\n"+ "fades away, the nightmare sky turns to\n"+ "blue, the heaps of monster corpses start \n"+ "to evaporate along with the evil stench \n"+ "that filled the air. Jeeze, maybe you've\n"+ "done it. Have you really won?\n"+ "\n"+ "Something rumbles in the distance.\n"+ "A blue light begins to glow inside the\n"+ "ruined skull of the demon-spitter."); public final static String T5TEXT =( "What now? Looks totally different. Kind\n"+ "of like King Tut's condo. Well,\n"+ "whatever's here can't be any worse\n"+ "than usual. Can it? Or maybe it's best\n"+ "to let sleeping gods lie.."); public final static String T6TEXT =( "Time for a vacation. You've burst the\n"+ "bowels of hell and by golly you're ready\n"+ "for a break. You mutter to yourself,\n"+ "Maybe someone else can kick Hell's ass\n"+ "next time around. Ahead lies a quiet town,\n"+ "with peaceful flowing water, quaint\n"+ "buildings, and presumably no Hellspawn.\n"+ "\n"+ "As you step off the transport, you hear\n"+ "the stomp of a cyberdemon's iron shoe."); // // Character cast strings F_FINALE.C // public final static String CC_ZOMBIE="ZOMBIEMAN"; public final static String CC_SHOTGUN="SHOTGUN GUY"; public final static String CC_HEAVY="HEAVY WEAPON DUDE"; public final static String CC_IMP="IMP"; public final static String CC_DEMON="DEMON"; public final static String CC_LOST="LOST SOUL"; public final static String CC_CACO="CACODEMON"; public final static String CC_HELL="HELL KNIGHT"; public final static String CC_BARON="BARON OF HELL"; public final static String CC_ARACH="ARACHNOTRON"; public final static String CC_PAIN="PAIN ELEMENTAL"; public final static String CC_REVEN="REVENANT"; public final static String CC_MANCU="MANCUBUS"; public final static String CC_ARCH="ARCH-VILE"; public final static String CC_SPIDER="THE SPIDER MASTERMIND"; public final static String CC_CYBER="THE CYBERDEMON"; public final static String CC_NAZI="WAFFEN SS. SIEG HEIL!"; public final static String CC_KEEN="COMMANDER KEEN"; public final static String CC_BARREL="EXPLODING BARREL"; public final static String CC_HERO="OUR HERO"; } package doom; import java.io.DataInputStream; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import w.CacheableDoomObject; import w.IReadableDoomObject; public class ticcmd_t implements IDatagramSerializable, IReadableDoomObject,CacheableDoomObject{ // The length datagrams are supposed to have, for full compatibility. public static final int TICCMDLEN=8; // Initializes ticcmd buffer, too. public ticcmd_t(){ this.buffer=new byte[TICCMDLEN]; } /** *2048 for move */ public byte forwardmove; /** *2048 for move */ public byte sidemove; /** <<16 for angle delta */ public short angleturn; /** checks for net game */ public short consistancy; /** MAES: these are unsigned bytes :-( * However over networks, if we wish for vanilla compatibility, * these must be reduced to 8-bit "chars" * */ public char chatchar, buttons; /** HERETIC look/fly up/down/centering */ public char lookfly; // TODO: will be ignored during vanilla demos. Consider using specialized // per-demotype readers instead of Cacheable/Unpackage. /** replaces G_CmdChecksum (ticcmd_t cmd) */ ///////////////////////////////////////////// // For datagram serialization private byte[] buffer; public int getChecksum(ticcmd_t cmd) { int sum = 0; sum += forwardmove; sum += sidemove; sum += angleturn; sum += consistancy; sum += chatchar; sum += buttons; return sum; } /** because Cloneable is bullshit */ public void copyTo(ticcmd_t dest){ dest.forwardmove = forwardmove; dest.sidemove = sidemove; dest.angleturn= angleturn; dest.consistancy= consistancy; dest.chatchar= chatchar; dest.buttons= buttons; dest.lookfly=lookfly; } private static StringBuilder sb=new StringBuilder(); public String toString(){ sb.setLength(0); sb.append(" forwardmove "); sb.append(Integer.toHexString(this.forwardmove)); sb.append(" sidemove "); sb.append(Integer.toHexString(this.sidemove)); sb.append(" angleturn "); sb.append(Integer.toHexString(this.angleturn)); sb.append(" consistancy "); sb.append(Integer.toHexString(this.consistancy)); sb.append(" chatchar "); sb.append(chatchar); sb.append(" buttons "); sb.append(Integer.toHexString(this.buttons)); return sb.toString(); } @Override public byte[] pack() { buffer[0]=forwardmove; buffer[1]=sidemove; buffer[2]=(byte) (angleturn>>>8); buffer[3]=(byte) (angleturn&0x00FF); buffer[4]=(byte) (consistancy>>>8); buffer[5]=(byte) (consistancy&0x00FF); // We only send 8 bytes because the original length was 8 bytes. buffer[6]=(byte) (chatchar&0x00FF); buffer[7]=(byte) (buttons&0x00FF); return buffer; } @Override public void pack(byte[] buf, int offset) { buf[0+offset]=forwardmove; buf[1+offset]=sidemove; buf[2+offset]=(byte) (angleturn>>>8); buf[3+offset]=(byte) (angleturn&0x00FF); buf[4+offset]=(byte) (consistancy>>>8); buf[5+offset]=(byte) (consistancy&0x00FF); // We only send 8 bytes because the original length was 8 bytes. buf[6+offset]=(byte) (chatchar&0x00FF); buf[7+offset]=(byte) (buttons&0x00FF); } @Override public void unpack(byte[] buf) { unpack(buf,0); } @Override public void unpack(byte[] buf, int offset) { forwardmove=buf[0+offset]; sidemove= buf[1+offset]; angleturn=(short)(buf[2+offset]<<8|buf[3+offset]); consistancy=(short)(buf[4+offset]<<8|buf[5+offset]); // We blow these up to full chars. chatchar=(char)(0x00FF& buf[6+offset]); buttons=(char) (0x00FF& buf[7+offset]); } @Override public byte[] cached() { return this.buffer; } @Override public void read(DataInputStream f) throws IOException { iobuffer.position(0); iobuffer.order(ByteOrder.LITTLE_ENDIAN); f.read(iobuffer.array()); unpack(iobuffer); } /** This is useful only when loading/saving players from savegames. * It's NOT interchangeable with datagram methods, because it * does not use the network byte order. */ @Override public void unpack(ByteBuffer f) throws IOException { f.order(ByteOrder.LITTLE_ENDIAN); forwardmove=f.get(); sidemove= f.get(); // Even if they use the "unsigned char" syntax, angleturn is signed. angleturn=f.getShort(); consistancy=f.getShort(); // We blow these up to full chars. chatchar=(char) f.get(); buttons=(char) f.get(); } /** Ditto, we only pack some of the fields. * * @param f * @throws IOException */ public void pack(ByteBuffer f) throws IOException { f.order(ByteOrder.LITTLE_ENDIAN); f.put(forwardmove); f.put(sidemove); // LE order on disk for vanilla compatibility. f.putShort(angleturn); f.putShort(consistancy); // We crimp these to bytes :-( f.put((byte) chatchar); f.put((byte) buttons); } private static ByteBuffer iobuffer=ByteBuffer.allocate(8); }; package doom; // // INTERMISSION // Structure passed e.g. to WI_Start(wb) // public class wbplayerstruct_t implements Cloneable{ public wbplayerstruct_t(){ frags=new int[4]; } public boolean in; // whether the player is in game /** Player stats, kills, collected items etc. */ public int skills; public int sitems; public int ssecret; public int stime; public int[] frags; /** current score on entry, modified on return */ public int score; public wbplayerstruct_t clone(){ wbplayerstruct_t r=null; try { r = (wbplayerstruct_t)super.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } /*r.in=this.in; r.skills=this.skills; r.sitems=this.sitems; r.ssecret=this.ssecret; r.stime=this.stime; */ System.arraycopy(this.frags, 0, r.frags,0,r.frags.length); // r.score=this.score; return r; } } package doom; import static data.Limits.MAXPLAYERS; import utils.C2JUtils; public class wbstartstruct_t implements Cloneable{ public wbstartstruct_t(){ plyr=new wbplayerstruct_t[MAXPLAYERS]; C2JUtils.initArrayOfObjects(plyr, wbplayerstruct_t.class); } public int epsd; // episode # (0-2) // if true, splash the secret level public boolean didsecret; // previous and next levels, origin 0 public int last; public int next; public int maxkills; public int maxitems; public int maxsecret; public int maxfrags; /** the par time */ public int partime; /** index of this player in game */ public int pnum; /** meant to be treated as a "struct", therefore assignments should be deep copies */ public wbplayerstruct_t[] plyr; public wbstartstruct_t clone(){ wbstartstruct_t cl=null; try { cl=(wbstartstruct_t)super.clone(); } catch (CloneNotSupportedException e) { // TODO Auto-generated catch block e.printStackTrace(); } /*cl.epsd=this.epsd; cl.didsecret=this.didsecret; cl.last=this.last; cl.next=this.next; cl.maxfrags=this.maxfrags; cl.maxitems=this.maxitems; cl.maxsecret=this.maxsecret; cl.maxkills=this.maxkills; cl.partime=this.partime; cl.pnum=this.pnum;*/ for (int i=0;i<cl.plyr.length;i++){ cl.plyr[i]=this.plyr[i].clone(); } //cl.plyr=this.plyr.clone(); return cl; } } package doom; import java.io.IOException; /** Stuff that the "main" is supposed to do. DoomMain implements those. * * @author Maes * */ public interface IDoom { /** Called by IO functions when input is detected. */ void PostEvent (event_t ev); void PageTicker (); void PageDrawer (); void AdvanceDemo (); void StartTitle (); void QuitNetGame() throws IOException; } package doom; // Player states. // public enum playerstate_t { // Playing or camping. PST_LIVE, // Dead on the ground, view follows killer. PST_DEAD, // Ready to restart/respawn??? PST_REBORN }; package n; import i.DoomStatusAware; import i.IDoomSystem; import doom.DoomMain; import doom.DoomStatus; import doom.IDoomGameNetworking; import doom.NetConsts; import doom.doomcom_t; /** Does nothing. * Allows running single-player games without an actual network. * Hopefully, it will be replaced by a real UDP-based driver one day. * * @author Velktron * */ public class DummyNetworkDriver implements NetConsts,DoomSystemNetworking, DoomStatusAware{ ////////////// STATUS /////////// IDoomSystem I; DoomMain DM; IDoomGameNetworking DGN; public DummyNetworkDriver(DoomStatus DC){ updateStatus(DC); } @Override public void InitNetwork() { doomcom_t doomcom =new doomcom_t(); doomcom.id=DOOMCOM_ID; doomcom.ticdup=1; // single player game DM.netgame = false; doomcom.id = DOOMCOM_ID; doomcom.numplayers = doomcom.numnodes = 1; doomcom.deathmatch = 0; doomcom.consoleplayer = 0; DGN.setDoomCom(doomcom); } @Override public void NetCmd() { // TODO Auto-generated method stub } @Override public void updateStatus(DoomStatus DC) { this.DM=DC.DM; this.DGN=DC.DM; } } package n; // Emacs style mode select -*- C++ -*- //----------------------------------------------------------------------------- // // $Id: DoomSystemNetworking.java,v 1.1 2010/11/17 23:55:06 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: // System specific network interface stuff. // //----------------------------------------------------------------------------- public interface DoomSystemNetworking{ // Called by D_DoomMain. public void InitNetwork (); public void NetCmd (); } //----------------------------------------------------------------------------- // // $Log: DoomSystemNetworking.java,v $ // Revision 1.1 2010/11/17 23:55:06 velktron // Kind of playable/controllable. // // Revision 1.1 2010/10/22 16:22:43 velktron // Renderer works stably enough but a ton of bleeding. Started working on netcode. // // //----------------------------------------------------------------------------- package n; import doom.IDoomGameNetworking; import doom.doomcom_t; public class DummyNetworkHandler implements IDoomGameNetworking{ @Override public void NetUpdate() { // TODO Auto-generated method stub } @Override public void TryRunTics() { // TODO Auto-generated method stub } @Override public doomcom_t getDoomCom() { // TODO Auto-generated method stub return null; } @Override public void setDoomCom(doomcom_t doomcom) { // TODO Auto-generated method stub } @Override public int getTicdup() { // TODO Auto-generated method stub return 0; } @Override public void setTicdup(int ticdup) { // TODO Auto-generated method stub } } package n; import static data.Limits.MAXNETNODES; import static doom.NetConsts.CMD_GET; import static doom.NetConsts.CMD_SEND; import static doom.NetConsts.DOOMCOM_ID; import i.DoomStatusAware; import i.IDoomSystem; import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.SocketException; import java.net.SocketTimeoutException; import w.DoomBuffer; import doom.DoomContext; import doom.DoomMain; import doom.DoomStatus; import doom.doomcom_t; import doom.doomdata_t; // Emacs style mode select -*- C++ -*- //----------------------------------------------------------------------------- // // $Id: BasicNetworkInterface.java,v 1.5 2011/05/26 13:39:06 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. // // $Log: BasicNetworkInterface.java,v $ // Revision 1.5 2011/05/26 13:39:06 velktron // Now using ICommandLineManager // // Revision 1.4 2011/05/18 16:54:31 velktron // Changed to DoomStatus // // Revision 1.3 2011/05/17 16:53:42 velktron // _D_'s version. // // Revision 1.2 2010/12/20 17:15:08 velktron // Made the renderer more OO -> TextureManager and other changes as well. // // Revision 1.1 2010/11/17 23:55:06 velktron // Kind of playable/controllable. // // Revision 1.2 2010/11/11 15:31:28 velktron // Fixed "warped floor" error. // // Revision 1.1 2010/10/22 16:22:43 velktron // Renderer works stably enough but a ton of bleeding. Started working on netcode. // // // DESCRIPTION: // //----------------------------------------------------------------------------- public class BasicNetworkInterface implements DoomSystemNetworking, DoomStatusAware { public static final String rcsid = "$Id: BasicNetworkInterface.java,v 1.5 2011/05/26 13:39:06 velktron Exp $"; ////////////// STATUS /////////// IDoomSystem I; protected DoomMain DM; public BasicNetworkInterface(DoomContext DC){ this.DM=DC.DM; //this.myargv=DM.myargv; //this.myargc=DM.myargc; sendData=new doomdata_t(); recvData=new doomdata_t(); // We can do that since the buffer is reused. // Note: this will effectively tie doomdata and the datapacket. recvPacket=new DatagramPacket(recvData.cached(),recvData.cached().length); sendPacket=new DatagramPacket(sendData.cached(),sendData.cached().length); } // Bind it to the ones inside DN and DM; //doomdata_t netbuffer; doomcom_t doomcom; // For some odd reason... /** Changes endianness of a number */ public static int ntohl(int x) { return ((((x & 0x000000ff) << 24) | ((x & 0x0000ff00) << 8) | ((x & 0x00ff0000) >>> 8) | ((x & 0xff000000) >>> 24))); } public static short ntohs(short x) { return (short) (((x & 0x00ff) << 8) | ((x & 0xff00) >>> 8)); } public static int htonl(int x) { return ntohl(x); } public static short htons(short x){ return ntohs(x); } //void NetSend (); //boolean NetListen (); // // NETWORKING // // Maes: come on, we all know it's 666. int DOOMPORT = 666;//(IPPORT_USERRESERVED +0x1d ); //_D_: for testing purposes. If testing on the same machine, we can't have two UDP servers on the same port int RECVPORT = DOOMPORT; int SENDPORT = DOOMPORT; //DatagramSocket sendsocket; DatagramSocket insocket; // MAES: closest java equivalent DatagramSocket /*InetAddress*/ sendaddress[]=new DatagramSocket/*InetAddress*/[MAXNETNODES]; interface NetFunction { public void invoke(); } // To use inside packetsend. Declare once and reuse to save on heap costs. private doomdata_t sendData; private doomdata_t recvData; // We also reuse always the same DatagramPacket, "peged" to sw's byte buffer. private DatagramPacket recvPacket; private DatagramPacket sendPacket; public void sendSocketPacket(DatagramSocket ds, DatagramPacket dp) throws IOException { ds.send(dp); } public PacketSend packetSend = new PacketSend(); public class PacketSend implements NetFunction { @Override public void invoke() { int c; doomdata_t netbuffer = DM.netbuffer; // byte swap: so this is transferred as little endian? Ugh /*sendData.checksum = htonl(netbuffer.checksum); sendData.player = netbuffer.player; sendData.retransmitfrom = netbuffer.retransmitfrom; sendData.starttic = netbuffer.starttic; sendData.numtics = netbuffer.numtics; for (c=0 ; c< netbuffer.numtics ; c++) { sendData.cmds[c].forwardmove = netbuffer.cmds[c].forwardmove; sendData.cmds[c].sidemove = netbuffer.cmds[c].sidemove; sendData.cmds[c].angleturn = htons(netbuffer.cmds[c].angleturn); sendData.cmds[c].consistancy = htons(netbuffer.cmds[c].consistancy); sendData.cmds[c].chatchar = netbuffer.cmds[c].chatchar; sendData.cmds[c].buttons = netbuffer.cmds[c].buttons; } */ //printf ("sending %i\n",gametic); sendData.copyFrom(netbuffer); // MAES: This will force the buffer to be refreshed. byte[] bytes = sendData.pack(); /*System.out.print("SEND >> Thisplayer: "+DM.consoleplayer+" numtics: "+sendData.numtics+" consistency: "); for (doom.ticcmd_t t: sendData.cmds) System.out.print(t.consistancy+","); System.out.println();*/ // The socket already contains the address it needs, // and the packet's buffer is already modified. Send away. sendPacket.setData(bytes, 0, doomcom.datalength); DatagramSocket sendsocket; try { sendsocket = sendaddress[doomcom.remotenode]; sendPacket.setSocketAddress(sendsocket.getRemoteSocketAddress()); sendSocketPacket(sendsocket, sendPacket); } catch (Exception e) { e.printStackTrace(); I.Error ("SendPacket error: %s",e.getMessage()); } // if (c == -1) // I_Error ("SendPacket error: %s",strerror(errno)); } } public void socketGetPacket(DatagramSocket ds, DatagramPacket dp) throws IOException { ds.receive(dp); } // Used inside PacketGet private boolean first=true; public PacketGet packetGet = new PacketGet(); public class PacketGet implements NetFunction { @Override public void invoke() { int i; int c; // Receive back into swp. try { //recvPacket.setSocketAddress(insocket.getLocalSocketAddress()); socketGetPacket(insocket, recvPacket); } catch (SocketTimeoutException e) { doomcom.remotenode = -1; // no packet return; } catch (Exception e) { if (e.getClass()!=java.nio.channels.IllegalBlockingModeException.class){ I.Error ("GetPacket: %s",e.getStackTrace()); } } recvData.unpack(recvPacket.getData()); InetAddress fromaddress = recvPacket.getAddress(); /*System.out.print("RECV << Thisplayer: "+DM.consoleplayer+" numtics: "+recvData.numtics+" consistency: "); for (doom.ticcmd_t t: recvData.cmds) System.out.print(t.consistancy+","); System.out.println();*/ { //static int first=1; if (first){ sb.setLength(0); sb.append("("+DM.consoleplayer+") PacketRECV len="); sb.append(recvPacket.getLength()); sb.append(":p=[0x"); sb.append(Integer.toHexString(recvData.checksum)); sb.append(" 0x"); sb.append(DoomBuffer.getBEInt(recvData.retransmitfrom,recvData.starttic,recvData.player,recvData.numtics)); sb.append("numtics: "+recvData.numtics); System.out.println(sb.toString()); first = false; } } // find remote node number for (i=0 ; i<doomcom.numnodes ; i++) { if (sendaddress[i] != null) { if (fromaddress.equals(sendaddress[i].getInetAddress())) break; } } if (i == doomcom.numnodes) { // packet is not from one of the players (new game broadcast) doomcom.remotenode = -1; // no packet return; } doomcom.remotenode = (short) i; // good packet from a game player doomcom.datalength = (short) recvPacket.getLength(); //_D_: temporary hack to test two player on single machine //doomcom.remotenode = (short)(RECVPORT-DOOMPORT); // byte swap /*doomdata_t netbuffer = DM.netbuffer; netbuffer.checksum = ntohl(recvData.checksum); netbuffer.player = recvData.player; netbuffer.retransmitfrom = recvData.retransmitfrom; netbuffer.starttic = recvData.starttic; netbuffer.numtics = recvData.numtics; for (c=0 ; c< netbuffer.numtics ; c++) { netbuffer.cmds[c].forwardmove = recvData.cmds[c].forwardmove; netbuffer.cmds[c].sidemove = recvData.cmds[c].sidemove; netbuffer.cmds[c].angleturn = ntohs(recvData.cmds[c].angleturn); netbuffer.cmds[c].consistancy = ntohs(recvData.cmds[c].consistancy); netbuffer.cmds[c].chatchar = recvData.cmds[c].chatchar; netbuffer.cmds[c].buttons = recvData.cmds[c].buttons; } */ DM.netbuffer.copyFrom(recvData); } }; // Maes: oh great. More function pointer "fun". NetFunction netget = packetGet; NetFunction netsend = packetSend; // // I_InitNetwork // @Override public void InitNetwork() { boolean trueval = true; int i; int p; //struct hostent* hostentry; // host information entry doomcom = new doomcom_t(); //netbuffer = new doomdata_t(); DM.setDoomCom(doomcom); //DM.netbuffer = netbuffer; // set up for network i = DM.CM.CheckParm ("-dup"); if ((i!=0) && i< DM.CM.getArgc()-1) { doomcom.ticdup = (short) (DM.CM.getArgv(i+1).charAt(0)-'0'); if (doomcom.ticdup < 1) doomcom.ticdup = 1; if (doomcom.ticdup > 9) doomcom.ticdup = 9; } else doomcom. ticdup = 1; if (DM.CM.CheckParm ("-extratic")!=0) doomcom. extratics = 1; else doomcom. extratics = 0; p = DM.CM.CheckParm ("-port"); if ((p!=0) && (p<DM.CM.getArgc()-1)) { DOOMPORT = Integer.parseInt(DM.CM.getArgv(p+1)); System.out.println ("using alternate port "+DOOMPORT); } // parse network game options, // -net <consoleplayer> <host> <host> ... i = DM.CM.CheckParm ("-net"); if (i==0) { // single player game DM.netgame = false; doomcom.id = DOOMCOM_ID; doomcom.numplayers = doomcom.numnodes = 1; doomcom.deathmatch = 0; // false doomcom.consoleplayer = 0; return; } DM.netgame = true; // parse player number and host list doomcom.consoleplayer = (short) (DM.CM.getArgv(i+1).charAt(0)-'1'); RECVPORT = SENDPORT = DOOMPORT; if (doomcom.consoleplayer == 0) SENDPORT++; else RECVPORT++; doomcom.numnodes = 1; // this node for sure i++; while (++i < DM.CM.getArgc() && DM.CM.getArgv(i).charAt(0) != '-') { try { InetAddress addr = InetAddress.getByName(DM.CM.getArgv(i)); DatagramSocket ds = new DatagramSocket(null); ds.setReuseAddress(true); ds.connect(addr, SENDPORT); sendaddress[doomcom.numnodes] = ds; }catch (Exception e) { e.printStackTrace(); } doomcom.numnodes++; } doomcom.id = DOOMCOM_ID; doomcom.numplayers = doomcom.numnodes; // build message to receive try { insocket = new DatagramSocket(null); insocket.setReuseAddress(true); insocket.setSoTimeout(1); insocket.bind(new InetSocketAddress(RECVPORT)); } catch (SocketException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } } @Override public void NetCmd() { if (insocket == null) //HACK in case "netgame" is due to "addbot" return; if (DM.doomcom.command == CMD_SEND) { netsend.invoke (); } else if (doomcom.command == CMD_GET) { netget.invoke (); } else I.Error ("Bad net cmd: %i\n",doomcom.command); } // Instance StringBuilder private StringBuilder sb=new StringBuilder(); @Override public void updateStatus(DoomStatus DC) { // TODO Auto-generated method stub } } package n; public interface IDoomNet { public void NetUpdate(); } package f; /* Emacs style mode select -*- C++ -*- //----------------------------------------------------------------------------- // // $Id: EndLevel.java,v 1.12 2016/07/04 07:52:26 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. // // $Log: EndLevel.java,v $ // Revision 1.12 2016/07/04 07:52:26 velktron // GPL license header, and cleanup for files that are not in direct correspondence with original ones. // // Revision 1.11 2012/09/24 17:16:23 velktron // Massive merge between HiColor and HEAD. There's no difference from now on, and development continues on HEAD. // // Revision 1.8.2.2 2012/09/24 16:57:43 velktron // Addressed generics warnings. // // Revision 1.8.2.1 2011/11/27 18:18:34 velktron // Use cacheClear() on deactivation. // // Revision 1.8 2011/11/01 19:02:57 velktron // Using screen number constants // // Revision 1.7 2011/10/23 18:11:32 velktron // Generic compliance for DoomVideoInterface // // Revision 1.6 2011/08/23 16:13:53 velktron // Got rid of Z remnants. // // Revision 1.5 2011/07/31 21:49:38 velktron // Changed endlevel drawer's behavior to be closer to prBoom+'s. Allows using 1994TU.WAD while backwards compatible. // // Revision 1.4 2011/06/02 14:56:48 velktron // imports // // Revision 1.3 2011/06/02 14:53:21 velktron // Moved Endlevel constants to AbstractEndLevel // // Revision 1.2 2011/06/02 14:14:28 velktron // Implemented endlevel unloading of graphics, changed state enum. // // Revision 1.1 2011/06/02 14:00:48 velktron // Moved Endlevel stuff to f, where it makes more sense. // // Revision 1.18 2011/05/31 12:25:14 velktron // Endlevel -mostly- scaled correctly. // // Revision 1.17 2011/05/29 22:15:32 velktron // Introduced IRandom interface. // // Revision 1.16 2011/05/24 17:54:02 velktron // Defaults tester // // Revision 1.15 2011/05/23 17:00:39 velktron // Got rid of verbosity // // Revision 1.14 2011/05/21 16:53:24 velktron // Adapted to use new gamemode system. // // Revision 1.13 2011/05/18 16:58:04 velktron // Changed to DoomStatus // // Revision 1.12 2011/05/17 16:52:19 velktron // Switched to DoomStatus // // Revision 1.11 2011/05/11 14:12:08 velktron // Interfaced with DoomGame // // Revision 1.10 2011/05/10 10:39:18 velktron // Semi-playable Techdemo v1.3 milestone // // Revision 1.9 2011/05/06 14:00:54 velktron // More of _D_'s changes committed. // // Revision 1.8 2011/02/11 00:11:13 velktron // A MUCH needed update to v1.3. // // Revision 1.7 2010/12/20 17:15:08 velktron // Made the renderer more OO -> TextureManager and other changes as well. // // Revision 1.6 2010/11/12 13:37:25 velktron // Rationalized the LUT system - now it's 100% procedurally generated. // // Revision 1.5 2010/09/23 07:31:11 velktron // fuck // // Revision 1.4 2010/09/02 15:56:54 velktron // Bulk of unified renderer copyediting done. // // Some changes like e.g. global separate limits class and instance methods for seg_t and node_t introduced. // // Revision 1.3 2010/08/23 14:36:08 velktron // Menu mostly working, implemented Killough's fast hash-based GetNumForName, although it can probably be finetuned even more. // // Revision 1.2 2010/08/13 14:06:36 velktron // Endlevel screen fully functional! // // Revision 1.1 2010/07/06 16:32:38 velktron // Threw some work in WI, now EndLevel. YEAH THERE'S GONNA BE A SEPARATE EndLevel OBJECT THAT'S HOW PIMP THE PROJECT IS!!!!11!!! // // Revision 1.1 2010/06/30 08:58:51 velktron // Let's see if this stuff will finally commit.... // // // Most stuff is still being worked on. For a good place to start and get an idea of what is being done, I suggest checking out the "testers" package. // // Revision 1.1 2010/06/29 11:07:34 velktron // Release often, release early they say... // // Commiting ALL stuff done so far. A lot of stuff is still broken/incomplete, and there's still mixed C code in there. I suggest you load everything up in Eclpise and see what gives from there. // // A good place to start is the testers/ directory, where you can get an idea of how a few of the implemented stuff works. // // // DESCRIPTION: // Intermission screens. // //-----------------------------------------------------------------------------*/ import static data.Defines.*; import static data.Limits.*; import static v.DoomVideoRenderer.*; import defines.*; import data.sounds.musicenum_t; import data.sounds.sfxenum_t; import doom.DoomMain; import doom.DoomStatus; import doom.IDoomGame; import doom.event_t; import doom.player_t; import doom.wbplayerstruct_t; import doom.wbstartstruct_t; import m.IRandom; import rr.*; import s.IDoomSound; import st.IDoomStatusBar; import v.DoomVideoRenderer; import v.IVideoScale; import w.IWadLoader; import w.animenum_t; /** This class (stuff.c) seems to implement the endlevel screens. * * @author Maes * */ public class EndLevel<T,V> extends AbstractEndLevel { ////////////////// STATUS /////////////////// private DoomMain<?,?> DS; private IDoomGame DG; private DoomVideoRenderer<?,?> V; private IDoomSound S; private IWadLoader W; private IDoomStatusBar ST; private IRandom RND; private static int COUNT_KILLS=2; private static int COUNT_ITEMS=4; private static int COUNT_SECRETS=6; private static int COUNT_TIME=8; private static int COUNT_DONE=10; static enum endlevel_state{ NoState, StatCount, ShowNextLoc, JustShutOff } //GLOBAL LOCATIONS private static final int WI_TITLEY =2; private static final int WI_SPACINGY =3; // // GENERAL DATA // // // Locally used stuff. // public static final int FB= 0; private static final boolean RANGECHECKING = true; // Where to draw some stuff. To be scaled up, so they // are not final. public static int SP_STATSX; public static int SP_STATSY; public static int SP_TIMEX; public static int SP_TIMEY; // States for single-player protected static int SP_KILLS = 0; protected static int SP_ITEMS = 2; protected static int SP_SECRET = 4; protected static int SP_FRAGS = 6 ; protected static int SP_TIME = 8 ; protected static int SP_PAR = SP_TIME; protected int SP_PAUSE = 1; // in seconds protected int SHOWNEXTLOCDELAY =4; protected int SHOWLASTLOCDELAY =SHOWNEXTLOCDELAY; // used to accelerate or skip a stage int acceleratestage; // wbs->pnum int me; // specifies current state ) endlevel_state state; // contains information passed into intermission public wbstartstruct_t wbs; wbplayerstruct_t[] plrs; // wbs->plyr[] // used for general timing int cnt; // used for timing of background animation int bcnt; // signals to refresh everything for one frame int firstrefresh; int[] cnt_kills=new int[MAXPLAYERS]; int[] cnt_items=new int[MAXPLAYERS]; int[] cnt_secret=new int[MAXPLAYERS]; int cnt_time; int cnt_par; int cnt_pause; // # of commercial levels int NUMCMAPS; // // GRAPHICS // // background (map of levels). patch_t bg; // You Are Here graphic patch_t[] yah=new patch_t[2]; // splat patch_t splat; /** %, : graphics */ patch_t percent,colon; /** 0-9 graphic */ patch_t[] num=new patch_t[10]; /** minus sign */ patch_t wiminus; // "Finished!" graphics patch_t finished; // "Entering" graphic patch_t entering; // "secret" patch_t sp_secret; /** "Kills", "Scrt", "Items", "Frags" */ patch_t kills, secret,items,frags; /** Time sucks. */ patch_t time, par,sucks; /** "killers", "victims" */ patch_t killers,victims; /** "Total", your face, your dead face */ patch_t total, star, bstar; /** "red P[1..MAXPLAYERS]"*/ patch_t[] p=new patch_t[MAXPLAYERS]; /** "gray P[1..MAXPLAYERS]" */ patch_t[] bp=new patch_t[MAXPLAYERS]; /** Name graphics of each level (centered) */ patch_t[] lnames; // // CODE // // slam background // UNUSED unsigned char *background=0; public EndLevel(DoomStatus<T,V> DC) { this.updateStatus(DC); // _D_: commented this, otherwise something didn't work //this.Start(DS.wminfo); } protected void slamBackground() { // memcpy(screens[0], screens[1], SCREENWIDTH * SCREENHEIGHT); // Remember, the second arg is the source! System.arraycopy(V.getScreen(SCREEN_BG), 0 ,V.getScreen(SCREEN_FG),0, SCREENWIDTH * SCREENHEIGHT); V.MarkRect (0, 0, SCREENWIDTH, SCREENHEIGHT); } // The ticker is used to detect keys // because of timing issues in netgames. public boolean Responder(event_t ev) { return false; } /** Draws "<Levelname> Finished!" */ protected void drawLF() { int y = WI_TITLEY; // draw <LevelName> V.DrawScaledPatch((320 - lnames[wbs.last].width)/2, y, FB,vs, lnames[wbs.last]); // draw "Finished!" y += (5*lnames[wbs.last].height)/4; V.DrawScaledPatch((320 - finished.width)/2, y, FB,vs, finished); } /** Draws "Entering <LevelName>" */ protected void drawEL() { int y = WI_TITLEY; // This is in 320 x 200 coords! // draw "Entering" V.DrawScaledPatch((320 - entering.width)/2, y, FB,vs, entering); // HACK: if lnames[wbs.next] DOES have a defined nonzero topoffset, use it. // implicitly in DrawScaledPatch, and trump the normal behavior. // FIXME: this is only useful in a handful of prBoom+ maps, which use // a modified endlevel screen. The reason it works there is the behavior of the // unified patch drawing function, which is approximated with this hack. if (lnames[wbs.next].topoffset==0) y += (5*lnames[wbs.next].height)/4; // draw level. V.DrawScaledPatch((320 - lnames[wbs.next].width)/2, y, FB,vs, lnames[wbs.next]); } /** For whatever fucked-up reason, it expects c to be an array of patches, * and may choose to draw from alternative ones...which however are never loaded, * or are supposed to be "next" in memory or whatever. I kept this behavior, however * in Java it will NOT work as intended, if ever. * * @param n * @param c */ protected void drawOnLnode ( int n, patch_t[] c ) { int i; int left; int top; int right; int bottom; boolean fits = false; i = 0; do { left = lnodes[wbs.epsd][n].x - c[i].leftoffset; top = lnodes[wbs.epsd][n].y - c[i].topoffset; right = left + c[i].width; bottom = top + c[i].height; if (left >= 0 && right < SCREENWIDTH && top >= 0 && bottom < SCREENHEIGHT) { fits = true; } else { i++; } } while (!fits && i!=2); if (fits && i<2) { //V.DrawPatch(lnodes[wbs.epsd][n].x, lnodes[wbs.epsd][n].y, // FB, c[i]); V./*DrawPatchDirect*/DrawScaledPatch(lnodes[wbs.epsd][n].x, lnodes[wbs.epsd][n].y, FB, vs,c[i]); } else { // DEBUG System.out.println("Could not place patch on level "+ n+1); } } protected void initAnimatedBack() { int i; anim_t a; if (DS.isCommercial()) return; if (wbs.epsd > 2) return; for (i=0;i<NUMANIMS[wbs.epsd];i++) { a = anims[wbs.epsd][i]; // init variables a.ctr = -1; // specify the next time to draw it if (a.type == animenum_t.ANIM_ALWAYS) a.nexttic = bcnt + 1 + (RND.M_Random()%a.period); else if (a.type == animenum_t.ANIM_RANDOM) a.nexttic = bcnt + 1 + a.data2+(RND.M_Random()%a.data1); else if (a.type == animenum_t.ANIM_LEVEL) a.nexttic = bcnt + 1; } } protected void updateAnimatedBack() { int i; anim_t a; if (DS.isCommercial()) return; if (wbs.epsd > 2) return; int aaptr=wbs.epsd; for (i=0;i<NUMANIMS[wbs.epsd];i++) { a = anims[aaptr][i]; if (bcnt == a.nexttic) { switch (a.type) { case ANIM_ALWAYS: if (++anims[aaptr][i].ctr >= a.nanims) a.ctr = 0; a.nexttic = bcnt + a.period; break; case ANIM_RANDOM: a.ctr++; if (a.ctr == a.nanims) { a.ctr = -1; a.nexttic = bcnt+a.data2+(RND.M_Random()%a.data1); } else a.nexttic = bcnt + a.period; break; case ANIM_LEVEL: // gawd-awful hack for level anims if (!(state == endlevel_state.StatCount && i == 7) && wbs.next == a.data1) { a.ctr++; if (a.ctr == a.nanims) a.ctr--; a.nexttic = bcnt + a.period; } break; } } } } protected void drawAnimatedBack() { int i; anim_t a; if (DS.isCommercial()) return; if (wbs.epsd > 2) return; for (i=0 ; i<NUMANIMS[wbs.epsd] ; i++) { a = anims[wbs.epsd][i]; if (a.ctr >= 0) V.DrawScaledPatch(a.loc.x, a.loc.y, FB,vs, a.p[a.ctr]); } } /** * Draws a number. * If digits > 0, then use that many digits minimum, * otherwise only use as many as necessary. * Returns new x position. */ protected int drawNum ( int x, int y, int n, int digits ) { int fontwidth = num[0].width; boolean neg; int temp; if (digits < 0) { if (n==0) { // make variable-length zeros 1 digit long digits = 1; } else { // figure out # of digits in # digits = 0; temp = n; while (temp!=0) { temp /= 10; digits++; } } } neg = (n < 0); if (neg) n = -n; // if non-number, do not draw it if (n == 1994) return 0; // draw the new number while ((digits--)!=0) { x -= fontwidth*BEST_X_SCALE; V.DrawScaledPatch(x, y, V_NOSCALESTART|FB,vs, num[ n % 10 ]); n /= 10; } // draw a minus sign if necessary if (neg) V.DrawScaledPatch(x-=8*BEST_X_SCALE, y, V_NOSCALESTART|FB,vs, wiminus); return x; } protected void drawPercent ( int x, int y, int p ) { if (p < 0) return; V.DrawScaledPatch(x, y, V_NOSCALESTART|FB,vs, percent); drawNum(x, y, p, -1); } // // Display level completion time and par, // or "sucks" message if overflow. // protected void drawTime ( int x, int y, int t ) { int div; int n; if (t<0) return; if (t <= 61*59) { div = 1; do { n = (t / div) % 60; x = drawNum(x, y, n, 2) - colon.width*BEST_X_SCALE; div *= 60; // draw if ((div==60) || (t / div)>0) V.DrawScaledPatch(x, y, V_NOSCALESTART|FB, vs,colon); } while ((t / div)>0); } else { // "sucks" V.DrawScaledPatch(x - sucks.width*BEST_X_SCALE, y, V_NOSCALESTART|FB, vs,sucks); } } protected void End() { state=endlevel_state.JustShutOff; V.clearCaches(); unloadData(); } protected void unloadData() { int i; int j; W.UnlockLumpNum(wiminus); wiminus=null; for (i=0 ; i<10 ; i++) { W.UnlockLumpNum(num[i]); num[i]=null; } if (DS.isCommercial()) { for (i=0 ; i<NUMCMAPS ; i++){ W.UnlockLumpNum(lnames[i]); lnames[i]=null; } } else { W.UnlockLumpNum(yah[0]); yah[0]=null; W.UnlockLumpNum(yah[1]); yah[1]=null; W.UnlockLumpNum(splat); splat=null; for (i=0 ; i<NUMMAPS ; i++) { W.UnlockLumpNum(lnames[i]); lnames[i]=null; } if (wbs.epsd < 3) { for (j=0;j<NUMANIMS[wbs.epsd];j++) { if (wbs.epsd != 1 || j != 8) for (i=0;i<anims[wbs.epsd][j].nanims;i++){ W.UnlockLumpNum(anims[wbs.epsd][j].p[i]); anims[wbs.epsd][j].p[i]=null; } } } } W.UnlockLumpNum(percent); percent=null; W.UnlockLumpNum(colon); colon=null; W.UnlockLumpNum(finished); finished=null; W.UnlockLumpNum(entering); entering=null; W.UnlockLumpNum(kills); kills=null; W.UnlockLumpNum(secret); secret=null; W.UnlockLumpNum(sp_secret); sp_secret=null; W.UnlockLumpNum(items); items=null; W.UnlockLumpNum(frags); frags=null; W.UnlockLumpNum(time); time=null; W.UnlockLumpNum(sucks); sucks=null; W.UnlockLumpNum(par); par=null; W.UnlockLumpNum(victims); victims=null; W.UnlockLumpNum(killers); killers=null; W.UnlockLumpNum(total); total=null; for (i=0 ; i<MAXPLAYERS ; i++) { W.UnlockLumpNum(p[i]); W.UnlockLumpNum(bp[i]); p[i]=null; bp[i]=null; } } protected void initNoState() { state = endlevel_state.NoState; acceleratestage = 0; cnt = 10; } protected void updateNoState() { updateAnimatedBack(); if (--cnt==00) { End(); DG.WorldDone(); } } boolean snl_pointeron = false; protected void initShowNextLoc() { state = endlevel_state.ShowNextLoc; acceleratestage = 0; cnt = SHOWNEXTLOCDELAY * TICRATE; initAnimatedBack(); } protected void updateShowNextLoc() { updateAnimatedBack(); if ((--cnt==0) || (acceleratestage!=0)) initNoState(); else snl_pointeron = (cnt & 31) < 20; } protected void drawShowNextLoc() { int i; int last; slamBackground(); // draw animated background drawAnimatedBack(); if ( !DS.isCommercial()) { if (wbs.epsd > 2) { drawEL(); return; } last = (wbs.last == 8) ? wbs.next - 1 : wbs.last; // draw a splat on taken cities. for (i=0 ; i<=last ; i++) drawOnLnode(i, new patch_t[]{splat}); // splat the secret level? if (wbs.didsecret) drawOnLnode(8, new patch_t[]{splat}); // draw flashing ptr if (snl_pointeron) drawOnLnode(wbs.next, yah); } // draws which level you are entering.. if ( (!DS.isCommercial()) || wbs.next != 30) drawEL(); } protected void drawNoState() { snl_pointeron = true; drawShowNextLoc(); } protected int fragSum(int playernum) { int i; int frags = 0; for (i=0 ; i<MAXPLAYERS ; i++) { if (DS.playeringame[i] && i!=playernum) { frags += plrs[playernum].frags[i]; } } // JDC hack - negative frags. frags -= plrs[playernum].frags[playernum]; // UNUSED if (frags < 0) // frags = 0; return frags; } int dm_state; int[][] dm_frags=new int[MAXPLAYERS][MAXPLAYERS]; int [] dm_totals=new int[MAXPLAYERS]; protected void initDeathmatchStats() { int i; int j; state = endlevel_state.StatCount; acceleratestage = 0; dm_state = 1; cnt_pause = TICRATE; for (i=0 ; i<MAXPLAYERS ; i++) { if (DS.playeringame[i]) { for (j=0 ; j<MAXPLAYERS ; j++) if (DS.playeringame[j]) dm_frags[i][j] = 0; dm_totals[i] = 0; } } initAnimatedBack(); } protected void updateDeathmatchStats() { int i; int j; boolean stillticking; updateAnimatedBack(); if ((acceleratestage!=0) && (dm_state != 4)) { acceleratestage = 0; for (i=0 ; i<MAXPLAYERS ; i++) { if (DS.playeringame[i]) { for (j=0 ; j<MAXPLAYERS ; j++) if (DS.playeringame[j]) dm_frags[i][j] = plrs[i].frags[j]; dm_totals[i] = fragSum(i); } } S.StartSound(null, sfxenum_t.sfx_barexp); dm_state = 4; } if (dm_state == 2) { if ((bcnt&3)==0) S.StartSound(null, sfxenum_t.sfx_pistol); stillticking = false; for (i=0 ; i<MAXPLAYERS ; i++) { if (DS.playeringame[i]) { for (j=0 ; j<MAXPLAYERS ; j++) { if (DS.playeringame[j] && dm_frags[i][j] != plrs[i].frags[j]) { if (plrs[i].frags[j] < 0) dm_frags[i][j]--; else dm_frags[i][j]++; if (dm_frags[i][j] > 99) dm_frags[i][j] = 99; if (dm_frags[i][j] < -99) dm_frags[i][j] = -99; stillticking = true; } } dm_totals[i] = fragSum(i); if (dm_totals[i] > 99) dm_totals[i] = 99; if (dm_totals[i] < -99) dm_totals[i] = -99; } } if (!stillticking) { S.StartSound(null,sfxenum_t.sfx_barexp); dm_state++; } } else if (dm_state == 4) { if (acceleratestage!=0) { S.StartSound(null, sfxenum_t.sfx_slop); if ( DS.isCommercial()) initNoState(); else initShowNextLoc(); } } else if ((dm_state & 1)!=0) { if (--cnt_pause==0) { dm_state++; cnt_pause = TICRATE; } } } protected void drawDeathmatchStats() { int i; int j; int x; int y; int w; int lh = WI_SPACINGY; // line height slamBackground(); // draw animated background drawAnimatedBack(); drawLF(); // draw stat titles (top line) V.DrawPatch(DM_TOTALSX-total.width/2, DM_MATRIXY-WI_SPACINGY+10, FB, total); V.DrawPatch(DM_KILLERSX, DM_KILLERSY, FB, killers); V.DrawPatch(DM_VICTIMSX, DM_VICTIMSY, FB, victims); // draw P? x = DM_MATRIXX + DM_SPACINGX; y = DM_MATRIXY; for (i=0 ; i<MAXPLAYERS ; i++) { if (DS.playeringame[i]) { V.DrawPatch(x-p[i].width/2, DM_MATRIXY - WI_SPACINGY, FB, p[i]); V.DrawPatch(DM_MATRIXX-p[i].width/2, y, FB, p[i]); if (i == me) { V.DrawPatch(x-p[i].width/2, DM_MATRIXY - WI_SPACINGY, FB, bstar); V.DrawPatch(DM_MATRIXX-p[i].width/2, y, FB, star); } } else { // V_DrawPatch(x-SHORT(bp[i].width)/2, // DM_MATRIXY - WI_SPACINGY, FB, bp[i]); // V_DrawPatch(DM_MATRIXX-SHORT(bp[i].width)/2, // y, FB, bp[i]); } x += DM_SPACINGX; y += WI_SPACINGY; } // draw stats y = DM_MATRIXY+10; w = num[0].width; for (i=0 ; i<MAXPLAYERS ; i++) { x = DM_MATRIXX + DM_SPACINGX; if (DS.playeringame[i]) { for (j=0 ; j<MAXPLAYERS ; j++) { if (DS.playeringame[j]) drawNum(x+w, y, dm_frags[i][j], 2); x += DM_SPACINGX; } drawNum(DM_TOTALSX+w, y, dm_totals[i], 2); } y += WI_SPACINGY; } } int[] cnt_frags=new int[MAXPLAYERS]; int dofrags; int ng_state; protected void initNetgameStats() { int i; state = endlevel_state.StatCount; acceleratestage = 0; ng_state = 1; cnt_pause = TICRATE; for (i=0 ; i<MAXPLAYERS ; i++) { if (!DS.playeringame[i]) continue; cnt_kills[i] = cnt_items[i] = cnt_secret[i] = cnt_frags[i] = 0; dofrags += fragSum(i); } dofrags = ~~dofrags; initAnimatedBack(); } protected void updateNetgameStats() { int i; int fsum; boolean stillticking; updateAnimatedBack(); if (acceleratestage!=0 && ng_state != 10) { acceleratestage = 0; for (i=0 ; i<MAXPLAYERS ; i++) { if (!DS.playeringame[i]) continue; cnt_kills[i] = (plrs[i].skills * 100) / wbs.maxkills; cnt_items[i] = (plrs[i].sitems * 100) / wbs.maxitems; cnt_secret[i] = (plrs[i].ssecret * 100) / wbs.maxsecret; if (dofrags!=0) cnt_frags[i] = fragSum(i); } S.StartSound(null, sfxenum_t.sfx_barexp); ng_state = 10; } if (ng_state == 2) { if ((bcnt&3)==0) S.StartSound(null, sfxenum_t.sfx_pistol); stillticking = false; for (i=0 ; i<MAXPLAYERS ; i++) { if (!DS.playeringame[i]) continue; cnt_kills[i] += 2; if (cnt_kills[i] >= (plrs[i].skills * 100) / wbs.maxkills) cnt_kills[i] = (plrs[i].skills * 100) / wbs.maxkills; else stillticking = true; } if (!stillticking) { S.StartSound(null, sfxenum_t.sfx_barexp); ng_state++; } } else if (ng_state == 4) { if ((bcnt&3)==0) S.StartSound(null, sfxenum_t.sfx_pistol); stillticking = false; for (i=0 ; i<MAXPLAYERS ; i++) { if (!DS.playeringame[i]) continue; cnt_items[i] += 2; if (cnt_items[i] >= (plrs[i].sitems * 100) / wbs.maxitems) cnt_items[i] = (plrs[i].sitems * 100) / wbs.maxitems; else stillticking = true; } if (!stillticking) { S.StartSound(null, sfxenum_t.sfx_barexp); ng_state++; } } else if (ng_state == 6) { if ((bcnt&3)==0) S.StartSound(null, sfxenum_t.sfx_pistol); stillticking = false; for (i=0 ; i<MAXPLAYERS ; i++) { if (!DS.playeringame[i]) continue; cnt_secret[i] += 2; if (cnt_secret[i] >= (plrs[i].ssecret * 100) / wbs.maxsecret) cnt_secret[i] = (plrs[i].ssecret * 100) / wbs.maxsecret; else stillticking = true; } if (!stillticking) { S.StartSound(null, sfxenum_t.sfx_barexp); ng_state += 1 + 2*~dofrags; } } else if (ng_state == 8) { if ((bcnt&3)==0) S.StartSound(null, sfxenum_t.sfx_pistol); stillticking = false; for (i=0 ; i<MAXPLAYERS ; i++) { if (!DS.playeringame[i]) continue; cnt_frags[i] += 1; if (cnt_frags[i] >= (fsum = fragSum(i))) cnt_frags[i] = fsum; else stillticking = true; } if (!stillticking) { S.StartSound(null, sfxenum_t.sfx_pldeth); ng_state++; } } else if (ng_state == 10) { if (acceleratestage!=0) { S.StartSound(null, sfxenum_t.sfx_sgcock); if ( DS.isCommercial() ) initNoState(); else initShowNextLoc(); } } else if ((ng_state & 1)!=0) { if (--cnt_pause==0) { ng_state++; cnt_pause = TICRATE; } } } protected void drawNetgameStats() { int i; int x; int y; int pwidth = percent.width; slamBackground(); // draw animated background drawAnimatedBack(); drawLF(); // draw stat titles (top line) V.DrawPatch(NG_STATSX()+NG_SPACINGX-kills.width, NG_STATSY, FB, kills); V.DrawPatch(NG_STATSX()+2*NG_SPACINGX-items.width, NG_STATSY, FB, items); V.DrawPatch(NG_STATSX()+3*NG_SPACINGX-secret.width, NG_STATSY, FB, secret); if (dofrags!=0) V.DrawPatch(NG_STATSX()+4*NG_SPACINGX-frags.width, NG_STATSY, FB, frags); // draw stats y = NG_STATSY + kills.height; for (i=0 ; i<MAXPLAYERS ; i++) { if (!DS.playeringame[i]) continue; x = NG_STATSX(); V.DrawPatch(x-p[i].width, y, FB, p[i]); if (i == me) V.DrawPatch(x-p[i].width, y, FB, star); x += NG_SPACINGX; drawPercent(x-pwidth, y+10, cnt_kills[i]); x += NG_SPACINGX; drawPercent(x-pwidth, y+10, cnt_items[i]); x += NG_SPACINGX; drawPercent(x-pwidth, y+10, cnt_secret[i]); x += NG_SPACINGX; if (dofrags!=0) drawNum(x, y+10, cnt_frags[i], -1); y += WI_SPACINGY; } } int sp_state; protected void initStats() { state = endlevel_state.StatCount; acceleratestage = 0; sp_state = 1; cnt_kills[0] = cnt_items[0] = cnt_secret[0] = -1; cnt_time = cnt_par = -1; cnt_pause = TICRATE; initAnimatedBack(); } protected void updateStats() { updateAnimatedBack(); //System.out.println("SP_State "+sp_state); if ((acceleratestage !=0) && sp_state != COUNT_DONE) { acceleratestage = 0; cnt_kills[0] = (plrs[me].skills * 100) / wbs.maxkills; cnt_items[0] = (plrs[me].sitems * 100) / wbs.maxitems; cnt_secret[0] = (plrs[me].ssecret * 100) / wbs.maxsecret; cnt_time = plrs[me].stime / TICRATE; cnt_par = wbs.partime / TICRATE; S.StartSound(null, sfxenum_t.sfx_barexp); sp_state = 10; } if (sp_state == COUNT_KILLS) { cnt_kills[0] += 2; if ((bcnt&3)==0) S.StartSound(null, sfxenum_t.sfx_pistol); if (cnt_kills[0] >= (plrs[me].skills * 100) / wbs.maxkills) { cnt_kills[0] = (plrs[me].skills * 100) / wbs.maxkills; S.StartSound(null, sfxenum_t.sfx_barexp); sp_state++; } } else if (sp_state == COUNT_ITEMS) { cnt_items[0] += 2; if ((bcnt&3)==0) S.StartSound(null, sfxenum_t.sfx_pistol); if (cnt_items[0] >= (plrs[me].sitems * 100) / wbs.maxitems) { cnt_items[0] = (plrs[me].sitems * 100) / wbs.maxitems; S.StartSound(null, sfxenum_t.sfx_barexp); sp_state++; } } else if (sp_state == COUNT_SECRETS) { cnt_secret[0] += 2; if ((bcnt&3)==0) S.StartSound(null, sfxenum_t.sfx_pistol); if (cnt_secret[0] >= (plrs[me].ssecret * 100) / wbs.maxsecret) { cnt_secret[0] = (plrs[me].ssecret * 100) / wbs.maxsecret; S.StartSound(null, sfxenum_t.sfx_barexp); sp_state++; } } else if (sp_state == COUNT_TIME) { if ((bcnt&3)==0) S.StartSound(null, sfxenum_t.sfx_pistol); cnt_time += 3; if (cnt_time >= plrs[me].stime / TICRATE) cnt_time = plrs[me].stime / TICRATE; cnt_par += 3; if (cnt_par >= wbs.partime / TICRATE) { cnt_par = wbs.partime / TICRATE; if (cnt_time >= plrs[me].stime / TICRATE) { S.StartSound(null, sfxenum_t.sfx_barexp); sp_state++; } } } else if (sp_state == COUNT_DONE) { if (acceleratestage!=0) { S.StartSound(null, sfxenum_t.sfx_sgcock); if (DS.isCommercial()) initNoState(); else initShowNextLoc(); } } // Non-drawing, pausing state. Any odd value introduces a 35 tic pause. else if ((sp_state & 1)>0) { if (--cnt_pause==0) { sp_state++; cnt_pause = TICRATE; } } } protected void drawStats() { // line height int lh; lh = (3*num[0].height*BEST_Y_SCALE)/2; slamBackground(); // draw animated background drawAnimatedBack(); drawLF(); V.DrawScaledPatch(SP_STATSX, SP_STATSY, V_NOSCALESTART|FB,vs, kills); drawPercent(SCREENWIDTH - SP_STATSX, SP_STATSY, cnt_kills[0]); V.DrawScaledPatch(SP_STATSX, SP_STATSY+lh, V_NOSCALESTART|FB,vs, items); drawPercent(SCREENWIDTH - SP_STATSX, SP_STATSY+lh, cnt_items[0]); V.DrawScaledPatch(SP_STATSX, SP_STATSY+2*lh, V_NOSCALESTART|FB,vs, sp_secret); drawPercent(SCREENWIDTH - SP_STATSX, SP_STATSY+2*lh, cnt_secret[0]); V.DrawScaledPatch(SP_TIMEX, SP_TIMEY, V_NOSCALESTART|FB,vs, time); drawTime(SCREENWIDTH/2 - SP_TIMEX, SP_TIMEY, cnt_time); if (wbs.epsd < 3) { V.DrawScaledPatch(SCREENWIDTH/2 + SP_TIMEX, SP_TIMEY, V_NOSCALESTART|FB,vs, par); drawTime(SCREENWIDTH - SP_TIMEX, SP_TIMEY, cnt_par); } } protected void checkForAccelerate() { // check for button presses to skip delays for (int i=0 ; i<MAXPLAYERS ; i++) { player_t player = DS.players[i]; if (DS.playeringame[i]) { if ((player.cmd.buttons & BT_ATTACK)!=0) { if (!player.attackdown) acceleratestage = 1; player.attackdown = true; } else player.attackdown = false; if ((player.cmd.buttons & BT_USE)!=0) { if (!player.usedown) acceleratestage = 1; player.usedown = true; } else player.usedown = false; } } } /** Updates stuff each tick */ public void Ticker() { // counter for general background animation bcnt++; if (bcnt == 1) { // intermission music if ( DS.isCommercial()) S.ChangeMusic(musicenum_t.mus_dm2int.ordinal(), true); else S.ChangeMusic(musicenum_t.mus_inter.ordinal(), true); } checkForAccelerate(); //System.out.println("State "+state); switch (state) { case StatCount: if (DS.deathmatch) updateDeathmatchStats(); else if (DS.netgame) updateNetgameStats(); else updateStats(); break; case ShowNextLoc: updateShowNextLoc(); break; case NoState: updateNoState(); break; case JustShutOff: // We just finished, and graphics have been unloaded. // If we don't consume a tick in this way, Doom // will try to draw unloaded graphics. state=endlevel_state.NoState; break; } } protected void loadData() { int i; int j; String name; anim_t a; if (DS.isCommercial()) name= "INTERPIC"; else //sprintf(name, "WIMAP%d", wbs.epsd); name=("WIMAP"+Integer.toString(wbs.epsd)); // MAES: For Ultimate Doom if ( DS.isRetail()) { if (wbs.epsd == 3) name= "INTERPIC"; } // background - draw it to screen 1 for quick redraw. bg = (patch_t) W.CacheLumpName(name, PU_CACHE,patch_t.class); V.DrawPatchSolidScaled(0, 0, BEST_X_SCALE, BEST_Y_SCALE,1, bg); // UNUSED unsigned char *pic = screens[1]; // if (gamemode == commercial) // { // darken the background image // while (pic != screens[1] + SCREENHEIGHT*SCREENWIDTH) // { // *pic = colormaps[256*25 + *pic]; // pic++; // } //} if (DS.isCommercial()) { NUMCMAPS = 32; // lnames = (patch_t **) Z_Malloc(sizeof(patch_t*) * NUMCMAPS, // PU_, 0); lnames=new patch_t[NUMCMAPS]; String xxx=new String("CWILV%02d"); //String buffer; for (i=0 ; i<NUMCMAPS ; i++) { name=String.format(xxx,i); lnames[i] = (patch_t) W.CacheLumpName(name, PU_STATIC,patch_t.class); } } else { lnames = new patch_t[NUMMAPS]; String xxx=new String("WILV%d%d"); for (i=0 ; i<NUMMAPS ; i++) { name=String.format(xxx,wbs.epsd, i); lnames[i] = (patch_t) W.CacheLumpName(name, PU_STATIC,patch_t.class); } // you are here yah[0] = (patch_t) W.CacheLumpName("WIURH0", PU_STATIC,patch_t.class); // you are here (alt.) yah[1] = (patch_t) W.CacheLumpName("WIURH1", PU_STATIC,patch_t.class); // splat splat = (patch_t) W.CacheLumpName("WISPLAT", PU_STATIC,patch_t.class); if (wbs.epsd < 3) { xxx=new String("WIA%d%02d%02d"); //xxx=new PrintfFormat("WIA%d%.2d%.2d"); for (j=0;j<NUMANIMS[wbs.epsd];j++) { a = anims[wbs.epsd][j]; for (i=0;i<a.nanims;i++) { // MONDO HACK! if (wbs.epsd != 1 || j != 8) { // animations name=String.format(xxx,wbs.epsd, j, i); a.p[i] = (patch_t)W.CacheLumpName(name, PU_STATIC,patch_t.class); } else { // HACK ALERT! a.p[i] = anims[1][4].p[i]; } } } } } // More hacks on minus sign. wiminus = (patch_t)W.CacheLumpName("WIMINUS", PU_STATIC,patch_t.class); String xxx=new String("WINUM%d"); for (i=0;i<10;i++) { // numbers 0-9 name=String.format(xxx,i); num[i] = (patch_t)W.CacheLumpName(name, PU_STATIC,patch_t.class); } // percent sign percent = (patch_t)W.CacheLumpName("WIPCNT", PU_STATIC,patch_t.class); // "finished" finished = (patch_t)W.CacheLumpName("WIF", PU_STATIC,patch_t.class); // "entering" entering = (patch_t)W.CacheLumpName("WIENTER", PU_STATIC,patch_t.class); // "kills" kills = (patch_t)W.CacheLumpName("WIOSTK", PU_STATIC,patch_t.class); // "scrt" secret = (patch_t)W.CacheLumpName("WIOSTS", PU_STATIC,patch_t.class); // "secret" sp_secret =(patch_t)W.CacheLumpName("WISCRT2", PU_STATIC,patch_t.class); // Yuck. if (DS.language==Language_t.french) { // "items" if (DS.netgame && !DS.deathmatch) items = (patch_t)W.CacheLumpName("WIOBJ", PU_STATIC,patch_t.class); else items = (patch_t)W.CacheLumpName("WIOSTI", PU_STATIC,patch_t.class); } else items = (patch_t)W.CacheLumpName("WIOSTI", PU_STATIC,patch_t.class); // "frgs" frags = (patch_t)W.CacheLumpName("WIFRGS", PU_STATIC,patch_t.class); // ":" colon = (patch_t)W.CacheLumpName("WICOLON", PU_STATIC,patch_t.class); // "time" time = (patch_t)W.CacheLumpName("WITIME", PU_STATIC,patch_t.class); // "sucks" sucks = (patch_t)W.CacheLumpName("WISUCKS", PU_STATIC,patch_t.class); // "par" par = (patch_t)W.CacheLumpName("WIPAR", PU_STATIC,patch_t.class); // "killers" (vertical) killers = (patch_t)W.CacheLumpName("WIKILRS", PU_STATIC,patch_t.class); // "victims" (horiz) victims = (patch_t)W.CacheLumpName("WIVCTMS", PU_STATIC,patch_t.class); // "total" total = (patch_t)W.CacheLumpName("WIMSTT", PU_STATIC,patch_t.class); // your face star = (patch_t)W.CacheLumpName("STFST01", PU_STATIC,patch_t.class); // dead face bstar = (patch_t)W.CacheLumpName("STFDEAD0", PU_STATIC,patch_t.class); String xx1=new String("STPB%d"); String xx2=new String("WIBP%d"); for (i=0 ; i<MAXPLAYERS ; i++) { // "1,2,3,4" name= String.format(xx1,i); p[i] = (patch_t)W.CacheLumpName(name, PU_STATIC,patch_t.class);; // "1,2,3,4" name= String.format(xx2,i+1); bp[i] = (patch_t)W.CacheLumpName(name, PU_STATIC,patch_t.class);; } } /* public void WI_unloadData() { int i; int j; W.UnlockLumpNum(wiminus, PU_CACHE); for (i=0 ; i<10 ; i++) W.UnlockLumpNum(num[i], PU_CACHE); if (gamemode == commercial) { for (i=0 ; i<NUMCMAPS ; i++) W.UnlockLumpNum(lnames[i], PU_CACHE); } else { W.UnlockLumpNum(yah[0], PU_CACHE); W.UnlockLumpNum(yah[1], PU_CACHE); W.UnlockLumpNum(splat, PU_CACHE); for (i=0 ; i<NUMMAPS ; i++) W.UnlockLumpNum(lnames[i], PU_CACHE); if (wbs.epsd < 3) { for (j=0;j<NUMANIMS[wbs.epsd];j++) { if (wbs.epsd != 1 || j != 8) for (i=0;i<anims[wbs.epsd][j].nanims;i++) W.UnlockLumpNum(anims[wbs.epsd][j].p[i], PU_CACHE); } } } Z_Free(lnames); W.UnlockLumpNum(percent, PU_CACHE); W.UnlockLumpNum(colon, PU_CACHE); W.UnlockLumpNum(finished, PU_CACHE); W.UnlockLumpNum(entering, PU_CACHE); W.UnlockLumpNum(kills, PU_CACHE); W.UnlockLumpNum(secret, PU_CACHE); W.UnlockLumpNum(sp_secret, PU_CACHE); W.UnlockLumpNum(items, PU_CACHE); W.UnlockLumpNum(frags, PU_CACHE); W.UnlockLumpNum(time, PU_CACHE); W.UnlockLumpNum(sucks, PU_CACHE); W.UnlockLumpNum(par, PU_CACHE); W.UnlockLumpNum(victims, PU_CACHE); W.UnlockLumpNum(killers, PU_CACHE); W.UnlockLumpNum(total, PU_CACHE); // W.UnlockLumpNum(star, PU_CACHE); // W.UnlockLumpNum(bstar, PU_CACHE); for (i=0 ; i<MAXPLAYERS ; i++) W.UnlockLumpNum(p[i], PU_CACHE); for (i=0 ; i<MAXPLAYERS ; i++) W.UnlockLumpNum(bp[i], PU_CACHE); } */ public void Drawer () { switch (state) { case StatCount: if (DS.deathmatch) drawDeathmatchStats(); else if (DS.netgame) drawNetgameStats(); else drawStats(); break; case ShowNextLoc: drawShowNextLoc(); break; case NoState: drawNoState(); break; } } protected void initVariables(wbstartstruct_t wbstartstruct) { wbs = wbstartstruct.clone(); if (RANGECHECKING){ if (!DS.isCommercial()) { if ( DS.isRetail()) RNGCHECK(wbs.epsd, 0, 3); else RNGCHECK(wbs.epsd, 0, 2); } else { RNGCHECK(wbs.last, 0, 8); RNGCHECK(wbs.next, 0, 8); } RNGCHECK(wbs.pnum, 0, MAXPLAYERS); RNGCHECK(wbs.pnum, 0, MAXPLAYERS); } acceleratestage = 0; cnt = bcnt = 0; firstrefresh = 1; me = wbs.pnum; plrs = wbs.plyr.clone(); if (wbs.maxkills==0) wbs.maxkills = 1; if (wbs.maxitems==0) wbs.maxitems = 1; if (wbs.maxsecret==0) wbs.maxsecret = 1; // Sanity check for Ultimate. if ( !DS.isRetail()) if (wbs.epsd > 2) wbs.epsd -= 3; } public void Start(wbstartstruct_t wbstartstruct) { initVariables(wbstartstruct); loadData(); if (DS.deathmatch) initDeathmatchStats(); else if (DS.netgame) initNetgameStats(); else initStats(); } protected int NG_STATSX(){ return 32 + star.width/2 + 32*~dofrags; } protected static boolean RNGCHECK(int what, int min, int max){ return (what>=min && what <=max)?true:false; } @Override public void updateStatus(DoomStatus DS) { this.DG=DS.DG; this.DS=DS.DM; this.V=DS.V; this.W=DS.W; this.RND=DS.RND; this.S=DS.S; this.ST=DS.ST; } //////////////////////////// VIDEO SCALE STUFF //////////////////////////////// protected int SCREENWIDTH; protected int SCREENHEIGHT; protected IVideoScale vs; protected int BEST_X_SCALE; protected int BEST_Y_SCALE; @Override public void setVideoScale(IVideoScale vs) { this.vs=vs; } @Override public void initScaling() { this.SCREENHEIGHT=vs.getScreenHeight(); this.SCREENWIDTH=vs.getScreenWidth(); this.BEST_X_SCALE=vs.getScalingX(); this.BEST_Y_SCALE=vs.getScalingY(); // Pre-scale stuff. SP_STATSX =50*vs.getSafeScaling(); SP_STATSY = 50*vs.getSafeScaling();; SP_TIMEX = 16*vs.getSafeScaling(); SP_TIMEY = (SCREENHEIGHT-ST.getHeight()); } } package f; import static data.Defines.HU_FONTSIZE; import static data.Defines.HU_FONTSTART; import static data.Defines.PU_CACHE; import static data.Defines.PU_LEVEL; import static data.Defines.FF_FRAMEMASK; import static data.Limits.MAXPLAYERS; import static data.info.mobjinfo; import static data.info.states; import static doom.englsh.*; import static utils.C2JUtils.*; import hu.HU; import i.DoomStatusAware; import java.io.IOException; import java.util.ArrayList; import rr.ISpriteManager; import rr.flat_t; import rr.patch_t; import rr.spritedef_t; import rr.spriteframe_t; import s.IDoomSound; import utils.C2JUtils; import v.DoomVideoRenderer; import v.IVideoScale; import v.IVideoScaleAware; import w.IWadLoader; import data.mobjtype_t; import data.sounds.musicenum_t; import data.state_t; import defines.*; import data.sounds.sfxenum_t; import doom.DoomStatus; import doom.IDoomGame; import doom.event_t; import doom.evtype_t; import doom.gameaction_t; // Emacs style mode select -*- C++ -*- //----------------------------------------------------------------------------- // // $Id: Finale.java,v 1.28 2012/09/24 17:16:23 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: // Game completion, final screen animation. // //----------------------------------------------------------------------------- public class Finale<T> implements DoomStatusAware, IVideoScaleAware { public static final String rcsid = "$Id: Finale.java,v 1.28 2012/09/24 17:16:23 velktron Exp $"; IDoomGame DG; DoomStatus<?,?> DS; DoomVideoRenderer<T,?> V; IDoomSound S; HU HU; IWadLoader W; ISpriteManager SM; int finalestage; int finalecount; private static int TEXTSPEED = 3; private static int TEXTWAIT = 250; String[] doom_text = { E1TEXT, E2TEXT, E3TEXT, E4TEXT }; String[] doom2_text = { C1TEXT, C2TEXT, C3TEXT, C4TEXT, C5TEXT, C6TEXT }; String[] plut_text = { P1TEXT, P2TEXT, P3TEXT, P4TEXT, P5TEXT, P6TEXT }; String[] tnt_text = { T1TEXT, T2TEXT, T3TEXT, T4TEXT, T5TEXT, T6TEXT }; String finaletext; String finaleflat; /** * F_StartFinale */ public void StartFinale() { DG.setGameAction(gameaction_t.ga_nothing); DS.gamestate = gamestate_t.GS_FINALE; DS.viewactive = false; DS.automapactive = false; String[] texts = null; // Pick proper text. switch (DS.getGameMode()) { case commercial: case pack_xbla: texts = doom2_text; break; case pack_tnt: texts = tnt_text; break; case pack_plut: texts = plut_text; break; case shareware: case registered: case retail: texts = doom_text; break; } // Okay - IWAD dependend stuff. // This has been changed severly, and // some stuff might have changed in the process. switch (DS.getGameMode()) { // DOOM 1 - E1, E3 or E4, but each nine missions case shareware: case registered: case retail: { S.ChangeMusic(musicenum_t.mus_victor, true); switch (DS.gameepisode) { case 1: finaleflat = "FLOOR4_8"; finaletext = texts[0]; break; case 2: finaleflat = "SFLR6_1"; finaletext = texts[1]; break; case 3: finaleflat = "MFLR8_4"; finaletext = texts[2]; break; case 4: finaleflat = "MFLR8_3"; finaletext = texts[3]; break; default: // Ouch. break; } break; } // DOOM II and missions packs with E1, M34 case commercial: case pack_xbla: case pack_tnt: case pack_plut: { S.ChangeMusic(musicenum_t.mus_read_m, true); switch (DS.gamemap) { case 6: finaleflat = "SLIME16"; finaletext = texts[0]; break; case 11: finaleflat = "RROCK14"; finaletext = texts[1]; break; case 20: finaleflat = "RROCK07"; finaleflat = texts[2]; break; case 30: finaleflat = "RROCK17"; finaletext = texts[3]; break; case 15: finaleflat = "RROCK13"; finaletext = texts[4]; break; case 31: finaleflat = "RROCK19"; finaletext = texts[5]; break; default: // Ouch. break; } break; } // Indeterminate. default: S.ChangeMusic(musicenum_t.mus_read_m, true); finaleflat = "F_SKY1"; // Not used anywhere else. finaletext = doom2_text[1]; ; // FIXME - other text, music? break; } finalestage = 0; finalecount = 0; } public boolean Responder(event_t event) { if (finalestage == 2) return CastResponder(event); return false; } /** * F_Ticker */ public void Ticker() { int i; // check for skipping if ((DS.isCommercial()) && (finalecount > 50)) { // go on to the next level for (i = 0; i < MAXPLAYERS; i++) if (DS.players[i].cmd.buttons != 0) break; if (i < MAXPLAYERS) { if (DS.gamemap == 30) StartCast(); else DG.setGameAction(gameaction_t.ga_worlddone); } } // advance animation finalecount++; if (finalestage == 2) { CastTicker(); return; } if (DS.isCommercial()) return; // MAES: this is when we can transition to bunny. if ((finalestage == 0) && finalecount > finaletext.length() * TEXTSPEED + TEXTWAIT) { finalecount = 0; finalestage = 1; DS.wipegamestate = gamestate_t.GS_MINUS_ONE; // force a wipe if (DS.gameepisode == 3) S.StartMusic(musicenum_t.mus_bunny); } } // // F_TextWrite // // #include "hu_stuff.h" patch_t[] hu_font; @SuppressWarnings("unchecked") public void TextWrite() { T src; //V dest; int w; int count; char[] ch; int c; int cx; int cy; // erase the entire screen to a tiled background src = (T)((flat_t) W.CacheLumpName(finaleflat, PU_CACHE, flat_t.class)).data; //dest = V.getScreen(DoomVideoRenderer.SCREEN_FG); for (int y = 0; y < SCREENHEIGHT; y+=64) { int y_maxdraw=Math.min(SCREENHEIGHT-y, 64); // Draw whole blocks. for (int x = 0; x < SCREENWIDTH; x+=64) { int x_maxdraw=Math.min(SCREENWIDTH-x, 64); V.DrawBlock(x, y, DoomVideoRenderer.SCREEN_FG, x_maxdraw,y_maxdraw, src); } } V.MarkRect(0, 0, SCREENWIDTH, SCREENHEIGHT); // draw some of the text onto the screen cx = 10; cy = 10; ch = finaletext.toCharArray(); count = (finalecount - 10) / TEXTSPEED; if (count < 0) count = 0; // _D_: added min between count and ch.length, so that the text is not // written all at once for (int i = 0; i < Math.min(ch.length, count); i++) { c = ch[i]; if (c == 0) break; if (c == '\n') { cx = 10; cy += 11; continue; } c = Character.toUpperCase(c) - HU_FONTSTART; if (c < 0 || c > HU_FONTSIZE) { cx += 4; continue; } w = hu_font[c].width; if (cx + w > SCREENWIDTH) break; V.DrawScaledPatch(cx, cy, 0, vs, hu_font[c]); cx += w; } } private final castinfo_t[] castorder; int castnum; int casttics; state_t caststate; boolean castdeath; int castframes; int castonmelee; boolean castattacking; // // F_StartCast // // extern gamestate_t wipegamestate; public void StartCast() { DS.wipegamestate = gamestate_t.GS_MINUS_ONE; // force a screen wipe castnum = 0; caststate = states[mobjinfo[castorder[castnum].type.ordinal()].seestate .ordinal()]; casttics = (int) caststate.tics; castdeath = false; finalestage = 2; castframes = 0; castonmelee = 0; castattacking = false; S.ChangeMusic(musicenum_t.mus_evil, true); } // // F_CastTicker // public void CastTicker() { statenum_t st; sfxenum_t sfx; if (--casttics > 0) return; // not time to change state yet if (caststate.tics == -1 || caststate.nextstate == statenum_t.S_NULL || caststate.nextstate == null) { // switch from deathstate to next monster castnum++; castdeath = false; if (castorder[castnum].name == null) castnum = 0; if (mobjinfo[castorder[castnum].type.ordinal()].seesound.ordinal() != 0) ; S.StartSound(null, mobjinfo[castorder[castnum].type.ordinal()].seesound); caststate = states[mobjinfo[castorder[castnum].type.ordinal()].seestate .ordinal()]; castframes = 0; } else { // just advance to next state in animation if (caststate == states[statenum_t.S_PLAY_ATK1.ordinal()]) { stopattack(); // Oh, gross hack! afterstopattack(); return; // bye ... } st = caststate.nextstate; caststate = states[st.ordinal()]; castframes++; // sound hacks.... switch (st) { case S_PLAY_ATK1: sfx = sfxenum_t.sfx_dshtgn; break; case S_POSS_ATK2: sfx = sfxenum_t.sfx_pistol; break; case S_SPOS_ATK2: sfx = sfxenum_t.sfx_shotgn; break; case S_VILE_ATK2: sfx = sfxenum_t.sfx_vilatk; break; case S_SKEL_FIST2: sfx = sfxenum_t.sfx_skeswg; break; case S_SKEL_FIST4: sfx = sfxenum_t.sfx_skepch; break; case S_SKEL_MISS2: sfx = sfxenum_t.sfx_skeatk; break; case S_FATT_ATK8: case S_FATT_ATK5: case S_FATT_ATK2: sfx = sfxenum_t.sfx_firsht; break; case S_CPOS_ATK2: case S_CPOS_ATK3: case S_CPOS_ATK4: sfx = sfxenum_t.sfx_shotgn; break; case S_TROO_ATK3: sfx = sfxenum_t.sfx_claw; break; case S_SARG_ATK2: sfx = sfxenum_t.sfx_sgtatk; break; case S_BOSS_ATK2: case S_BOS2_ATK2: case S_HEAD_ATK2: sfx = sfxenum_t.sfx_firsht; break; case S_SKULL_ATK2: sfx = sfxenum_t.sfx_sklatk; break; case S_SPID_ATK2: case S_SPID_ATK3: sfx = sfxenum_t.sfx_shotgn; break; case S_BSPI_ATK2: sfx = sfxenum_t.sfx_plasma; break; case S_CYBER_ATK2: case S_CYBER_ATK4: case S_CYBER_ATK6: sfx = sfxenum_t.sfx_rlaunc; break; case S_PAIN_ATK3: sfx = sfxenum_t.sfx_sklatk; break; default: sfx = null; break; } if (sfx != null) // Fixed mute thanks to _D_ 8/6/2011 S.StartSound(null, sfx); } if (castframes == 12) { // go into attack frame castattacking = true; if (castonmelee != 0) caststate = states[mobjinfo[castorder[castnum].type.ordinal()].meleestate .ordinal()]; else caststate = states[mobjinfo[castorder[castnum].type.ordinal()].missilestate .ordinal()]; castonmelee ^= 1; if (caststate == states[statenum_t.S_NULL.ordinal()]) { if (castonmelee != 0) caststate = states[mobjinfo[castorder[castnum].type .ordinal()].meleestate.ordinal()]; else caststate = states[mobjinfo[castorder[castnum].type .ordinal()].missilestate.ordinal()]; } } if (castattacking) { if (castframes == 24 || caststate == states[mobjinfo[castorder[castnum].type .ordinal()].seestate.ordinal()]) stopattack(); } afterstopattack(); } protected void stopattack() { castattacking = false; castframes = 0; caststate = states[mobjinfo[castorder[castnum].type.ordinal()].seestate .ordinal()]; } protected void afterstopattack() { casttics = (int) caststate.tics; if (casttics == -1) casttics = 15; } /** * CastResponder */ public boolean CastResponder(event_t ev) { if (ev.type != evtype_t.ev_keydown) return false; if (castdeath) return true; // already in dying frames // go into death frame castdeath = true; caststate = states[mobjinfo[castorder[castnum].type.ordinal()].deathstate .ordinal()]; casttics = (int) caststate.tics; castframes = 0; castattacking = false; if (mobjinfo[castorder[castnum].type.ordinal()].deathsound != null) S.StartSound(null, mobjinfo[castorder[castnum].type.ordinal()].deathsound); return true; } public void CastPrint(String text) { char[] ch; int c; int cx; int w; int width; // find width ch = text.toCharArray(); width = 0; for (int i = 0; i < ch.length; i++) { c = ch[i]; if (c == 0) break; c = Character.toUpperCase(c) - HU_FONTSTART; if (c < 0 || c > HU_FONTSIZE) { width += 4; continue; } w = hu_font[c].width; width += w; } // draw it cx = 160 - width / 2; // ch = text; for (int i = 0; i < ch.length; i++) { c = ch[i]; if (c == 0) break; c = Character.toUpperCase(c) - HU_FONTSTART; if (c < 0 || c > HU_FONTSIZE) { cx += 4; continue; } w = hu_font[c].width; V.DrawScaledPatch(cx, 180, 0, vs, hu_font[c]); cx += w; } } /** * F_CastDrawer * * @throws IOException */ // public void V_DrawPatchFlipped (int x, int y, int scrn, patch_t patch); public void CastDrawer() { spritedef_t sprdef; spriteframe_t sprframe; int lump; boolean flip; patch_t patch = null; // erase the entire screen to a background V.DrawPatchSolidScaled(0, 0, SAFE_SCALE, SAFE_SCALE, 0, W.CachePatchName("BOSSBACK", PU_CACHE)); this.CastPrint(castorder[castnum].name); // draw the current frame in the middle of the screen sprdef = SM.getSprite(caststate.sprite.ordinal()); sprframe = sprdef.spriteframes[caststate.frame & FF_FRAMEMASK]; lump = sprframe.lump[0]; flip = eval(sprframe.flip[0]); // flip=false; // lump=0; patch = W.CachePatchNum(lump + SM.getFirstSpriteLump()); if (flip) V.DrawScaledPatch(160, 170, 0 | DoomVideoRenderer.V_FLIPPEDPATCH, vs, patch); else V.DrawScaledPatch(160, 170, 0, vs, patch); } protected int laststage; /** * F_BunnyScroll */ public void BunnyScroll() { int scrolled; int x; patch_t p1; patch_t p2; String name; int stage; p1 = W.CachePatchName("PFUB2", PU_LEVEL); p2 = W.CachePatchName("PFUB1", PU_LEVEL); V.MarkRect(0, 0, SCREENWIDTH, SCREENHEIGHT); scrolled = 320 - (finalecount - 230) / 2; if (scrolled > 320) scrolled = 320; if (scrolled < 0) scrolled = 0; for (x = 0; x < 320; x++) { if (x + scrolled < 320) V.DrawPatchColScaled(x, p1, x + scrolled, vs, 0); else V.DrawPatchColScaled(x, p2, x + scrolled - 320, vs, 0); } if (finalecount < 1130) return; if (finalecount < 1180) { V.DrawScaledPatch((320 - 13 * 8) / 2, (320 - 8 * 8) / 2, 0, vs, W.CachePatchName("END0", PU_CACHE)); laststage = 0; return; } stage = (finalecount - 1180) / 5; if (stage > 6) stage = 6; if (stage > laststage) { S.StartSound(null, sfxenum_t.sfx_pistol); laststage = stage; } name = ("END" + stage); V.DrawScaledPatch((320 - 13 * 8) / 2, (320 - 8 * 8) / 2, 0, vs, W.CachePatchName(name, PU_CACHE)); } // // F_Drawer // public void Drawer() { if (finalestage == 2) { CastDrawer(); return; } if (finalestage == 0) TextWrite(); else { switch (DS.gameepisode) { case 1: if (DS.isCommercial() || DS.isRegistered()) V.DrawPatchSolidScaled(0, 0, this.SAFE_SCALE, this.SAFE_SCALE, 0, W.CachePatchName("CREDIT", PU_CACHE)); else // Fun fact: Registered/Ultimate Doom has no "HELP2" lump. V.DrawPatchSolidScaled(0, 0, this.SAFE_SCALE, this.SAFE_SCALE, 0, W.CachePatchName("HELP2", PU_CACHE)); break; case 2: V.DrawPatchSolidScaled(0, 0, this.SAFE_SCALE, this.SAFE_SCALE, 0, W.CachePatchName("VICTORY2", PU_CACHE)); break; case 3: BunnyScroll(); break; case 4: V.DrawPatchSolidScaled(0, 0, this.SAFE_SCALE, this.SAFE_SCALE, 0, W.CachePatchName("ENDPIC", PU_CACHE)); break; } } } @SuppressWarnings("unchecked") public Finale(DoomStatus DC) { this.updateStatus(DC); hu_font = HU.getHUFonts(); castinfo_t shit = new castinfo_t(CC_ZOMBIE, mobjtype_t.MT_POSSESSED); ArrayList<castinfo_t> crap = new ArrayList<castinfo_t>(); crap.add(new castinfo_t(CC_ZOMBIE, mobjtype_t.MT_POSSESSED)); crap.add(new castinfo_t(CC_SHOTGUN, mobjtype_t.MT_SHOTGUY)); crap.add(new castinfo_t(CC_HEAVY, mobjtype_t.MT_CHAINGUY)); crap.add(new castinfo_t(CC_IMP, mobjtype_t.MT_TROOP)); crap.add(new castinfo_t(CC_DEMON, mobjtype_t.MT_SERGEANT)); crap.add(new castinfo_t(CC_LOST, mobjtype_t.MT_SKULL)); crap.add(new castinfo_t(CC_CACO, mobjtype_t.MT_HEAD)); crap.add(new castinfo_t(CC_HELL, mobjtype_t.MT_KNIGHT)); crap.add(new castinfo_t(CC_BARON, mobjtype_t.MT_BRUISER)); crap.add(new castinfo_t(CC_ARACH, mobjtype_t.MT_BABY)); crap.add(new castinfo_t(CC_PAIN, mobjtype_t.MT_PAIN)); crap.add(new castinfo_t(CC_REVEN, mobjtype_t.MT_UNDEAD)); crap.add(new castinfo_t(CC_MANCU, mobjtype_t.MT_FATSO)); crap.add(new castinfo_t(CC_ARCH, mobjtype_t.MT_VILE)); crap.add(new castinfo_t(CC_SPIDER, mobjtype_t.MT_SPIDER)); crap.add(new castinfo_t(CC_CYBER, mobjtype_t.MT_CYBORG)); crap.add(new castinfo_t(CC_HERO, mobjtype_t.MT_PLAYER)); crap.add(new castinfo_t(null, null)); castorder = C2JUtils .createArrayOfObjects(castinfo_t.class, crap.size()); crap.toArray(castorder); } @Override public void updateStatus(DoomStatus<?,?> DC) { this.DG = DC.DG; this.DS = DC.DM; V = (DoomVideoRenderer<T,?>) DC.V; S = DC.S; HU = DC.HU; W = DC.W; SM = DC.SM; } // //////////////////////////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(); // Pre-scale stuff. } } // /$Log package f; import data.mobjtype_t; /** * Final DOOM 2 animation Casting by id Software. in order of appearance */ public class castinfo_t { String name; mobjtype_t type; public castinfo_t() { } public castinfo_t(String name, mobjtype_t type) { this.name = name; this.type = type; } } package f; public interface IWiper { boolean ScreenWipe(int wipeno, int x, int y, int width, int height, int ticks); boolean EndScreen(int x, int y, int width, int height); boolean StartScreen(int x, int y, int width, int height); } package f; public class point_t { public point_t(int x, int y) { this.x=x; this.y=y; } public int x; public int y; } package f; import doom.DoomStatus; import m.IRandom; import v.DoomVideoRenderer; import v.IVideoScale; import v.IVideoScaleAware; import i.DoomStatusAware; import i.DoomVideoInterface; public abstract class AbstractWiper<T,V> implements IWiper, DoomStatusAware, IVideoScaleAware{ // // SCREEN WIPE PACKAGE // /** These don't seem to be used anywhere */ public static enum wipe { // simple gradual pixel change for 8-bit only // MAES: this transition isn't guaranteed to always terminate // see Chocolate Strife develpment. Unused in Doom anyway. ColorXForm, // weird screen melt Melt, NUMWIPES }; /** when false, stop the wipe */ protected volatile boolean go = false; protected V wipe_scr_start; protected V wipe_scr_end; protected V wipe_scr; ////////////////////////////VIDEO SCALE STUFF //////////////////////////////// protected int SCREENWIDTH; protected int SCREENHEIGHT; protected int Y_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.Y_SCALE=vs.getScalingY(); // Pre-scale stuff. } ///////////////////// STATUS ///////////////////// @SuppressWarnings("unchecked") @Override public void updateStatus(DoomStatus<?,?> DS){ this.RND=DS.RND; this.V=(DoomVideoRenderer<T, V>) DS.V; this.VI=(DoomVideoInterface<V>) DS.VI; } IRandom RND; DoomVideoRenderer<T,V> V; DoomVideoInterface<V> VI; } package f; import w.animenum_t; import rr.patch_t; // //Animation. //There is another anim_t used in p_spec. // public class anim_t { public anim_t(animenum_t type, int period, int nanims, point_t loc, int data1, int data2, patch_t[] p, int nexttic, int lastdrawn, int ctr, int state) { this.type = type; this.period = period; this.nanims = nanims; this.loc = loc; this.data1 = data1; this.data2 = data2; this.p = p; this.nexttic = nexttic; this.lastdrawn = lastdrawn; this.ctr = ctr; this.state = state; } // Partial constructor, only 4 first fields. public anim_t(animenum_t animAlways, int period, int nanims, point_t loc ) { this.type = animAlways; this.period = period; this.nanims = nanims; this.loc = loc; } // Partial constructor, only 5 first fields. public anim_t(animenum_t type, int period, int nanims, point_t loc, int data1 ) { this.type = type; this.period = period; this.nanims = nanims; this.loc = loc; this.data1=data1; } public animenum_t type; // period in tics between animations public int period; // number of animation frames public int nanims; // location of animation point_t loc; // ALWAYS: n/a, // RANDOM: period deviation (<256), // LEVEL: level public int data1; // ALWAYS: n/a, // RANDOM: random base period, // LEVEL: n/a public int data2; // actual graphics for frames of animations //Maes: was pointer to array public patch_t[] p= new patch_t[3]; // following must be initialized to zero before use! // next value of bcnt (used in conjunction with period) public int nexttic; // last drawn animation frame public int lastdrawn; // next frame number to animate public int ctr; // used by RANDOM and LEVEL when animating public int state; } package f; import i.DoomStatusAware; import v.IVideoScaleAware; import w.animenum_t; import static data.Defines.TICRATE; public abstract class AbstractEndLevel implements DoomStatusAware, IVideoScaleAware{ //NET GAME STUFF public static final int NG_STATSY =50; public static final int NG_SPACINGX = 64; //DEATHMATCH STUFF public static final int DM_MATRIXX = 42; public static final int DM_MATRIXY = 68; public static final int DM_SPACINGX = 40; public static final int DM_TOTALSX = 269; public static final int DM_KILLERSX = 10; public static final int DM_KILLERSY = 100; public static final int DM_VICTIMSX = 5; public static final int DM_VICTIMSY = 50; // static point_t lnodes[NUMEPISODES][NUMMAPS] final static public point_t[][] lnodes = { // Episode 0 World Map { new point_t( 185, 164 ), // location of level 0 (CJ) new point_t( 148, 143 ), // location of level 1 new point_t(CJ) new point_t( 69, 122 ), // location of level 2 new point_t(CJ) new point_t( 209, 102 ), // location of level 3 new point_t(CJ) new point_t( 116, 89 ), // location of level 4 new point_t(CJ) new point_t( 166, 55 ), // location of level 5 new point_t(CJ) new point_t( 71, 56 ), // location of level 6 new point_t(CJ) new point_t( 135, 29 ), // location of level 7 new point_t(CJ) new point_t( 71, 24 ) // location of level 8 new point_t(CJ) }, // Episode 1 World Map should go here { new point_t( 254, 25 ), // location of level 0 new point_t(CJ) new point_t( 97, 50 ), // location of level 1 new point_t(CJ) new point_t( 188, 64 ), // location of level 2 new point_t(CJ) new point_t( 128, 78 ), // location of level 3 new point_t(CJ) new point_t( 214, 92 ), // location of level 4 new point_t(CJ) new point_t( 133, 130 ), // location of level 5 new point_t(CJ) new point_t( 208, 136 ), // location of level 6 new point_t(CJ) new point_t( 148, 140 ), // location of level 7 new point_t(CJ) new point_t( 235, 158 ) // location of level 8 new point_t(CJ) }, // Episode 2 World Map should go here { new point_t( 156, 168 ), // location of level 0 new point_t(CJ) new point_t( 48, 154 ), // location of level 1 new point_t(CJ) new point_t( 174, 95 ), // location of level 2 new point_t(CJ) new point_t( 265, 75 ), // location of level 3 new point_t(CJ) new point_t( 130, 48 ), // location of level 4 new point_t(CJ) new point_t( 279, 23 ), // location of level 5 new point_t(CJ) new point_t( 198, 48 ), // location of level 6 new point_t(CJ) new point_t( 140, 25 ), // location of level 7 new point_t(CJ) new point_t( 281, 136 ) // location of level 8 new point_t(CJ) } }; // //Animation locations for episode 0 (1). //Using patches saves a lot of space, //as they replace 320x200 full screen frames. // public static final anim_t[] epsd0animinfo = { new anim_t(animenum_t.ANIM_ALWAYS, TICRATE/3, 3, new point_t( 224, 104 ) ), new anim_t( animenum_t.ANIM_ALWAYS, TICRATE/3, 3, new point_t( 184, 160 ) ), new anim_t( animenum_t.ANIM_ALWAYS, TICRATE/3, 3, new point_t( 112, 136 ) ), new anim_t( animenum_t.ANIM_ALWAYS, TICRATE/3, 3, new point_t( 72, 112 ) ), new anim_t( animenum_t.ANIM_ALWAYS, TICRATE/3, 3, new point_t( 88, 96 ) ), new anim_t( animenum_t.ANIM_ALWAYS, TICRATE/3, 3, new point_t( 64, 48 ) ), new anim_t( animenum_t.ANIM_ALWAYS, TICRATE/3, 3, new point_t( 192, 40 ) ), new anim_t(animenum_t.ANIM_ALWAYS, TICRATE/3, 3, new point_t( 136, 16 ) ), new anim_t(animenum_t. ANIM_ALWAYS, TICRATE/3, 3, new point_t( 80, 16 ) ), new anim_t( animenum_t.ANIM_ALWAYS, TICRATE/3, 3, new point_t( 64, 24 ) ) }; public static final anim_t[] epsd1animinfo = { new anim_t( animenum_t.ANIM_LEVEL, TICRATE/3, 1, new point_t( 128, 136 ), 1 ), new anim_t( animenum_t.ANIM_LEVEL, TICRATE/3, 1, new point_t( 128, 136 ), 2 ), new anim_t( animenum_t.ANIM_LEVEL, TICRATE/3, 1, new point_t( 128, 136 ), 3 ), new anim_t( animenum_t.ANIM_LEVEL, TICRATE/3, 1, new point_t( 128, 136 ), 4 ), new anim_t( animenum_t.ANIM_LEVEL, TICRATE/3, 1, new point_t(128, 136 ), 5 ), new anim_t( animenum_t.ANIM_LEVEL, TICRATE/3, 1, new point_t( 128, 136 ), 6 ), new anim_t( animenum_t.ANIM_LEVEL, TICRATE/3, 1, new point_t( 128, 136 ), 7 ), new anim_t( animenum_t.ANIM_LEVEL, TICRATE/3, 3, new point_t( 192, 144 ), 8 ), new anim_t( animenum_t.ANIM_LEVEL, TICRATE/3, 1, new point_t( 128, 136 ), 8 ) }; public static final anim_t[] epsd2animinfo = { new anim_t( animenum_t.ANIM_ALWAYS, TICRATE/3, 3, new point_t( 104, 168 ) ), new anim_t( animenum_t.ANIM_ALWAYS, TICRATE/3, 3, new point_t( 40, 136 ) ), new anim_t( animenum_t.ANIM_ALWAYS, TICRATE/3, 3, new point_t( 160, 96 ) ), new anim_t( animenum_t.ANIM_ALWAYS, TICRATE/3, 3, new point_t(104, 80 ) ), new anim_t( animenum_t.ANIM_ALWAYS, TICRATE/3, 3, new point_t( 120, 32 ) ), new anim_t( animenum_t.ANIM_ALWAYS, TICRATE/4, 3, new point_t( 40, 0 ) ) }; /*static int NUMANIMS[NUMEPISODES] = { sizeof(epsd0animinfo)/sizeof(anim_t), sizeof(epsd1animinfo)/sizeof(anim_t), sizeof(epsd2animinfo)/sizeof(anim_t) };*/ // MAES: cute, but we can do it in a more Java-friendly way :-p public static final int[] NUMANIMS={epsd0animinfo.length,epsd1animinfo.length,epsd2animinfo.length}; /** ATTENTION: there's a difference between these "anims" and those used in p_spec.c */ public static final anim_t[][] anims = { epsd0animinfo, epsd1animinfo, epsd2animinfo }; } package f; import v.DoomVideoRenderer; import doom.DoomMain; public abstract class Wiper<T,V> extends AbstractWiper<T,V> { static final String rcsid = "$Id: Wiper.java,v 1.18 2012/09/24 17:16:23 velktron Exp $"; protected wipefun[] wipes; public Wiper(DoomMain<T,V> DC){ this.updateStatus(DC); } public static final class HiColor extends Wiper<byte[],short[]>{ public HiColor(DoomMain<byte[], short[]> DC) { super(DC); wipes=new wipefun[]{ new wipe_initColorXForm(), new wipe_doColorXForm(), new wipe_exitColorXForm(), new wipe_initMelt(), new wipe_doMelt(), new wipe_exitMelt() }; } /** Those guys sure have an obsession with shit...this is supposed to do some * lame-ass transpose. * * @param array * @param width * @param height */ protected final void shittyColMajorXform ( short[] array, int width, int height ) { int x; int y; short[] dest; dest = new short[width*height]; for(y=0;y<height;y++) for(x=0;x<width;x++){ dest[x*height+y] = array[y*width+x]; //dest[(1+x)*height+y] = array[y*width+(1+x)]; } System.arraycopy(dest, 0, array, 0, width*height); //Z_Free(dest); } class wipe_doColorXForm implements wipefun{ public boolean invoke ( int width, int height, int ticks ) { boolean changed; short[] w=wipe_scr; short[] e=wipe_scr_end; int newval; changed = false; int pw =0;// wipe_scr; int pe = 0; //wipe_scr_end; while (pw!=width*height) { if (w[pw] != e[pe]) { if (w[pw] > e[pe]) { newval = w[pw] - ticks; if (newval < e[pe]) w[pw] = e[pe]; else w[pw] = (byte) newval; changed = true; } else if (w[pw] < e[pe]) { newval = w[pw] + ticks; if (newval > e[pe]) w[pw] = e[pe]; else w[pw] = (byte) newval; changed = true; } } pw++; pe++; } return !changed; } } class wipe_doMelt implements wipefun{ public boolean invoke ( int width, int height, int ticks ) { // int w2=2*width; // int w3=3*width; // int w4=4*width; int dy; int idx; // Indexex to short* ?! WTF... int ps; int pd; short[] s;//=wipe_scr_end; short[] d=wipe_scr; boolean done = true; //width=2; while (ticks-->0) { for (int i=0;i<width;i++) { // Column won't start yet. if (y[i]<0) { y[i]++; done = false; } else if (y[i] < height) { dy = (y[i] < 16*Y_SCALE) ? y[i]+Y_SCALE : 8*Y_SCALE; if (y[i]+dy >= height) dy = height - y[i]; ps = i*height+y[i];// &((short *)wipe_scr_end)[i*height+y[i]]; pd = y[i]*width+i;//&((short *)wipe_scr)[y[i]*width+i]; idx = 0; s=wipe_scr_end; // MAES: this part should draw the END SCREEN "behind" the melt. for (int j=dy;j>0;j--) { d[pd+idx] = s[ps++]; idx += width; } y[i] += dy; s=wipe_scr_start; ps = i*height; //&((short *)wipe_scr_start)[i*height]; pd = y[i]*width+i; //&((short *)wipe_scr)[y[i]*width+i]; idx = 0; // This draws a column shifted by y[i] for (int j=height-y[i];j>0;j--) { d[pd+idx] = s[ps++]; idx+=width; } done = false; } } } return done; } } } public static final class Indexed extends Wiper<byte[],byte[]>{ public Indexed(DoomMain<byte[], byte[]> DC) { super(DC); wipes=new wipefun[]{ new wipe_initColorXForm(), new wipe_doColorXForm(), new wipe_exitColorXForm(), new wipe_initMelt(), new wipe_doMelt(), new wipe_exitMelt() }; } /** Those guys sure have an obsession with shit...this is supposed to do some * lame-ass transpose. * * @param array * @param width * @param height */ protected final void shittyColMajorXform ( byte[] array, int width, int height ) { int x; int y; byte[] dest; dest = new byte[width*height]; for(y=0;y<height;y++) for(x=0;x<width;x++){ dest[x*height+y] = array[y*width+x]; //dest[(1+x)*height+y] = array[y*width+(1+x)]; } System.arraycopy(dest, 0, array, 0, width*height); //Z_Free(dest); } class wipe_doColorXForm implements wipefun{ public boolean invoke ( int width, int height, int ticks ) { boolean changed; byte[] w=wipe_scr; byte[] e=wipe_scr_end; int newval; changed = false; int pw =0;// wipe_scr; int pe = 0; //wipe_scr_end; while (pw!=width*height) { if (w[pw] != e[pe]) { if (w[pw] > e[pe]) { newval = w[pw] - ticks; if (newval < e[pe]) w[pw] = e[pe]; else w[pw] = (byte) newval; changed = true; } else if (w[pw] < e[pe]) { newval = w[pw] + ticks; if (newval > e[pe]) w[pw] = e[pe]; else w[pw] = (byte) newval; changed = true; } } pw++; pe++; } return !changed; } } class wipe_doMelt implements wipefun{ public boolean invoke ( int width, int height, int ticks ) { // int w2=2*width; // int w3=3*width; // int w4=4*width; int dy; int idx; // Indexex to short* ?! WTF... int ps; int pd; byte[] s;//=wipe_scr_end; byte[] d=wipe_scr; boolean done = true; //width=2; while (ticks-->0) { for (int i=0;i<width;i++) { // Column won't start yet. if (y[i]<0) { y[i]++; done = false; } else if (y[i] < height) { dy = (y[i] < 16*Y_SCALE) ? y[i]+Y_SCALE : 8*Y_SCALE; if (y[i]+dy >= height) dy = height - y[i]; ps = i*height+y[i];// &((short *)wipe_scr_end)[i*height+y[i]]; pd = y[i]*width+i;//&((short *)wipe_scr)[y[i]*width+i]; idx = 0; s=wipe_scr_end; // MAES: this part should draw the END SCREEN "behind" the melt. for (int j=dy;j>0;j--) { d[pd+idx] = s[ps++]; idx += width; } y[i] += dy; s=wipe_scr_start; ps = i*height; //&((short *)wipe_scr_start)[i*height]; pd = y[i]*width+i; //&((short *)wipe_scr)[y[i]*width+i]; idx = 0; // This draws a column shifted by y[i] for (int j=height-y[i];j>0;j--) { d[pd+idx] = s[ps++]; idx+=width; } done = false; } } } return done; } } } public static final class TrueColor extends Wiper<byte[],int[]>{ public TrueColor(DoomMain<byte[], int[]> DC) { super(DC); wipes=new wipefun[]{ new wipe_initColorXForm(), new wipe_doColorXForm(), new wipe_exitColorXForm(), new wipe_initMelt(), new wipe_doMelt(), new wipe_exitMelt() }; } /** Those guys sure have an obsession with shit...this is supposed to do some * lame-ass transpose. * * @param array * @param width * @param height */ protected final void shittyColMajorXform ( int[] array, int width, int height ) { int x; int y; int[] dest; dest = new int[width*height]; for(y=0;y<height;y++) for(x=0;x<width;x++){ dest[x*height+y] = array[y*width+x]; //dest[(1+x)*height+y] = array[y*width+(1+x)]; } System.arraycopy(dest, 0, array, 0, width*height); //Z_Free(dest); } class wipe_doColorXForm implements wipefun{ public boolean invoke ( int width, int height, int ticks ) { boolean changed; int[] w=wipe_scr; int[] e=wipe_scr_end; int newval; changed = false; int pw =0;// wipe_scr; int pe = 0; //wipe_scr_end; while (pw!=width*height) { if (w[pw] != e[pe]) { if (w[pw] > e[pe]) { newval = w[pw] - ticks; if (newval < e[pe]) w[pw] = e[pe]; else w[pw] = (byte) newval; changed = true; } else if (w[pw] < e[pe]) { newval = w[pw] + ticks; if (newval > e[pe]) w[pw] = e[pe]; else w[pw] = (byte) newval; changed = true; } } pw++; pe++; } return !changed; } } class wipe_doMelt implements wipefun{ public boolean invoke ( int width, int height, int ticks ) { // int w2=2*width; // int w3=3*width; // int w4=4*width; int dy; int idx; // Indexex to short* ?! WTF... int ps; int pd; int[] s;//=wipe_scr_end; int[] d=wipe_scr; boolean done = true; //width=2; while (ticks-->0) { for (int i=0;i<width;i++) { // Column won't start yet. if (y[i]<0) { y[i]++; done = false; } else if (y[i] < height) { dy = (y[i] < 16*Y_SCALE) ? y[i]+Y_SCALE : 8*Y_SCALE; if (y[i]+dy >= height) dy = height - y[i]; ps = i*height+y[i];// &((short *)wipe_scr_end)[i*height+y[i]]; pd = y[i]*width+i;//&((short *)wipe_scr)[y[i]*width+i]; idx = 0; s=wipe_scr_end; // MAES: this part should draw the END SCREEN "behind" the melt. for (int j=dy;j>0;j--) { d[pd+idx] = s[ps++]; idx += width; } y[i] += dy; s=wipe_scr_start; ps = i*height; //&((short *)wipe_scr_start)[i*height]; pd = y[i]*width+i; //&((short *)wipe_scr)[y[i]*width+i]; idx = 0; // This draws a column shifted by y[i] for (int j=height-y[i];j>0;j--) { d[pd+idx] = s[ps++]; idx+=width; } done = false; } } } return done; } } } protected abstract void shittyColMajorXform( V array, int width, int height ); class wipe_initColorXForm implements wipefun{ public boolean invoke ( int width, int height, int ticks ) { System.arraycopy(wipe_scr_start,0 ,wipe_scr, 0,width*height); return false; } } class wipe_exitColorXForm implements wipefun{ public boolean invoke ( int width, int height, int ticks ) { return false; } } protected int[] y; class wipe_initMelt implements wipefun{ public boolean invoke ( int width, int height, int ticks ) { int i, r; // copy start screen to main screen System.arraycopy(wipe_scr_start, 0,wipe_scr, 0,width*height); // makes this wipe faster (in theory) // to have stuff in column-major format shittyColMajorXform(wipe_scr_start, width, height); shittyColMajorXform(wipe_scr_end, width, height); // setup initial column positions // (y<0 => not ready to scroll yet) y = new int[width]; y[0] = -(RND.M_Random()%16); for (int j=1;j<Y_SCALE;j++){ y[j]=y[j-1]; } for (i=Y_SCALE;i<width;i+=Y_SCALE) { r = (RND.M_Random()%3) - 1; y[i] = y[i-1] + r; if (y[i] > 0) y[i] = 0; else if (y[i] == -16) y[i] = -15; for (int j=1;j<Y_SCALE;j++){ y[i+j]=y[i]; } } return false; } } class wipe_exitMelt implements wipefun{ public boolean invoke ( int width, int height, int ticks ) { y=null; //Z_Free(y); return false; } } /** Sets "from" screen and stores it in "screen 2"*/ @Override public boolean StartScreen ( int x, int y, int width, int height ) { wipe_scr_start = V.getScreen(DoomVideoRenderer.SCREEN_WS); // byte[] screen_zero=V.getScreen(0); VI.ReadScreen(wipe_scr_start); //System.arraycopy(screen_zero,0,wipe_scr_start, 0, SCREENWIDTH*SCREENHEIGHT); return false; } /** Sets "to" screen and stores it to "screen 3" */ @Override public boolean EndScreen ( int x, int y, int width, int height ) { // Set end screen to "screen 3" and copy visible screen to it. wipe_scr_end = V.getScreen(DoomVideoRenderer.SCREEN_WE); VI.ReadScreen(wipe_scr_end); // Restore starting screen. V screen_zero= V.getScreen(DoomVideoRenderer.SCREEN_FG); System.arraycopy(wipe_scr_start,0,screen_zero, 0, SCREENWIDTH*SCREENHEIGHT); return false; } @Override public boolean ScreenWipe ( int wipeno, int x, int y, int width, int height, int ticks ) { boolean rc; //System.out.println("Ticks do "+ticks); // initial stuff if (!go) { go = true; //wipe_scr = new byte[width*height]; // DEBUG wipe_scr = V.getScreen(DoomVideoRenderer.SCREEN_FG); // HOW'S THAT FOR A FUNCTION POINTER, BIATCH?! (wipes[wipeno*3]).invoke(width, height, ticks); } // do a piece of wipe-in V.MarkRect(0, 0, width, height); rc = (wipes[wipeno*3+1]).invoke(width, height, ticks); // V.DrawBlock(x, y, 0, width, height, wipe_scr); // DEBUG // final stuff if (rc) { go = false; (wipes[wipeno*3+2]).invoke(width, height, ticks); } return !go; } /** Interface for ASS-WIPING functions */ interface wipefun{ public boolean invoke(int width, int height, int ticks ); } } package data; // Emacs style mode select -*- C++ -*- //----------------------------------------------------------------------------- // // $Id: dstrings.java,v 1.4 2010/10/01 16:47:51 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. // // $Log: dstrings.java,v $ // Revision 1.4 2010/10/01 16:47:51 velktron // Fixed tab interception. // // Revision 1.3 2010/09/22 16:40:02 velktron // MASSIVE changes in the status passing model. // DoomMain and DoomGame unified. // Doomstat merged into DoomMain (now status and game functions are one). // // Most of DoomMain implemented. Possible to attempt a "classic type" start but will stop when reading sprites. // // Revision 1.2 2010/07/22 15:37:53 velktron // MAJOR changes in Menu system. // // Revision 1.1 2010/06/30 08:58:51 velktron // Let's see if this stuff will finally commit.... // // // Most stuff is still being worked on. For a good place to start and get an idea of what is being done, I suggest checking out the "testers" package. // // Revision 1.1 2010/06/29 11:07:34 velktron // Release often, release early they say... // // Commiting ALL stuff done so far. A lot of stuff is still broken/incomplete, and there's still mixed C code in there. I suggest you load everything up in Eclpise and see what gives from there. // // A good place to start is the testers/ directory, where you can get an idea of how a few of the implemented stuff works. // // // DESCRIPTION: // Globally defined strings. // //----------------------------------------------------------------------------- import static doom.englsh.*; public class dstrings{ //public static const char rcsid[] = "$Id: dstrings.java,v 1.4 2010/10/01 16:47:51 velktron Exp $"; // Misc. other strings. public static final String SAVEGAMENAME= "doomsav"; /** File locations, relative to current position. * Path names are OS-sensitive. * Only really used with the -shdev command line parameter. * * MAES: shouldn't those need path separator characters? :-S */ public static final String DEVMAPS= "devmaps", DEVDATA ="devdata"; // Not done in french? // QuitDOOM messages public static final int NUM_QUITMESSAGES= 22; public static final String[] endmsg= { // DOOM1 QUITMSG, "please don't leave, there's more\ndemons to toast!", "let's beat it -- this is turning\ninto a bloodbath!", "i wouldn't leave if i were you.\ndos is much worse.", "you're trying to say you like dos\nbetter than me, right?", "don't leave yet -- there's a\ndemon around that corner!", "ya know, next time you come in here\ni'm gonna toast ya.", "go ahead and leave. see if i care.", // QuitDOOM II messages "you want to quit?\nthen, thou hast lost an eighth!", "don't go now, there's a \ndimensional shambler waiting\nat the dos prompt!", "get outta here and go back\nto your boring programs.", "if i were your boss, i'd \n deathmatch ya in a minute!", "look, bud. you leave now\nand you forfeit your body count!", "just leave. when you come\nback, i'll be waiting with a bat.", "you're lucky i don't smack\nyou for thinking about leaving.", // FinalDOOM? "fuck you, pussy!\nget the fuck out!", "you quit and i'll jizz\nin your cystholes!", "if you leave, i'll make\nthe lord drink my jizz.", "hey, ron! can we say\n'fuck' in the game?", "i'd leave: this is just\nmore monsters and levels.\nwhat a load.", "suck it down, asshole!\nyou're a fucking wimp!", "don't quit now! we're \nstill spending your money!", // Internal debug. Different style, too. "THIS IS NO MESSAGE!\nPage intentionally left blank." }; } package data; /** * SoundFX struct. * * * */ public class sfxinfo_t { public sfxinfo_t(){ } /** up to 6-character name */ public String name; /** Sfx singularity (only one at a time) */ public boolean singularity; /** Sfx priority */ public int priority; // referenced sound if a link // MAES: since in pure hackish C style, a "0" value would be used as a boolean, we'll need to distinguish more // unambiguously. So for querying, look at the "linked" boolean or a getter. public boolean linked; public sfxinfo_t link; public sfxinfo_t getLink() { if (linked) return link; else return null; } public void setLink(sfxinfo_t link) { this.link=link; } // pitch if a link public int pitch; // volume if a link public int volume; /** sound data (used to be void*) */ public byte[] data; // this is checked every second to see if sound // can be thrown out (if 0, then decrement, if -1, // then throw out, if > 0, then it is in use) public int usefulness; // lump number of sfx public int lumpnum; public sfxinfo_t(String name, boolean singularity, int priority, sfxinfo_t link, int pitch, int volume, byte[] data, int usefulness, int lumpnum) { this.name = name; this.singularity = singularity; this.priority = priority; this.link = link; this.pitch = pitch; this.volume = volume; this.data = data; this.usefulness = usefulness; this.lumpnum = lumpnum; } /** MAES: Call this constructor if you don't want a cross-linked sound. * * @param name * @param singularity * @param priority * @param pitch * @param volume * @param usefulness */ public sfxinfo_t(String name, boolean singularity, int priority, int pitch, int volume, int usefulness) { this.name = name; this.singularity = singularity; this.priority = priority; this.linked = false; this.pitch = pitch; this.volume = volume; this.usefulness = usefulness; } public sfxinfo_t(String name, boolean singularity, int priority, boolean linked, int pitch, int volume, int usefulness) { this.name = name; this.singularity = singularity; this.priority = priority; this.linked = linked; this.pitch = pitch; this.volume = volume; this.usefulness = usefulness; } public int identify(sfxinfo_t[] array){ for (int i=0;i<array.length;i++){ if (array[i]==this){ return i; } } // Duh return 0; } }; package data; // Emacs style mode select -*- C++ -*- //----------------------------------------------------------------------------- // // $Id: doomtype.java,v 1.3 2011/02/11 00:11:13 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: // Simple basic typedefs, isolated here to make it easier // separating modules. // //----------------------------------------------------------------------------- public class doomtype { // C's "chars" are actually Java signed bytes. public static byte MAXCHAR =((byte)0x7f); public static short MAXSHORT= ((short)0x7fff); // Max pos 32-bit int. public static int MAXINT=((int)0x7fffffff); public static long MAXLONG=((long)0x7fffffff); public static byte MINCHAR=((byte)0x80); public static short MINSHORT=((short)0x8000); // Max negative 32-bit integer. public static int MININT=((int)0x80000000); public static long MINLONG=((long)0x80000000); } package data; //import m.define; import static data.Limits.MAXINT; import static data.Limits.MININT; import static m.fixed_t.FRACBITS; import static m.fixed_t.FRACUNIT; import static m.fixed_t.MAPFRACUNIT; import g.Keys; import defines.ammotype_t; import defines.card_t; import doom.weapontype_t; // Emacs style mode select -*- C++ -*- //----------------------------------------------------------------------------- // // $Id: Defines.java,v 1.48 2012/09/24 17:16:22 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: // Internally used data structures for virtually everything, // key definitions, lots of other stuff. // //----------------------------------------------------------------------------- //#ifndef __DOOMDEF__ //#define __DOOMDEF__ //#include <stdio.h> //#include <string.h> // // Global parameters/defines. // // DOOM version public final class Defines{ /** Seems to be 109 for shareware 1.9, wtf is this*/ public static final int VERSION = 109 ; /** Some parts of the code may actually be better used as if in a UNIX environment */ public static final boolean NORMALUNIX =false; /** If rangecheck is undefined, ost parameter validation debugging code will not be compiled */ public static final boolean RANGECHECK=false; // Do or do not use external soundserver. // The sndserver binary to be run separately // has been introduced by Dave Taylor. // The integrated sound support is experimental, // and unfinished. Default is synchronous. // Experimental asynchronous timer based is // handled by SNDINTR. //#define SNDSERV 1 //#define SNDINTR 1 // Defines suck. C sucks. // C++ might sucks for OOP, but it sure is a better C. // So there. // MAES: moved static defines out of here and into VideoScaleInfo. // State updates, number of tics / second. public static final int BASETICRATE = 35; public static final int TIC_MUL = 1; public static final int TICRATE = BASETICRATE*TIC_MUL; // // Difficulty/skill settings/filters. // // Skill flags. public static int MTF_EASY = 1; public static int MTF_NORMAL = 2; public static int MTF_HARD = 4; // Deaf monsters/do not react to sound. public static int MTF_AMBUSH =8; //Maes: this makes it a bit less retarded. public static final int NUMCARDS=card_t.NUMCARDS.ordinal(); //Maes: this makes it a bit less retarded. public static final int NUMWEAPONS=weapontype_t.NUMWEAPONS.ordinal(); //Maes: this makes it a bit less retarded. public static final int NUMAMMO=ammotype_t.NUMAMMO.ordinal(); // Power up artifacts. public static final int pw_invulnerability=0; public static final int pw_strength=1; public static final int pw_invisibility=2; public static final int pw_ironfeet=3; public static final int pw_allmap=4; public static final int pw_infrared=5; public static final int NUMPOWERS=6; /** Power up durations, * how many seconds till expiration, * assuming TICRATE is 35 ticks/second. */ public static final int INVULNTICS=(30*TICRATE), INVISTICS=(60*TICRATE), INFRATICS=(120*TICRATE), IRONTICS =(60*TICRATE); // Center command from Heretic public final static int TOCENTER=-8; // from r_defs.h: //Silhouette, needed for clipping Segs (mainly) //and sprites representing things. public static final int SIL_NONE =0; public static final int SIL_BOTTOM =1; public static final int SIL_TOP = 2; public static final int SIL_BOTH = 3; //SKY, store the number for name. static public final String SKYFLATNAME ="F_SKY1"; // The sky map is 256*128*4 maps. public static final int ANGLETOSKYSHIFT = 22; // From r_draw.c // status bar height at bottom of screen public static final int SBARHEIGHT = 32; // //Different vetween registered DOOM (1994) and //Ultimate DOOM - Final edition (retail, 1995?). //This is supposedly ignored for commercial //release (aka DOOM II), which had 34 maps //in one episode. So there. public static final int NUMEPISODES=4; public static final int NUMMAPS =9; //in tics //U #define PAUSELEN (TICRATE*2) //U #define SCORESTEP 100 //U #define ANIMPERIOD 32 //pixel distance from "(YOU)" to "PLAYER N" //U #define STARDIST 10 //U #define WK 1 // MAES 23/5/2011: moved SP_... stuff to EndLevel public static final int BACKUPTICS = 12; // From Zone: // //ZONE MEMORY //PU - purge tags. //Tags < 100 are not overwritten until freed. public static final int PU_STATIC = 1; // static entire execution time public static final int PU_SOUND = 2; // static while playing public static final int PU_MUSIC = 3; // static while playing public static final int PU_DAVE = 4; // anything else Dave wants static public static final int PU_LEVEL = 50; // static until level exited public static final int PU_LEVSPEC = 51; // a special thinker in a level //Tags >= 100 are purgable whenever needed. public static final int PU_PURGELEVEL = 100; public static final int PU_CACHE = 101; // From hu_lib.h: //background and foreground screen numbers //different from other modules. public static final int BG = 1; public static final int FG = 0; //font stuff static public final char HU_CHARERASE = Keys.KEY_BACKSPACE; public static final int HU_MAXLINES = 4; public static final int HU_MAXLINELENGTH = 80; // From hu_stuff.h // //Globally visible constants. // static public final byte HU_FONTSTART = '!'; // the first font characters static public final byte HU_FONTEND ='_'; // the last font characters //Calculate # of glyphs in font. public static final int HU_FONTSIZE = (HU_FONTEND - HU_FONTSTART + 1); static public final char HU_BROADCAST = 5; static public final char HU_MSGREFRESH = Keys.KEY_ENTER; static public final char HU_MSGX =0; static public final char HU_MSGY =0; static public final char HU_MSGWIDTH =64; // in characters static public final char HU_MSGHEIGHT = 1; // in lines public static final int HU_MSGTIMEOUT = (4*TICRATE); public static final int SAVESTRINGSIZE = 24; // // Button/action code definitions. // From d_event.h // Press "Fire". public static final int BT_ATTACK = 1; // Use button, to open doors, activate switches. public static final int BT_USE = 2; // Flag: game events, not really buttons. public static final int BT_SPECIAL = 128; public static final int BT_SPECIALMASK = 3; // Flag, weapon change pending. // If true, the next 3 bits hold weapon num. public static final int BT_CHANGE = 4; // The 3bit weapon mask and shift, convenience. public static final int BT_WEAPONMASK = (8+16+32); public static final int BT_WEAPONSHIFT = 3; // Pause the game. public static final int BTS_PAUSE = 1; // Save the game at each console. public static final int BTS_SAVEGAME = 2; // Savegame slot numbers // occupy the second byte of buttons. public static final int BTS_SAVEMASK = (4+8+16); public static final int BTS_SAVESHIFT = 2; //==================== Stuff from r_local.c ========================================= public static final int FLOATSPEED =(FRACUNIT*4); public static final int VIEWHEIGHT = (41*FRACUNIT); // mapblocks are used to check movement // against lines and things public static final int MAPBLOCKUNITS= 128; public static final int MAPBLOCKSIZE =(MAPBLOCKUNITS*FRACUNIT); public static final int MAPBLOCKSHIFT =(FRACBITS+7); public static final int MAPBMASK =(MAPBLOCKSIZE-1); public static final int MAPBTOFRAC= (MAPBLOCKSHIFT-FRACBITS); public static final int BLOCKMAPPADDING= 8*FRACUNIT; // player radius for movement checking public static final int PLAYERRADIUS =16*FRACUNIT; public static final int GRAVITY = MAPFRACUNIT; public static int USERANGE =(64*FRACUNIT); public static int MELEERANGE = (64*FRACUNIT); public static int MISSILERANGE=(32*64*FRACUNIT); // follow a player exlusively for 3 seconds public static int BASETHRESHOLD= 100; public static int PT_ADDLINES =1; public static int PT_ADDTHINGS =2; public static int PT_EARLYOUT =4; // // P_MOBJ // public static int ONFLOORZ = MININT; public static int ONCEILINGZ = MAXINT; // Time interval for item respawning. public static int ITEMQUESIZE =128; /** Indicate a leaf. e6y: support for extended nodes */ public static final int NF_SUBSECTOR = 0x80000000; /** This is the regular leaf indicator. Use for reference/conversions */ public static final int NF_SUBSECTOR_CLASSIC = 0x8000; /** Player states. */ public static final int PST_LIVE=0, // Playing or camping. PST_DEAD=1, // Dead on the ground, view follows killer. PST_REBORN=2; // Ready to restart/respawn??? public static final int FF_FULLBRIGHT = 0x8000; // flag in thing->frame public static final int FF_FRAMEMASK = 0x7fff; static final String rcsid = "$Id: Defines.java,v 1.48 2012/09/24 17:16:22 velktron Exp $"; } package data; import defines.statenum_t; import data.sounds.sfxenum_t; public class mobjinfo_t { public mobjinfo_t(int doomednum, statenum_t spawnstate, int spawnhealth, statenum_t seestate, sfxenum_t seesound, int reactiontime, sfxenum_t attacksound, statenum_t painstate, int painchance, sfxenum_t painsound, statenum_t meleestate, statenum_t missilestate, statenum_t deathstate, statenum_t xdeathstate, sfxenum_t deathsound, int speed, int radius, int height, int mass, int damage, sfxenum_t activesound, long flags, statenum_t raisestate) { super(); this.doomednum = doomednum; this.spawnstate = spawnstate; this.spawnhealth = spawnhealth; this.seestate = seestate; this.seesound = seesound; this.reactiontime = reactiontime; this.attacksound = attacksound; this.painstate = painstate; this.painchance = painchance; this.painsound = painsound; this.meleestate = meleestate; this.missilestate = missilestate; this.deathstate = deathstate; this.xdeathstate = xdeathstate; this.deathsound = deathsound; this.speed = speed; this.radius = radius; this.height = height; this.mass = mass; this.damage = damage; this.activesound = activesound; this.flags = flags; this.raisestate = raisestate; } public int doomednum; public statenum_t spawnstate; public int spawnhealth; public statenum_t seestate; public sfxenum_t seesound; public int reactiontime; public sfxenum_t attacksound; public statenum_t painstate; public int painchance; public sfxenum_t painsound; public statenum_t meleestate; public statenum_t missilestate; public statenum_t deathstate; public statenum_t xdeathstate; public sfxenum_t deathsound; public int speed; public int radius; public int height; public int mass; public int damage; public sfxenum_t activesound; public long flags; public statenum_t raisestate; } package data; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import w.CacheableDoomObject; /** * LineSeg, generated by splitting LineDefs * using partition lines selected by BSP builder. * MAES: this is the ON-DISK structure. The corresponding memory structure, * segs_t, has fixed_t members. */ public class mapseg_t implements CacheableDoomObject{ public mapseg_t(){ } public char v1; public char v2; public char angle; public char linedef; public char side; public char offset; public static int sizeOf(){ return 12; } @Override public void unpack(ByteBuffer buf) throws IOException { buf.order(ByteOrder.LITTLE_ENDIAN); this.v1 = buf.getChar(); this.v2 = buf.getChar(); this.angle=buf.getChar(); this.linedef=buf.getChar(); this.side=buf.getChar(); this.offset=buf.getChar(); } public String toString(){ return String.format("mapseg_t v1,2: %d %d ang: %d ld: %d sd: %d off: %d", (int)v1,(int)v2,(int)angle,(int)linedef,(int)side,(int)offset); } }; package data; // Emacs style mode select -*- C++ -*- //----------------------------------------------------------------------------- // // $Id: doomdata.java,v 1.4 2011/02/11 00:11:13 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: // all external data is defined here // most of the data is loaded into different structures at run time // some internal structures shared by many modules are here // //----------------------------------------------------------------------------- // The most basic types we use, portability. //#include "doomtype.h" // Some global defines, that configure the game. public class doomdata{ // // Map level types. // The following data structures define the persistent format // used in the lumps of the WAD files. // // Lump order in a map WAD: each map needs a couple of lumps // to provide a complete scene geometry description. // Maes: this was defined as a typeless enum, probably intended to be used as cardinality? // Turning it into "ML" enum or int consts. public static enum ML { ML_LABEL, // A separator, name, ExMx or MAPxx ML_THINGS, // Monsters, items.. ML_LINEDEFS, // LineDefs, from editing ML_SIDEDEFS, // SideDefs, from editing ML_VERTEXES, // Vertices, edited and BSP splits generated ML_SEGS, // LineSegs, from LineDefs split by BSP ML_SSECTORS, // SubSectors, list of LineSegs ML_NODES, // BSP nodes ML_SECTORS, // Sectors, from editing ML_REJECT, // LUT, sector-sector visibility ML_BLOCKMAP // LUT, motion clipping, walls/grid element }; // A single Vertex. static class mapvertex_t { short x; short y; }; // A SideDef, defining the visual appearance of a wall, // by setting textures and offsets. static class mapsidedef_t { short textureoffset; short rowoffset; char[] toptexture=new char[8]; char[] bottomtexture=new char[8]; char[] midtexture=new char[8]; // Front sector, towards viewer. short sector; } // A LineDef, as used for editing, and as input // to the BSP builder. static class maplinedef_t { short v1; short v2; short flags; short special; short tag; // sidenum[1] will be -1 if one sided int[] sidenum=new int[2]; } // // LineDef attributes. // // Solid, is an obstacle. public static final int ML_BLOCKING =1; // Blocks monsters only. public static final int ML_BLOCKMONSTERS= 2; // Backside will not be present at all // if not two sided. public static final int ML_TWOSIDED =4; // If a texture is pegged, the texture will have // the end exposed to air held constant at the // top or bottom of the texture (stairs or pulled // down things) and will move with a height change // of one of the neighbor sectors. // Unpegged textures allways have the first row of // the texture at the top pixel of the line for both // top and bottom textures (use next to windows). // upper texture unpegged public static final int ML_DONTPEGTOP=8; // lower texture unpegged public static final int ML_DONTPEGBOTTOM= 16 ; // In AutoMap: don't map as two sided: IT'S A SECRET! public static final int ML_SECRET= 32; // Sound rendering: don't let sound cross two of these. public static final int ML_SOUNDBLOCK= 64; // Don't draw on the automap at all. public static final int ML_DONTDRAW= 128; // Set if already seen, thus drawn in automap. public static final int ML_MAPPED =256; // Sector definition, from editing. class mapsector_t { short floorheight; short ceilingheight; char[] floorpic=new char[8]; char[] ceilingpic=new char[8]; short lightlevel; short special; short tag; }; // SubSector, as generated by BSP. class mapsubsector_t { short numsegs; // Index of first one, segs are stored sequentially. short firstseg; }; // LineSeg, generated by splitting LineDefs // using partition lines selected by BSP builder. class mapseg_t { short v1; short v2; short angle; short linedef; short side; short offset; }; // BSP node structure. // Indicate a leaf. public static int NF_SUBSECTOR =0x8000; class mapnode_t { // Partition line from (x,y) to x+dx,y+dy) short x; short y; short dx; short dy; // Bounding box for each child, // clip against view frustum. short[][] bbox=new short[2][4]; // If NF_SUBSECTOR its a subsector, // else it's a node of another subtree. //Maes: used to be unsigned short int[] children=new int[2]; } ; } package data; import static data.sounds.*; import static m.fixed_t.*; import static p.MobjFlags.*; import defines.statenum_t; import doom.think_t; // Emacs style mode select -*- C++ -*- //----------------------------------------------------------------------------- // // $Id: info.java,v 1.13 2013/06/03 12:36:42 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: * Thing frame/state LUT, generated by multigen utilitiy. * This one is the original DOOM version, preserved. * * @author velktron * */ public class info{ public static final state_t[] states = { // MAES: ATTENTION! The "null" state looks just like an imp. If you see it, then something's // wrong. When you check for "null" state, you should actually check whether something // is set to S_NULL! new state_t(spritenum_t.SPR_TROO,0,-1,null,statenum_t.S_NULL,0,0), // S_NULL // _D_: replaced "null" by "think_t.A_Light0" (see linuxdoom source) // I was trying to make the red fire around the gun apprear when we shoot // but I didn't succeed. This modification below is minor as I didn't see // any change in the game new state_t(spritenum_t.SPR_SHTG,4,0,think_t.A_Light0,statenum_t.S_NULL,0,0), // S_LIGHTDONE new state_t(spritenum_t.SPR_PUNG,0,1,think_t.A_WeaponReady,statenum_t.S_PUNCH,0,0), // S_PUNCH new state_t(spritenum_t.SPR_PUNG,0,1,think_t.A_Lower,statenum_t.S_PUNCHDOWN,0,0), // S_PUNCHDOWN new state_t(spritenum_t.SPR_PUNG,0,1,think_t.A_Raise,statenum_t.S_PUNCHUP,0,0), // S_PUNCHUP new state_t(spritenum_t.SPR_PUNG,1,4,null,statenum_t.S_PUNCH2,0,0), // S_PUNCH1 new state_t(spritenum_t.SPR_PUNG,2,4,think_t.A_Punch,statenum_t.S_PUNCH3,0,0), // S_PUNCH2 new state_t(spritenum_t.SPR_PUNG,3,5,null,statenum_t.S_PUNCH4,0,0), // S_PUNCH3 new state_t(spritenum_t.SPR_PUNG,2,4,null,statenum_t.S_PUNCH5,0,0), // S_PUNCH4 new state_t(spritenum_t.SPR_PUNG,1,5,think_t.A_ReFire,statenum_t.S_PUNCH,0,0), // S_PUNCH5 new state_t(spritenum_t.SPR_PISG,0,1,think_t.A_WeaponReady,statenum_t.S_PISTOL,0,0),// S_PISTOL new state_t(spritenum_t.SPR_PISG,0,1,think_t.A_Lower,statenum_t.S_PISTOLDOWN,0,0), // S_PISTOLDOWN new state_t(spritenum_t.SPR_PISG,0,1,think_t.A_Raise,statenum_t.S_PISTOLUP,0,0), // S_PISTOLUP new state_t(spritenum_t.SPR_PISG,0,4,null,statenum_t.S_PISTOL2,0,0), // S_PISTOL1 new state_t(spritenum_t.SPR_PISG,1,6,think_t.A_FirePistol,statenum_t.S_PISTOL3,0,0),// S_PISTOL2 new state_t(spritenum_t.SPR_PISG,2,4,null,statenum_t.S_PISTOL4,0,0), // S_PISTOL3 new state_t(spritenum_t.SPR_PISG,1,5,think_t.A_ReFire,statenum_t.S_PISTOL,0,0), // S_PISTOL4 new state_t(spritenum_t.SPR_PISF,32768,7,think_t.A_Light1,statenum_t.S_LIGHTDONE,0,0), // S_PISTOLFLASH new state_t(spritenum_t.SPR_SHTG,0,1,think_t.A_WeaponReady,statenum_t.S_SGUN,0,0), // S_SGUN new state_t(spritenum_t.SPR_SHTG,0,1,think_t.A_Lower,statenum_t.S_SGUNDOWN,0,0), // S_SGUNDOWN new state_t(spritenum_t.SPR_SHTG,0,1,think_t.A_Raise,statenum_t.S_SGUNUP,0,0), // S_SGUNUP new state_t(spritenum_t.SPR_SHTG,0,3,null,statenum_t.S_SGUN2,0,0), // S_SGUN1 new state_t(spritenum_t.SPR_SHTG,0,7,think_t.A_FireShotgun,statenum_t.S_SGUN3,0,0), // S_SGUN2 new state_t(spritenum_t.SPR_SHTG,1,5,null,statenum_t.S_SGUN4,0,0), // S_SGUN3 new state_t(spritenum_t.SPR_SHTG,2,5,null,statenum_t.S_SGUN5,0,0), // S_SGUN4 new state_t(spritenum_t.SPR_SHTG,3,4,null,statenum_t.S_SGUN6,0,0), // S_SGUN5 new state_t(spritenum_t.SPR_SHTG,2,5,null,statenum_t.S_SGUN7,0,0), // S_SGUN6 new state_t(spritenum_t.SPR_SHTG,1,5,null,statenum_t.S_SGUN8,0,0), // S_SGUN7 new state_t(spritenum_t.SPR_SHTG,0,3,null,statenum_t.S_SGUN9,0,0), // S_SGUN8 new state_t(spritenum_t.SPR_SHTG,0,7,think_t.A_ReFire,statenum_t.S_SGUN,0,0), // S_SGUN9 new state_t(spritenum_t.SPR_SHTF,32768,4,think_t.A_Light1,statenum_t.S_SGUNFLASH2,0,0), // S_SGUNFLASH1 new state_t(spritenum_t.SPR_SHTF,32769,3,think_t.A_Light2,statenum_t.S_LIGHTDONE,0,0), // S_SGUNFLASH2 new state_t(spritenum_t.SPR_SHT2,0,1,think_t.A_WeaponReady,statenum_t.S_DSGUN,0,0), // S_DSGUN new state_t(spritenum_t.SPR_SHT2,0,1,think_t.A_Lower,statenum_t.S_DSGUNDOWN,0,0), // S_DSGUNDOWN new state_t(spritenum_t.SPR_SHT2,0,1,think_t.A_Raise,statenum_t.S_DSGUNUP,0,0), // S_DSGUNUP new state_t(spritenum_t.SPR_SHT2,0,3,null,statenum_t.S_DSGUN2,0,0), // S_DSGUN1 new state_t(spritenum_t.SPR_SHT2,0,7,think_t.A_FireShotgun2,statenum_t.S_DSGUN3,0,0), // S_DSGUN2 new state_t(spritenum_t.SPR_SHT2,1,7,null,statenum_t.S_DSGUN4,0,0), // S_DSGUN3 new state_t(spritenum_t.SPR_SHT2,2,7,think_t.A_CheckReload,statenum_t.S_DSGUN5,0,0), // S_DSGUN4 new state_t(spritenum_t.SPR_SHT2,3,7,think_t.A_OpenShotgun2,statenum_t.S_DSGUN6,0,0), // S_DSGUN5 new state_t(spritenum_t.SPR_SHT2,4,7,null,statenum_t.S_DSGUN7,0,0), // S_DSGUN6 new state_t(spritenum_t.SPR_SHT2,5,7,think_t.A_LoadShotgun2,statenum_t.S_DSGUN8,0,0), // S_DSGUN7 new state_t(spritenum_t.SPR_SHT2,6,6,null,statenum_t.S_DSGUN9,0,0), // S_DSGUN8 new state_t(spritenum_t.SPR_SHT2,7,6,think_t.A_CloseShotgun2,statenum_t.S_DSGUN10,0,0), // S_DSGUN9 new state_t(spritenum_t.SPR_SHT2,0,5,think_t.A_ReFire,statenum_t.S_DSGUN,0,0), // S_DSGUN10 new state_t(spritenum_t.SPR_SHT2,1,7,null,statenum_t.S_DSNR2,0,0), // S_DSNR1 new state_t(spritenum_t.SPR_SHT2,0,3,null,statenum_t.S_DSGUNDOWN,0,0), // S_DSNR2 new state_t(spritenum_t.SPR_SHT2,32776,5,think_t.A_Light1,statenum_t.S_DSGUNFLASH2,0,0), // S_DSGUNFLASH1 new state_t(spritenum_t.SPR_SHT2,32777,4,think_t.A_Light2,statenum_t.S_LIGHTDONE,0,0), // S_DSGUNFLASH2 new state_t(spritenum_t.SPR_CHGG,0,1,think_t.A_WeaponReady,statenum_t.S_CHAIN,0,0), // S_CHAIN new state_t(spritenum_t.SPR_CHGG,0,1,think_t.A_Lower,statenum_t.S_CHAINDOWN,0,0), // S_CHAINDOWN new state_t(spritenum_t.SPR_CHGG,0,1,think_t.A_Raise,statenum_t.S_CHAINUP,0,0), // S_CHAINUP new state_t(spritenum_t.SPR_CHGG,0,4,think_t.A_FireCGun,statenum_t.S_CHAIN2,0,0), // S_CHAIN1 new state_t(spritenum_t.SPR_CHGG,1,4,think_t.A_FireCGun,statenum_t.S_CHAIN3,0,0), // S_CHAIN2 new state_t(spritenum_t.SPR_CHGG,1,0,think_t.A_ReFire,statenum_t.S_CHAIN,0,0), // S_CHAIN3 new state_t(spritenum_t.SPR_CHGF,32768,5,think_t.A_Light1,statenum_t.S_LIGHTDONE,0,0), // S_CHAINFLASH1 new state_t(spritenum_t.SPR_CHGF,32769,5,think_t.A_Light2,statenum_t.S_LIGHTDONE,0,0), // S_CHAINFLASH2 new state_t(spritenum_t.SPR_MISG,0,1,think_t.A_WeaponReady,statenum_t.S_MISSILE,0,0), // S_MISSILE new state_t(spritenum_t.SPR_MISG,0,1,think_t.A_Lower,statenum_t.S_MISSILEDOWN,0,0), // S_MISSILEDOWN new state_t(spritenum_t.SPR_MISG,0,1,think_t.A_Raise,statenum_t.S_MISSILEUP,0,0), // S_MISSILEUP new state_t(spritenum_t.SPR_MISG,1,8,think_t.A_GunFlash,statenum_t.S_MISSILE2,0,0), // S_MISSILE1 new state_t(spritenum_t.SPR_MISG,1,12,think_t.A_FireMissile,statenum_t.S_MISSILE3,0,0), // S_MISSILE2 new state_t(spritenum_t.SPR_MISG,1,0,think_t.A_ReFire,statenum_t.S_MISSILE,0,0), // S_MISSILE3 new state_t(spritenum_t.SPR_MISF,32768,3,think_t.A_Light1,statenum_t.S_MISSILEFLASH2,0,0), // S_MISSILEFLASH1 new state_t(spritenum_t.SPR_MISF,32769,4,null,statenum_t.S_MISSILEFLASH3,0,0), // S_MISSILEFLASH2 new state_t(spritenum_t.SPR_MISF,32770,4,think_t.A_Light2,statenum_t.S_MISSILEFLASH4,0,0), // S_MISSILEFLASH3 new state_t(spritenum_t.SPR_MISF,32771,4,think_t.A_Light2,statenum_t.S_LIGHTDONE,0,0), // S_MISSILEFLASH4 new state_t(spritenum_t.SPR_SAWG,2,4,think_t.A_WeaponReady,statenum_t.S_SAWB,0,0), // S_SAW new state_t(spritenum_t.SPR_SAWG,3,4,think_t.A_WeaponReady,statenum_t.S_SAW,0,0), // S_SAWB new state_t(spritenum_t.SPR_SAWG,2,1,think_t.A_Lower,statenum_t.S_SAWDOWN,0,0), // S_SAWDOWN new state_t(spritenum_t.SPR_SAWG,2,1,think_t.A_Raise,statenum_t.S_SAWUP,0,0), // S_SAWUP new state_t(spritenum_t.SPR_SAWG,0,4,think_t.A_Saw,statenum_t.S_SAW2,0,0), // S_SAW1 new state_t(spritenum_t.SPR_SAWG,1,4,think_t.A_Saw,statenum_t.S_SAW3,0,0), // S_SAW2 new state_t(spritenum_t.SPR_SAWG,1,0,think_t.A_ReFire,statenum_t.S_SAW,0,0), // S_SAW3 new state_t(spritenum_t.SPR_PLSG,0,1,think_t.A_WeaponReady,statenum_t.S_PLASMA,0,0), // S_PLASMA new state_t(spritenum_t.SPR_PLSG,0,1,think_t.A_Lower,statenum_t.S_PLASMADOWN,0,0), // S_PLASMADOWN new state_t(spritenum_t.SPR_PLSG,0,1,think_t.A_Raise,statenum_t.S_PLASMAUP,0,0), // S_PLASMAUP new state_t(spritenum_t.SPR_PLSG,0,3,think_t.A_FirePlasma,statenum_t.S_PLASMA2,0,0), // S_PLASMA1 new state_t(spritenum_t.SPR_PLSG,1,20,think_t.A_ReFire,statenum_t.S_PLASMA,0,0), // S_PLASMA2 new state_t(spritenum_t.SPR_PLSF,32768,4,think_t.A_Light1,statenum_t.S_LIGHTDONE,0,0), // S_PLASMAFLASH1 new state_t(spritenum_t.SPR_PLSF,32769,4,think_t.A_Light1,statenum_t.S_LIGHTDONE,0,0), // S_PLASMAFLASH2 new state_t(spritenum_t.SPR_BFGG,0,1,think_t.A_WeaponReady,statenum_t.S_BFG,0,0), // S_BFG new state_t(spritenum_t.SPR_BFGG,0,1,think_t.A_Lower,statenum_t.S_BFGDOWN,0,0), // S_BFGDOWN new state_t(spritenum_t.SPR_BFGG,0,1,think_t.A_Raise,statenum_t.S_BFGUP,0,0), // S_BFGUP new state_t(spritenum_t.SPR_BFGG,0,20,think_t.A_BFGsound,statenum_t.S_BFG2,0,0), // S_BFG1 new state_t(spritenum_t.SPR_BFGG,1,10,think_t.A_GunFlash,statenum_t.S_BFG3,0,0), // S_BFG2 new state_t(spritenum_t.SPR_BFGG,1,10,think_t.A_FireBFG,statenum_t.S_BFG4,0,0), // S_BFG3 new state_t(spritenum_t.SPR_BFGG,1,20,think_t.A_ReFire,statenum_t.S_BFG,0,0), // S_BFG4 new state_t(spritenum_t.SPR_BFGF,32768,11,think_t.A_Light1,statenum_t.S_BFGFLASH2,0,0), // S_BFGFLASH1 new state_t(spritenum_t.SPR_BFGF,32769,6,think_t.A_Light2,statenum_t.S_LIGHTDONE,0,0), // S_BFGFLASH2 new state_t(spritenum_t.SPR_BLUD,2,8,null,statenum_t.S_BLOOD2,0,0), // S_BLOOD1 new state_t(spritenum_t.SPR_BLUD,1,8,null,statenum_t.S_BLOOD3,0,0), // S_BLOOD2 new state_t(spritenum_t.SPR_BLUD,0,8,null,statenum_t.S_NULL,0,0), // S_BLOOD3 new state_t(spritenum_t.SPR_PUFF,32768,4,null,statenum_t.S_PUFF2,0,0), // S_PUFF1 new state_t(spritenum_t.SPR_PUFF,1,4,null,statenum_t.S_PUFF3,0,0), // S_PUFF2 new state_t(spritenum_t.SPR_PUFF,2,4,null,statenum_t.S_PUFF4,0,0), // S_PUFF3 new state_t(spritenum_t.SPR_PUFF,3,4,null,statenum_t.S_NULL,0,0), // S_PUFF4 new state_t(spritenum_t.SPR_BAL1,32768,4,null,statenum_t.S_TBALL2,0,0), // S_TBALL1 new state_t(spritenum_t.SPR_BAL1,32769,4,null,statenum_t.S_TBALL1,0,0), // S_TBALL2 new state_t(spritenum_t.SPR_BAL1,32770,6,null,statenum_t.S_TBALLX2,0,0), // S_TBALLX1 new state_t(spritenum_t.SPR_BAL1,32771,6,null,statenum_t.S_TBALLX3,0,0), // S_TBALLX2 new state_t(spritenum_t.SPR_BAL1,32772,6,null,statenum_t.S_NULL,0,0), // S_TBALLX3 new state_t(spritenum_t.SPR_BAL2,32768,4,null,statenum_t.S_RBALL2,0,0), // S_RBALL1 new state_t(spritenum_t.SPR_BAL2,32769,4,null,statenum_t.S_RBALL1,0,0), // S_RBALL2 new state_t(spritenum_t.SPR_BAL2,32770,6,null,statenum_t.S_RBALLX2,0,0), // S_RBALLX1 new state_t(spritenum_t.SPR_BAL2,32771,6,null,statenum_t.S_RBALLX3,0,0), // S_RBALLX2 new state_t(spritenum_t.SPR_BAL2,32772,6,null,statenum_t.S_NULL,0,0), // S_RBALLX3 new state_t(spritenum_t.SPR_PLSS,32768,6,null,statenum_t.S_PLASBALL2,0,0), // S_PLASBALL new state_t(spritenum_t.SPR_PLSS,32769,6,null,statenum_t.S_PLASBALL,0,0), // S_PLASBALL2 new state_t(spritenum_t.SPR_PLSE,32768,4,null,statenum_t.S_PLASEXP2,0,0), // S_PLASEXP new state_t(spritenum_t.SPR_PLSE,32769,4,null,statenum_t.S_PLASEXP3,0,0), // S_PLASEXP2 new state_t(spritenum_t.SPR_PLSE,32770,4,null,statenum_t.S_PLASEXP4,0,0), // S_PLASEXP3 new state_t(spritenum_t.SPR_PLSE,32771,4,null,statenum_t.S_PLASEXP5,0,0), // S_PLASEXP4 new state_t(spritenum_t.SPR_PLSE,32772,4,null,statenum_t.S_NULL,0,0), // S_PLASEXP5 new state_t(spritenum_t.SPR_MISL,32768,1,null,statenum_t.S_ROCKET,0,0), // S_ROCKET new state_t(spritenum_t.SPR_BFS1,32768,4,null,statenum_t.S_BFGSHOT2,0,0), // S_BFGSHOT new state_t(spritenum_t.SPR_BFS1,32769,4,null,statenum_t.S_BFGSHOT,0,0), // S_BFGSHOT2 new state_t(spritenum_t.SPR_BFE1,32768,8,null,statenum_t.S_BFGLAND2,0,0), // S_BFGLAND new state_t(spritenum_t.SPR_BFE1,32769,8,null,statenum_t.S_BFGLAND3,0,0), // S_BFGLAND2 new state_t(spritenum_t.SPR_BFE1,32770,8,think_t.A_BFGSpray,statenum_t.S_BFGLAND4,0,0), // S_BFGLAND3 new state_t(spritenum_t.SPR_BFE1,32771,8,null,statenum_t.S_BFGLAND5,0,0), // S_BFGLAND4 new state_t(spritenum_t.SPR_BFE1,32772,8,null,statenum_t.S_BFGLAND6,0,0), // S_BFGLAND5 new state_t(spritenum_t.SPR_BFE1,32773,8,null,statenum_t.S_NULL,0,0), // S_BFGLAND6 new state_t(spritenum_t.SPR_BFE2,32768,8,null,statenum_t.S_BFGEXP2,0,0), // S_BFGEXP new state_t(spritenum_t.SPR_BFE2,32769,8,null,statenum_t.S_BFGEXP3,0,0), // S_BFGEXP2 new state_t(spritenum_t.SPR_BFE2,32770,8,null,statenum_t.S_BFGEXP4,0,0), // S_BFGEXP3 new state_t(spritenum_t.SPR_BFE2,32771,8,null,statenum_t.S_NULL,0,0), // S_BFGEXP4 new state_t(spritenum_t.SPR_MISL,32769,8,think_t.A_Explode,statenum_t.S_EXPLODE2,0,0), // S_EXPLODE1 new state_t(spritenum_t.SPR_MISL,32770,6,null,statenum_t.S_EXPLODE3,0,0), // S_EXPLODE2 new state_t(spritenum_t.SPR_MISL,32771,4,null,statenum_t.S_NULL,0,0), // S_EXPLODE3 new state_t(spritenum_t.SPR_TFOG,32768,6,null,statenum_t.S_TFOG01,0,0), // S_TFOG new state_t(spritenum_t.SPR_TFOG,32769,6,null,statenum_t.S_TFOG02,0,0), // S_TFOG01 new state_t(spritenum_t.SPR_TFOG,32768,6,null,statenum_t.S_TFOG2,0,0), // S_TFOG02 new state_t(spritenum_t.SPR_TFOG,32769,6,null,statenum_t.S_TFOG3,0,0), // S_TFOG2 new state_t(spritenum_t.SPR_TFOG,32770,6,null,statenum_t.S_TFOG4,0,0), // S_TFOG3 new state_t(spritenum_t.SPR_TFOG,32771,6,null,statenum_t.S_TFOG5,0,0), // S_TFOG4 new state_t(spritenum_t.SPR_TFOG,32772,6,null,statenum_t.S_TFOG6,0,0), // S_TFOG5 new state_t(spritenum_t.SPR_TFOG,32773,6,null,statenum_t.S_TFOG7,0,0), // S_TFOG6 new state_t(spritenum_t.SPR_TFOG,32774,6,null,statenum_t.S_TFOG8,0,0), // S_TFOG7 new state_t(spritenum_t.SPR_TFOG,32775,6,null,statenum_t.S_TFOG9,0,0), // S_TFOG8 new state_t(spritenum_t.SPR_TFOG,32776,6,null,statenum_t.S_TFOG10,0,0), // S_TFOG9 new state_t(spritenum_t.SPR_TFOG,32777,6,null,statenum_t.S_NULL,0,0), // S_TFOG10 new state_t(spritenum_t.SPR_IFOG,32768,6,null,statenum_t.S_IFOG01,0,0), // S_IFOG new state_t(spritenum_t.SPR_IFOG,32769,6,null,statenum_t.S_IFOG02,0,0), // S_IFOG01 new state_t(spritenum_t.SPR_IFOG,32768,6,null,statenum_t.S_IFOG2,0,0), // S_IFOG02 new state_t(spritenum_t.SPR_IFOG,32769,6,null,statenum_t.S_IFOG3,0,0), // S_IFOG2 new state_t(spritenum_t.SPR_IFOG,32770,6,null,statenum_t.S_IFOG4,0,0), // S_IFOG3 new state_t(spritenum_t.SPR_IFOG,32771,6,null,statenum_t.S_IFOG5,0,0), // S_IFOG4 new state_t(spritenum_t.SPR_IFOG,32772,6,null,statenum_t.S_NULL,0,0), // S_IFOG5 new state_t(spritenum_t.SPR_PLAY,0,-1,null,statenum_t.S_NULL,0,0), // S_PLAY new state_t(spritenum_t.SPR_PLAY,0,4,null,statenum_t.S_PLAY_RUN2,0,0), // S_PLAY_RUN1 new state_t(spritenum_t.SPR_PLAY,1,4,null,statenum_t.S_PLAY_RUN3,0,0), // S_PLAY_RUN2 new state_t(spritenum_t.SPR_PLAY,2,4,null,statenum_t.S_PLAY_RUN4,0,0), // S_PLAY_RUN3 new state_t(spritenum_t.SPR_PLAY,3,4,null,statenum_t.S_PLAY_RUN1,0,0), // S_PLAY_RUN4 new state_t(spritenum_t.SPR_PLAY,4,12,null,statenum_t.S_PLAY,0,0), // S_PLAY_ATK1 new state_t(spritenum_t.SPR_PLAY,32773,6,null,statenum_t.S_PLAY_ATK1,0,0), // S_PLAY_ATK2 new state_t(spritenum_t.SPR_PLAY,6,4,null,statenum_t.S_PLAY_PAIN2,0,0), // S_PLAY_PAIN new state_t(spritenum_t.SPR_PLAY,6,4,think_t.A_Pain,statenum_t.S_PLAY,0,0), // S_PLAY_PAIN2 new state_t(spritenum_t.SPR_PLAY,7,10,null,statenum_t.S_PLAY_DIE2,0,0), // S_PLAY_DIE1 new state_t(spritenum_t.SPR_PLAY,8,10,think_t.A_PlayerScream,statenum_t.S_PLAY_DIE3,0,0), // S_PLAY_DIE2 new state_t(spritenum_t.SPR_PLAY,9,10,think_t.A_Fall,statenum_t.S_PLAY_DIE4,0,0), // S_PLAY_DIE3 new state_t(spritenum_t.SPR_PLAY,10,10,null,statenum_t.S_PLAY_DIE5,0,0), // S_PLAY_DIE4 new state_t(spritenum_t.SPR_PLAY,11,10,null,statenum_t.S_PLAY_DIE6,0,0), // S_PLAY_DIE5 new state_t(spritenum_t.SPR_PLAY,12,10,null,statenum_t.S_PLAY_DIE7,0,0), // S_PLAY_DIE6 new state_t(spritenum_t.SPR_PLAY,13,-1,null,statenum_t.S_NULL,0,0), // S_PLAY_DIE7 new state_t(spritenum_t.SPR_PLAY,14,5,null,statenum_t.S_PLAY_XDIE2,0,0), // S_PLAY_XDIE1 new state_t(spritenum_t.SPR_PLAY,15,5,think_t.A_XScream,statenum_t.S_PLAY_XDIE3,0,0), // S_PLAY_XDIE2 new state_t(spritenum_t.SPR_PLAY,16,5,think_t.A_Fall,statenum_t.S_PLAY_XDIE4,0,0), // S_PLAY_XDIE3 new state_t(spritenum_t.SPR_PLAY,17,5,null,statenum_t.S_PLAY_XDIE5,0,0), // S_PLAY_XDIE4 new state_t(spritenum_t.SPR_PLAY,18,5,null,statenum_t.S_PLAY_XDIE6,0,0), // S_PLAY_XDIE5 new state_t(spritenum_t.SPR_PLAY,19,5,null,statenum_t.S_PLAY_XDIE7,0,0), // S_PLAY_XDIE6 new state_t(spritenum_t.SPR_PLAY,20,5,null,statenum_t.S_PLAY_XDIE8,0,0), // S_PLAY_XDIE7 new state_t(spritenum_t.SPR_PLAY,21,5,null,statenum_t.S_PLAY_XDIE9,0,0), // S_PLAY_XDIE8 new state_t(spritenum_t.SPR_PLAY,22,-1,null,statenum_t.S_NULL,0,0), // S_PLAY_XDIE9 new state_t(spritenum_t.SPR_POSS,0,10,think_t.A_Look,statenum_t.S_POSS_STND2,0,0), // S_POSS_STND new state_t(spritenum_t.SPR_POSS,1,10,think_t.A_Look,statenum_t.S_POSS_STND,0,0), // S_POSS_STND2 new state_t(spritenum_t.SPR_POSS,0,4,think_t.A_Chase,statenum_t.S_POSS_RUN2,0,0), // S_POSS_RUN1 new state_t(spritenum_t.SPR_POSS,0,4,think_t.A_Chase,statenum_t.S_POSS_RUN3,0,0), // S_POSS_RUN2 new state_t(spritenum_t.SPR_POSS,1,4,think_t.A_Chase,statenum_t.S_POSS_RUN4,0,0), // S_POSS_RUN3 new state_t(spritenum_t.SPR_POSS,1,4,think_t.A_Chase,statenum_t.S_POSS_RUN5,0,0), // S_POSS_RUN4 new state_t(spritenum_t.SPR_POSS,2,4,think_t.A_Chase,statenum_t.S_POSS_RUN6,0,0), // S_POSS_RUN5 new state_t(spritenum_t.SPR_POSS,2,4,think_t.A_Chase,statenum_t.S_POSS_RUN7,0,0), // S_POSS_RUN6 new state_t(spritenum_t.SPR_POSS,3,4,think_t.A_Chase,statenum_t.S_POSS_RUN8,0,0), // S_POSS_RUN7 new state_t(spritenum_t.SPR_POSS,3,4,think_t.A_Chase,statenum_t.S_POSS_RUN1,0,0), // S_POSS_RUN8 new state_t(spritenum_t.SPR_POSS,4,10,think_t.A_FaceTarget,statenum_t.S_POSS_ATK2,0,0), // S_POSS_ATK1 new state_t(spritenum_t.SPR_POSS,5,8,think_t.A_PosAttack,statenum_t.S_POSS_ATK3,0,0), // S_POSS_ATK2 new state_t(spritenum_t.SPR_POSS,4,8,null,statenum_t.S_POSS_RUN1,0,0), // S_POSS_ATK3 new state_t(spritenum_t.SPR_POSS,6,3,null,statenum_t.S_POSS_PAIN2,0,0), // S_POSS_PAIN new state_t(spritenum_t.SPR_POSS,6,3,think_t.A_Pain,statenum_t.S_POSS_RUN1,0,0), // S_POSS_PAIN2 new state_t(spritenum_t.SPR_POSS,7,5,null,statenum_t.S_POSS_DIE2,0,0), // S_POSS_DIE1 new state_t(spritenum_t.SPR_POSS,8,5,think_t.A_Scream,statenum_t.S_POSS_DIE3,0,0), // S_POSS_DIE2 new state_t(spritenum_t.SPR_POSS,9,5,think_t.A_Fall,statenum_t.S_POSS_DIE4,0,0), // S_POSS_DIE3 new state_t(spritenum_t.SPR_POSS,10,5,null,statenum_t.S_POSS_DIE5,0,0), // S_POSS_DIE4 new state_t(spritenum_t.SPR_POSS,11,-1,null,statenum_t.S_NULL,0,0), // S_POSS_DIE5 new state_t(spritenum_t.SPR_POSS,12,5,null,statenum_t.S_POSS_XDIE2,0,0), // S_POSS_XDIE1 new state_t(spritenum_t.SPR_POSS,13,5,think_t.A_XScream,statenum_t.S_POSS_XDIE3,0,0), // S_POSS_XDIE2 new state_t(spritenum_t.SPR_POSS,14,5,think_t.A_Fall,statenum_t.S_POSS_XDIE4,0,0), // S_POSS_XDIE3 new state_t(spritenum_t.SPR_POSS,15,5,null,statenum_t.S_POSS_XDIE5,0,0), // S_POSS_XDIE4 new state_t(spritenum_t.SPR_POSS,16,5,null,statenum_t.S_POSS_XDIE6,0,0), // S_POSS_XDIE5 new state_t(spritenum_t.SPR_POSS,17,5,null,statenum_t.S_POSS_XDIE7,0,0), // S_POSS_XDIE6 new state_t(spritenum_t.SPR_POSS,18,5,null,statenum_t.S_POSS_XDIE8,0,0), // S_POSS_XDIE7 new state_t(spritenum_t.SPR_POSS,19,5,null,statenum_t.S_POSS_XDIE9,0,0), // S_POSS_XDIE8 new state_t(spritenum_t.SPR_POSS,20,-1,null,statenum_t.S_NULL,0,0), // S_POSS_XDIE9 new state_t(spritenum_t.SPR_POSS,10,5,null,statenum_t.S_POSS_RAISE2,0,0), // S_POSS_RAISE1 new state_t(spritenum_t.SPR_POSS,9,5,null,statenum_t.S_POSS_RAISE3,0,0), // S_POSS_RAISE2 new state_t(spritenum_t.SPR_POSS,8,5,null,statenum_t.S_POSS_RAISE4,0,0), // S_POSS_RAISE3 new state_t(spritenum_t.SPR_POSS,7,5,null,statenum_t.S_POSS_RUN1,0,0), // S_POSS_RAISE4 new state_t(spritenum_t.SPR_SPOS,0,10,think_t.A_Look,statenum_t.S_SPOS_STND2,0,0), // S_SPOS_STND new state_t(spritenum_t.SPR_SPOS,1,10,think_t.A_Look,statenum_t.S_SPOS_STND,0,0), // S_SPOS_STND2 new state_t(spritenum_t.SPR_SPOS,0,3,think_t.A_Chase,statenum_t.S_SPOS_RUN2,0,0), // S_SPOS_RUN1 new state_t(spritenum_t.SPR_SPOS,0,3,think_t.A_Chase,statenum_t.S_SPOS_RUN3,0,0), // S_SPOS_RUN2 new state_t(spritenum_t.SPR_SPOS,1,3,think_t.A_Chase,statenum_t.S_SPOS_RUN4,0,0), // S_SPOS_RUN3 new state_t(spritenum_t.SPR_SPOS,1,3,think_t.A_Chase,statenum_t.S_SPOS_RUN5,0,0), // S_SPOS_RUN4 new state_t(spritenum_t.SPR_SPOS,2,3,think_t.A_Chase,statenum_t.S_SPOS_RUN6,0,0), // S_SPOS_RUN5 new state_t(spritenum_t.SPR_SPOS,2,3,think_t.A_Chase,statenum_t.S_SPOS_RUN7,0,0), // S_SPOS_RUN6 new state_t(spritenum_t.SPR_SPOS,3,3,think_t.A_Chase,statenum_t.S_SPOS_RUN8,0,0), // S_SPOS_RUN7 new state_t(spritenum_t.SPR_SPOS,3,3,think_t.A_Chase,statenum_t.S_SPOS_RUN1,0,0), // S_SPOS_RUN8 new state_t(spritenum_t.SPR_SPOS,4,10,think_t.A_FaceTarget,statenum_t.S_SPOS_ATK2,0,0), // S_SPOS_ATK1 new state_t(spritenum_t.SPR_SPOS,32773,10,think_t.A_SPosAttack,statenum_t.S_SPOS_ATK3,0,0), // S_SPOS_ATK2 new state_t(spritenum_t.SPR_SPOS,4,10,null,statenum_t.S_SPOS_RUN1,0,0), // S_SPOS_ATK3 new state_t(spritenum_t.SPR_SPOS,6,3,null,statenum_t.S_SPOS_PAIN2,0,0), // S_SPOS_PAIN new state_t(spritenum_t.SPR_SPOS,6,3,think_t.A_Pain,statenum_t.S_SPOS_RUN1,0,0), // S_SPOS_PAIN2 new state_t(spritenum_t.SPR_SPOS,7,5,null,statenum_t.S_SPOS_DIE2,0,0), // S_SPOS_DIE1 new state_t(spritenum_t.SPR_SPOS,8,5,think_t.A_Scream,statenum_t.S_SPOS_DIE3,0,0), // S_SPOS_DIE2 new state_t(spritenum_t.SPR_SPOS,9,5,think_t.A_Fall,statenum_t.S_SPOS_DIE4,0,0), // S_SPOS_DIE3 new state_t(spritenum_t.SPR_SPOS,10,5,null,statenum_t.S_SPOS_DIE5,0,0), // S_SPOS_DIE4 new state_t(spritenum_t.SPR_SPOS,11,-1,null,statenum_t.S_NULL,0,0), // S_SPOS_DIE5 new state_t(spritenum_t.SPR_SPOS,12,5,null,statenum_t.S_SPOS_XDIE2,0,0), // S_SPOS_XDIE1 new state_t(spritenum_t.SPR_SPOS,13,5,think_t.A_XScream,statenum_t.S_SPOS_XDIE3,0,0), // S_SPOS_XDIE2 new state_t(spritenum_t.SPR_SPOS,14,5,think_t.A_Fall,statenum_t.S_SPOS_XDIE4,0,0), // S_SPOS_XDIE3 new state_t(spritenum_t.SPR_SPOS,15,5,null,statenum_t.S_SPOS_XDIE5,0,0), // S_SPOS_XDIE4 new state_t(spritenum_t.SPR_SPOS,16,5,null,statenum_t.S_SPOS_XDIE6,0,0), // S_SPOS_XDIE5 new state_t(spritenum_t.SPR_SPOS,17,5,null,statenum_t.S_SPOS_XDIE7,0,0), // S_SPOS_XDIE6 new state_t(spritenum_t.SPR_SPOS,18,5,null,statenum_t.S_SPOS_XDIE8,0,0), // S_SPOS_XDIE7 new state_t(spritenum_t.SPR_SPOS,19,5,null,statenum_t.S_SPOS_XDIE9,0,0), // S_SPOS_XDIE8 new state_t(spritenum_t.SPR_SPOS,20,-1,null,statenum_t.S_NULL,0,0), // S_SPOS_XDIE9 new state_t(spritenum_t.SPR_SPOS,11,5,null,statenum_t.S_SPOS_RAISE2,0,0), // S_SPOS_RAISE1 new state_t(spritenum_t.SPR_SPOS,10,5,null,statenum_t.S_SPOS_RAISE3,0,0), // S_SPOS_RAISE2 new state_t(spritenum_t.SPR_SPOS,9,5,null,statenum_t.S_SPOS_RAISE4,0,0), // S_SPOS_RAISE3 new state_t(spritenum_t.SPR_SPOS,8,5,null,statenum_t.S_SPOS_RAISE5,0,0), // S_SPOS_RAISE4 new state_t(spritenum_t.SPR_SPOS,7,5,null,statenum_t.S_SPOS_RUN1,0,0), // S_SPOS_RAISE5 new state_t(spritenum_t.SPR_VILE,0,10,think_t.A_Look,statenum_t.S_VILE_STND2,0,0), // S_VILE_STND new state_t(spritenum_t.SPR_VILE,1,10,think_t.A_Look,statenum_t.S_VILE_STND,0,0), // S_VILE_STND2 new state_t(spritenum_t.SPR_VILE,0,2,think_t.A_VileChase,statenum_t.S_VILE_RUN2,0,0), // S_VILE_RUN1 new state_t(spritenum_t.SPR_VILE,0,2,think_t.A_VileChase,statenum_t.S_VILE_RUN3,0,0), // S_VILE_RUN2 new state_t(spritenum_t.SPR_VILE,1,2,think_t.A_VileChase,statenum_t.S_VILE_RUN4,0,0), // S_VILE_RUN3 new state_t(spritenum_t.SPR_VILE,1,2,think_t.A_VileChase,statenum_t.S_VILE_RUN5,0,0), // S_VILE_RUN4 new state_t(spritenum_t.SPR_VILE,2,2,think_t.A_VileChase,statenum_t.S_VILE_RUN6,0,0), // S_VILE_RUN5 new state_t(spritenum_t.SPR_VILE,2,2,think_t.A_VileChase,statenum_t.S_VILE_RUN7,0,0), // S_VILE_RUN6 new state_t(spritenum_t.SPR_VILE,3,2,think_t.A_VileChase,statenum_t.S_VILE_RUN8,0,0), // S_VILE_RUN7 new state_t(spritenum_t.SPR_VILE,3,2,think_t.A_VileChase,statenum_t.S_VILE_RUN9,0,0), // S_VILE_RUN8 new state_t(spritenum_t.SPR_VILE,4,2,think_t.A_VileChase,statenum_t.S_VILE_RUN10,0,0), // S_VILE_RUN9 new state_t(spritenum_t.SPR_VILE,4,2,think_t.A_VileChase,statenum_t.S_VILE_RUN11,0,0), // S_VILE_RUN10 new state_t(spritenum_t.SPR_VILE,5,2,think_t.A_VileChase,statenum_t.S_VILE_RUN12,0,0), // S_VILE_RUN11 new state_t(spritenum_t.SPR_VILE,5,2,think_t.A_VileChase,statenum_t.S_VILE_RUN1,0,0), // S_VILE_RUN12 new state_t(spritenum_t.SPR_VILE,32774,0,think_t.A_VileStart,statenum_t.S_VILE_ATK2,0,0), // S_VILE_ATK1 new state_t(spritenum_t.SPR_VILE,32774,10,think_t.A_FaceTarget,statenum_t.S_VILE_ATK3,0,0), // S_VILE_ATK2 new state_t(spritenum_t.SPR_VILE,32775,8,think_t.A_VileTarget,statenum_t.S_VILE_ATK4,0,0), // S_VILE_ATK3 new state_t(spritenum_t.SPR_VILE,32776,8,think_t.A_FaceTarget,statenum_t.S_VILE_ATK5,0,0), // S_VILE_ATK4 new state_t(spritenum_t.SPR_VILE,32777,8,think_t.A_FaceTarget,statenum_t.S_VILE_ATK6,0,0), // S_VILE_ATK5 new state_t(spritenum_t.SPR_VILE,32778,8,think_t.A_FaceTarget,statenum_t.S_VILE_ATK7,0,0), // S_VILE_ATK6 new state_t(spritenum_t.SPR_VILE,32779,8,think_t.A_FaceTarget,statenum_t.S_VILE_ATK8,0,0), // S_VILE_ATK7 new state_t(spritenum_t.SPR_VILE,32780,8,think_t.A_FaceTarget,statenum_t.S_VILE_ATK9,0,0), // S_VILE_ATK8 new state_t(spritenum_t.SPR_VILE,32781,8,think_t.A_FaceTarget,statenum_t.S_VILE_ATK10,0,0), // S_VILE_ATK9 new state_t(spritenum_t.SPR_VILE,32782,8,think_t.A_VileAttack,statenum_t.S_VILE_ATK11,0,0), // S_VILE_ATK10 new state_t(spritenum_t.SPR_VILE,32783,20,null,statenum_t.S_VILE_RUN1,0,0), // S_VILE_ATK11 new state_t(spritenum_t.SPR_VILE,32794,10,null,statenum_t.S_VILE_HEAL2,0,0), // S_VILE_HEAL1 new state_t(spritenum_t.SPR_VILE,32795,10,null,statenum_t.S_VILE_HEAL3,0,0), // S_VILE_HEAL2 new state_t(spritenum_t.SPR_VILE,32796,10,null,statenum_t.S_VILE_RUN1,0,0), // S_VILE_HEAL3 new state_t(spritenum_t.SPR_VILE,16,5,null,statenum_t.S_VILE_PAIN2,0,0), // S_VILE_PAIN new state_t(spritenum_t.SPR_VILE,16,5,think_t.A_Pain,statenum_t.S_VILE_RUN1,0,0), // S_VILE_PAIN2 new state_t(spritenum_t.SPR_VILE,16,7,null,statenum_t.S_VILE_DIE2,0,0), // S_VILE_DIE1 new state_t(spritenum_t.SPR_VILE,17,7,think_t.A_Scream,statenum_t.S_VILE_DIE3,0,0), // S_VILE_DIE2 new state_t(spritenum_t.SPR_VILE,18,7,think_t.A_Fall,statenum_t.S_VILE_DIE4,0,0), // S_VILE_DIE3 new state_t(spritenum_t.SPR_VILE,19,7,null,statenum_t.S_VILE_DIE5,0,0), // S_VILE_DIE4 new state_t(spritenum_t.SPR_VILE,20,7,null,statenum_t.S_VILE_DIE6,0,0), // S_VILE_DIE5 new state_t(spritenum_t.SPR_VILE,21,7,null,statenum_t.S_VILE_DIE7,0,0), // S_VILE_DIE6 new state_t(spritenum_t.SPR_VILE,22,7,null,statenum_t.S_VILE_DIE8,0,0), // S_VILE_DIE7 new state_t(spritenum_t.SPR_VILE,23,5,null,statenum_t.S_VILE_DIE9,0,0), // S_VILE_DIE8 new state_t(spritenum_t.SPR_VILE,24,5,null,statenum_t.S_VILE_DIE10,0,0), // S_VILE_DIE9 new state_t(spritenum_t.SPR_VILE,25,-1,null,statenum_t.S_NULL,0,0), // S_VILE_DIE10 new state_t(spritenum_t.SPR_FIRE,32768,2,think_t.A_StartFire,statenum_t.S_FIRE2,0,0), // S_FIRE1 new state_t(spritenum_t.SPR_FIRE,32769,2,think_t.A_Fire,statenum_t.S_FIRE3,0,0), // S_FIRE2 new state_t(spritenum_t.SPR_FIRE,32768,2,think_t.A_Fire,statenum_t.S_FIRE4,0,0), // S_FIRE3 new state_t(spritenum_t.SPR_FIRE,32769,2,think_t.A_Fire,statenum_t.S_FIRE5,0,0), // S_FIRE4 new state_t(spritenum_t.SPR_FIRE,32770,2,think_t.A_FireCrackle,statenum_t.S_FIRE6,0,0), // S_FIRE5 new state_t(spritenum_t.SPR_FIRE,32769,2,think_t.A_Fire,statenum_t.S_FIRE7,0,0), // S_FIRE6 new state_t(spritenum_t.SPR_FIRE,32770,2,think_t.A_Fire,statenum_t.S_FIRE8,0,0), // S_FIRE7 new state_t(spritenum_t.SPR_FIRE,32769,2,think_t.A_Fire,statenum_t.S_FIRE9,0,0), // S_FIRE8 new state_t(spritenum_t.SPR_FIRE,32770,2,think_t.A_Fire,statenum_t.S_FIRE10,0,0), // S_FIRE9 new state_t(spritenum_t.SPR_FIRE,32771,2,think_t.A_Fire,statenum_t.S_FIRE11,0,0), // S_FIRE10 new state_t(spritenum_t.SPR_FIRE,32770,2,think_t.A_Fire,statenum_t.S_FIRE12,0,0), // S_FIRE11 new state_t(spritenum_t.SPR_FIRE,32771,2,think_t.A_Fire,statenum_t.S_FIRE13,0,0), // S_FIRE12 new state_t(spritenum_t.SPR_FIRE,32770,2,think_t.A_Fire,statenum_t.S_FIRE14,0,0), // S_FIRE13 new state_t(spritenum_t.SPR_FIRE,32771,2,think_t.A_Fire,statenum_t.S_FIRE15,0,0), // S_FIRE14 new state_t(spritenum_t.SPR_FIRE,32772,2,think_t.A_Fire,statenum_t.S_FIRE16,0,0), // S_FIRE15 new state_t(spritenum_t.SPR_FIRE,32771,2,think_t.A_Fire,statenum_t.S_FIRE17,0,0), // S_FIRE16 new state_t(spritenum_t.SPR_FIRE,32772,2,think_t.A_Fire,statenum_t.S_FIRE18,0,0), // S_FIRE17 new state_t(spritenum_t.SPR_FIRE,32771,2,think_t.A_Fire,statenum_t.S_FIRE19,0,0), // S_FIRE18 new state_t(spritenum_t.SPR_FIRE,32772,2,think_t.A_FireCrackle,statenum_t.S_FIRE20,0,0), // S_FIRE19 new state_t(spritenum_t.SPR_FIRE,32773,2,think_t.A_Fire,statenum_t.S_FIRE21,0,0), // S_FIRE20 new state_t(spritenum_t.SPR_FIRE,32772,2,think_t.A_Fire,statenum_t.S_FIRE22,0,0), // S_FIRE21 new state_t(spritenum_t.SPR_FIRE,32773,2,think_t.A_Fire,statenum_t.S_FIRE23,0,0), // S_FIRE22 new state_t(spritenum_t.SPR_FIRE,32772,2,think_t.A_Fire,statenum_t.S_FIRE24,0,0), // S_FIRE23 new state_t(spritenum_t.SPR_FIRE,32773,2,think_t.A_Fire,statenum_t.S_FIRE25,0,0), // S_FIRE24 new state_t(spritenum_t.SPR_FIRE,32774,2,think_t.A_Fire,statenum_t.S_FIRE26,0,0), // S_FIRE25 new state_t(spritenum_t.SPR_FIRE,32775,2,think_t.A_Fire,statenum_t.S_FIRE27,0,0), // S_FIRE26 new state_t(spritenum_t.SPR_FIRE,32774,2,think_t.A_Fire,statenum_t.S_FIRE28,0,0), // S_FIRE27 new state_t(spritenum_t.SPR_FIRE,32775,2,think_t.A_Fire,statenum_t.S_FIRE29,0,0), // S_FIRE28 new state_t(spritenum_t.SPR_FIRE,32774,2,think_t.A_Fire,statenum_t.S_FIRE30,0,0), // S_FIRE29 new state_t(spritenum_t.SPR_FIRE,32775,2,think_t.A_Fire,statenum_t.S_NULL,0,0), // S_FIRE30 new state_t(spritenum_t.SPR_PUFF,1,4,null,statenum_t.S_SMOKE2,0,0), // S_SMOKE1 new state_t(spritenum_t.SPR_PUFF,2,4,null,statenum_t.S_SMOKE3,0,0), // S_SMOKE2 new state_t(spritenum_t.SPR_PUFF,1,4,null,statenum_t.S_SMOKE4,0,0), // S_SMOKE3 new state_t(spritenum_t.SPR_PUFF,2,4,null,statenum_t.S_SMOKE5,0,0), // S_SMOKE4 new state_t(spritenum_t.SPR_PUFF,3,4,null,statenum_t.S_NULL,0,0), // S_SMOKE5 new state_t(spritenum_t.SPR_FATB,32768,2,think_t.A_Tracer,statenum_t.S_TRACER2,0,0), // S_TRACER new state_t(spritenum_t.SPR_FATB,32769,2,think_t.A_Tracer,statenum_t.S_TRACER,0,0), // S_TRACER2 new state_t(spritenum_t.SPR_FBXP,32768,8,null,statenum_t.S_TRACEEXP2,0,0), // S_TRACEEXP1 new state_t(spritenum_t.SPR_FBXP,32769,6,null,statenum_t.S_TRACEEXP3,0,0), // S_TRACEEXP2 new state_t(spritenum_t.SPR_FBXP,32770,4,null,statenum_t.S_NULL,0,0), // S_TRACEEXP3 new state_t(spritenum_t.SPR_SKEL,0,10,think_t.A_Look,statenum_t.S_SKEL_STND2,0,0), // S_SKEL_STND new state_t(spritenum_t.SPR_SKEL,1,10,think_t.A_Look,statenum_t.S_SKEL_STND,0,0), // S_SKEL_STND2 new state_t(spritenum_t.SPR_SKEL,0,2,think_t.A_Chase,statenum_t.S_SKEL_RUN2,0,0), // S_SKEL_RUN1 new state_t(spritenum_t.SPR_SKEL,0,2,think_t.A_Chase,statenum_t.S_SKEL_RUN3,0,0), // S_SKEL_RUN2 new state_t(spritenum_t.SPR_SKEL,1,2,think_t.A_Chase,statenum_t.S_SKEL_RUN4,0,0), // S_SKEL_RUN3 new state_t(spritenum_t.SPR_SKEL,1,2,think_t.A_Chase,statenum_t.S_SKEL_RUN5,0,0), // S_SKEL_RUN4 new state_t(spritenum_t.SPR_SKEL,2,2,think_t.A_Chase,statenum_t.S_SKEL_RUN6,0,0), // S_SKEL_RUN5 new state_t(spritenum_t.SPR_SKEL,2,2,think_t.A_Chase,statenum_t.S_SKEL_RUN7,0,0), // S_SKEL_RUN6 new state_t(spritenum_t.SPR_SKEL,3,2,think_t.A_Chase,statenum_t.S_SKEL_RUN8,0,0), // S_SKEL_RUN7 new state_t(spritenum_t.SPR_SKEL,3,2,think_t.A_Chase,statenum_t.S_SKEL_RUN9,0,0), // S_SKEL_RUN8 new state_t(spritenum_t.SPR_SKEL,4,2,think_t.A_Chase,statenum_t.S_SKEL_RUN10,0,0), // S_SKEL_RUN9 new state_t(spritenum_t.SPR_SKEL,4,2,think_t.A_Chase,statenum_t.S_SKEL_RUN11,0,0), // S_SKEL_RUN10 new state_t(spritenum_t.SPR_SKEL,5,2,think_t.A_Chase,statenum_t.S_SKEL_RUN12,0,0), // S_SKEL_RUN11 new state_t(spritenum_t.SPR_SKEL,5,2,think_t.A_Chase,statenum_t.S_SKEL_RUN1,0,0), // S_SKEL_RUN12 new state_t(spritenum_t.SPR_SKEL,6,0,think_t.A_FaceTarget,statenum_t.S_SKEL_FIST2,0,0), // S_SKEL_FIST1 new state_t(spritenum_t.SPR_SKEL,6,6,think_t.A_SkelWhoosh,statenum_t.S_SKEL_FIST3,0,0), // S_SKEL_FIST2 new state_t(spritenum_t.SPR_SKEL,7,6,think_t.A_FaceTarget,statenum_t.S_SKEL_FIST4,0,0), // S_SKEL_FIST3 new state_t(spritenum_t.SPR_SKEL,8,6,think_t.A_SkelFist,statenum_t.S_SKEL_RUN1,0,0), // S_SKEL_FIST4 new state_t(spritenum_t.SPR_SKEL,32777,0,think_t.A_FaceTarget,statenum_t.S_SKEL_MISS2,0,0), // S_SKEL_MISS1 new state_t(spritenum_t.SPR_SKEL,32777,10,think_t.A_FaceTarget,statenum_t.S_SKEL_MISS3,0,0), // S_SKEL_MISS2 new state_t(spritenum_t.SPR_SKEL,10,10,think_t.A_SkelMissile,statenum_t.S_SKEL_MISS4,0,0), // S_SKEL_MISS3 new state_t(spritenum_t.SPR_SKEL,10,10,think_t.A_FaceTarget,statenum_t.S_SKEL_RUN1,0,0), // S_SKEL_MISS4 new state_t(spritenum_t.SPR_SKEL,11,5,null,statenum_t.S_SKEL_PAIN2,0,0), // S_SKEL_PAIN new state_t(spritenum_t.SPR_SKEL,11,5,think_t.A_Pain,statenum_t.S_SKEL_RUN1,0,0), // S_SKEL_PAIN2 new state_t(spritenum_t.SPR_SKEL,11,7,null,statenum_t.S_SKEL_DIE2,0,0), // S_SKEL_DIE1 new state_t(spritenum_t.SPR_SKEL,12,7,null,statenum_t.S_SKEL_DIE3,0,0), // S_SKEL_DIE2 new state_t(spritenum_t.SPR_SKEL,13,7,think_t.A_Scream,statenum_t.S_SKEL_DIE4,0,0), // S_SKEL_DIE3 new state_t(spritenum_t.SPR_SKEL,14,7,think_t.A_Fall,statenum_t.S_SKEL_DIE5,0,0), // S_SKEL_DIE4 new state_t(spritenum_t.SPR_SKEL,15,7,null,statenum_t.S_SKEL_DIE6,0,0), // S_SKEL_DIE5 new state_t(spritenum_t.SPR_SKEL,16,-1,null,statenum_t.S_NULL,0,0), // S_SKEL_DIE6 new state_t(spritenum_t.SPR_SKEL,16,5,null,statenum_t.S_SKEL_RAISE2,0,0), // S_SKEL_RAISE1 new state_t(spritenum_t.SPR_SKEL,15,5,null,statenum_t.S_SKEL_RAISE3,0,0), // S_SKEL_RAISE2 new state_t(spritenum_t.SPR_SKEL,14,5,null,statenum_t.S_SKEL_RAISE4,0,0), // S_SKEL_RAISE3 new state_t(spritenum_t.SPR_SKEL,13,5,null,statenum_t.S_SKEL_RAISE5,0,0), // S_SKEL_RAISE4 new state_t(spritenum_t.SPR_SKEL,12,5,null,statenum_t.S_SKEL_RAISE6,0,0), // S_SKEL_RAISE5 new state_t(spritenum_t.SPR_SKEL,11,5,null,statenum_t.S_SKEL_RUN1,0,0), // S_SKEL_RAISE6 new state_t(spritenum_t.SPR_MANF,32768,4,null,statenum_t.S_FATSHOT2,0,0), // S_FATSHOT1 new state_t(spritenum_t.SPR_MANF,32769,4,null,statenum_t.S_FATSHOT1,0,0), // S_FATSHOT2 new state_t(spritenum_t.SPR_MISL,32769,8,null,statenum_t.S_FATSHOTX2,0,0), // S_FATSHOTX1 new state_t(spritenum_t.SPR_MISL,32770,6,null,statenum_t.S_FATSHOTX3,0,0), // S_FATSHOTX2 new state_t(spritenum_t.SPR_MISL,32771,4,null,statenum_t.S_NULL,0,0), // S_FATSHOTX3 new state_t(spritenum_t.SPR_FATT,0,15,think_t.A_Look,statenum_t.S_FATT_STND2,0,0), // S_FATT_STND new state_t(spritenum_t.SPR_FATT,1,15,think_t.A_Look,statenum_t.S_FATT_STND,0,0), // S_FATT_STND2 new state_t(spritenum_t.SPR_FATT,0,4,think_t.A_Chase,statenum_t.S_FATT_RUN2,0,0), // S_FATT_RUN1 new state_t(spritenum_t.SPR_FATT,0,4,think_t.A_Chase,statenum_t.S_FATT_RUN3,0,0), // S_FATT_RUN2 new state_t(spritenum_t.SPR_FATT,1,4,think_t.A_Chase,statenum_t.S_FATT_RUN4,0,0), // S_FATT_RUN3 new state_t(spritenum_t.SPR_FATT,1,4,think_t.A_Chase,statenum_t.S_FATT_RUN5,0,0), // S_FATT_RUN4 new state_t(spritenum_t.SPR_FATT,2,4,think_t.A_Chase,statenum_t.S_FATT_RUN6,0,0), // S_FATT_RUN5 new state_t(spritenum_t.SPR_FATT,2,4,think_t.A_Chase,statenum_t.S_FATT_RUN7,0,0), // S_FATT_RUN6 new state_t(spritenum_t.SPR_FATT,3,4,think_t.A_Chase,statenum_t.S_FATT_RUN8,0,0), // S_FATT_RUN7 new state_t(spritenum_t.SPR_FATT,3,4,think_t.A_Chase,statenum_t.S_FATT_RUN9,0,0), // S_FATT_RUN8 new state_t(spritenum_t.SPR_FATT,4,4,think_t.A_Chase,statenum_t.S_FATT_RUN10,0,0), // S_FATT_RUN9 new state_t(spritenum_t.SPR_FATT,4,4,think_t.A_Chase,statenum_t.S_FATT_RUN11,0,0), // S_FATT_RUN10 new state_t(spritenum_t.SPR_FATT,5,4,think_t.A_Chase,statenum_t.S_FATT_RUN12,0,0), // S_FATT_RUN11 new state_t(spritenum_t.SPR_FATT,5,4,think_t.A_Chase,statenum_t.S_FATT_RUN1,0,0), // S_FATT_RUN12 new state_t(spritenum_t.SPR_FATT,6,20,think_t.A_FatRaise,statenum_t.S_FATT_ATK2,0,0), // S_FATT_ATK1 new state_t(spritenum_t.SPR_FATT,32775,10,think_t.A_FatAttack1,statenum_t.S_FATT_ATK3,0,0), // S_FATT_ATK2 new state_t(spritenum_t.SPR_FATT,8,5,think_t.A_FaceTarget,statenum_t.S_FATT_ATK4,0,0), // S_FATT_ATK3 new state_t(spritenum_t.SPR_FATT,6,5,think_t.A_FaceTarget,statenum_t.S_FATT_ATK5,0,0), // S_FATT_ATK4 new state_t(spritenum_t.SPR_FATT,32775,10,think_t.A_FatAttack2,statenum_t.S_FATT_ATK6,0,0), // S_FATT_ATK5 new state_t(spritenum_t.SPR_FATT,8,5,think_t.A_FaceTarget,statenum_t.S_FATT_ATK7,0,0), // S_FATT_ATK6 new state_t(spritenum_t.SPR_FATT,6,5,think_t.A_FaceTarget,statenum_t.S_FATT_ATK8,0,0), // S_FATT_ATK7 new state_t(spritenum_t.SPR_FATT,32775,10,think_t.A_FatAttack3,statenum_t.S_FATT_ATK9,0,0), // S_FATT_ATK8 new state_t(spritenum_t.SPR_FATT,8,5,think_t.A_FaceTarget,statenum_t.S_FATT_ATK10,0,0), // S_FATT_ATK9 new state_t(spritenum_t.SPR_FATT,6,5,think_t.A_FaceTarget,statenum_t.S_FATT_RUN1,0,0), // S_FATT_ATK10 new state_t(spritenum_t.SPR_FATT,9,3,null,statenum_t.S_FATT_PAIN2,0,0), // S_FATT_PAIN new state_t(spritenum_t.SPR_FATT,9,3,think_t.A_Pain,statenum_t.S_FATT_RUN1,0,0), // S_FATT_PAIN2 new state_t(spritenum_t.SPR_FATT,10,6,null,statenum_t.S_FATT_DIE2,0,0), // S_FATT_DIE1 new state_t(spritenum_t.SPR_FATT,11,6,think_t.A_Scream,statenum_t.S_FATT_DIE3,0,0), // S_FATT_DIE2 new state_t(spritenum_t.SPR_FATT,12,6,think_t.A_Fall,statenum_t.S_FATT_DIE4,0,0), // S_FATT_DIE3 new state_t(spritenum_t.SPR_FATT,13,6,null,statenum_t.S_FATT_DIE5,0,0), // S_FATT_DIE4 new state_t(spritenum_t.SPR_FATT,14,6,null,statenum_t.S_FATT_DIE6,0,0), // S_FATT_DIE5 new state_t(spritenum_t.SPR_FATT,15,6,null,statenum_t.S_FATT_DIE7,0,0), // S_FATT_DIE6 new state_t(spritenum_t.SPR_FATT,16,6,null,statenum_t.S_FATT_DIE8,0,0), // S_FATT_DIE7 new state_t(spritenum_t.SPR_FATT,17,6,null,statenum_t.S_FATT_DIE9,0,0), // S_FATT_DIE8 new state_t(spritenum_t.SPR_FATT,18,6,null,statenum_t.S_FATT_DIE10,0,0), // S_FATT_DIE9 new state_t(spritenum_t.SPR_FATT,19,-1,think_t.A_BossDeath,statenum_t.S_NULL,0,0), // S_FATT_DIE10 new state_t(spritenum_t.SPR_FATT,17,5,null,statenum_t.S_FATT_RAISE2,0,0), // S_FATT_RAISE1 new state_t(spritenum_t.SPR_FATT,16,5,null,statenum_t.S_FATT_RAISE3,0,0), // S_FATT_RAISE2 new state_t(spritenum_t.SPR_FATT,15,5,null,statenum_t.S_FATT_RAISE4,0,0), // S_FATT_RAISE3 new state_t(spritenum_t.SPR_FATT,14,5,null,statenum_t.S_FATT_RAISE5,0,0), // S_FATT_RAISE4 new state_t(spritenum_t.SPR_FATT,13,5,null,statenum_t.S_FATT_RAISE6,0,0), // S_FATT_RAISE5 new state_t(spritenum_t.SPR_FATT,12,5,null,statenum_t.S_FATT_RAISE7,0,0), // S_FATT_RAISE6 new state_t(spritenum_t.SPR_FATT,11,5,null,statenum_t.S_FATT_RAISE8,0,0), // S_FATT_RAISE7 new state_t(spritenum_t.SPR_FATT,10,5,null,statenum_t.S_FATT_RUN1,0,0), // S_FATT_RAISE8 new state_t(spritenum_t.SPR_CPOS,0,10,think_t.A_Look,statenum_t.S_CPOS_STND2,0,0), // S_CPOS_STND new state_t(spritenum_t.SPR_CPOS,1,10,think_t.A_Look,statenum_t.S_CPOS_STND,0,0), // S_CPOS_STND2 new state_t(spritenum_t.SPR_CPOS,0,3,think_t.A_Chase,statenum_t.S_CPOS_RUN2,0,0), // S_CPOS_RUN1 new state_t(spritenum_t.SPR_CPOS,0,3,think_t.A_Chase,statenum_t.S_CPOS_RUN3,0,0), // S_CPOS_RUN2 new state_t(spritenum_t.SPR_CPOS,1,3,think_t.A_Chase,statenum_t.S_CPOS_RUN4,0,0), // S_CPOS_RUN3 new state_t(spritenum_t.SPR_CPOS,1,3,think_t.A_Chase,statenum_t.S_CPOS_RUN5,0,0), // S_CPOS_RUN4 new state_t(spritenum_t.SPR_CPOS,2,3,think_t.A_Chase,statenum_t.S_CPOS_RUN6,0,0), // S_CPOS_RUN5 new state_t(spritenum_t.SPR_CPOS,2,3,think_t.A_Chase,statenum_t.S_CPOS_RUN7,0,0), // S_CPOS_RUN6 new state_t(spritenum_t.SPR_CPOS,3,3,think_t.A_Chase,statenum_t.S_CPOS_RUN8,0,0), // S_CPOS_RUN7 new state_t(spritenum_t.SPR_CPOS,3,3,think_t.A_Chase,statenum_t.S_CPOS_RUN1,0,0), // S_CPOS_RUN8 new state_t(spritenum_t.SPR_CPOS,4,10,think_t.A_FaceTarget,statenum_t.S_CPOS_ATK2,0,0), // S_CPOS_ATK1 new state_t(spritenum_t.SPR_CPOS,32773,4,think_t.A_CPosAttack,statenum_t.S_CPOS_ATK3,0,0), // S_CPOS_ATK2 new state_t(spritenum_t.SPR_CPOS,32772,4,think_t.A_CPosAttack,statenum_t.S_CPOS_ATK4,0,0), // S_CPOS_ATK3 new state_t(spritenum_t.SPR_CPOS,5,1,think_t.A_CPosRefire,statenum_t.S_CPOS_ATK2,0,0), // S_CPOS_ATK4 new state_t(spritenum_t.SPR_CPOS,6,3,null,statenum_t.S_CPOS_PAIN2,0,0), // S_CPOS_PAIN new state_t(spritenum_t.SPR_CPOS,6,3,think_t.A_Pain,statenum_t.S_CPOS_RUN1,0,0), // S_CPOS_PAIN2 new state_t(spritenum_t.SPR_CPOS,7,5,null,statenum_t.S_CPOS_DIE2,0,0), // S_CPOS_DIE1 new state_t(spritenum_t.SPR_CPOS,8,5,think_t.A_Scream,statenum_t.S_CPOS_DIE3,0,0), // S_CPOS_DIE2 new state_t(spritenum_t.SPR_CPOS,9,5,think_t.A_Fall,statenum_t.S_CPOS_DIE4,0,0), // S_CPOS_DIE3 new state_t(spritenum_t.SPR_CPOS,10,5,null,statenum_t.S_CPOS_DIE5,0,0), // S_CPOS_DIE4 new state_t(spritenum_t.SPR_CPOS,11,5,null,statenum_t.S_CPOS_DIE6,0,0), // S_CPOS_DIE5 new state_t(spritenum_t.SPR_CPOS,12,5,null,statenum_t.S_CPOS_DIE7,0,0), // S_CPOS_DIE6 new state_t(spritenum_t.SPR_CPOS,13,-1,null,statenum_t.S_NULL,0,0), // S_CPOS_DIE7 new state_t(spritenum_t.SPR_CPOS,14,5,null,statenum_t.S_CPOS_XDIE2,0,0), // S_CPOS_XDIE1 new state_t(spritenum_t.SPR_CPOS,15,5,think_t.A_XScream,statenum_t.S_CPOS_XDIE3,0,0), // S_CPOS_XDIE2 new state_t(spritenum_t.SPR_CPOS,16,5,think_t.A_Fall,statenum_t.S_CPOS_XDIE4,0,0), // S_CPOS_XDIE3 new state_t(spritenum_t.SPR_CPOS,17,5,null,statenum_t.S_CPOS_XDIE5,0,0), // S_CPOS_XDIE4 new state_t(spritenum_t.SPR_CPOS,18,5,null,statenum_t.S_CPOS_XDIE6,0,0), // S_CPOS_XDIE5 new state_t(spritenum_t.SPR_CPOS,19,-1,null,statenum_t.S_NULL,0,0), // S_CPOS_XDIE6 new state_t(spritenum_t.SPR_CPOS,13,5,null,statenum_t.S_CPOS_RAISE2,0,0), // S_CPOS_RAISE1 new state_t(spritenum_t.SPR_CPOS,12,5,null,statenum_t.S_CPOS_RAISE3,0,0), // S_CPOS_RAISE2 new state_t(spritenum_t.SPR_CPOS,11,5,null,statenum_t.S_CPOS_RAISE4,0,0), // S_CPOS_RAISE3 new state_t(spritenum_t.SPR_CPOS,10,5,null,statenum_t.S_CPOS_RAISE5,0,0), // S_CPOS_RAISE4 new state_t(spritenum_t.SPR_CPOS,9,5,null,statenum_t.S_CPOS_RAISE6,0,0), // S_CPOS_RAISE5 new state_t(spritenum_t.SPR_CPOS,8,5,null,statenum_t.S_CPOS_RAISE7,0,0), // S_CPOS_RAISE6 new state_t(spritenum_t.SPR_CPOS,7,5,null,statenum_t.S_CPOS_RUN1,0,0), // S_CPOS_RAISE7 new state_t(spritenum_t.SPR_TROO,0,10,think_t.A_Look,statenum_t.S_TROO_STND2,0,0), // S_TROO_STND new state_t(spritenum_t.SPR_TROO,1,10,think_t.A_Look,statenum_t.S_TROO_STND,0,0), // S_TROO_STND2 new state_t(spritenum_t.SPR_TROO,0,3,think_t.A_Chase,statenum_t.S_TROO_RUN2,0,0), // S_TROO_RUN1 new state_t(spritenum_t.SPR_TROO,0,3,think_t.A_Chase,statenum_t.S_TROO_RUN3,0,0), // S_TROO_RUN2 new state_t(spritenum_t.SPR_TROO,1,3,think_t.A_Chase,statenum_t.S_TROO_RUN4,0,0), // S_TROO_RUN3 new state_t(spritenum_t.SPR_TROO,1,3,think_t.A_Chase,statenum_t.S_TROO_RUN5,0,0), // S_TROO_RUN4 new state_t(spritenum_t.SPR_TROO,2,3,think_t.A_Chase,statenum_t.S_TROO_RUN6,0,0), // S_TROO_RUN5 new state_t(spritenum_t.SPR_TROO,2,3,think_t.A_Chase,statenum_t.S_TROO_RUN7,0,0), // S_TROO_RUN6 new state_t(spritenum_t.SPR_TROO,3,3,think_t.A_Chase,statenum_t.S_TROO_RUN8,0,0), // S_TROO_RUN7 new state_t(spritenum_t.SPR_TROO,3,3,think_t.A_Chase,statenum_t.S_TROO_RUN1,0,0), // S_TROO_RUN8 new state_t(spritenum_t.SPR_TROO,4,8,think_t.A_FaceTarget,statenum_t.S_TROO_ATK2,0,0), // S_TROO_ATK1 new state_t(spritenum_t.SPR_TROO,5,8,think_t.A_FaceTarget,statenum_t.S_TROO_ATK3,0,0), // S_TROO_ATK2 new state_t(spritenum_t.SPR_TROO,6,6,think_t.A_TroopAttack,statenum_t.S_TROO_RUN1,0,0), // S_TROO_ATK3 new state_t(spritenum_t.SPR_TROO,7,2,null,statenum_t.S_TROO_PAIN2,0,0), // S_TROO_PAIN new state_t(spritenum_t.SPR_TROO,7,2,think_t.A_Pain,statenum_t.S_TROO_RUN1,0,0), // S_TROO_PAIN2 new state_t(spritenum_t.SPR_TROO,8,8,null,statenum_t.S_TROO_DIE2,0,0), // S_TROO_DIE1 new state_t(spritenum_t.SPR_TROO,9,8,think_t.A_Scream,statenum_t.S_TROO_DIE3,0,0), // S_TROO_DIE2 new state_t(spritenum_t.SPR_TROO,10,6,null,statenum_t.S_TROO_DIE4,0,0), // S_TROO_DIE3 new state_t(spritenum_t.SPR_TROO,11,6,think_t.A_Fall,statenum_t.S_TROO_DIE5,0,0), // S_TROO_DIE4 new state_t(spritenum_t.SPR_TROO,12,-1,null,statenum_t.S_NULL,0,0), // S_TROO_DIE5 new state_t(spritenum_t.SPR_TROO,13,5,null,statenum_t.S_TROO_XDIE2,0,0), // S_TROO_XDIE1 new state_t(spritenum_t.SPR_TROO,14,5,think_t.A_XScream,statenum_t.S_TROO_XDIE3,0,0), // S_TROO_XDIE2 new state_t(spritenum_t.SPR_TROO,15,5,null,statenum_t.S_TROO_XDIE4,0,0), // S_TROO_XDIE3 new state_t(spritenum_t.SPR_TROO,16,5,think_t.A_Fall,statenum_t.S_TROO_XDIE5,0,0), // S_TROO_XDIE4 new state_t(spritenum_t.SPR_TROO,17,5,null,statenum_t.S_TROO_XDIE6,0,0), // S_TROO_XDIE5 new state_t(spritenum_t.SPR_TROO,18,5,null,statenum_t.S_TROO_XDIE7,0,0), // S_TROO_XDIE6 new state_t(spritenum_t.SPR_TROO,19,5,null,statenum_t.S_TROO_XDIE8,0,0), // S_TROO_XDIE7 new state_t(spritenum_t.SPR_TROO,20,-1,null,statenum_t.S_NULL,0,0), // S_TROO_XDIE8 new state_t(spritenum_t.SPR_TROO,12,8,null,statenum_t.S_TROO_RAISE2,0,0), // S_TROO_RAISE1 new state_t(spritenum_t.SPR_TROO,11,8,null,statenum_t.S_TROO_RAISE3,0,0), // S_TROO_RAISE2 new state_t(spritenum_t.SPR_TROO,10,6,null,statenum_t.S_TROO_RAISE4,0,0), // S_TROO_RAISE3 new state_t(spritenum_t.SPR_TROO,9,6,null,statenum_t.S_TROO_RAISE5,0,0), // S_TROO_RAISE4 new state_t(spritenum_t.SPR_TROO,8,6,null,statenum_t.S_TROO_RUN1,0,0), // S_TROO_RAISE5 new state_t(spritenum_t.SPR_SARG,0,10,think_t.A_Look,statenum_t.S_SARG_STND2,0,0), // S_SARG_STND new state_t(spritenum_t.SPR_SARG,1,10,think_t.A_Look,statenum_t.S_SARG_STND,0,0), // S_SARG_STND2 new state_t(spritenum_t.SPR_SARG,0,2,think_t.A_Chase,statenum_t.S_SARG_RUN2,0,0), // S_SARG_RUN1 new state_t(spritenum_t.SPR_SARG,0,2,think_t.A_Chase,statenum_t.S_SARG_RUN3,0,0), // S_SARG_RUN2 new state_t(spritenum_t.SPR_SARG,1,2,think_t.A_Chase,statenum_t.S_SARG_RUN4,0,0), // S_SARG_RUN3 new state_t(spritenum_t.SPR_SARG,1,2,think_t.A_Chase,statenum_t.S_SARG_RUN5,0,0), // S_SARG_RUN4 new state_t(spritenum_t.SPR_SARG,2,2,think_t.A_Chase,statenum_t.S_SARG_RUN6,0,0), // S_SARG_RUN5 new state_t(spritenum_t.SPR_SARG,2,2,think_t.A_Chase,statenum_t.S_SARG_RUN7,0,0), // S_SARG_RUN6 new state_t(spritenum_t.SPR_SARG,3,2,think_t.A_Chase,statenum_t.S_SARG_RUN8,0,0), // S_SARG_RUN7 new state_t(spritenum_t.SPR_SARG,3,2,think_t.A_Chase,statenum_t.S_SARG_RUN1,0,0), // S_SARG_RUN8 new state_t(spritenum_t.SPR_SARG,4,8,think_t.A_FaceTarget,statenum_t.S_SARG_ATK2,0,0), // S_SARG_ATK1 new state_t(spritenum_t.SPR_SARG,5,8,think_t.A_FaceTarget,statenum_t.S_SARG_ATK3,0,0), // S_SARG_ATK2 new state_t(spritenum_t.SPR_SARG,6,8,think_t.A_SargAttack,statenum_t.S_SARG_RUN1,0,0), // S_SARG_ATK3 new state_t(spritenum_t.SPR_SARG,7,2,null,statenum_t.S_SARG_PAIN2,0,0), // S_SARG_PAIN new state_t(spritenum_t.SPR_SARG,7,2,think_t.A_Pain,statenum_t.S_SARG_RUN1,0,0), // S_SARG_PAIN2 new state_t(spritenum_t.SPR_SARG,8,8,null,statenum_t.S_SARG_DIE2,0,0), // S_SARG_DIE1 new state_t(spritenum_t.SPR_SARG,9,8,think_t.A_Scream,statenum_t.S_SARG_DIE3,0,0), // S_SARG_DIE2 new state_t(spritenum_t.SPR_SARG,10,4,null,statenum_t.S_SARG_DIE4,0,0), // S_SARG_DIE3 new state_t(spritenum_t.SPR_SARG,11,4,think_t.A_Fall,statenum_t.S_SARG_DIE5,0,0), // S_SARG_DIE4 new state_t(spritenum_t.SPR_SARG,12,4,null,statenum_t.S_SARG_DIE6,0,0), // S_SARG_DIE5 new state_t(spritenum_t.SPR_SARG,13,-1,null,statenum_t.S_NULL,0,0), // S_SARG_DIE6 new state_t(spritenum_t.SPR_SARG,13,5,null,statenum_t.S_SARG_RAISE2,0,0), // S_SARG_RAISE1 new state_t(spritenum_t.SPR_SARG,12,5,null,statenum_t.S_SARG_RAISE3,0,0), // S_SARG_RAISE2 new state_t(spritenum_t.SPR_SARG,11,5,null,statenum_t.S_SARG_RAISE4,0,0), // S_SARG_RAISE3 new state_t(spritenum_t.SPR_SARG,10,5,null,statenum_t.S_SARG_RAISE5,0,0), // S_SARG_RAISE4 new state_t(spritenum_t.SPR_SARG,9,5,null,statenum_t.S_SARG_RAISE6,0,0), // S_SARG_RAISE5 new state_t(spritenum_t.SPR_SARG,8,5,null,statenum_t.S_SARG_RUN1,0,0), // S_SARG_RAISE6 new state_t(spritenum_t.SPR_HEAD,0,10,think_t.A_Look,statenum_t.S_HEAD_STND,0,0), // S_HEAD_STND new state_t(spritenum_t.SPR_HEAD,0,3,think_t.A_Chase,statenum_t.S_HEAD_RUN1,0,0), // S_HEAD_RUN1 new state_t(spritenum_t.SPR_HEAD,1,5,think_t.A_FaceTarget,statenum_t.S_HEAD_ATK2,0,0), // S_HEAD_ATK1 new state_t(spritenum_t.SPR_HEAD,2,5,think_t.A_FaceTarget,statenum_t.S_HEAD_ATK3,0,0), // S_HEAD_ATK2 new state_t(spritenum_t.SPR_HEAD,32771,5,think_t.A_HeadAttack,statenum_t.S_HEAD_RUN1,0,0), // S_HEAD_ATK3 new state_t(spritenum_t.SPR_HEAD,4,3,null,statenum_t.S_HEAD_PAIN2,0,0), // S_HEAD_PAIN new state_t(spritenum_t.SPR_HEAD,4,3,think_t.A_Pain,statenum_t.S_HEAD_PAIN3,0,0), // S_HEAD_PAIN2 new state_t(spritenum_t.SPR_HEAD,5,6,null,statenum_t.S_HEAD_RUN1,0,0), // S_HEAD_PAIN3 new state_t(spritenum_t.SPR_HEAD,6,8,null,statenum_t.S_HEAD_DIE2,0,0), // S_HEAD_DIE1 new state_t(spritenum_t.SPR_HEAD,7,8,think_t.A_Scream,statenum_t.S_HEAD_DIE3,0,0), // S_HEAD_DIE2 new state_t(spritenum_t.SPR_HEAD,8,8,null,statenum_t.S_HEAD_DIE4,0,0), // S_HEAD_DIE3 new state_t(spritenum_t.SPR_HEAD,9,8,null,statenum_t.S_HEAD_DIE5,0,0), // S_HEAD_DIE4 new state_t(spritenum_t.SPR_HEAD,10,8,think_t.A_Fall,statenum_t.S_HEAD_DIE6,0,0), // S_HEAD_DIE5 new state_t(spritenum_t.SPR_HEAD,11,-1,null,statenum_t.S_NULL,0,0), // S_HEAD_DIE6 new state_t(spritenum_t.SPR_HEAD,11,8,null,statenum_t.S_HEAD_RAISE2,0,0), // S_HEAD_RAISE1 new state_t(spritenum_t.SPR_HEAD,10,8,null,statenum_t.S_HEAD_RAISE3,0,0), // S_HEAD_RAISE2 new state_t(spritenum_t.SPR_HEAD,9,8,null,statenum_t.S_HEAD_RAISE4,0,0), // S_HEAD_RAISE3 new state_t(spritenum_t.SPR_HEAD,8,8,null,statenum_t.S_HEAD_RAISE5,0,0), // S_HEAD_RAISE4 new state_t(spritenum_t.SPR_HEAD,7,8,null,statenum_t.S_HEAD_RAISE6,0,0), // S_HEAD_RAISE5 new state_t(spritenum_t.SPR_HEAD,6,8,null,statenum_t.S_HEAD_RUN1,0,0), // S_HEAD_RAISE6 new state_t(spritenum_t.SPR_BAL7,32768,4,null,statenum_t.S_BRBALL2,0,0), // S_BRBALL1 new state_t(spritenum_t.SPR_BAL7,32769,4,null,statenum_t.S_BRBALL1,0,0), // S_BRBALL2 new state_t(spritenum_t.SPR_BAL7,32770,6,null,statenum_t.S_BRBALLX2,0,0), // S_BRBALLX1 new state_t(spritenum_t.SPR_BAL7,32771,6,null,statenum_t.S_BRBALLX3,0,0), // S_BRBALLX2 new state_t(spritenum_t.SPR_BAL7,32772,6,null,statenum_t.S_NULL,0,0), // S_BRBALLX3 new state_t(spritenum_t.SPR_BOSS,0,10,think_t.A_Look,statenum_t.S_BOSS_STND2,0,0), // S_BOSS_STND new state_t(spritenum_t.SPR_BOSS,1,10,think_t.A_Look,statenum_t.S_BOSS_STND,0,0), // S_BOSS_STND2 new state_t(spritenum_t.SPR_BOSS,0,3,think_t.A_Chase,statenum_t.S_BOSS_RUN2,0,0), // S_BOSS_RUN1 new state_t(spritenum_t.SPR_BOSS,0,3,think_t.A_Chase,statenum_t.S_BOSS_RUN3,0,0), // S_BOSS_RUN2 new state_t(spritenum_t.SPR_BOSS,1,3,think_t.A_Chase,statenum_t.S_BOSS_RUN4,0,0), // S_BOSS_RUN3 new state_t(spritenum_t.SPR_BOSS,1,3,think_t.A_Chase,statenum_t.S_BOSS_RUN5,0,0), // S_BOSS_RUN4 new state_t(spritenum_t.SPR_BOSS,2,3,think_t.A_Chase,statenum_t.S_BOSS_RUN6,0,0), // S_BOSS_RUN5 new state_t(spritenum_t.SPR_BOSS,2,3,think_t.A_Chase,statenum_t.S_BOSS_RUN7,0,0), // S_BOSS_RUN6 new state_t(spritenum_t.SPR_BOSS,3,3,think_t.A_Chase,statenum_t.S_BOSS_RUN8,0,0), // S_BOSS_RUN7 new state_t(spritenum_t.SPR_BOSS,3,3,think_t.A_Chase,statenum_t.S_BOSS_RUN1,0,0), // S_BOSS_RUN8 new state_t(spritenum_t.SPR_BOSS,4,8,think_t.A_FaceTarget,statenum_t.S_BOSS_ATK2,0,0), // S_BOSS_ATK1 new state_t(spritenum_t.SPR_BOSS,5,8,think_t.A_FaceTarget,statenum_t.S_BOSS_ATK3,0,0), // S_BOSS_ATK2 new state_t(spritenum_t.SPR_BOSS,6,8,think_t.A_BruisAttack,statenum_t.S_BOSS_RUN1,0,0), // S_BOSS_ATK3 new state_t(spritenum_t.SPR_BOSS,7,2,null,statenum_t.S_BOSS_PAIN2,0,0), // S_BOSS_PAIN new state_t(spritenum_t.SPR_BOSS,7,2,think_t.A_Pain,statenum_t.S_BOSS_RUN1,0,0), // S_BOSS_PAIN2 new state_t(spritenum_t.SPR_BOSS,8,8,null,statenum_t.S_BOSS_DIE2,0,0), // S_BOSS_DIE1 new state_t(spritenum_t.SPR_BOSS,9,8,think_t.A_Scream,statenum_t.S_BOSS_DIE3,0,0), // S_BOSS_DIE2 new state_t(spritenum_t.SPR_BOSS,10,8,null,statenum_t.S_BOSS_DIE4,0,0), // S_BOSS_DIE3 new state_t(spritenum_t.SPR_BOSS,11,8,think_t.A_Fall,statenum_t.S_BOSS_DIE5,0,0), // S_BOSS_DIE4 new state_t(spritenum_t.SPR_BOSS,12,8,null,statenum_t.S_BOSS_DIE6,0,0), // S_BOSS_DIE5 new state_t(spritenum_t.SPR_BOSS,13,8,null,statenum_t.S_BOSS_DIE7,0,0), // S_BOSS_DIE6 new state_t(spritenum_t.SPR_BOSS,14,-1,think_t.A_BossDeath,statenum_t.S_NULL,0,0), // S_BOSS_DIE7 new state_t(spritenum_t.SPR_BOSS,14,8,null,statenum_t.S_BOSS_RAISE2,0,0), // S_BOSS_RAISE1 new state_t(spritenum_t.SPR_BOSS,13,8,null,statenum_t.S_BOSS_RAISE3,0,0), // S_BOSS_RAISE2 new state_t(spritenum_t.SPR_BOSS,12,8,null,statenum_t.S_BOSS_RAISE4,0,0), // S_BOSS_RAISE3 new state_t(spritenum_t.SPR_BOSS,11,8,null,statenum_t.S_BOSS_RAISE5,0,0), // S_BOSS_RAISE4 new state_t(spritenum_t.SPR_BOSS,10,8,null,statenum_t.S_BOSS_RAISE6,0,0), // S_BOSS_RAISE5 new state_t(spritenum_t.SPR_BOSS,9,8,null,statenum_t.S_BOSS_RAISE7,0,0), // S_BOSS_RAISE6 new state_t(spritenum_t.SPR_BOSS,8,8,null,statenum_t.S_BOSS_RUN1,0,0), // S_BOSS_RAISE7 new state_t(spritenum_t.SPR_BOS2,0,10,think_t.A_Look,statenum_t.S_BOS2_STND2,0,0), // S_BOS2_STND new state_t(spritenum_t.SPR_BOS2,1,10,think_t.A_Look,statenum_t.S_BOS2_STND,0,0), // S_BOS2_STND2 new state_t(spritenum_t.SPR_BOS2,0,3,think_t.A_Chase,statenum_t.S_BOS2_RUN2,0,0), // S_BOS2_RUN1 new state_t(spritenum_t.SPR_BOS2,0,3,think_t.A_Chase,statenum_t.S_BOS2_RUN3,0,0), // S_BOS2_RUN2 new state_t(spritenum_t.SPR_BOS2,1,3,think_t.A_Chase,statenum_t.S_BOS2_RUN4,0,0), // S_BOS2_RUN3 new state_t(spritenum_t.SPR_BOS2,1,3,think_t.A_Chase,statenum_t.S_BOS2_RUN5,0,0), // S_BOS2_RUN4 new state_t(spritenum_t.SPR_BOS2,2,3,think_t.A_Chase,statenum_t.S_BOS2_RUN6,0,0), // S_BOS2_RUN5 new state_t(spritenum_t.SPR_BOS2,2,3,think_t.A_Chase,statenum_t.S_BOS2_RUN7,0,0), // S_BOS2_RUN6 new state_t(spritenum_t.SPR_BOS2,3,3,think_t.A_Chase,statenum_t.S_BOS2_RUN8,0,0), // S_BOS2_RUN7 new state_t(spritenum_t.SPR_BOS2,3,3,think_t.A_Chase,statenum_t.S_BOS2_RUN1,0,0), // S_BOS2_RUN8 new state_t(spritenum_t.SPR_BOS2,4,8,think_t.A_FaceTarget,statenum_t.S_BOS2_ATK2,0,0), // S_BOS2_ATK1 new state_t(spritenum_t.SPR_BOS2,5,8,think_t.A_FaceTarget,statenum_t.S_BOS2_ATK3,0,0), // S_BOS2_ATK2 new state_t(spritenum_t.SPR_BOS2,6,8,think_t.A_BruisAttack,statenum_t.S_BOS2_RUN1,0,0), // S_BOS2_ATK3 new state_t(spritenum_t.SPR_BOS2,7,2,null,statenum_t.S_BOS2_PAIN2,0,0), // S_BOS2_PAIN new state_t(spritenum_t.SPR_BOS2,7,2,think_t.A_Pain,statenum_t.S_BOS2_RUN1,0,0), // S_BOS2_PAIN2 new state_t(spritenum_t.SPR_BOS2,8,8,null,statenum_t.S_BOS2_DIE2,0,0), // S_BOS2_DIE1 new state_t(spritenum_t.SPR_BOS2,9,8,think_t.A_Scream,statenum_t.S_BOS2_DIE3,0,0), // S_BOS2_DIE2 new state_t(spritenum_t.SPR_BOS2,10,8,null,statenum_t.S_BOS2_DIE4,0,0), // S_BOS2_DIE3 new state_t(spritenum_t.SPR_BOS2,11,8,think_t.A_Fall,statenum_t.S_BOS2_DIE5,0,0), // S_BOS2_DIE4 new state_t(spritenum_t.SPR_BOS2,12,8,null,statenum_t.S_BOS2_DIE6,0,0), // S_BOS2_DIE5 new state_t(spritenum_t.SPR_BOS2,13,8,null,statenum_t.S_BOS2_DIE7,0,0), // S_BOS2_DIE6 new state_t(spritenum_t.SPR_BOS2,14,-1,null,statenum_t.S_NULL,0,0), // S_BOS2_DIE7 new state_t(spritenum_t.SPR_BOS2,14,8,null,statenum_t.S_BOS2_RAISE2,0,0), // S_BOS2_RAISE1 new state_t(spritenum_t.SPR_BOS2,13,8,null,statenum_t.S_BOS2_RAISE3,0,0), // S_BOS2_RAISE2 new state_t(spritenum_t.SPR_BOS2,12,8,null,statenum_t.S_BOS2_RAISE4,0,0), // S_BOS2_RAISE3 new state_t(spritenum_t.SPR_BOS2,11,8,null,statenum_t.S_BOS2_RAISE5,0,0), // S_BOS2_RAISE4 new state_t(spritenum_t.SPR_BOS2,10,8,null,statenum_t.S_BOS2_RAISE6,0,0), // S_BOS2_RAISE5 new state_t(spritenum_t.SPR_BOS2,9,8,null,statenum_t.S_BOS2_RAISE7,0,0), // S_BOS2_RAISE6 new state_t(spritenum_t.SPR_BOS2,8,8,null,statenum_t.S_BOS2_RUN1,0,0), // S_BOS2_RAISE7 new state_t(spritenum_t.SPR_SKUL,32768,10,think_t.A_Look,statenum_t.S_SKULL_STND2,0,0), // S_SKULL_STND new state_t(spritenum_t.SPR_SKUL,32769,10,think_t.A_Look,statenum_t.S_SKULL_STND,0,0), // S_SKULL_STND2 new state_t(spritenum_t.SPR_SKUL,32768,6,think_t.A_Chase,statenum_t.S_SKULL_RUN2,0,0), // S_SKULL_RUN1 new state_t(spritenum_t.SPR_SKUL,32769,6,think_t.A_Chase,statenum_t.S_SKULL_RUN1,0,0), // S_SKULL_RUN2 new state_t(spritenum_t.SPR_SKUL,32770,10,think_t.A_FaceTarget,statenum_t.S_SKULL_ATK2,0,0), // S_SKULL_ATK1 new state_t(spritenum_t.SPR_SKUL,32771,4,think_t.A_SkullAttack,statenum_t.S_SKULL_ATK3,0,0), // S_SKULL_ATK2 new state_t(spritenum_t.SPR_SKUL,32770,4,null,statenum_t.S_SKULL_ATK4,0,0), // S_SKULL_ATK3 new state_t(spritenum_t.SPR_SKUL,32771,4,null,statenum_t.S_SKULL_ATK3,0,0), // S_SKULL_ATK4 new state_t(spritenum_t.SPR_SKUL,32772,3,null,statenum_t.S_SKULL_PAIN2,0,0), // S_SKULL_PAIN new state_t(spritenum_t.SPR_SKUL,32772,3,think_t.A_Pain,statenum_t.S_SKULL_RUN1,0,0), // S_SKULL_PAIN2 new state_t(spritenum_t.SPR_SKUL,32773,6,null,statenum_t.S_SKULL_DIE2,0,0), // S_SKULL_DIE1 new state_t(spritenum_t.SPR_SKUL,32774,6,think_t.A_Scream,statenum_t.S_SKULL_DIE3,0,0), // S_SKULL_DIE2 new state_t(spritenum_t.SPR_SKUL,32775,6,null,statenum_t.S_SKULL_DIE4,0,0), // S_SKULL_DIE3 new state_t(spritenum_t.SPR_SKUL,32776,6,think_t.A_Fall,statenum_t.S_SKULL_DIE5,0,0), // S_SKULL_DIE4 new state_t(spritenum_t.SPR_SKUL,9,6,null,statenum_t.S_SKULL_DIE6,0,0), // S_SKULL_DIE5 new state_t(spritenum_t.SPR_SKUL,10,6,null,statenum_t.S_NULL,0,0), // S_SKULL_DIE6 new state_t(spritenum_t.SPR_SPID,0,10,think_t.A_Look,statenum_t.S_SPID_STND2,0,0), // S_SPID_STND new state_t(spritenum_t.SPR_SPID,1,10,think_t.A_Look,statenum_t.S_SPID_STND,0,0), // S_SPID_STND2 new state_t(spritenum_t.SPR_SPID,0,3,think_t.A_Metal,statenum_t.S_SPID_RUN2,0,0), // S_SPID_RUN1 new state_t(spritenum_t.SPR_SPID,0,3,think_t.A_Chase,statenum_t.S_SPID_RUN3,0,0), // S_SPID_RUN2 new state_t(spritenum_t.SPR_SPID,1,3,think_t.A_Chase,statenum_t.S_SPID_RUN4,0,0), // S_SPID_RUN3 new state_t(spritenum_t.SPR_SPID,1,3,think_t.A_Chase,statenum_t.S_SPID_RUN5,0,0), // S_SPID_RUN4 new state_t(spritenum_t.SPR_SPID,2,3,think_t.A_Metal,statenum_t.S_SPID_RUN6,0,0), // S_SPID_RUN5 new state_t(spritenum_t.SPR_SPID,2,3,think_t.A_Chase,statenum_t.S_SPID_RUN7,0,0), // S_SPID_RUN6 new state_t(spritenum_t.SPR_SPID,3,3,think_t.A_Chase,statenum_t.S_SPID_RUN8,0,0), // S_SPID_RUN7 new state_t(spritenum_t.SPR_SPID,3,3,think_t.A_Chase,statenum_t.S_SPID_RUN9,0,0), // S_SPID_RUN8 new state_t(spritenum_t.SPR_SPID,4,3,think_t.A_Metal,statenum_t.S_SPID_RUN10,0,0), // S_SPID_RUN9 new state_t(spritenum_t.SPR_SPID,4,3,think_t.A_Chase,statenum_t.S_SPID_RUN11,0,0), // S_SPID_RUN10 new state_t(spritenum_t.SPR_SPID,5,3,think_t.A_Chase,statenum_t.S_SPID_RUN12,0,0), // S_SPID_RUN11 new state_t(spritenum_t.SPR_SPID,5,3,think_t.A_Chase,statenum_t.S_SPID_RUN1,0,0), // S_SPID_RUN12 new state_t(spritenum_t.SPR_SPID,32768,20,think_t.A_FaceTarget,statenum_t.S_SPID_ATK2,0,0), // S_SPID_ATK1 new state_t(spritenum_t.SPR_SPID,32774,4,think_t.A_SPosAttack,statenum_t.S_SPID_ATK3,0,0), // S_SPID_ATK2 new state_t(spritenum_t.SPR_SPID,32775,4,think_t.A_SPosAttack,statenum_t.S_SPID_ATK4,0,0), // S_SPID_ATK3 new state_t(spritenum_t.SPR_SPID,32775,1,think_t.A_SpidRefire,statenum_t.S_SPID_ATK2,0,0), // S_SPID_ATK4 new state_t(spritenum_t.SPR_SPID,8,3,null,statenum_t.S_SPID_PAIN2,0,0), // S_SPID_PAIN new state_t(spritenum_t.SPR_SPID,8,3,think_t.A_Pain,statenum_t.S_SPID_RUN1,0,0), // S_SPID_PAIN2 new state_t(spritenum_t.SPR_SPID,9,20,think_t.A_Scream,statenum_t.S_SPID_DIE2,0,0), // S_SPID_DIE1 new state_t(spritenum_t.SPR_SPID,10,10,think_t.A_Fall,statenum_t.S_SPID_DIE3,0,0), // S_SPID_DIE2 new state_t(spritenum_t.SPR_SPID,11,10,null,statenum_t.S_SPID_DIE4,0,0), // S_SPID_DIE3 new state_t(spritenum_t.SPR_SPID,12,10,null,statenum_t.S_SPID_DIE5,0,0), // S_SPID_DIE4 new state_t(spritenum_t.SPR_SPID,13,10,null,statenum_t.S_SPID_DIE6,0,0), // S_SPID_DIE5 new state_t(spritenum_t.SPR_SPID,14,10,null,statenum_t.S_SPID_DIE7,0,0), // S_SPID_DIE6 new state_t(spritenum_t.SPR_SPID,15,10,null,statenum_t.S_SPID_DIE8,0,0), // S_SPID_DIE7 new state_t(spritenum_t.SPR_SPID,16,10,null,statenum_t.S_SPID_DIE9,0,0), // S_SPID_DIE8 new state_t(spritenum_t.SPR_SPID,17,10,null,statenum_t.S_SPID_DIE10,0,0), // S_SPID_DIE9 new state_t(spritenum_t.SPR_SPID,18,30,null,statenum_t.S_SPID_DIE11,0,0), // S_SPID_DIE10 new state_t(spritenum_t.SPR_SPID,18,-1,think_t.A_BossDeath,statenum_t.S_NULL,0,0), // S_SPID_DIE11 new state_t(spritenum_t.SPR_BSPI,0,10,think_t.A_Look,statenum_t.S_BSPI_STND2,0,0), // S_BSPI_STND new state_t(spritenum_t.SPR_BSPI,1,10,think_t.A_Look,statenum_t.S_BSPI_STND,0,0), // S_BSPI_STND2 new state_t(spritenum_t.SPR_BSPI,0,20,null,statenum_t.S_BSPI_RUN1,0,0), // S_BSPI_SIGHT new state_t(spritenum_t.SPR_BSPI,0,3,think_t.A_BabyMetal,statenum_t.S_BSPI_RUN2,0,0), // S_BSPI_RUN1 new state_t(spritenum_t.SPR_BSPI,0,3,think_t.A_Chase,statenum_t.S_BSPI_RUN3,0,0), // S_BSPI_RUN2 new state_t(spritenum_t.SPR_BSPI,1,3,think_t.A_Chase,statenum_t.S_BSPI_RUN4,0,0), // S_BSPI_RUN3 new state_t(spritenum_t.SPR_BSPI,1,3,think_t.A_Chase,statenum_t.S_BSPI_RUN5,0,0), // S_BSPI_RUN4 new state_t(spritenum_t.SPR_BSPI,2,3,think_t.A_Chase,statenum_t.S_BSPI_RUN6,0,0), // S_BSPI_RUN5 new state_t(spritenum_t.SPR_BSPI,2,3,think_t.A_Chase,statenum_t.S_BSPI_RUN7,0,0), // S_BSPI_RUN6 new state_t(spritenum_t.SPR_BSPI,3,3,think_t.A_BabyMetal,statenum_t.S_BSPI_RUN8,0,0), // S_BSPI_RUN7 new state_t(spritenum_t.SPR_BSPI,3,3,think_t.A_Chase,statenum_t.S_BSPI_RUN9,0,0), // S_BSPI_RUN8 new state_t(spritenum_t.SPR_BSPI,4,3,think_t.A_Chase,statenum_t.S_BSPI_RUN10,0,0), // S_BSPI_RUN9 new state_t(spritenum_t.SPR_BSPI,4,3,think_t.A_Chase,statenum_t.S_BSPI_RUN11,0,0), // S_BSPI_RUN10 new state_t(spritenum_t.SPR_BSPI,5,3,think_t.A_Chase,statenum_t.S_BSPI_RUN12,0,0), // S_BSPI_RUN11 new state_t(spritenum_t.SPR_BSPI,5,3,think_t.A_Chase,statenum_t.S_BSPI_RUN1,0,0), // S_BSPI_RUN12 new state_t(spritenum_t.SPR_BSPI,32768,20,think_t.A_FaceTarget,statenum_t.S_BSPI_ATK2,0,0), // S_BSPI_ATK1 new state_t(spritenum_t.SPR_BSPI,32774,4,think_t.A_BspiAttack,statenum_t.S_BSPI_ATK3,0,0), // S_BSPI_ATK2 new state_t(spritenum_t.SPR_BSPI,32775,4,null,statenum_t.S_BSPI_ATK4,0,0), // S_BSPI_ATK3 new state_t(spritenum_t.SPR_BSPI,32775,1,think_t.A_SpidRefire,statenum_t.S_BSPI_ATK2,0,0), // S_BSPI_ATK4 new state_t(spritenum_t.SPR_BSPI,8,3,null,statenum_t.S_BSPI_PAIN2,0,0), // S_BSPI_PAIN new state_t(spritenum_t.SPR_BSPI,8,3,think_t.A_Pain,statenum_t.S_BSPI_RUN1,0,0), // S_BSPI_PAIN2 new state_t(spritenum_t.SPR_BSPI,9,20,think_t.A_Scream,statenum_t.S_BSPI_DIE2,0,0), // S_BSPI_DIE1 new state_t(spritenum_t.SPR_BSPI,10,7,think_t.A_Fall,statenum_t.S_BSPI_DIE3,0,0), // S_BSPI_DIE2 new state_t(spritenum_t.SPR_BSPI,11,7,null,statenum_t.S_BSPI_DIE4,0,0), // S_BSPI_DIE3 new state_t(spritenum_t.SPR_BSPI,12,7,null,statenum_t.S_BSPI_DIE5,0,0), // S_BSPI_DIE4 new state_t(spritenum_t.SPR_BSPI,13,7,null,statenum_t.S_BSPI_DIE6,0,0), // S_BSPI_DIE5 new state_t(spritenum_t.SPR_BSPI,14,7,null,statenum_t.S_BSPI_DIE7,0,0), // S_BSPI_DIE6 new state_t(spritenum_t.SPR_BSPI,15,-1,think_t.A_BossDeath,statenum_t.S_NULL,0,0), // S_BSPI_DIE7 new state_t(spritenum_t.SPR_BSPI,15,5,null,statenum_t.S_BSPI_RAISE2,0,0), // S_BSPI_RAISE1 new state_t(spritenum_t.SPR_BSPI,14,5,null,statenum_t.S_BSPI_RAISE3,0,0), // S_BSPI_RAISE2 new state_t(spritenum_t.SPR_BSPI,13,5,null,statenum_t.S_BSPI_RAISE4,0,0), // S_BSPI_RAISE3 new state_t(spritenum_t.SPR_BSPI,12,5,null,statenum_t.S_BSPI_RAISE5,0,0), // S_BSPI_RAISE4 new state_t(spritenum_t.SPR_BSPI,11,5,null,statenum_t.S_BSPI_RAISE6,0,0), // S_BSPI_RAISE5 new state_t(spritenum_t.SPR_BSPI,10,5,null,statenum_t.S_BSPI_RAISE7,0,0), // S_BSPI_RAISE6 new state_t(spritenum_t.SPR_BSPI,9,5,null,statenum_t.S_BSPI_RUN1,0,0), // S_BSPI_RAISE7 new state_t(spritenum_t.SPR_APLS,32768,5,null,statenum_t.S_ARACH_PLAZ2,0,0), // S_ARACH_PLAZ new state_t(spritenum_t.SPR_APLS,32769,5,null,statenum_t.S_ARACH_PLAZ,0,0), // S_ARACH_PLAZ2 new state_t(spritenum_t.SPR_APBX,32768,5,null,statenum_t.S_ARACH_PLEX2,0,0), // S_ARACH_PLEX new state_t(spritenum_t.SPR_APBX,32769,5,null,statenum_t.S_ARACH_PLEX3,0,0), // S_ARACH_PLEX2 new state_t(spritenum_t.SPR_APBX,32770,5,null,statenum_t.S_ARACH_PLEX4,0,0), // S_ARACH_PLEX3 new state_t(spritenum_t.SPR_APBX,32771,5,null,statenum_t.S_ARACH_PLEX5,0,0), // S_ARACH_PLEX4 new state_t(spritenum_t.SPR_APBX,32772,5,null,statenum_t.S_NULL,0,0), // S_ARACH_PLEX5 new state_t(spritenum_t.SPR_CYBR,0,10,think_t.A_Look,statenum_t.S_CYBER_STND2,0,0), // S_CYBER_STND new state_t(spritenum_t.SPR_CYBR,1,10,think_t.A_Look,statenum_t.S_CYBER_STND,0,0), // S_CYBER_STND2 new state_t(spritenum_t.SPR_CYBR,0,3,think_t.A_Hoof,statenum_t.S_CYBER_RUN2,0,0), // S_CYBER_RUN1 new state_t(spritenum_t.SPR_CYBR,0,3,think_t.A_Chase,statenum_t.S_CYBER_RUN3,0,0), // S_CYBER_RUN2 new state_t(spritenum_t.SPR_CYBR,1,3,think_t.A_Chase,statenum_t.S_CYBER_RUN4,0,0), // S_CYBER_RUN3 new state_t(spritenum_t.SPR_CYBR,1,3,think_t.A_Chase,statenum_t.S_CYBER_RUN5,0,0), // S_CYBER_RUN4 new state_t(spritenum_t.SPR_CYBR,2,3,think_t.A_Chase,statenum_t.S_CYBER_RUN6,0,0), // S_CYBER_RUN5 new state_t(spritenum_t.SPR_CYBR,2,3,think_t.A_Chase,statenum_t.S_CYBER_RUN7,0,0), // S_CYBER_RUN6 new state_t(spritenum_t.SPR_CYBR,3,3,think_t.A_Metal,statenum_t.S_CYBER_RUN8,0,0), // S_CYBER_RUN7 new state_t(spritenum_t.SPR_CYBR,3,3,think_t.A_Chase,statenum_t.S_CYBER_RUN1,0,0), // S_CYBER_RUN8 new state_t(spritenum_t.SPR_CYBR,4,6,think_t.A_FaceTarget,statenum_t.S_CYBER_ATK2,0,0), // S_CYBER_ATK1 new state_t(spritenum_t.SPR_CYBR,5,12,think_t.A_CyberAttack,statenum_t.S_CYBER_ATK3,0,0), // S_CYBER_ATK2 new state_t(spritenum_t.SPR_CYBR,4,12,think_t.A_FaceTarget,statenum_t.S_CYBER_ATK4,0,0), // S_CYBER_ATK3 new state_t(spritenum_t.SPR_CYBR,5,12,think_t.A_CyberAttack,statenum_t.S_CYBER_ATK5,0,0), // S_CYBER_ATK4 new state_t(spritenum_t.SPR_CYBR,4,12,think_t.A_FaceTarget,statenum_t.S_CYBER_ATK6,0,0), // S_CYBER_ATK5 new state_t(spritenum_t.SPR_CYBR,5,12,think_t.A_CyberAttack,statenum_t.S_CYBER_RUN1,0,0), // S_CYBER_ATK6 new state_t(spritenum_t.SPR_CYBR,6,10,think_t.A_Pain,statenum_t.S_CYBER_RUN1,0,0), // S_CYBER_PAIN new state_t(spritenum_t.SPR_CYBR,7,10,null,statenum_t.S_CYBER_DIE2,0,0), // S_CYBER_DIE1 new state_t(spritenum_t.SPR_CYBR,8,10,think_t.A_Scream,statenum_t.S_CYBER_DIE3,0,0), // S_CYBER_DIE2 new state_t(spritenum_t.SPR_CYBR,9,10,null,statenum_t.S_CYBER_DIE4,0,0), // S_CYBER_DIE3 new state_t(spritenum_t.SPR_CYBR,10,10,null,statenum_t.S_CYBER_DIE5,0,0), // S_CYBER_DIE4 new state_t(spritenum_t.SPR_CYBR,11,10,null,statenum_t.S_CYBER_DIE6,0,0), // S_CYBER_DIE5 new state_t(spritenum_t.SPR_CYBR,12,10,think_t.A_Fall,statenum_t.S_CYBER_DIE7,0,0), // S_CYBER_DIE6 new state_t(spritenum_t.SPR_CYBR,13,10,null,statenum_t.S_CYBER_DIE8,0,0), // S_CYBER_DIE7 new state_t(spritenum_t.SPR_CYBR,14,10,null,statenum_t.S_CYBER_DIE9,0,0), // S_CYBER_DIE8 new state_t(spritenum_t.SPR_CYBR,15,30,null,statenum_t.S_CYBER_DIE10,0,0), // S_CYBER_DIE9 new state_t(spritenum_t.SPR_CYBR,15,-1,think_t.A_BossDeath,statenum_t.S_NULL,0,0), // S_CYBER_DIE10 new state_t(spritenum_t.SPR_PAIN,0,10,think_t.A_Look,statenum_t.S_PAIN_STND,0,0), // S_PAIN_STND new state_t(spritenum_t.SPR_PAIN,0,3,think_t.A_Chase,statenum_t.S_PAIN_RUN2,0,0), // S_PAIN_RUN1 new state_t(spritenum_t.SPR_PAIN,0,3,think_t.A_Chase,statenum_t.S_PAIN_RUN3,0,0), // S_PAIN_RUN2 new state_t(spritenum_t.SPR_PAIN,1,3,think_t.A_Chase,statenum_t.S_PAIN_RUN4,0,0), // S_PAIN_RUN3 new state_t(spritenum_t.SPR_PAIN,1,3,think_t.A_Chase,statenum_t.S_PAIN_RUN5,0,0), // S_PAIN_RUN4 new state_t(spritenum_t.SPR_PAIN,2,3,think_t.A_Chase,statenum_t.S_PAIN_RUN6,0,0), // S_PAIN_RUN5 new state_t(spritenum_t.SPR_PAIN,2,3,think_t.A_Chase,statenum_t.S_PAIN_RUN1,0,0), // S_PAIN_RUN6 new state_t(spritenum_t.SPR_PAIN,3,5,think_t.A_FaceTarget,statenum_t.S_PAIN_ATK2,0,0), // S_PAIN_ATK1 new state_t(spritenum_t.SPR_PAIN,4,5,think_t.A_FaceTarget,statenum_t.S_PAIN_ATK3,0,0), // S_PAIN_ATK2 new state_t(spritenum_t.SPR_PAIN,32773,5,think_t.A_FaceTarget,statenum_t.S_PAIN_ATK4,0,0), // S_PAIN_ATK3 new state_t(spritenum_t.SPR_PAIN,32773,0,think_t.A_PainAttack,statenum_t.S_PAIN_RUN1,0,0), // S_PAIN_ATK4 new state_t(spritenum_t.SPR_PAIN,6,6,null,statenum_t.S_PAIN_PAIN2,0,0), // S_PAIN_PAIN new state_t(spritenum_t.SPR_PAIN,6,6,think_t.A_Pain,statenum_t.S_PAIN_RUN1,0,0), // S_PAIN_PAIN2 new state_t(spritenum_t.SPR_PAIN,32775,8,null,statenum_t.S_PAIN_DIE2,0,0), // S_PAIN_DIE1 new state_t(spritenum_t.SPR_PAIN,32776,8,think_t.A_Scream,statenum_t.S_PAIN_DIE3,0,0), // S_PAIN_DIE2 new state_t(spritenum_t.SPR_PAIN,32777,8,null,statenum_t.S_PAIN_DIE4,0,0), // S_PAIN_DIE3 new state_t(spritenum_t.SPR_PAIN,32778,8,null,statenum_t.S_PAIN_DIE5,0,0), // S_PAIN_DIE4 new state_t(spritenum_t.SPR_PAIN,32779,8,think_t.A_PainDie,statenum_t.S_PAIN_DIE6,0,0), // S_PAIN_DIE5 new state_t(spritenum_t.SPR_PAIN,32780,8,null,statenum_t.S_NULL,0,0), // S_PAIN_DIE6 new state_t(spritenum_t.SPR_PAIN,12,8,null,statenum_t.S_PAIN_RAISE2,0,0), // S_PAIN_RAISE1 new state_t(spritenum_t.SPR_PAIN,11,8,null,statenum_t.S_PAIN_RAISE3,0,0), // S_PAIN_RAISE2 new state_t(spritenum_t.SPR_PAIN,10,8,null,statenum_t.S_PAIN_RAISE4,0,0), // S_PAIN_RAISE3 new state_t(spritenum_t.SPR_PAIN,9,8,null,statenum_t.S_PAIN_RAISE5,0,0), // S_PAIN_RAISE4 new state_t(spritenum_t.SPR_PAIN,8,8,null,statenum_t.S_PAIN_RAISE6,0,0), // S_PAIN_RAISE5 new state_t(spritenum_t.SPR_PAIN,7,8,null,statenum_t.S_PAIN_RUN1,0,0), // S_PAIN_RAISE6 new state_t(spritenum_t.SPR_SSWV,0,10,think_t.A_Look,statenum_t.S_SSWV_STND2,0,0), // S_SSWV_STND new state_t(spritenum_t.SPR_SSWV,1,10,think_t.A_Look,statenum_t.S_SSWV_STND,0,0), // S_SSWV_STND2 new state_t(spritenum_t.SPR_SSWV,0,3,think_t.A_Chase,statenum_t.S_SSWV_RUN2,0,0), // S_SSWV_RUN1 new state_t(spritenum_t.SPR_SSWV,0,3,think_t.A_Chase,statenum_t.S_SSWV_RUN3,0,0), // S_SSWV_RUN2 new state_t(spritenum_t.SPR_SSWV,1,3,think_t.A_Chase,statenum_t.S_SSWV_RUN4,0,0), // S_SSWV_RUN3 new state_t(spritenum_t.SPR_SSWV,1,3,think_t.A_Chase,statenum_t.S_SSWV_RUN5,0,0), // S_SSWV_RUN4 new state_t(spritenum_t.SPR_SSWV,2,3,think_t.A_Chase,statenum_t.S_SSWV_RUN6,0,0), // S_SSWV_RUN5 new state_t(spritenum_t.SPR_SSWV,2,3,think_t.A_Chase,statenum_t.S_SSWV_RUN7,0,0), // S_SSWV_RUN6 new state_t(spritenum_t.SPR_SSWV,3,3,think_t.A_Chase,statenum_t.S_SSWV_RUN8,0,0), // S_SSWV_RUN7 new state_t(spritenum_t.SPR_SSWV,3,3,think_t.A_Chase,statenum_t.S_SSWV_RUN1,0,0), // S_SSWV_RUN8 new state_t(spritenum_t.SPR_SSWV,4,10,think_t.A_FaceTarget,statenum_t.S_SSWV_ATK2,0,0), // S_SSWV_ATK1 new state_t(spritenum_t.SPR_SSWV,5,10,think_t.A_FaceTarget,statenum_t.S_SSWV_ATK3,0,0), // S_SSWV_ATK2 new state_t(spritenum_t.SPR_SSWV,32774,4,think_t.A_CPosAttack,statenum_t.S_SSWV_ATK4,0,0), // S_SSWV_ATK3 new state_t(spritenum_t.SPR_SSWV,5,6,think_t.A_FaceTarget,statenum_t.S_SSWV_ATK5,0,0), // S_SSWV_ATK4 new state_t(spritenum_t.SPR_SSWV,32774,4,think_t.A_CPosAttack,statenum_t.S_SSWV_ATK6,0,0), // S_SSWV_ATK5 new state_t(spritenum_t.SPR_SSWV,5,1,think_t.A_CPosRefire,statenum_t.S_SSWV_ATK2,0,0), // S_SSWV_ATK6 new state_t(spritenum_t.SPR_SSWV,7,3,null,statenum_t.S_SSWV_PAIN2,0,0), // S_SSWV_PAIN new state_t(spritenum_t.SPR_SSWV,7,3,think_t.A_Pain,statenum_t.S_SSWV_RUN1,0,0), // S_SSWV_PAIN2 new state_t(spritenum_t.SPR_SSWV,8,5,null,statenum_t.S_SSWV_DIE2,0,0), // S_SSWV_DIE1 new state_t(spritenum_t.SPR_SSWV,9,5,think_t.A_Scream,statenum_t.S_SSWV_DIE3,0,0), // S_SSWV_DIE2 new state_t(spritenum_t.SPR_SSWV,10,5,think_t.A_Fall,statenum_t.S_SSWV_DIE4,0,0), // S_SSWV_DIE3 new state_t(spritenum_t.SPR_SSWV,11,5,null,statenum_t.S_SSWV_DIE5,0,0), // S_SSWV_DIE4 new state_t(spritenum_t.SPR_SSWV,12,-1,null,statenum_t.S_NULL,0,0), // S_SSWV_DIE5 new state_t(spritenum_t.SPR_SSWV,13,5,null,statenum_t.S_SSWV_XDIE2,0,0), // S_SSWV_XDIE1 new state_t(spritenum_t.SPR_SSWV,14,5,think_t.A_XScream,statenum_t.S_SSWV_XDIE3,0,0), // S_SSWV_XDIE2 new state_t(spritenum_t.SPR_SSWV,15,5,think_t.A_Fall,statenum_t.S_SSWV_XDIE4,0,0), // S_SSWV_XDIE3 new state_t(spritenum_t.SPR_SSWV,16,5,null,statenum_t.S_SSWV_XDIE5,0,0), // S_SSWV_XDIE4 new state_t(spritenum_t.SPR_SSWV,17,5,null,statenum_t.S_SSWV_XDIE6,0,0), // S_SSWV_XDIE5 new state_t(spritenum_t.SPR_SSWV,18,5,null,statenum_t.S_SSWV_XDIE7,0,0), // S_SSWV_XDIE6 new state_t(spritenum_t.SPR_SSWV,19,5,null,statenum_t.S_SSWV_XDIE8,0,0), // S_SSWV_XDIE7 new state_t(spritenum_t.SPR_SSWV,20,5,null,statenum_t.S_SSWV_XDIE9,0,0), // S_SSWV_XDIE8 new state_t(spritenum_t.SPR_SSWV,21,-1,null,statenum_t.S_NULL,0,0), // S_SSWV_XDIE9 new state_t(spritenum_t.SPR_SSWV,12,5,null,statenum_t.S_SSWV_RAISE2,0,0), // S_SSWV_RAISE1 new state_t(spritenum_t.SPR_SSWV,11,5,null,statenum_t.S_SSWV_RAISE3,0,0), // S_SSWV_RAISE2 new state_t(spritenum_t.SPR_SSWV,10,5,null,statenum_t.S_SSWV_RAISE4,0,0), // S_SSWV_RAISE3 new state_t(spritenum_t.SPR_SSWV,9,5,null,statenum_t.S_SSWV_RAISE5,0,0), // S_SSWV_RAISE4 new state_t(spritenum_t.SPR_SSWV,8,5,null,statenum_t.S_SSWV_RUN1,0,0), // S_SSWV_RAISE5 new state_t(spritenum_t.SPR_KEEN,0,-1,null,statenum_t.S_KEENSTND,0,0), // S_KEENSTND new state_t(spritenum_t.SPR_KEEN,0,6,null,statenum_t.S_COMMKEEN2,0,0), // S_COMMKEEN new state_t(spritenum_t.SPR_KEEN,1,6,null,statenum_t.S_COMMKEEN3,0,0), // S_COMMKEEN2 new state_t(spritenum_t.SPR_KEEN,2,6,think_t.A_Scream,statenum_t.S_COMMKEEN4,0,0), // S_COMMKEEN3 new state_t(spritenum_t.SPR_KEEN,3,6,null,statenum_t.S_COMMKEEN5,0,0), // S_COMMKEEN4 new state_t(spritenum_t.SPR_KEEN,4,6,null,statenum_t.S_COMMKEEN6,0,0), // S_COMMKEEN5 new state_t(spritenum_t.SPR_KEEN,5,6,null,statenum_t.S_COMMKEEN7,0,0), // S_COMMKEEN6 new state_t(spritenum_t.SPR_KEEN,6,6,null,statenum_t.S_COMMKEEN8,0,0), // S_COMMKEEN7 new state_t(spritenum_t.SPR_KEEN,7,6,null,statenum_t.S_COMMKEEN9,0,0), // S_COMMKEEN8 new state_t(spritenum_t.SPR_KEEN,8,6,null,statenum_t.S_COMMKEEN10,0,0), // S_COMMKEEN9 new state_t(spritenum_t.SPR_KEEN,9,6,null,statenum_t.S_COMMKEEN11,0,0), // S_COMMKEEN10 new state_t(spritenum_t.SPR_KEEN,10,6,think_t.A_KeenDie,statenum_t.S_COMMKEEN12,0,0),// S_COMMKEEN11 new state_t(spritenum_t.SPR_KEEN,11,-1,null,statenum_t.S_NULL,0,0), // S_COMMKEEN12 new state_t(spritenum_t.SPR_KEEN,12,4,null,statenum_t.S_KEENPAIN2,0,0), // S_KEENPAIN new state_t(spritenum_t.SPR_KEEN,12,8,think_t.A_Pain,statenum_t.S_KEENSTND,0,0), // S_KEENPAIN2 new state_t(spritenum_t.SPR_BBRN,0,-1,null,statenum_t.S_NULL,0,0), // S_BRAIN new state_t(spritenum_t.SPR_BBRN,1,36,think_t.A_BrainPain,statenum_t.S_BRAIN,0,0), // S_BRAIN_PAIN new state_t(spritenum_t.SPR_BBRN,0,100,think_t.A_BrainScream,statenum_t.S_BRAIN_DIE2,0,0), // S_BRAIN_DIE1 new state_t(spritenum_t.SPR_BBRN,0,10,null,statenum_t.S_BRAIN_DIE3,0,0), // S_BRAIN_DIE2 new state_t(spritenum_t.SPR_BBRN,0,10,null,statenum_t.S_BRAIN_DIE4,0,0), // S_BRAIN_DIE3 new state_t(spritenum_t.SPR_BBRN,0,-1,think_t.A_BrainDie,statenum_t.S_NULL,0,0), // S_BRAIN_DIE4 new state_t(spritenum_t.SPR_SSWV,0,10,think_t.A_Look,statenum_t.S_BRAINEYE,0,0), // S_BRAINEYE new state_t(spritenum_t.SPR_SSWV,0,181,think_t.A_BrainAwake,statenum_t.S_BRAINEYE1,0,0), // S_BRAINEYESEE new state_t(spritenum_t.SPR_SSWV,0,150,think_t.A_BrainSpit,statenum_t.S_BRAINEYE1,0,0), // S_BRAINEYE1 new state_t(spritenum_t.SPR_BOSF,32768,3,think_t.A_SpawnSound,statenum_t.S_SPAWN2,0,0), // S_SPAWN1 new state_t(spritenum_t.SPR_BOSF,32769,3,think_t.A_SpawnFly,statenum_t.S_SPAWN3,0,0), // S_SPAWN2 new state_t(spritenum_t.SPR_BOSF,32770,3,think_t.A_SpawnFly,statenum_t.S_SPAWN4,0,0), // S_SPAWN3 new state_t(spritenum_t.SPR_BOSF,32771,3,think_t.A_SpawnFly,statenum_t.S_SPAWN1,0,0), // S_SPAWN4 new state_t(spritenum_t.SPR_FIRE,32768,4,think_t.A_Fire,statenum_t.S_SPAWNFIRE2,0,0), // S_SPAWNFIRE1 new state_t(spritenum_t.SPR_FIRE,32769,4,think_t.A_Fire,statenum_t.S_SPAWNFIRE3,0,0), // S_SPAWNFIRE2 new state_t(spritenum_t.SPR_FIRE,32770,4,think_t.A_Fire,statenum_t.S_SPAWNFIRE4,0,0), // S_SPAWNFIRE3 new state_t(spritenum_t.SPR_FIRE,32771,4,think_t.A_Fire,statenum_t.S_SPAWNFIRE5,0,0), // S_SPAWNFIRE4 new state_t(spritenum_t.SPR_FIRE,32772,4,think_t.A_Fire,statenum_t.S_SPAWNFIRE6,0,0), // S_SPAWNFIRE5 new state_t(spritenum_t.SPR_FIRE,32773,4,think_t.A_Fire,statenum_t.S_SPAWNFIRE7,0,0), // S_SPAWNFIRE6 new state_t(spritenum_t.SPR_FIRE,32774,4,think_t.A_Fire,statenum_t.S_SPAWNFIRE8,0,0), // S_SPAWNFIRE7 new state_t(spritenum_t.SPR_FIRE,32775,4,think_t.A_Fire,statenum_t.S_NULL,0,0), // S_SPAWNFIRE8 new state_t(spritenum_t.SPR_MISL,32769,10,null,statenum_t.S_BRAINEXPLODE2,0,0), // S_BRAINEXPLODE1 new state_t(spritenum_t.SPR_MISL,32770,10,null,statenum_t.S_BRAINEXPLODE3,0,0), // S_BRAINEXPLODE2 new state_t(spritenum_t.SPR_MISL,32771,10,think_t.A_BrainExplode,statenum_t.S_NULL,0,0), // S_BRAINEXPLODE3 new state_t(spritenum_t.SPR_ARM1,0,6,null,statenum_t.S_ARM1A,0,0), // S_ARM1 new state_t(spritenum_t.SPR_ARM1,32769,7,null,statenum_t.S_ARM1,0,0), // S_ARM1A new state_t(spritenum_t.SPR_ARM2,0,6,null,statenum_t.S_ARM2A,0,0), // S_ARM2 new state_t(spritenum_t.SPR_ARM2,32769,6,null,statenum_t.S_ARM2,0,0), // S_ARM2A new state_t(spritenum_t.SPR_BAR1,0,6,null,statenum_t.S_BAR2,0,0), // S_BAR1 new state_t(spritenum_t.SPR_BAR1,1,6,null,statenum_t.S_BAR1,0,0), // S_BAR2 new state_t(spritenum_t.SPR_BEXP,32768,5,null,statenum_t.S_BEXP2,0,0), // S_BEXP new state_t(spritenum_t.SPR_BEXP,32769,5,think_t.A_Scream,statenum_t.S_BEXP3,0,0), // S_BEXP2 new state_t(spritenum_t.SPR_BEXP,32770,5,null,statenum_t.S_BEXP4,0,0), // S_BEXP3 new state_t(spritenum_t.SPR_BEXP,32771,10,think_t.A_Explode,statenum_t.S_BEXP5,0,0), // S_BEXP4 new state_t(spritenum_t.SPR_BEXP,32772,10,null,statenum_t.S_NULL,0,0), // S_BEXP5 new state_t(spritenum_t.SPR_FCAN,32768,4,null,statenum_t.S_BBAR2,0,0), // S_BBAR1 new state_t(spritenum_t.SPR_FCAN,32769,4,null,statenum_t.S_BBAR3,0,0), // S_BBAR2 new state_t(spritenum_t.SPR_FCAN,32770,4,null,statenum_t.S_BBAR1,0,0), // S_BBAR3 new state_t(spritenum_t.SPR_BON1,0,6,null,statenum_t.S_BON1A,0,0), // S_BON1 new state_t(spritenum_t.SPR_BON1,1,6,null,statenum_t.S_BON1B,0,0), // S_BON1A new state_t(spritenum_t.SPR_BON1,2,6,null,statenum_t.S_BON1C,0,0), // S_BON1B new state_t(spritenum_t.SPR_BON1,3,6,null,statenum_t.S_BON1D,0,0), // S_BON1C new state_t(spritenum_t.SPR_BON1,2,6,null,statenum_t.S_BON1E,0,0), // S_BON1D new state_t(spritenum_t.SPR_BON1,1,6,null,statenum_t.S_BON1,0,0), // S_BON1E new state_t(spritenum_t.SPR_BON2,0,6,null,statenum_t.S_BON2A,0,0), // S_BON2 new state_t(spritenum_t.SPR_BON2,1,6,null,statenum_t.S_BON2B,0,0), // S_BON2A new state_t(spritenum_t.SPR_BON2,2,6,null,statenum_t.S_BON2C,0,0), // S_BON2B new state_t(spritenum_t.SPR_BON2,3,6,null,statenum_t.S_BON2D,0,0), // S_BON2C new state_t(spritenum_t.SPR_BON2,2,6,null,statenum_t.S_BON2E,0,0), // S_BON2D new state_t(spritenum_t.SPR_BON2,1,6,null,statenum_t.S_BON2,0,0), // S_BON2E new state_t(spritenum_t.SPR_BKEY,0,10,null,statenum_t.S_BKEY2,0,0), // S_BKEY new state_t(spritenum_t.SPR_BKEY,32769,10,null,statenum_t.S_BKEY,0,0), // S_BKEY2 new state_t(spritenum_t.SPR_RKEY,0,10,null,statenum_t.S_RKEY2,0,0), // S_RKEY new state_t(spritenum_t.SPR_RKEY,32769,10,null,statenum_t.S_RKEY,0,0), // S_RKEY2 new state_t(spritenum_t.SPR_YKEY,0,10,null,statenum_t.S_YKEY2,0,0), // S_YKEY new state_t(spritenum_t.SPR_YKEY,32769,10,null,statenum_t.S_YKEY,0,0), // S_YKEY2 new state_t(spritenum_t.SPR_BSKU,0,10,null,statenum_t.S_BSKULL2,0,0), // S_BSKULL new state_t(spritenum_t.SPR_BSKU,32769,10,null,statenum_t.S_BSKULL,0,0), // S_BSKULL2 new state_t(spritenum_t.SPR_RSKU,0,10,null,statenum_t.S_RSKULL2,0,0), // S_RSKULL new state_t(spritenum_t.SPR_RSKU,32769,10,null,statenum_t.S_RSKULL,0,0), // S_RSKULL2 new state_t(spritenum_t.SPR_YSKU,0,10,null,statenum_t.S_YSKULL2,0,0), // S_YSKULL new state_t(spritenum_t.SPR_YSKU,32769,10,null,statenum_t.S_YSKULL,0,0), // S_YSKULL2 new state_t(spritenum_t.SPR_STIM,0,-1,null,statenum_t.S_NULL,0,0), // S_STIM new state_t(spritenum_t.SPR_MEDI,0,-1,null,statenum_t.S_NULL,0,0), // S_MEDI new state_t(spritenum_t.SPR_SOUL,32768,6,null,statenum_t.S_SOUL2,0,0), // S_SOUL new state_t(spritenum_t.SPR_SOUL,32769,6,null,statenum_t.S_SOUL3,0,0), // S_SOUL2 new state_t(spritenum_t.SPR_SOUL,32770,6,null,statenum_t.S_SOUL4,0,0), // S_SOUL3 new state_t(spritenum_t.SPR_SOUL,32771,6,null,statenum_t.S_SOUL5,0,0), // S_SOUL4 new state_t(spritenum_t.SPR_SOUL,32770,6,null,statenum_t.S_SOUL6,0,0), // S_SOUL5 new state_t(spritenum_t.SPR_SOUL,32769,6,null,statenum_t.S_SOUL,0,0), // S_SOUL6 new state_t(spritenum_t.SPR_PINV,32768,6,null,statenum_t.S_PINV2,0,0), // S_PINV new state_t(spritenum_t.SPR_PINV,32769,6,null,statenum_t.S_PINV3,0,0), // S_PINV2 new state_t(spritenum_t.SPR_PINV,32770,6,null,statenum_t.S_PINV4,0,0), // S_PINV3 new state_t(spritenum_t.SPR_PINV,32771,6,null,statenum_t.S_PINV,0,0), // S_PINV4 new state_t(spritenum_t.SPR_PSTR,32768,-1,null,statenum_t.S_NULL,0,0), // S_PSTR new state_t(spritenum_t.SPR_PINS,32768,6,null,statenum_t.S_PINS2,0,0), // S_PINS new state_t(spritenum_t.SPR_PINS,32769,6,null,statenum_t.S_PINS3,0,0), // S_PINS2 new state_t(spritenum_t.SPR_PINS,32770,6,null,statenum_t.S_PINS4,0,0), // S_PINS3 new state_t(spritenum_t.SPR_PINS,32771,6,null,statenum_t.S_PINS,0,0), // S_PINS4 new state_t(spritenum_t.SPR_MEGA,32768,6,null,statenum_t.S_MEGA2,0,0), // S_MEGA new state_t(spritenum_t.SPR_MEGA,32769,6,null,statenum_t.S_MEGA3,0,0), // S_MEGA2 new state_t(spritenum_t.SPR_MEGA,32770,6,null,statenum_t.S_MEGA4,0,0), // S_MEGA3 new state_t(spritenum_t.SPR_MEGA,32771,6,null,statenum_t.S_MEGA,0,0), // S_MEGA4 new state_t(spritenum_t.SPR_SUIT,32768,-1,null,statenum_t.S_NULL,0,0), // S_SUIT new state_t(spritenum_t.SPR_PMAP,32768,6,null,statenum_t.S_PMAP2,0,0), // S_PMAP new state_t(spritenum_t.SPR_PMAP,32769,6,null,statenum_t.S_PMAP3,0,0), // S_PMAP2 new state_t(spritenum_t.SPR_PMAP,32770,6,null,statenum_t.S_PMAP4,0,0), // S_PMAP3 new state_t(spritenum_t.SPR_PMAP,32771,6,null,statenum_t.S_PMAP5,0,0), // S_PMAP4 new state_t(spritenum_t.SPR_PMAP,32770,6,null,statenum_t.S_PMAP6,0,0), // S_PMAP5 new state_t(spritenum_t.SPR_PMAP,32769,6,null,statenum_t.S_PMAP,0,0), // S_PMAP6 new state_t(spritenum_t.SPR_PVIS,32768,6,null,statenum_t.S_PVIS2,0,0), // S_PVIS new state_t(spritenum_t.SPR_PVIS,1,6,null,statenum_t.S_PVIS,0,0), // S_PVIS2 new state_t(spritenum_t.SPR_CLIP,0,-1,null,statenum_t.S_NULL,0,0), // S_CLIP new state_t(spritenum_t.SPR_AMMO,0,-1,null,statenum_t.S_NULL,0,0), // S_AMMO new state_t(spritenum_t.SPR_ROCK,0,-1,null,statenum_t.S_NULL,0,0), // S_ROCK new state_t(spritenum_t.SPR_BROK,0,-1,null,statenum_t.S_NULL,0,0), // S_BROK new state_t(spritenum_t.SPR_CELL,0,-1,null,statenum_t.S_NULL,0,0), // S_CELL new state_t(spritenum_t.SPR_CELP,0,-1,null,statenum_t.S_NULL,0,0), // S_CELP new state_t(spritenum_t.SPR_SHEL,0,-1,null,statenum_t.S_NULL,0,0), // S_SHEL new state_t(spritenum_t.SPR_SBOX,0,-1,null,statenum_t.S_NULL,0,0), // S_SBOX new state_t(spritenum_t.SPR_BPAK,0,-1,null,statenum_t.S_NULL,0,0), // S_BPAK new state_t(spritenum_t.SPR_BFUG,0,-1,null,statenum_t.S_NULL,0,0), // S_BFUG new state_t(spritenum_t.SPR_MGUN,0,-1,null,statenum_t.S_NULL,0,0), // S_MGUN new state_t(spritenum_t.SPR_CSAW,0,-1,null,statenum_t.S_NULL,0,0), // S_CSAW new state_t(spritenum_t.SPR_LAUN,0,-1,null,statenum_t.S_NULL,0,0), // S_LAUN new state_t(spritenum_t.SPR_PLAS,0,-1,null,statenum_t.S_NULL,0,0), // S_PLAS new state_t(spritenum_t.SPR_SHOT,0,-1,null,statenum_t.S_NULL,0,0), // S_SHOT new state_t(spritenum_t.SPR_SGN2,0,-1,null,statenum_t.S_NULL,0,0), // S_SHOT2 new state_t(spritenum_t.SPR_COLU,32768,-1,null,statenum_t.S_NULL,0,0), // S_COLU new state_t(spritenum_t.SPR_SMT2,0,-1,null,statenum_t.S_NULL,0,0), // S_STALAG new state_t(spritenum_t.SPR_GOR1,0,10,null,statenum_t.S_BLOODYTWITCH2,0,0), // S_BLOODYTWITCH new state_t(spritenum_t.SPR_GOR1,1,15,null,statenum_t.S_BLOODYTWITCH3,0,0), // S_BLOODYTWITCH2 new state_t(spritenum_t.SPR_GOR1,2,8,null,statenum_t.S_BLOODYTWITCH4,0,0), // S_BLOODYTWITCH3 new state_t(spritenum_t.SPR_GOR1,1,6,null,statenum_t.S_BLOODYTWITCH,0,0), // S_BLOODYTWITCH4 new state_t(spritenum_t.SPR_PLAY,13,-1,null,statenum_t.S_NULL,0,0), // S_DEADTORSO new state_t(spritenum_t.SPR_PLAY,18,-1,null,statenum_t.S_NULL,0,0), // S_DEADBOTTOM new state_t(spritenum_t.SPR_POL2,0,-1,null,statenum_t.S_NULL,0,0), // S_HEADSONSTICK new state_t(spritenum_t.SPR_POL5,0,-1,null,statenum_t.S_NULL,0,0), // S_GIBS new state_t(spritenum_t.SPR_POL4,0,-1,null,statenum_t.S_NULL,0,0), // S_HEADONASTICK new state_t(spritenum_t.SPR_POL3,32768,6,null,statenum_t.S_HEADCANDLES2,0,0), // S_HEADCANDLES new state_t(spritenum_t.SPR_POL3,32769,6,null,statenum_t.S_HEADCANDLES,0,0), // S_HEADCANDLES2 new state_t(spritenum_t.SPR_POL1,0,-1,null,statenum_t.S_NULL,0,0), // S_DEADSTICK new state_t(spritenum_t.SPR_POL6,0,6,null,statenum_t.S_LIVESTICK2,0,0), // S_LIVESTICK new state_t(spritenum_t.SPR_POL6,1,8,null,statenum_t.S_LIVESTICK,0,0), // S_LIVESTICK2 new state_t(spritenum_t.SPR_GOR2,0,-1,null,statenum_t.S_NULL,0,0), // S_MEAT2 new state_t(spritenum_t.SPR_GOR3,0,-1,null,statenum_t.S_NULL,0,0), // S_MEAT3 new state_t(spritenum_t.SPR_GOR4,0,-1,null,statenum_t.S_NULL,0,0), // S_MEAT4 new state_t(spritenum_t.SPR_GOR5,0,-1,null,statenum_t.S_NULL,0,0), // S_MEAT5 new state_t(spritenum_t.SPR_SMIT,0,-1,null,statenum_t.S_NULL,0,0), // S_STALAGTITE new state_t(spritenum_t.SPR_COL1,0,-1,null,statenum_t.S_NULL,0,0), // S_TALLGRNCOL new state_t(spritenum_t.SPR_COL2,0,-1,null,statenum_t.S_NULL,0,0), // S_SHRTGRNCOL new state_t(spritenum_t.SPR_COL3,0,-1,null,statenum_t.S_NULL,0,0), // S_TALLREDCOL new state_t(spritenum_t.SPR_COL4,0,-1,null,statenum_t.S_NULL,0,0), // S_SHRTREDCOL new state_t(spritenum_t.SPR_CAND,32768,-1,null,statenum_t.S_NULL,0,0), // S_CANDLESTIK new state_t(spritenum_t.SPR_CBRA,32768,-1,null,statenum_t.S_NULL,0,0), // S_CANDELABRA new state_t(spritenum_t.SPR_COL6,0,-1,null,statenum_t.S_NULL,0,0), // S_SKULLCOL new state_t(spritenum_t.SPR_TRE1,0,-1,null,statenum_t.S_NULL,0,0), // S_TORCHTREE new state_t(spritenum_t.SPR_TRE2,0,-1,null,statenum_t.S_NULL,0,0), // S_BIGTREE new state_t(spritenum_t.SPR_ELEC,0,-1,null,statenum_t.S_NULL,0,0), // S_TECHPILLAR new state_t(spritenum_t.SPR_CEYE,32768,6,null,statenum_t.S_EVILEYE2,0,0), // S_EVILEYE new state_t(spritenum_t.SPR_CEYE,32769,6,null,statenum_t.S_EVILEYE3,0,0), // S_EVILEYE2 new state_t(spritenum_t.SPR_CEYE,32770,6,null,statenum_t.S_EVILEYE4,0,0), // S_EVILEYE3 new state_t(spritenum_t.SPR_CEYE,32769,6,null,statenum_t.S_EVILEYE,0,0), // S_EVILEYE4 new state_t(spritenum_t.SPR_FSKU,32768,6,null,statenum_t.S_FLOATSKULL2,0,0), // S_FLOATSKULL new state_t(spritenum_t.SPR_FSKU,32769,6,null,statenum_t.S_FLOATSKULL3,0,0), // S_FLOATSKULL2 new state_t(spritenum_t.SPR_FSKU,32770,6,null,statenum_t.S_FLOATSKULL,0,0), // S_FLOATSKULL3 new state_t(spritenum_t.SPR_COL5,0,14,null,statenum_t.S_HEARTCOL2,0,0), // S_HEARTCOL new state_t(spritenum_t.SPR_COL5,1,14,null,statenum_t.S_HEARTCOL,0,0), // S_HEARTCOL2 new state_t(spritenum_t.SPR_TBLU,32768,4,null,statenum_t.S_BLUETORCH2,0,0), // S_BLUETORCH new state_t(spritenum_t.SPR_TBLU,32769,4,null,statenum_t.S_BLUETORCH3,0,0), // S_BLUETORCH2 new state_t(spritenum_t.SPR_TBLU,32770,4,null,statenum_t.S_BLUETORCH4,0,0), // S_BLUETORCH3 new state_t(spritenum_t.SPR_TBLU,32771,4,null,statenum_t.S_BLUETORCH,0,0), // S_BLUETORCH4 new state_t(spritenum_t.SPR_TGRN,32768,4,null,statenum_t.S_GREENTORCH2,0,0), // S_GREENTORCH new state_t(spritenum_t.SPR_TGRN,32769,4,null,statenum_t.S_GREENTORCH3,0,0), // S_GREENTORCH2 new state_t(spritenum_t.SPR_TGRN,32770,4,null,statenum_t.S_GREENTORCH4,0,0), // S_GREENTORCH3 new state_t(spritenum_t.SPR_TGRN,32771,4,null,statenum_t.S_GREENTORCH,0,0), // S_GREENTORCH4 new state_t(spritenum_t.SPR_TRED,32768,4,null,statenum_t.S_REDTORCH2,0,0), // S_REDTORCH new state_t(spritenum_t.SPR_TRED,32769,4,null,statenum_t.S_REDTORCH3,0,0), // S_REDTORCH2 new state_t(spritenum_t.SPR_TRED,32770,4,null,statenum_t.S_REDTORCH4,0,0), // S_REDTORCH3 new state_t(spritenum_t.SPR_TRED,32771,4,null,statenum_t.S_REDTORCH,0,0), // S_REDTORCH4 new state_t(spritenum_t.SPR_SMBT,32768,4,null,statenum_t.S_BTORCHSHRT2,0,0), // S_BTORCHSHRT new state_t(spritenum_t.SPR_SMBT,32769,4,null,statenum_t.S_BTORCHSHRT3,0,0), // S_BTORCHSHRT2 new state_t(spritenum_t.SPR_SMBT,32770,4,null,statenum_t.S_BTORCHSHRT4,0,0), // S_BTORCHSHRT3 new state_t(spritenum_t.SPR_SMBT,32771,4,null,statenum_t.S_BTORCHSHRT,0,0), // S_BTORCHSHRT4 new state_t(spritenum_t.SPR_SMGT,32768,4,null,statenum_t.S_GTORCHSHRT2,0,0), // S_GTORCHSHRT new state_t(spritenum_t.SPR_SMGT,32769,4,null,statenum_t.S_GTORCHSHRT3,0,0), // S_GTORCHSHRT2 new state_t(spritenum_t.SPR_SMGT,32770,4,null,statenum_t.S_GTORCHSHRT4,0,0), // S_GTORCHSHRT3 new state_t(spritenum_t.SPR_SMGT,32771,4,null,statenum_t.S_GTORCHSHRT,0,0), // S_GTORCHSHRT4 new state_t(spritenum_t.SPR_SMRT,32768,4,null,statenum_t.S_RTORCHSHRT2,0,0), // S_RTORCHSHRT new state_t(spritenum_t.SPR_SMRT,32769,4,null,statenum_t.S_RTORCHSHRT3,0,0), // S_RTORCHSHRT2 new state_t(spritenum_t.SPR_SMRT,32770,4,null,statenum_t.S_RTORCHSHRT4,0,0), // S_RTORCHSHRT3 new state_t(spritenum_t.SPR_SMRT,32771,4,null,statenum_t.S_RTORCHSHRT,0,0), // S_RTORCHSHRT4 new state_t(spritenum_t.SPR_HDB1,0,-1,null,statenum_t.S_NULL,0,0), // S_HANGNOGUTS new state_t(spritenum_t.SPR_HDB2,0,-1,null,statenum_t.S_NULL,0,0), // S_HANGBNOBRAIN new state_t(spritenum_t.SPR_HDB3,0,-1,null,statenum_t.S_NULL,0,0), // S_HANGTLOOKDN new state_t(spritenum_t.SPR_HDB4,0,-1,null,statenum_t.S_NULL,0,0), // S_HANGTSKULL new state_t(spritenum_t.SPR_HDB5,0,-1,null,statenum_t.S_NULL,0,0), // S_HANGTLOOKUP new state_t(spritenum_t.SPR_HDB6,0,-1,null,statenum_t.S_NULL,0,0), // S_HANGTNOBRAIN new state_t(spritenum_t.SPR_POB1,0,-1,null,statenum_t.S_NULL,0,0), // S_COLONGIBS new state_t(spritenum_t.SPR_POB2,0,-1,null,statenum_t.S_NULL,0,0), // S_SMALLPOOL new state_t(spritenum_t.SPR_BRS1,0,-1,null,statenum_t.S_NULL,0,0), // S_BRAINSTEM new state_t(spritenum_t.SPR_TLMP,32768,4,null,statenum_t.S_TECHLAMP2,0,0), // S_TECHLAMP new state_t(spritenum_t.SPR_TLMP,32769,4,null,statenum_t.S_TECHLAMP3,0,0), // S_TECHLAMP2 new state_t(spritenum_t.SPR_TLMP,32770,4,null,statenum_t.S_TECHLAMP4,0,0), // S_TECHLAMP3 new state_t(spritenum_t.SPR_TLMP,32771,4,null,statenum_t.S_TECHLAMP,0,0), // S_TECHLAMP4 new state_t(spritenum_t.SPR_TLP2,32768,4,null,statenum_t.S_TECH2LAMP2,0,0), // S_TECH2LAMP new state_t(spritenum_t.SPR_TLP2,32769,4,null,statenum_t.S_TECH2LAMP3,0,0), // S_TECH2LAMP2 new state_t(spritenum_t.SPR_TLP2,32770,4,null,statenum_t.S_TECH2LAMP4,0,0), // S_TECH2LAMP3 new state_t(spritenum_t.SPR_TLP2,32771,4,null,statenum_t.S_TECH2LAMP,0,0) // S_TECH2LAMP4 }; public static mobjinfo_t[] mobjinfo = { new mobjinfo_t( // MT_PLAYER -1, // doomednum statenum_t.S_PLAY, // spawnstate 100, // spawnhealth statenum_t.S_PLAY_RUN1, // seestate sfxenum_t.sfx_None, // seesound 0, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_PLAY_PAIN, // painstate 255, // painchance sfxenum_t.sfx_plpain, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_PLAY_ATK1, // missilestate statenum_t.S_PLAY_DIE1, // deathstate statenum_t.S_PLAY_XDIE1, // xdeathstate sfxenum_t.sfx_pldeth, // deathsound 0, // speed 16*FRACUNIT, // radius 56*FRACUNIT, // height 100, // mass 0, // damage sfxenum_t.sfx_None, // activesound MF_SOLID|MF_SHOOTABLE|MF_DROPOFF|MF_PICKUP|MF_NOTDMATCH, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_POSSESSED 3004, // doomednum statenum_t.S_POSS_STND, // spawnstate 20, // spawnhealth statenum_t.S_POSS_RUN1, // seestate sfxenum_t.sfx_posit1, // seesound 8, // reactiontime sfxenum_t.sfx_pistol, // attacksound statenum_t.S_POSS_PAIN, // painstate 200, // painchance sfxenum_t.sfx_popain, // painsound statenum_t.S_NULL, // meleestate MAES: BE careful with "0 - null" states! statenum_t.S_POSS_ATK1, // missilestate statenum_t.S_POSS_DIE1, // deathstate statenum_t.S_POSS_XDIE1, // xdeathstate sfxenum_t.sfx_podth1, // deathsound 8, // speed 20*FRACUNIT, // radius 56*FRACUNIT, // height 100, // mass 0, // damage sfxenum_t.sfx_posact, // activesound MF_SOLID|MF_SHOOTABLE|MF_COUNTKILL, // flags statenum_t.S_POSS_RAISE1 // raisestate ), new mobjinfo_t( // MT_SHOTGUY 9, // doomednum statenum_t.S_SPOS_STND, // spawnstate 30, // spawnhealth statenum_t.S_SPOS_RUN1, // seestate sfxenum_t.sfx_posit2, // seesound 8, // reactiontime sfxenum_t.sfx_shotgn, // attacksound MAES: Weird, this was 0 :-S statenum_t.S_SPOS_PAIN, // painstate 170, // painchance sfxenum_t.sfx_popain, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_SPOS_ATK1, // missilestate statenum_t.S_SPOS_DIE1, // deathstate statenum_t.S_SPOS_XDIE1, // xdeathstate sfxenum_t.sfx_podth2, // deathsound 8, // speed 20*FRACUNIT, // radius 56*FRACUNIT, // height 100, // mass 0, // damage sfxenum_t.sfx_posact, // activesound MF_SOLID|MF_SHOOTABLE|MF_COUNTKILL, // flags statenum_t.S_SPOS_RAISE1 // raisestate ), new mobjinfo_t( // MT_VILE 64, // doomednum statenum_t.S_VILE_STND, // spawnstate 700, // spawnhealth statenum_t.S_VILE_RUN1, // seestate sfxenum_t.sfx_vilsit, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_VILE_PAIN, // painstate 10, // painchance sfxenum_t.sfx_vipain, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_VILE_ATK1, // missilestate statenum_t.S_VILE_DIE1, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_vildth, // deathsound 15, // speed 20*FRACUNIT, // radius 56*FRACUNIT, // height 500, // mass 0, // damage sfxenum_t.sfx_vilact, // activesound MF_SOLID|MF_SHOOTABLE|MF_COUNTKILL, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_FIRE -1, // doomednum statenum_t.S_FIRE1, // spawnstate 1000, // spawnhealth statenum_t.S_NULL, // seestate sfxenum_t.sfx_None, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_NULL, // painstate 0, // painchance sfxenum_t.sfx_None, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_NULL, // missilestate statenum_t.S_NULL, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_None, // deathsound 0, // speed 20*FRACUNIT, // radius 16*FRACUNIT, // height 100, // mass 0, // damage sfxenum_t.sfx_None, // activesound MF_NOBLOCKMAP|MF_NOGRAVITY, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_UNDEAD 66, // doomednum statenum_t.S_SKEL_STND, // spawnstate 300, // spawnhealth statenum_t.S_SKEL_RUN1, // seestate sfxenum_t.sfx_skesit, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_SKEL_PAIN, // painstate 100, // painchance sfxenum_t.sfx_popain, // painsound statenum_t.S_SKEL_FIST1, // meleestate statenum_t.S_SKEL_MISS1, // missilestate statenum_t.S_SKEL_DIE1, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_skedth, // deathsound 10, // speed 20*FRACUNIT, // radius 56*FRACUNIT, // height 500, // mass 0, // damage sfxenum_t.sfx_skeact, // activesound MF_SOLID|MF_SHOOTABLE|MF_COUNTKILL, // flags statenum_t.S_SKEL_RAISE1 // raisestate ), new mobjinfo_t( // MT_TRACER -1, // doomednum statenum_t.S_TRACER, // spawnstate 1000, // spawnhealth statenum_t.S_NULL, // seestate sfxenum_t.sfx_skeatk, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_NULL, // painstate 0, // painchance sfxenum_t.sfx_None, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_NULL, // missilestate statenum_t.S_TRACEEXP1, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_barexp, // deathsound 10*FRACUNIT, // speed 11*FRACUNIT, // radius 8*FRACUNIT, // height 100, // mass 10, // damage sfxenum_t.sfx_None, // activesound MF_NOBLOCKMAP|MF_MISSILE|MF_DROPOFF|MF_NOGRAVITY, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_SMOKE -1, // doomednum statenum_t.S_SMOKE1, // spawnstate 1000, // spawnhealth statenum_t.S_NULL, // seestate sfxenum_t.sfx_None, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_NULL, // painstate 0, // painchance sfxenum_t.sfx_None, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_NULL, // missilestate statenum_t.S_NULL, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_None, // deathsound 0, // speed 20*FRACUNIT, // radius 16*FRACUNIT, // height 100, // mass 0, // damage sfxenum_t.sfx_None, // activesound MF_NOBLOCKMAP|MF_NOGRAVITY, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_FATSO 67, // doomednum statenum_t.S_FATT_STND, // spawnstate 600, // spawnhealth statenum_t.S_FATT_RUN1, // seestate sfxenum_t.sfx_mansit, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_FATT_PAIN, // painstate 80, // painchance sfxenum_t.sfx_mnpain, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_FATT_ATK1, // missilestate statenum_t.S_FATT_DIE1, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_mandth, // deathsound 8, // speed 48*FRACUNIT, // radius 64*FRACUNIT, // height 1000, // mass 0, // damage sfxenum_t.sfx_posact, // activesound MF_SOLID|MF_SHOOTABLE|MF_COUNTKILL, // flags statenum_t.S_FATT_RAISE1 // raisestate ), new mobjinfo_t( // MT_FATSHOT -1, // doomednum statenum_t.S_FATSHOT1, // spawnstate 1000, // spawnhealth statenum_t.S_NULL, // seestate sfxenum_t.sfx_firsht, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_NULL, // painstate 0, // painchance sfxenum_t.sfx_None, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_NULL, // missilestate statenum_t.S_FATSHOTX1, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_firxpl, // deathsound 20*FRACUNIT, // speed 6*FRACUNIT, // radius 8*FRACUNIT, // height 100, // mass 8, // damage sfxenum_t.sfx_None, // activesound MF_NOBLOCKMAP|MF_MISSILE|MF_DROPOFF|MF_NOGRAVITY, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_CHAINGUY 65, // doomednum statenum_t.S_CPOS_STND, // spawnstate 70, // spawnhealth statenum_t.S_CPOS_RUN1, // seestate sfxenum_t.sfx_posit2, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_CPOS_PAIN, // painstate 170, // painchance sfxenum_t.sfx_popain, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_CPOS_ATK1, // missilestate statenum_t.S_CPOS_DIE1, // deathstate statenum_t.S_CPOS_XDIE1, // xdeathstate sfxenum_t.sfx_podth2, // deathsound 8, // speed 20*FRACUNIT, // radius 56*FRACUNIT, // height 100, // mass 0, // damage sfxenum_t.sfx_posact, // activesound MF_SOLID|MF_SHOOTABLE|MF_COUNTKILL, // flags statenum_t.S_CPOS_RAISE1 // raisestate ), new mobjinfo_t( // MT_TROOP 3001, // doomednum statenum_t.S_TROO_STND, // spawnstate 60, // spawnhealth statenum_t.S_TROO_RUN1, // seestate sfxenum_t.sfx_bgsit1, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_TROO_PAIN, // painstate 200, // painchance sfxenum_t.sfx_popain, // painsound statenum_t.S_TROO_ATK1, // meleestate statenum_t.S_TROO_ATK1, // missilestate statenum_t.S_TROO_DIE1, // deathstate statenum_t.S_TROO_XDIE1, // xdeathstate sfxenum_t.sfx_bgdth1, // deathsound 8, // speed 20*FRACUNIT, // radius 56*FRACUNIT, // height 100, // mass 0, // damage sfxenum_t.sfx_bgact, // activesound MF_SOLID|MF_SHOOTABLE|MF_COUNTKILL, // flags statenum_t.S_TROO_RAISE1 // raisestate ), new mobjinfo_t( // MT_SERGEANT 3002, // doomednum statenum_t.S_SARG_STND, // spawnstate 150, // spawnhealth statenum_t.S_SARG_RUN1, // seestate sfxenum_t.sfx_sgtsit, // seesound 8, // reactiontime sfxenum_t.sfx_sgtatk, // attacksound statenum_t.S_SARG_PAIN, // painstate 180, // painchance sfxenum_t.sfx_dmpain, // painsound statenum_t.S_SARG_ATK1, // meleestate statenum_t.S_NULL, // missilestate statenum_t.S_SARG_DIE1, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_sgtdth, // deathsound 10, // speed 30*FRACUNIT, // radius 56*FRACUNIT, // height 400, // mass 0, // damage sfxenum_t.sfx_dmact, // activesound MF_SOLID|MF_SHOOTABLE|MF_COUNTKILL, // flags statenum_t.S_SARG_RAISE1 // raisestate ), new mobjinfo_t( // MT_SHADOWS 58, // doomednum statenum_t.S_SARG_STND, // spawnstate 150, // spawnhealth statenum_t.S_SARG_RUN1, // seestate sfxenum_t.sfx_sgtsit, // seesound 8, // reactiontime sfxenum_t.sfx_sgtatk, // attacksound statenum_t.S_SARG_PAIN, // painstate 180, // painchance sfxenum_t.sfx_dmpain, // painsound statenum_t.S_SARG_ATK1, // meleestate statenum_t.S_NULL, // missilestate statenum_t.S_SARG_DIE1, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_sgtdth, // deathsound 10, // speed 30*FRACUNIT, // radius 56*FRACUNIT, // height 400, // mass 0, // damage sfxenum_t.sfx_dmact, // activesound MF_SOLID|MF_SHOOTABLE|MF_SHADOW|MF_COUNTKILL, // flags statenum_t.S_SARG_RAISE1 // raisestate ), new mobjinfo_t( // MT_HEAD 3005, // doomednum statenum_t.S_HEAD_STND, // spawnstate 400, // spawnhealth statenum_t.S_HEAD_RUN1, // seestate sfxenum_t.sfx_cacsit, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_HEAD_PAIN, // painstate 128, // painchance sfxenum_t.sfx_dmpain, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_HEAD_ATK1, // missilestate statenum_t.S_HEAD_DIE1, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_cacdth, // deathsound 8, // speed 31*FRACUNIT, // radius 56*FRACUNIT, // height 400, // mass 0, // damage sfxenum_t.sfx_dmact, // activesound MF_SOLID|MF_SHOOTABLE|MF_FLOAT|MF_NOGRAVITY|MF_COUNTKILL, // flags statenum_t.S_HEAD_RAISE1 // raisestate ), new mobjinfo_t( // MT_BRUISER 3003, // doomednum statenum_t.S_BOSS_STND, // spawnstate 1000, // spawnhealth statenum_t.S_BOSS_RUN1, // seestate sfxenum_t.sfx_brssit, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_BOSS_PAIN, // painstate 50, // painchance sfxenum_t.sfx_dmpain, // painsound statenum_t.S_BOSS_ATK1, // meleestate statenum_t.S_BOSS_ATK1, // missilestate statenum_t.S_BOSS_DIE1, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_brsdth, // deathsound 8, // speed 24*FRACUNIT, // radius 64*FRACUNIT, // height 1000, // mass 0, // damage sfxenum_t.sfx_dmact, // activesound MF_SOLID|MF_SHOOTABLE|MF_COUNTKILL, // flags statenum_t.S_BOSS_RAISE1 // raisestate ), new mobjinfo_t( // MT_BRUISERSHOT -1, // doomednum statenum_t.S_BRBALL1, // spawnstate 1000, // spawnhealth statenum_t.S_NULL, // seestate sfxenum_t.sfx_firsht, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_NULL, // painstate 0, // painchance sfxenum_t.sfx_None, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_NULL, // missilestate statenum_t.S_BRBALLX1, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_firxpl, // deathsound 15*FRACUNIT, // speed 6*FRACUNIT, // radius 8*FRACUNIT, // height 100, // mass 8, // damage sfxenum_t.sfx_None, // activesound MF_NOBLOCKMAP|MF_MISSILE|MF_DROPOFF|MF_NOGRAVITY, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_KNIGHT 69, // doomednum statenum_t.S_BOS2_STND, // spawnstate 500, // spawnhealth statenum_t.S_BOS2_RUN1, // seestate sfxenum_t.sfx_kntsit, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_BOS2_PAIN, // painstate 50, // painchance sfxenum_t.sfx_dmpain, // painsound statenum_t.S_BOS2_ATK1, // meleestate statenum_t.S_BOS2_ATK1, // missilestate statenum_t.S_BOS2_DIE1, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_kntdth, // deathsound 8, // speed 24*FRACUNIT, // radius 64*FRACUNIT, // height 1000, // mass 0, // damage sfxenum_t.sfx_dmact, // activesound MF_SOLID|MF_SHOOTABLE|MF_COUNTKILL, // flags statenum_t.S_BOS2_RAISE1 // raisestate ), new mobjinfo_t( // MT_SKULL 3006, // doomednum statenum_t.S_SKULL_STND, // spawnstate 100, // spawnhealth statenum_t.S_SKULL_RUN1, // seestate sfxenum_t.sfx_None, // seesound 8, // reactiontime sfxenum_t.sfx_sklatk, // attacksound statenum_t.S_SKULL_PAIN, // painstate 256, // painchance sfxenum_t.sfx_dmpain, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_SKULL_ATK1, // missilestate statenum_t.S_SKULL_DIE1, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_firxpl, // deathsound 8, // speed 16*FRACUNIT, // radius 56*FRACUNIT, // height 50, // mass 3, // damage sfxenum_t.sfx_dmact, // activesound MF_SOLID|MF_SHOOTABLE|MF_FLOAT|MF_NOGRAVITY, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_SPIDER 7, // doomednum statenum_t.S_SPID_STND, // spawnstate 3000, // spawnhealth statenum_t.S_SPID_RUN1, // seestate sfxenum_t.sfx_spisit, // seesound 8, // reactiontime sfxenum_t.sfx_shotgn, // attacksound statenum_t.S_SPID_PAIN, // painstate 40, // painchance sfxenum_t.sfx_dmpain, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_SPID_ATK1, // missilestate statenum_t.S_SPID_DIE1, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_spidth, // deathsound 12, // speed 128*FRACUNIT, // radius 100*FRACUNIT, // height 1000, // mass 0, // damage sfxenum_t.sfx_dmact, // activesound MF_SOLID|MF_SHOOTABLE|MF_COUNTKILL, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_BABY 68, // doomednum statenum_t.S_BSPI_STND, // spawnstate 500, // spawnhealth statenum_t.S_BSPI_SIGHT, // seestate sfxenum_t.sfx_bspsit, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_BSPI_PAIN, // painstate 128, // painchance sfxenum_t.sfx_dmpain, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_BSPI_ATK1, // missilestate statenum_t.S_BSPI_DIE1, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_bspdth, // deathsound 12, // speed 64*FRACUNIT, // radius 64*FRACUNIT, // height 600, // mass 0, // damage sfxenum_t.sfx_bspact, // activesound MF_SOLID|MF_SHOOTABLE|MF_COUNTKILL, // flags statenum_t.S_BSPI_RAISE1 // raisestate ), new mobjinfo_t( // MT_CYBORG 16, // doomednum statenum_t.S_CYBER_STND, // spawnstate 4000, // spawnhealth statenum_t.S_CYBER_RUN1, // seestate sfxenum_t.sfx_cybsit, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_CYBER_PAIN, // painstate 20, // painchance sfxenum_t.sfx_dmpain, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_CYBER_ATK1, // missilestate statenum_t.S_CYBER_DIE1, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_cybdth, // deathsound 16, // speed 40*FRACUNIT, // radius 110*FRACUNIT, // height 1000, // mass 0, // damage sfxenum_t.sfx_dmact, // activesound MF_SOLID|MF_SHOOTABLE|MF_COUNTKILL, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_PAIN 71, // doomednum statenum_t.S_PAIN_STND, // spawnstate 400, // spawnhealth statenum_t.S_PAIN_RUN1, // seestate sfxenum_t.sfx_pesit, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_PAIN_PAIN, // painstate 128, // painchance sfxenum_t.sfx_pepain, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_PAIN_ATK1, // missilestate statenum_t.S_PAIN_DIE1, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_pedth, // deathsound 8, // speed 31*FRACUNIT, // radius 56*FRACUNIT, // height 400, // mass 0, // damage sfxenum_t.sfx_dmact, // activesound MF_SOLID|MF_SHOOTABLE|MF_FLOAT|MF_NOGRAVITY|MF_COUNTKILL, // flags statenum_t.S_PAIN_RAISE1 // raisestate ), new mobjinfo_t( // MT_WOLFSS 84, // doomednum statenum_t.S_SSWV_STND, // spawnstate 50, // spawnhealth statenum_t.S_SSWV_RUN1, // seestate sfxenum_t.sfx_sssit, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_SSWV_PAIN, // painstate 170, // painchance sfxenum_t.sfx_popain, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_SSWV_ATK1, // missilestate statenum_t.S_SSWV_DIE1, // deathstate statenum_t.S_SSWV_XDIE1, // xdeathstate sfxenum_t.sfx_ssdth, // deathsound 8, // speed 20*FRACUNIT, // radius 56*FRACUNIT, // height 100, // mass 0, // damage sfxenum_t.sfx_posact, // activesound MF_SOLID|MF_SHOOTABLE|MF_COUNTKILL, // flags statenum_t.S_SSWV_RAISE1 // raisestate ), new mobjinfo_t( // MT_KEEN 72, // doomednum statenum_t.S_KEENSTND, // spawnstate 100, // spawnhealth statenum_t.S_NULL, // seestate sfxenum_t.sfx_None, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_KEENPAIN, // painstate 256, // painchance sfxenum_t.sfx_keenpn, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_NULL, // missilestate statenum_t.S_COMMKEEN, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_keendt, // deathsound 0, // speed 16*FRACUNIT, // radius 72*FRACUNIT, // height 10000000, // mass 0, // damage sfxenum_t.sfx_None, // activesound MF_SOLID|MF_SPAWNCEILING|MF_NOGRAVITY|MF_SHOOTABLE|MF_COUNTKILL, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_BOSSBRAIN 88, // doomednum statenum_t.S_BRAIN, // spawnstate 250, // spawnhealth statenum_t.S_NULL, // seestate sfxenum_t.sfx_None, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_BRAIN_PAIN, // painstate 255, // painchance sfxenum_t.sfx_bospn, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_NULL, // missilestate statenum_t.S_BRAIN_DIE1, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_bosdth, // deathsound 0, // speed 16*FRACUNIT, // radius 16*FRACUNIT, // height 10000000, // mass 0, // damage sfxenum_t.sfx_None, // activesound MF_SOLID|MF_SHOOTABLE, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_BOSSSPIT 89, // doomednum statenum_t.S_BRAINEYE, // spawnstate 1000, // spawnhealth statenum_t.S_BRAINEYESEE, // seestate sfxenum_t.sfx_None, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_NULL, // painstate 0, // painchance sfxenum_t.sfx_None, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_NULL, // missilestate statenum_t.S_NULL, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_None, // deathsound 0, // speed 20*FRACUNIT, // radius 32*FRACUNIT, // height 100, // mass 0, // damage sfxenum_t.sfx_None, // activesound MF_NOBLOCKMAP|MF_NOSECTOR, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_BOSSTARGET 87, // doomednum statenum_t.S_NULL, // spawnstate 1000, // spawnhealth statenum_t.S_NULL, // seestate sfxenum_t.sfx_None, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_NULL, // painstate 0, // painchance sfxenum_t.sfx_None, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_NULL, // missilestate statenum_t.S_NULL, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_None, // deathsound 0, // speed 20*FRACUNIT, // radius 32*FRACUNIT, // height 100, // mass 0, // damage sfxenum_t.sfx_None, // activesound MF_NOBLOCKMAP|MF_NOSECTOR, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_SPAWNSHOT -1, // doomednum statenum_t.S_SPAWN1, // spawnstate 1000, // spawnhealth statenum_t.S_NULL, // seestate sfxenum_t.sfx_bospit, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_NULL, // painstate 0, // painchance sfxenum_t.sfx_None, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_NULL, // missilestate statenum_t.S_NULL, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_firxpl, // deathsound 10*FRACUNIT, // speed 6*FRACUNIT, // radius 32*FRACUNIT, // height 100, // mass 3, // damage sfxenum_t.sfx_None, // activesound MF_NOBLOCKMAP|MF_MISSILE|MF_DROPOFF|MF_NOGRAVITY|MF_NOCLIP, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_SPAWNFIRE -1, // doomednum statenum_t.S_SPAWNFIRE1, // spawnstate 1000, // spawnhealth statenum_t.S_NULL, // seestate sfxenum_t.sfx_None, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_NULL, // painstate 0, // painchance sfxenum_t.sfx_None, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_NULL, // missilestate statenum_t.S_NULL, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_None, // deathsound 0, // speed 20*FRACUNIT, // radius 16*FRACUNIT, // height 100, // mass 0, // damage sfxenum_t.sfx_None, // activesound MF_NOBLOCKMAP|MF_NOGRAVITY, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_BARREL 2035, // doomednum statenum_t.S_BAR1, // spawnstate 20, // spawnhealth statenum_t.S_NULL, // seestate sfxenum_t.sfx_None, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_NULL, // painstate 0, // painchance sfxenum_t.sfx_None, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_NULL, // missilestate statenum_t.S_BEXP, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_barexp, // deathsound 0, // speed 10*FRACUNIT, // radius 42*FRACUNIT, // height 100, // mass 0, // damage sfxenum_t.sfx_None, // activesound MF_SOLID|MF_SHOOTABLE|MF_NOBLOOD, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_TROOPSHOT -1, // doomednum statenum_t.S_TBALL1, // spawnstate 1000, // spawnhealth statenum_t.S_NULL, // seestate sfxenum_t.sfx_firsht, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_NULL, // painstate 0, // painchance sfxenum_t.sfx_None, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_NULL, // missilestate statenum_t.S_TBALLX1, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_firxpl, // deathsound 10*FRACUNIT, // speed 6*FRACUNIT, // radius 8*FRACUNIT, // height 100, // mass 3, // damage sfxenum_t.sfx_None, // activesound MF_NOBLOCKMAP|MF_MISSILE|MF_DROPOFF|MF_NOGRAVITY, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_HEADSHOT -1, // doomednum statenum_t.S_RBALL1, // spawnstate 1000, // spawnhealth statenum_t.S_NULL, // seestate sfxenum_t.sfx_firsht, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_NULL, // painstate 0, // painchance sfxenum_t.sfx_None, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_NULL, // missilestate statenum_t.S_RBALLX1, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_firxpl, // deathsound 10*FRACUNIT, // speed 6*FRACUNIT, // radius 8*FRACUNIT, // height 100, // mass 5, // damage sfxenum_t.sfx_None, // activesound MF_NOBLOCKMAP|MF_MISSILE|MF_DROPOFF|MF_NOGRAVITY, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_ROCKET -1, // doomednum statenum_t.S_ROCKET, // spawnstate 1000, // spawnhealth statenum_t.S_NULL, // seestate sfxenum_t.sfx_rlaunc, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_NULL, // painstate 0, // painchance sfxenum_t.sfx_None, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_NULL, // missilestate statenum_t.S_EXPLODE1, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_barexp, // deathsound 20*FRACUNIT, // speed 11*FRACUNIT, // radius 8*FRACUNIT, // height 100, // mass 20, // damage sfxenum_t.sfx_None, // activesound MF_NOBLOCKMAP|MF_MISSILE|MF_DROPOFF|MF_NOGRAVITY, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_PLASMA -1, // doomednum statenum_t.S_PLASBALL, // spawnstate 1000, // spawnhealth statenum_t.S_NULL, // seestate sfxenum_t.sfx_plasma, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_NULL, // painstate 0, // painchance sfxenum_t.sfx_None, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_NULL, // missilestate statenum_t.S_PLASEXP, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_firxpl, // deathsound 25*FRACUNIT, // speed 13*FRACUNIT, // radius 8*FRACUNIT, // height 100, // mass 5, // damage sfxenum_t.sfx_None, // activesound MF_NOBLOCKMAP|MF_MISSILE|MF_DROPOFF|MF_NOGRAVITY, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_BFG -1, // doomednum statenum_t.S_BFGSHOT, // spawnstate 1000, // spawnhealth statenum_t.S_NULL, // seestate sfxenum_t.sfx_None, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_NULL, // painstate 0, // painchance sfxenum_t.sfx_None, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_NULL, // missilestate statenum_t.S_BFGLAND, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_rxplod, // deathsound 25*FRACUNIT, // speed 13*FRACUNIT, // radius 8*FRACUNIT, // height 100, // mass 100, // damage sfxenum_t.sfx_None, // activesound MF_NOBLOCKMAP|MF_MISSILE|MF_DROPOFF|MF_NOGRAVITY, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_ARACHPLAZ -1, // doomednum statenum_t.S_ARACH_PLAZ, // spawnstate 1000, // spawnhealth statenum_t.S_NULL, // seestate sfxenum_t.sfx_plasma, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_NULL, // painstate 0, // painchance sfxenum_t.sfx_None, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_NULL, // missilestate statenum_t.S_ARACH_PLEX, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_firxpl, // deathsound 25*FRACUNIT, // speed 13*FRACUNIT, // radius 8*FRACUNIT, // height 100, // mass 5, // damage sfxenum_t.sfx_None, // activesound MF_NOBLOCKMAP|MF_MISSILE|MF_DROPOFF|MF_NOGRAVITY, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_PUFF -1, // doomednum statenum_t.S_PUFF1, // spawnstate 1000, // spawnhealth statenum_t.S_NULL, // seestate sfxenum_t.sfx_None, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_NULL, // painstate 0, // painchance sfxenum_t.sfx_None, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_NULL, // missilestate statenum_t.S_NULL, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_None, // deathsound 0, // speed 20*FRACUNIT, // radius 16*FRACUNIT, // height 100, // mass 0, // damage sfxenum_t.sfx_None, // activesound MF_NOBLOCKMAP|MF_NOGRAVITY, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_BLOOD -1, // doomednum statenum_t.S_BLOOD1, // spawnstate 1000, // spawnhealth statenum_t.S_NULL, // seestate sfxenum_t.sfx_None, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_NULL, // painstate 0, // painchance sfxenum_t.sfx_None, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_NULL, // missilestate statenum_t.S_NULL, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_None, // deathsound 0, // speed 20*FRACUNIT, // radius 16*FRACUNIT, // height 100, // mass 0, // damage sfxenum_t.sfx_None, // activesound MF_NOBLOCKMAP, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_TFOG -1, // doomednum statenum_t.S_TFOG, // spawnstate 1000, // spawnhealth statenum_t.S_NULL, // seestate sfxenum_t.sfx_None, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_NULL, // painstate 0, // painchance sfxenum_t.sfx_None, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_NULL, // missilestate statenum_t.S_NULL, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_None, // deathsound 0, // speed 20*FRACUNIT, // radius 16*FRACUNIT, // height 100, // mass 0, // damage sfxenum_t.sfx_None, // activesound MF_NOBLOCKMAP|MF_NOGRAVITY, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_IFOG -1, // doomednum statenum_t.S_IFOG, // spawnstate 1000, // spawnhealth statenum_t.S_NULL, // seestate sfxenum_t.sfx_None, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_NULL, // painstate 0, // painchance sfxenum_t.sfx_None, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_NULL, // missilestate statenum_t.S_NULL, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_None, // deathsound 0, // speed 20*FRACUNIT, // radius 16*FRACUNIT, // height 100, // mass 0, // damage sfxenum_t.sfx_None, // activesound MF_NOBLOCKMAP|MF_NOGRAVITY, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_TELEPORTMAN 14, // doomednum statenum_t.S_NULL, // spawnstate 1000, // spawnhealth statenum_t.S_NULL, // seestate sfxenum_t.sfx_None, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_NULL, // painstate 0, // painchance sfxenum_t.sfx_None, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_NULL, // missilestate statenum_t.S_NULL, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_None, // deathsound 0, // speed 20*FRACUNIT, // radius 16*FRACUNIT, // height 100, // mass 0, // damage sfxenum_t.sfx_None, // activesound MF_NOBLOCKMAP|MF_NOSECTOR, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_EXTRABFG -1, // doomednum statenum_t.S_BFGEXP, // spawnstate 1000, // spawnhealth statenum_t.S_NULL, // seestate sfxenum_t.sfx_None, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_NULL, // painstate 0, // painchance sfxenum_t.sfx_None, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_NULL, // missilestate statenum_t.S_NULL, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_None, // deathsound 0, // speed 20*FRACUNIT, // radius 16*FRACUNIT, // height 100, // mass 0, // damage sfxenum_t.sfx_None, // activesound MF_NOBLOCKMAP|MF_NOGRAVITY, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_MISC0 2018, // doomednum statenum_t.S_ARM1, // spawnstate 1000, // spawnhealth statenum_t.S_NULL, // seestate sfxenum_t.sfx_None, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_NULL, // painstate 0, // painchance sfxenum_t.sfx_None, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_NULL, // missilestate statenum_t.S_NULL, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_None, // deathsound 0, // speed 20*FRACUNIT, // radius 16*FRACUNIT, // height 100, // mass 0, // damage sfxenum_t.sfx_None, // activesound MF_SPECIAL, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_MISC1 2019, // doomednum statenum_t.S_ARM2, // spawnstate 1000, // spawnhealth statenum_t.S_NULL, // seestate sfxenum_t.sfx_None, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_NULL, // painstate 0, // painchance sfxenum_t.sfx_None, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_NULL, // missilestate statenum_t.S_NULL, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_None, // deathsound 0, // speed 20*FRACUNIT, // radius 16*FRACUNIT, // height 100, // mass 0, // damage sfxenum_t.sfx_None, // activesound MF_SPECIAL, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_MISC2 2014, // doomednum statenum_t.S_BON1, // spawnstate 1000, // spawnhealth statenum_t.S_NULL, // seestate sfxenum_t.sfx_None, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_NULL, // painstate 0, // painchance sfxenum_t.sfx_None, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_NULL, // missilestate statenum_t.S_NULL, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_None, // deathsound 0, // speed 20*FRACUNIT, // radius 16*FRACUNIT, // height 100, // mass 0, // damage sfxenum_t.sfx_None, // activesound MF_SPECIAL|MF_COUNTITEM, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_MISC3 2015, // doomednum statenum_t.S_BON2, // spawnstate 1000, // spawnhealth statenum_t.S_NULL, // seestate sfxenum_t.sfx_None, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_NULL, // painstate 0, // painchance sfxenum_t.sfx_None, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_NULL, // missilestate statenum_t.S_NULL, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_None, // deathsound 0, // speed 20*FRACUNIT, // radius 16*FRACUNIT, // height 100, // mass 0, // damage sfxenum_t.sfx_None, // activesound MF_SPECIAL|MF_COUNTITEM, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_MISC4 5, // doomednum statenum_t.S_BKEY, // spawnstate 1000, // spawnhealth statenum_t.S_NULL, // seestate sfxenum_t.sfx_None, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_NULL, // painstate 0, // painchance sfxenum_t.sfx_None, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_NULL, // missilestate statenum_t.S_NULL, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_None, // deathsound 0, // speed 20*FRACUNIT, // radius 16*FRACUNIT, // height 100, // mass 0, // damage sfxenum_t.sfx_None, // activesound MF_SPECIAL|MF_NOTDMATCH, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_MISC5 13, // doomednum statenum_t.S_RKEY, // spawnstate 1000, // spawnhealth statenum_t.S_NULL, // seestate sfxenum_t.sfx_None, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_NULL, // painstate 0, // painchance sfxenum_t.sfx_None, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_NULL, // missilestate statenum_t.S_NULL, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_None, // deathsound 0, // speed 20*FRACUNIT, // radius 16*FRACUNIT, // height 100, // mass 0, // damage sfxenum_t.sfx_None, // activesound MF_SPECIAL|MF_NOTDMATCH, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_MISC6 6, // doomednum statenum_t.S_YKEY, // spawnstate 1000, // spawnhealth statenum_t.S_NULL, // seestate sfxenum_t.sfx_None, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_NULL, // painstate 0, // painchance sfxenum_t.sfx_None, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_NULL, // missilestate statenum_t.S_NULL, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_None, // deathsound 0, // speed 20*FRACUNIT, // radius 16*FRACUNIT, // height 100, // mass 0, // damage sfxenum_t.sfx_None, // activesound MF_SPECIAL|MF_NOTDMATCH, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_MISC7 39, // doomednum statenum_t.S_YSKULL, // spawnstate 1000, // spawnhealth statenum_t.S_NULL, // seestate sfxenum_t.sfx_None, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_NULL, // painstate 0, // painchance sfxenum_t.sfx_None, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_NULL, // missilestate statenum_t.S_NULL, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_None, // deathsound 0, // speed 20*FRACUNIT, // radius 16*FRACUNIT, // height 100, // mass 0, // damage sfxenum_t.sfx_None, // activesound MF_SPECIAL|MF_NOTDMATCH, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_MISC8 38, // doomednum statenum_t.S_RSKULL, // spawnstate 1000, // spawnhealth statenum_t.S_NULL, // seestate sfxenum_t.sfx_None, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_NULL, // painstate 0, // painchance sfxenum_t.sfx_None, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_NULL, // missilestate statenum_t.S_NULL, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_None, // deathsound 0, // speed 20*FRACUNIT, // radius 16*FRACUNIT, // height 100, // mass 0, // damage sfxenum_t.sfx_None, // activesound MF_SPECIAL|MF_NOTDMATCH, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_MISC9 40, // doomednum statenum_t.S_BSKULL, // spawnstate 1000, // spawnhealth statenum_t.S_NULL, // seestate sfxenum_t.sfx_None, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_NULL, // painstate 0, // painchance sfxenum_t.sfx_None, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_NULL, // missilestate statenum_t.S_NULL, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_None, // deathsound 0, // speed 20*FRACUNIT, // radius 16*FRACUNIT, // height 100, // mass 0, // damage sfxenum_t.sfx_None, // activesound MF_SPECIAL|MF_NOTDMATCH, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_MISC10 2011, // doomednum statenum_t.S_STIM, // spawnstate 1000, // spawnhealth statenum_t.S_NULL, // seestate sfxenum_t.sfx_None, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_NULL, // painstate 0, // painchance sfxenum_t.sfx_None, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_NULL, // missilestate statenum_t.S_NULL, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_None, // deathsound 0, // speed 20*FRACUNIT, // radius 16*FRACUNIT, // height 100, // mass 0, // damage sfxenum_t.sfx_None, // activesound MF_SPECIAL, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_MISC11 2012, // doomednum statenum_t.S_MEDI, // spawnstate 1000, // spawnhealth statenum_t.S_NULL, // seestate sfxenum_t.sfx_None, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_NULL, // painstate 0, // painchance sfxenum_t.sfx_None, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_NULL, // missilestate statenum_t.S_NULL, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_None, // deathsound 0, // speed 20*FRACUNIT, // radius 16*FRACUNIT, // height 100, // mass 0, // damage sfxenum_t.sfx_None, // activesound MF_SPECIAL, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_MISC12 2013, // doomednum statenum_t.S_SOUL, // spawnstate 1000, // spawnhealth statenum_t.S_NULL, // seestate sfxenum_t.sfx_None, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_NULL, // painstate 0, // painchance sfxenum_t.sfx_None, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_NULL, // missilestate statenum_t.S_NULL, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_None, // deathsound 0, // speed 20*FRACUNIT, // radius 16*FRACUNIT, // height 100, // mass 0, // damage sfxenum_t.sfx_None, // activesound MF_SPECIAL|MF_COUNTITEM, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_INV 2022, // doomednum statenum_t.S_PINV, // spawnstate 1000, // spawnhealth statenum_t.S_NULL, // seestate sfxenum_t.sfx_None, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_NULL, // painstate 0, // painchance sfxenum_t.sfx_None, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_NULL, // missilestate statenum_t.S_NULL, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_None, // deathsound 0, // speed 20*FRACUNIT, // radius 16*FRACUNIT, // height 100, // mass 0, // damage sfxenum_t.sfx_None, // activesound MF_SPECIAL|MF_COUNTITEM, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_MISC13 2023, // doomednum statenum_t.S_PSTR, // spawnstate 1000, // spawnhealth statenum_t.S_NULL, // seestate sfxenum_t.sfx_None, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_NULL, // painstate 0, // painchance sfxenum_t.sfx_None, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_NULL, // missilestate statenum_t.S_NULL, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_None, // deathsound 0, // speed 20*FRACUNIT, // radius 16*FRACUNIT, // height 100, // mass 0, // damage sfxenum_t.sfx_None, // activesound MF_SPECIAL|MF_COUNTITEM, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_INS 2024, // doomednum statenum_t.S_PINS, // spawnstate 1000, // spawnhealth statenum_t.S_NULL, // seestate sfxenum_t.sfx_None, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_NULL, // painstate 0, // painchance sfxenum_t.sfx_None, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_NULL, // missilestate statenum_t.S_NULL, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_None, // deathsound 0, // speed 20*FRACUNIT, // radius 16*FRACUNIT, // height 100, // mass 0, // damage sfxenum_t.sfx_None, // activesound MF_SPECIAL|MF_COUNTITEM, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_MISC14 2025, // doomednum statenum_t.S_SUIT, // spawnstate 1000, // spawnhealth statenum_t.S_NULL, // seestate sfxenum_t.sfx_None, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_NULL, // painstate 0, // painchance sfxenum_t.sfx_None, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_NULL, // missilestate statenum_t.S_NULL, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_None, // deathsound 0, // speed 20*FRACUNIT, // radius 16*FRACUNIT, // height 100, // mass 0, // damage sfxenum_t.sfx_None, // activesound MF_SPECIAL, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_MISC15 2026, // doomednum statenum_t.S_PMAP, // spawnstate 1000, // spawnhealth statenum_t.S_NULL, // seestate sfxenum_t.sfx_None, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_NULL, // painstate 0, // painchance sfxenum_t.sfx_None, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_NULL, // missilestate statenum_t.S_NULL, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_None, // deathsound 0, // speed 20*FRACUNIT, // radius 16*FRACUNIT, // height 100, // mass 0, // damage sfxenum_t.sfx_None, // activesound MF_SPECIAL|MF_COUNTITEM, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_MISC16 2045, // doomednum statenum_t.S_PVIS, // spawnstate 1000, // spawnhealth statenum_t.S_NULL, // seestate sfxenum_t.sfx_None, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_NULL, // painstate 0, // painchance sfxenum_t.sfx_None, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_NULL, // missilestate statenum_t.S_NULL, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_None, // deathsound 0, // speed 20*FRACUNIT, // radius 16*FRACUNIT, // height 100, // mass 0, // damage sfxenum_t.sfx_None, // activesound MF_SPECIAL|MF_COUNTITEM, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_MEGA 83, // doomednum statenum_t.S_MEGA, // spawnstate 1000, // spawnhealth statenum_t.S_NULL, // seestate sfxenum_t.sfx_None, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_NULL, // painstate 0, // painchance sfxenum_t.sfx_None, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_NULL, // missilestate statenum_t.S_NULL, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_None, // deathsound 0, // speed 20*FRACUNIT, // radius 16*FRACUNIT, // height 100, // mass 0, // damage sfxenum_t.sfx_None, // activesound MF_SPECIAL|MF_COUNTITEM, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_CLIP 2007, // doomednum statenum_t.S_CLIP, // spawnstate 1000, // spawnhealth statenum_t.S_NULL, // seestate sfxenum_t.sfx_None, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_NULL, // painstate 0, // painchance sfxenum_t.sfx_None, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_NULL, // missilestate statenum_t.S_NULL, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_None, // deathsound 0, // speed 20*FRACUNIT, // radius 16*FRACUNIT, // height 100, // mass 0, // damage sfxenum_t.sfx_None, // activesound MF_SPECIAL, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_MISC17 2048, // doomednum statenum_t.S_AMMO, // spawnstate 1000, // spawnhealth statenum_t.S_NULL, // seestate sfxenum_t.sfx_None, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_NULL, // painstate 0, // painchance sfxenum_t.sfx_None, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_NULL, // missilestate statenum_t.S_NULL, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_None, // deathsound 0, // speed 20*FRACUNIT, // radius 16*FRACUNIT, // height 100, // mass 0, // damage sfxenum_t.sfx_None, // activesound MF_SPECIAL, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_MISC18 2010, // doomednum statenum_t.S_ROCK, // spawnstate 1000, // spawnhealth statenum_t.S_NULL, // seestate sfxenum_t.sfx_None, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_NULL, // painstate 0, // painchance sfxenum_t.sfx_None, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_NULL, // missilestate statenum_t.S_NULL, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_None, // deathsound 0, // speed 20*FRACUNIT, // radius 16*FRACUNIT, // height 100, // mass 0, // damage sfxenum_t.sfx_None, // activesound MF_SPECIAL, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_MISC19 2046, // doomednum statenum_t.S_BROK, // spawnstate 1000, // spawnhealth statenum_t.S_NULL, // seestate sfxenum_t.sfx_None, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_NULL, // painstate 0, // painchance sfxenum_t.sfx_None, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_NULL, // missilestate statenum_t.S_NULL, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_None, // deathsound 0, // speed 20*FRACUNIT, // radius 16*FRACUNIT, // height 100, // mass 0, // damage sfxenum_t.sfx_None, // activesound MF_SPECIAL, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_MISC20 2047, // doomednum statenum_t.S_CELL, // spawnstate 1000, // spawnhealth statenum_t.S_NULL, // seestate sfxenum_t.sfx_None, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_NULL, // painstate 0, // painchance sfxenum_t.sfx_None, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_NULL, // missilestate statenum_t.S_NULL, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_None, // deathsound 0, // speed 20*FRACUNIT, // radius 16*FRACUNIT, // height 100, // mass 0, // damage sfxenum_t.sfx_None, // activesound MF_SPECIAL, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_MISC21 17, // doomednum statenum_t.S_CELP, // spawnstate 1000, // spawnhealth statenum_t.S_NULL, // seestate sfxenum_t.sfx_None, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_NULL, // painstate 0, // painchance sfxenum_t.sfx_None, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_NULL, // missilestate statenum_t.S_NULL, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_None, // deathsound 0, // speed 20*FRACUNIT, // radius 16*FRACUNIT, // height 100, // mass 0, // damage sfxenum_t.sfx_None, // activesound MF_SPECIAL, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_MISC22 2008, // doomednum statenum_t.S_SHEL, // spawnstate 1000, // spawnhealth statenum_t.S_NULL, // seestate sfxenum_t.sfx_None, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_NULL, // painstate 0, // painchance sfxenum_t.sfx_None, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_NULL, // missilestate statenum_t.S_NULL, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_None, // deathsound 0, // speed 20*FRACUNIT, // radius 16*FRACUNIT, // height 100, // mass 0, // damage sfxenum_t.sfx_None, // activesound MF_SPECIAL, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_MISC23 2049, // doomednum statenum_t.S_SBOX, // spawnstate 1000, // spawnhealth statenum_t.S_NULL, // seestate sfxenum_t.sfx_None, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_NULL, // painstate 0, // painchance sfxenum_t.sfx_None, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_NULL, // missilestate statenum_t.S_NULL, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_None, // deathsound 0, // speed 20*FRACUNIT, // radius 16*FRACUNIT, // height 100, // mass 0, // damage sfxenum_t.sfx_None, // activesound MF_SPECIAL, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_MISC24 8, // doomednum statenum_t.S_BPAK, // spawnstate 1000, // spawnhealth statenum_t.S_NULL, // seestate sfxenum_t.sfx_None, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_NULL, // painstate 0, // painchance sfxenum_t.sfx_None, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_NULL, // missilestate statenum_t.S_NULL, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_None, // deathsound 0, // speed 20*FRACUNIT, // radius 16*FRACUNIT, // height 100, // mass 0, // damage sfxenum_t.sfx_None, // activesound MF_SPECIAL, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_MISC25 2006, // doomednum statenum_t.S_BFUG, // spawnstate 1000, // spawnhealth statenum_t.S_NULL, // seestate sfxenum_t.sfx_None, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_NULL, // painstate 0, // painchance sfxenum_t.sfx_None, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_NULL, // missilestate statenum_t.S_NULL, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_None, // deathsound 0, // speed 20*FRACUNIT, // radius 16*FRACUNIT, // height 100, // mass 0, // damage sfxenum_t.sfx_None, // activesound MF_SPECIAL, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_CHAINGUN 2002, // doomednum statenum_t.S_MGUN, // spawnstate 1000, // spawnhealth statenum_t.S_NULL, // seestate sfxenum_t.sfx_None, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_NULL, // painstate 0, // painchance sfxenum_t.sfx_None, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_NULL, // missilestate statenum_t.S_NULL, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_None, // deathsound 0, // speed 20*FRACUNIT, // radius 16*FRACUNIT, // height 100, // mass 0, // damage sfxenum_t.sfx_None, // activesound MF_SPECIAL, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_MISC26 2005, // doomednum statenum_t.S_CSAW, // spawnstate 1000, // spawnhealth statenum_t.S_NULL, // seestate sfxenum_t.sfx_None, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_NULL, // painstate 0, // painchance sfxenum_t.sfx_None, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_NULL, // missilestate statenum_t.S_NULL, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_None, // deathsound 0, // speed 20*FRACUNIT, // radius 16*FRACUNIT, // height 100, // mass 0, // damage sfxenum_t.sfx_None, // activesound MF_SPECIAL, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_MISC27 2003, // doomednum statenum_t.S_LAUN, // spawnstate 1000, // spawnhealth statenum_t.S_NULL, // seestate sfxenum_t.sfx_None, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_NULL, // painstate 0, // painchance sfxenum_t.sfx_None, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_NULL, // missilestate statenum_t.S_NULL, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_None, // deathsound 0, // speed 20*FRACUNIT, // radius 16*FRACUNIT, // height 100, // mass 0, // damage sfxenum_t.sfx_None, // activesound MF_SPECIAL, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_MISC28 2004, // doomednum statenum_t.S_PLAS, // spawnstate 1000, // spawnhealth statenum_t.S_NULL, // seestate sfxenum_t.sfx_None, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_NULL, // painstate 0, // painchance sfxenum_t.sfx_None, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_NULL, // missilestate statenum_t.S_NULL, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_None, // deathsound 0, // speed 20*FRACUNIT, // radius 16*FRACUNIT, // height 100, // mass 0, // damage sfxenum_t.sfx_None, // activesound MF_SPECIAL, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_SHOTGUN 2001, // doomednum statenum_t.S_SHOT, // spawnstate 1000, // spawnhealth statenum_t.S_NULL, // seestate sfxenum_t.sfx_None, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_NULL, // painstate 0, // painchance sfxenum_t.sfx_None, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_NULL, // missilestate statenum_t.S_NULL, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_None, // deathsound 0, // speed 20*FRACUNIT, // radius 16*FRACUNIT, // height 100, // mass 0, // damage sfxenum_t.sfx_None, // activesound MF_SPECIAL, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_SUPERSHOTGUN 82, // doomednum statenum_t.S_SHOT2, // spawnstate 1000, // spawnhealth statenum_t.S_NULL, // seestate sfxenum_t.sfx_None, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_NULL, // painstate 0, // painchance sfxenum_t.sfx_None, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_NULL, // missilestate statenum_t.S_NULL, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_None, // deathsound 0, // speed 20*FRACUNIT, // radius 16*FRACUNIT, // height 100, // mass 0, // damage sfxenum_t.sfx_None, // activesound MF_SPECIAL, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_MISC29 85, // doomednum statenum_t.S_TECHLAMP, // spawnstate 1000, // spawnhealth statenum_t.S_NULL, // seestate sfxenum_t.sfx_None, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_NULL, // painstate 0, // painchance sfxenum_t.sfx_None, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_NULL, // missilestate statenum_t.S_NULL, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_None, // deathsound 0, // speed 16*FRACUNIT, // radius 16*FRACUNIT, // height 100, // mass 0, // damage sfxenum_t.sfx_None, // activesound MF_SOLID, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_MISC30 86, // doomednum statenum_t.S_TECH2LAMP, // spawnstate 1000, // spawnhealth statenum_t.S_NULL, // seestate sfxenum_t.sfx_None, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_NULL, // painstate 0, // painchance sfxenum_t.sfx_None, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_NULL, // missilestate statenum_t.S_NULL, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_None, // deathsound 0, // speed 16*FRACUNIT, // radius 16*FRACUNIT, // height 100, // mass 0, // damage sfxenum_t.sfx_None, // activesound MF_SOLID, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_MISC31 2028, // doomednum statenum_t.S_COLU, // spawnstate 1000, // spawnhealth statenum_t.S_NULL, // seestate sfxenum_t.sfx_None, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_NULL, // painstate 0, // painchance sfxenum_t.sfx_None, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_NULL, // missilestate statenum_t.S_NULL, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_None, // deathsound 0, // speed 16*FRACUNIT, // radius 16*FRACUNIT, // height 100, // mass 0, // damage sfxenum_t.sfx_None, // activesound MF_SOLID, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_MISC32 30, // doomednum statenum_t.S_TALLGRNCOL, // spawnstate 1000, // spawnhealth statenum_t.S_NULL, // seestate sfxenum_t.sfx_None, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_NULL, // painstate 0, // painchance sfxenum_t.sfx_None, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_NULL, // missilestate statenum_t.S_NULL, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_None, // deathsound 0, // speed 16*FRACUNIT, // radius 16*FRACUNIT, // height 100, // mass 0, // damage sfxenum_t.sfx_None, // activesound MF_SOLID, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_MISC33 31, // doomednum statenum_t.S_SHRTGRNCOL, // spawnstate 1000, // spawnhealth statenum_t.S_NULL, // seestate sfxenum_t.sfx_None, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_NULL, // painstate 0, // painchance sfxenum_t.sfx_None, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_NULL, // missilestate statenum_t.S_NULL, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_None, // deathsound 0, // speed 16*FRACUNIT, // radius 16*FRACUNIT, // height 100, // mass 0, // damage sfxenum_t.sfx_None, // activesound MF_SOLID, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_MISC34 32, // doomednum statenum_t.S_TALLREDCOL, // spawnstate 1000, // spawnhealth statenum_t.S_NULL, // seestate sfxenum_t.sfx_None, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_NULL, // painstate 0, // painchance sfxenum_t.sfx_None, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_NULL, // missilestate statenum_t.S_NULL, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_None, // deathsound 0, // speed 16*FRACUNIT, // radius 16*FRACUNIT, // height 100, // mass 0, // damage sfxenum_t.sfx_None, // activesound MF_SOLID, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_MISC35 33, // doomednum statenum_t.S_SHRTREDCOL, // spawnstate 1000, // spawnhealth statenum_t.S_NULL, // seestate sfxenum_t.sfx_None, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_NULL, // painstate 0, // painchance sfxenum_t.sfx_None, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_NULL, // missilestate statenum_t.S_NULL, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_None, // deathsound 0, // speed 16*FRACUNIT, // radius 16*FRACUNIT, // height 100, // mass 0, // damage sfxenum_t.sfx_None, // activesound MF_SOLID, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_MISC36 37, // doomednum statenum_t.S_SKULLCOL, // spawnstate 1000, // spawnhealth statenum_t.S_NULL, // seestate sfxenum_t.sfx_None, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_NULL, // painstate 0, // painchance sfxenum_t.sfx_None, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_NULL, // missilestate statenum_t.S_NULL, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_None, // deathsound 0, // speed 16*FRACUNIT, // radius 16*FRACUNIT, // height 100, // mass 0, // damage sfxenum_t.sfx_None, // activesound MF_SOLID, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_MISC37 36, // doomednum statenum_t.S_HEARTCOL, // spawnstate 1000, // spawnhealth statenum_t.S_NULL, // seestate sfxenum_t.sfx_None, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_NULL, // painstate 0, // painchance sfxenum_t.sfx_None, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_NULL, // missilestate statenum_t.S_NULL, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_None, // deathsound 0, // speed 16*FRACUNIT, // radius 16*FRACUNIT, // height 100, // mass 0, // damage sfxenum_t.sfx_None, // activesound MF_SOLID, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_MISC38 41, // doomednum statenum_t.S_EVILEYE, // spawnstate 1000, // spawnhealth statenum_t.S_NULL, // seestate sfxenum_t.sfx_None, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_NULL, // painstate 0, // painchance sfxenum_t.sfx_None, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_NULL, // missilestate statenum_t.S_NULL, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_None, // deathsound 0, // speed 16*FRACUNIT, // radius 16*FRACUNIT, // height 100, // mass 0, // damage sfxenum_t.sfx_None, // activesound MF_SOLID, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_MISC39 42, // doomednum statenum_t.S_FLOATSKULL, // spawnstate 1000, // spawnhealth statenum_t.S_NULL, // seestate sfxenum_t.sfx_None, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_NULL, // painstate 0, // painchance sfxenum_t.sfx_None, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_NULL, // missilestate statenum_t.S_NULL, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_None, // deathsound 0, // speed 16*FRACUNIT, // radius 16*FRACUNIT, // height 100, // mass 0, // damage sfxenum_t.sfx_None, // activesound MF_SOLID, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_MISC40 43, // doomednum statenum_t.S_TORCHTREE, // spawnstate 1000, // spawnhealth statenum_t.S_NULL, // seestate sfxenum_t.sfx_None, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_NULL, // painstate 0, // painchance sfxenum_t.sfx_None, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_NULL, // missilestate statenum_t.S_NULL, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_None, // deathsound 0, // speed 16*FRACUNIT, // radius 16*FRACUNIT, // height 100, // mass 0, // damage sfxenum_t.sfx_None, // activesound MF_SOLID, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_MISC41 44, // doomednum statenum_t.S_BLUETORCH, // spawnstate 1000, // spawnhealth statenum_t.S_NULL, // seestate sfxenum_t.sfx_None, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_NULL, // painstate 0, // painchance sfxenum_t.sfx_None, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_NULL, // missilestate statenum_t.S_NULL, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_None, // deathsound 0, // speed 16*FRACUNIT, // radius 16*FRACUNIT, // height 100, // mass 0, // damage sfxenum_t.sfx_None, // activesound MF_SOLID, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_MISC42 45, // doomednum statenum_t.S_GREENTORCH, // spawnstate 1000, // spawnhealth statenum_t.S_NULL, // seestate sfxenum_t.sfx_None, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_NULL, // painstate 0, // painchance sfxenum_t.sfx_None, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_NULL, // missilestate statenum_t.S_NULL, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_None, // deathsound 0, // speed 16*FRACUNIT, // radius 16*FRACUNIT, // height 100, // mass 0, // damage sfxenum_t.sfx_None, // activesound MF_SOLID, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_MISC43 46, // doomednum statenum_t.S_REDTORCH, // spawnstate 1000, // spawnhealth statenum_t.S_NULL, // seestate sfxenum_t.sfx_None, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_NULL, // painstate 0, // painchance sfxenum_t.sfx_None, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_NULL, // missilestate statenum_t.S_NULL, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_None, // deathsound 0, // speed 16*FRACUNIT, // radius 16*FRACUNIT, // height 100, // mass 0, // damage sfxenum_t.sfx_None, // activesound MF_SOLID, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_MISC44 55, // doomednum statenum_t.S_BTORCHSHRT, // spawnstate 1000, // spawnhealth statenum_t.S_NULL, // seestate sfxenum_t.sfx_None, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_NULL, // painstate 0, // painchance sfxenum_t.sfx_None, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_NULL, // missilestate statenum_t.S_NULL, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_None, // deathsound 0, // speed 16*FRACUNIT, // radius 16*FRACUNIT, // height 100, // mass 0, // damage sfxenum_t.sfx_None, // activesound MF_SOLID, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_MISC45 56, // doomednum statenum_t.S_GTORCHSHRT, // spawnstate 1000, // spawnhealth statenum_t.S_NULL, // seestate sfxenum_t.sfx_None, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_NULL, // painstate 0, // painchance sfxenum_t.sfx_None, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_NULL, // missilestate statenum_t.S_NULL, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_None, // deathsound 0, // speed 16*FRACUNIT, // radius 16*FRACUNIT, // height 100, // mass 0, // damage sfxenum_t.sfx_None, // activesound MF_SOLID, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_MISC46 57, // doomednum statenum_t.S_RTORCHSHRT, // spawnstate 1000, // spawnhealth statenum_t.S_NULL, // seestate sfxenum_t.sfx_None, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_NULL, // painstate 0, // painchance sfxenum_t.sfx_None, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_NULL, // missilestate statenum_t.S_NULL, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_None, // deathsound 0, // speed 16*FRACUNIT, // radius 16*FRACUNIT, // height 100, // mass 0, // damage sfxenum_t.sfx_None, // activesound MF_SOLID, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_MISC47 47, // doomednum statenum_t.S_STALAGTITE, // spawnstate 1000, // spawnhealth statenum_t.S_NULL, // seestate sfxenum_t.sfx_None, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_NULL, // painstate 0, // painchance sfxenum_t.sfx_None, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_NULL, // missilestate statenum_t.S_NULL, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_None, // deathsound 0, // speed 16*FRACUNIT, // radius 16*FRACUNIT, // height 100, // mass 0, // damage sfxenum_t.sfx_None, // activesound MF_SOLID, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_MISC48 48, // doomednum statenum_t.S_TECHPILLAR, // spawnstate 1000, // spawnhealth statenum_t.S_NULL, // seestate sfxenum_t.sfx_None, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_NULL, // painstate 0, // painchance sfxenum_t.sfx_None, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_NULL, // missilestate statenum_t.S_NULL, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_None, // deathsound 0, // speed 16*FRACUNIT, // radius 16*FRACUNIT, // height 100, // mass 0, // damage sfxenum_t.sfx_None, // activesound MF_SOLID, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_MISC49 34, // doomednum statenum_t.S_CANDLESTIK, // spawnstate 1000, // spawnhealth statenum_t.S_NULL, // seestate sfxenum_t.sfx_None, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_NULL, // painstate 0, // painchance sfxenum_t.sfx_None, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_NULL, // missilestate statenum_t.S_NULL, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_None, // deathsound 0, // speed 20*FRACUNIT, // radius 16*FRACUNIT, // height 100, // mass 0, // damage sfxenum_t.sfx_None, // activesound 0, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_MISC50 35, // doomednum statenum_t.S_CANDELABRA, // spawnstate 1000, // spawnhealth statenum_t.S_NULL, // seestate sfxenum_t.sfx_None, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_NULL, // painstate 0, // painchance sfxenum_t.sfx_None, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_NULL, // missilestate statenum_t.S_NULL, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_None, // deathsound 0, // speed 16*FRACUNIT, // radius 16*FRACUNIT, // height 100, // mass 0, // damage sfxenum_t.sfx_None, // activesound MF_SOLID, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_MISC51 49, // doomednum statenum_t.S_BLOODYTWITCH, // spawnstate 1000, // spawnhealth statenum_t.S_NULL, // seestate sfxenum_t.sfx_None, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_NULL, // painstate 0, // painchance sfxenum_t.sfx_None, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_NULL, // missilestate statenum_t.S_NULL, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_None, // deathsound 0, // speed 16*FRACUNIT, // radius 68*FRACUNIT, // height 100, // mass 0, // damage sfxenum_t.sfx_None, // activesound MF_SOLID|MF_SPAWNCEILING|MF_NOGRAVITY, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_MISC52 50, // doomednum statenum_t.S_MEAT2, // spawnstate 1000, // spawnhealth statenum_t.S_NULL, // seestate sfxenum_t.sfx_None, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_NULL, // painstate 0, // painchance sfxenum_t.sfx_None, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_NULL, // missilestate statenum_t.S_NULL, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_None, // deathsound 0, // speed 16*FRACUNIT, // radius 84*FRACUNIT, // height 100, // mass 0, // damage sfxenum_t.sfx_None, // activesound MF_SOLID|MF_SPAWNCEILING|MF_NOGRAVITY, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_MISC53 51, // doomednum statenum_t.S_MEAT3, // spawnstate 1000, // spawnhealth statenum_t.S_NULL, // seestate sfxenum_t.sfx_None, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_NULL, // painstate 0, // painchance sfxenum_t.sfx_None, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_NULL, // missilestate statenum_t.S_NULL, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_None, // deathsound 0, // speed 16*FRACUNIT, // radius 84*FRACUNIT, // height 100, // mass 0, // damage sfxenum_t.sfx_None, // activesound MF_SOLID|MF_SPAWNCEILING|MF_NOGRAVITY, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_MISC54 52, // doomednum statenum_t.S_MEAT4, // spawnstate 1000, // spawnhealth statenum_t.S_NULL, // seestate sfxenum_t.sfx_None, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_NULL, // painstate 0, // painchance sfxenum_t.sfx_None, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_NULL, // missilestate statenum_t.S_NULL, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_None, // deathsound 0, // speed 16*FRACUNIT, // radius 68*FRACUNIT, // height 100, // mass 0, // damage sfxenum_t.sfx_None, // activesound MF_SOLID|MF_SPAWNCEILING|MF_NOGRAVITY, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_MISC55 53, // doomednum statenum_t.S_MEAT5, // spawnstate 1000, // spawnhealth statenum_t.S_NULL, // seestate sfxenum_t.sfx_None, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_NULL, // painstate 0, // painchance sfxenum_t.sfx_None, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_NULL, // missilestate statenum_t.S_NULL, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_None, // deathsound 0, // speed 16*FRACUNIT, // radius 52*FRACUNIT, // height 100, // mass 0, // damage sfxenum_t.sfx_None, // activesound MF_SOLID|MF_SPAWNCEILING|MF_NOGRAVITY, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_MISC56 59, // doomednum statenum_t.S_MEAT2, // spawnstate 1000, // spawnhealth statenum_t.S_NULL, // seestate sfxenum_t.sfx_None, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_NULL, // painstate 0, // painchance sfxenum_t.sfx_None, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_NULL, // missilestate statenum_t.S_NULL, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_None, // deathsound 0, // speed 20*FRACUNIT, // radius 84*FRACUNIT, // height 100, // mass 0, // damage sfxenum_t.sfx_None, // activesound MF_SPAWNCEILING|MF_NOGRAVITY, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_MISC57 60, // doomednum statenum_t.S_MEAT4, // spawnstate 1000, // spawnhealth statenum_t.S_NULL, // seestate sfxenum_t.sfx_None, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_NULL, // painstate 0, // painchance sfxenum_t.sfx_None, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_NULL, // missilestate statenum_t.S_NULL, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_None, // deathsound 0, // speed 20*FRACUNIT, // radius 68*FRACUNIT, // height 100, // mass 0, // damage sfxenum_t.sfx_None, // activesound MF_SPAWNCEILING|MF_NOGRAVITY, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_MISC58 61, // doomednum statenum_t.S_MEAT3, // spawnstate 1000, // spawnhealth statenum_t.S_NULL, // seestate sfxenum_t.sfx_None, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_NULL, // painstate 0, // painchance sfxenum_t.sfx_None, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_NULL, // missilestate statenum_t.S_NULL, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_None, // deathsound 0, // speed 20*FRACUNIT, // radius 52*FRACUNIT, // height 100, // mass 0, // damage sfxenum_t.sfx_None, // activesound MF_SPAWNCEILING|MF_NOGRAVITY, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_MISC59 62, // doomednum statenum_t.S_MEAT5, // spawnstate 1000, // spawnhealth statenum_t.S_NULL, // seestate sfxenum_t.sfx_None, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_NULL, // painstate 0, // painchance sfxenum_t.sfx_None, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_NULL, // missilestate statenum_t.S_NULL, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_None, // deathsound 0, // speed 20*FRACUNIT, // radius 52*FRACUNIT, // height 100, // mass 0, // damage sfxenum_t.sfx_None, // activesound MF_SPAWNCEILING|MF_NOGRAVITY, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_MISC60 63, // doomednum statenum_t.S_BLOODYTWITCH, // spawnstate 1000, // spawnhealth statenum_t.S_NULL, // seestate sfxenum_t.sfx_None, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_NULL, // painstate 0, // painchance sfxenum_t.sfx_None, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_NULL, // missilestate statenum_t.S_NULL, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_None, // deathsound 0, // speed 20*FRACUNIT, // radius 68*FRACUNIT, // height 100, // mass 0, // damage sfxenum_t.sfx_None, // activesound MF_SPAWNCEILING|MF_NOGRAVITY, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_MISC61 22, // doomednum statenum_t.S_HEAD_DIE6, // spawnstate 1000, // spawnhealth statenum_t.S_NULL, // seestate sfxenum_t.sfx_None, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_NULL, // painstate 0, // painchance sfxenum_t.sfx_None, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_NULL, // missilestate statenum_t.S_NULL, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_None, // deathsound 0, // speed 20*FRACUNIT, // radius 16*FRACUNIT, // height 100, // mass 0, // damage sfxenum_t.sfx_None, // activesound 0, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_MISC62 15, // doomednum statenum_t.S_PLAY_DIE7, // spawnstate 1000, // spawnhealth statenum_t.S_NULL, // seestate sfxenum_t.sfx_None, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_NULL, // painstate 0, // painchance sfxenum_t.sfx_None, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_NULL, // missilestate statenum_t.S_NULL, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_None, // deathsound 0, // speed 20*FRACUNIT, // radius 16*FRACUNIT, // height 100, // mass 0, // damage sfxenum_t.sfx_None, // activesound 0, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_MISC63 18, // doomednum statenum_t.S_POSS_DIE5, // spawnstate 1000, // spawnhealth statenum_t.S_NULL, // seestate sfxenum_t.sfx_None, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_NULL, // painstate 0, // painchance sfxenum_t.sfx_None, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_NULL, // missilestate statenum_t.S_NULL, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_None, // deathsound 0, // speed 20*FRACUNIT, // radius 16*FRACUNIT, // height 100, // mass 0, // damage sfxenum_t.sfx_None, // activesound 0, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_MISC64 21, // doomednum statenum_t.S_SARG_DIE6, // spawnstate 1000, // spawnhealth statenum_t.S_NULL, // seestate sfxenum_t.sfx_None, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_NULL, // painstate 0, // painchance sfxenum_t.sfx_None, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_NULL, // missilestate statenum_t.S_NULL, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_None, // deathsound 0, // speed 20*FRACUNIT, // radius 16*FRACUNIT, // height 100, // mass 0, // damage sfxenum_t.sfx_None, // activesound 0, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_MISC65 23, // doomednum statenum_t.S_SKULL_DIE6, // spawnstate 1000, // spawnhealth statenum_t.S_NULL, // seestate sfxenum_t.sfx_None, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_NULL, // painstate 0, // painchance sfxenum_t.sfx_None, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_NULL, // missilestate statenum_t.S_NULL, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_None, // deathsound 0, // speed 20*FRACUNIT, // radius 16*FRACUNIT, // height 100, // mass 0, // damage sfxenum_t.sfx_None, // activesound 0, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_MISC66 20, // doomednum statenum_t.S_TROO_DIE5, // spawnstate 1000, // spawnhealth statenum_t.S_NULL, // seestate sfxenum_t.sfx_None, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_NULL, // painstate 0, // painchance sfxenum_t.sfx_None, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_NULL, // missilestate statenum_t.S_NULL, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_None, // deathsound 0, // speed 20*FRACUNIT, // radius 16*FRACUNIT, // height 100, // mass 0, // damage sfxenum_t.sfx_None, // activesound 0, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_MISC67 19, // doomednum statenum_t.S_SPOS_DIE5, // spawnstate 1000, // spawnhealth statenum_t.S_NULL, // seestate sfxenum_t.sfx_None, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_NULL, // painstate 0, // painchance sfxenum_t.sfx_None, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_NULL, // missilestate statenum_t.S_NULL, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_None, // deathsound 0, // speed 20*FRACUNIT, // radius 16*FRACUNIT, // height 100, // mass 0, // damage sfxenum_t.sfx_None, // activesound 0, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_MISC68 10, // doomednum statenum_t.S_PLAY_XDIE9, // spawnstate 1000, // spawnhealth statenum_t.S_NULL, // seestate sfxenum_t.sfx_None, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_NULL, // painstate 0, // painchance sfxenum_t.sfx_None, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_NULL, // missilestate statenum_t.S_NULL, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_None, // deathsound 0, // speed 20*FRACUNIT, // radius 16*FRACUNIT, // height 100, // mass 0, // damage sfxenum_t.sfx_None, // activesound 0, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_MISC69 12, // doomednum statenum_t.S_PLAY_XDIE9, // spawnstate 1000, // spawnhealth statenum_t.S_NULL, // seestate sfxenum_t.sfx_None, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_NULL, // painstate 0, // painchance sfxenum_t.sfx_None, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_NULL, // missilestate statenum_t.S_NULL, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_None, // deathsound 0, // speed 20*FRACUNIT, // radius 16*FRACUNIT, // height 100, // mass 0, // damage sfxenum_t.sfx_None, // activesound 0, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_MISC70 28, // doomednum statenum_t.S_HEADSONSTICK, // spawnstate 1000, // spawnhealth statenum_t.S_NULL, // seestate sfxenum_t.sfx_None, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_NULL, // painstate 0, // painchance sfxenum_t.sfx_None, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_NULL, // missilestate statenum_t.S_NULL, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_None, // deathsound 0, // speed 16*FRACUNIT, // radius 16*FRACUNIT, // height 100, // mass 0, // damage sfxenum_t.sfx_None, // activesound MF_SOLID, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_MISC71 24, // doomednum statenum_t.S_GIBS, // spawnstate 1000, // spawnhealth statenum_t.S_NULL, // seestate sfxenum_t.sfx_None, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_NULL, // painstate 0, // painchance sfxenum_t.sfx_None, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_NULL, // missilestate statenum_t.S_NULL, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_None, // deathsound 0, // speed 20*FRACUNIT, // radius 16*FRACUNIT, // height 100, // mass 0, // damage sfxenum_t.sfx_None, // activesound 0, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_MISC72 27, // doomednum statenum_t.S_HEADONASTICK, // spawnstate 1000, // spawnhealth statenum_t.S_NULL, // seestate sfxenum_t.sfx_None, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_NULL, // painstate 0, // painchance sfxenum_t.sfx_None, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_NULL, // missilestate statenum_t.S_NULL, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_None, // deathsound 0, // speed 16*FRACUNIT, // radius 16*FRACUNIT, // height 100, // mass 0, // damage sfxenum_t.sfx_None, // activesound MF_SOLID, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_MISC73 29, // doomednum statenum_t.S_HEADCANDLES, // spawnstate 1000, // spawnhealth statenum_t.S_NULL, // seestate sfxenum_t.sfx_None, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_NULL, // painstate 0, // painchance sfxenum_t.sfx_None, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_NULL, // missilestate statenum_t.S_NULL, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_None, // deathsound 0, // speed 16*FRACUNIT, // radius 16*FRACUNIT, // height 100, // mass 0, // damage sfxenum_t.sfx_None, // activesound MF_SOLID, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_MISC74 25, // doomednum statenum_t.S_DEADSTICK, // spawnstate 1000, // spawnhealth statenum_t.S_NULL, // seestate sfxenum_t.sfx_None, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_NULL, // painstate 0, // painchance sfxenum_t.sfx_None, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_NULL, // missilestate statenum_t.S_NULL, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_None, // deathsound 0, // speed 16*FRACUNIT, // radius 16*FRACUNIT, // height 100, // mass 0, // damage sfxenum_t.sfx_None, // activesound MF_SOLID, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_MISC75 26, // doomednum statenum_t.S_LIVESTICK, // spawnstate 1000, // spawnhealth statenum_t.S_NULL, // seestate sfxenum_t.sfx_None, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_NULL, // painstate 0, // painchance sfxenum_t.sfx_None, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_NULL, // missilestate statenum_t.S_NULL, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_None, // deathsound 0, // speed 16*FRACUNIT, // radius 16*FRACUNIT, // height 100, // mass 0, // damage sfxenum_t.sfx_None, // activesound MF_SOLID, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_MISC76 54, // doomednum statenum_t.S_BIGTREE, // spawnstate 1000, // spawnhealth statenum_t.S_NULL, // seestate sfxenum_t.sfx_None, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_NULL, // painstate 0, // painchance sfxenum_t.sfx_None, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_NULL, // missilestate statenum_t.S_NULL, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_None, // deathsound 0, // speed 32*FRACUNIT, // radius 16*FRACUNIT, // height 100, // mass 0, // damage sfxenum_t.sfx_None, // activesound MF_SOLID, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_MISC77 70, // doomednum statenum_t.S_BBAR1, // spawnstate 1000, // spawnhealth statenum_t.S_NULL, // seestate sfxenum_t.sfx_None, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_NULL, // painstate 0, // painchance sfxenum_t.sfx_None, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_NULL, // missilestate statenum_t.S_NULL, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_None, // deathsound 0, // speed 16*FRACUNIT, // radius 16*FRACUNIT, // height 100, // mass 0, // damage sfxenum_t.sfx_None, // activesound MF_SOLID, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_MISC78 73, // doomednum statenum_t.S_HANGNOGUTS, // spawnstate 1000, // spawnhealth statenum_t.S_NULL, // seestate sfxenum_t.sfx_None, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_NULL, // painstate 0, // painchance sfxenum_t.sfx_None, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_NULL, // missilestate statenum_t.S_NULL, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_None, // deathsound 0, // speed 16*FRACUNIT, // radius 88*FRACUNIT, // height 100, // mass 0, // damage sfxenum_t.sfx_None, // activesound MF_SOLID|MF_SPAWNCEILING|MF_NOGRAVITY, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_MISC79 74, // doomednum statenum_t.S_HANGBNOBRAIN, // spawnstate 1000, // spawnhealth statenum_t.S_NULL, // seestate sfxenum_t.sfx_None, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_NULL, // painstate 0, // painchance sfxenum_t.sfx_None, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_NULL, // missilestate statenum_t.S_NULL, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_None, // deathsound 0, // speed 16*FRACUNIT, // radius 88*FRACUNIT, // height 100, // mass 0, // damage sfxenum_t.sfx_None, // activesound MF_SOLID|MF_SPAWNCEILING|MF_NOGRAVITY, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_MISC80 75, // doomednum statenum_t.S_HANGTLOOKDN, // spawnstate 1000, // spawnhealth statenum_t.S_NULL, // seestate sfxenum_t.sfx_None, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_NULL, // painstate 0, // painchance sfxenum_t.sfx_None, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_NULL, // missilestate statenum_t.S_NULL, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_None, // deathsound 0, // speed 16*FRACUNIT, // radius 64*FRACUNIT, // height 100, // mass 0, // damage sfxenum_t.sfx_None, // activesound MF_SOLID|MF_SPAWNCEILING|MF_NOGRAVITY, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_MISC81 76, // doomednum statenum_t.S_HANGTSKULL, // spawnstate 1000, // spawnhealth statenum_t.S_NULL, // seestate sfxenum_t.sfx_None, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_NULL, // painstate 0, // painchance sfxenum_t.sfx_None, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_NULL, // missilestate statenum_t.S_NULL, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_None, // deathsound 0, // speed 16*FRACUNIT, // radius 64*FRACUNIT, // height 100, // mass 0, // damage sfxenum_t.sfx_None, // activesound MF_SOLID|MF_SPAWNCEILING|MF_NOGRAVITY, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_MISC82 77, // doomednum statenum_t.S_HANGTLOOKUP, // spawnstate 1000, // spawnhealth statenum_t.S_NULL, // seestate sfxenum_t.sfx_None, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_NULL, // painstate 0, // painchance sfxenum_t.sfx_None, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_NULL, // missilestate statenum_t.S_NULL, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_None, // deathsound 0, // speed 16*FRACUNIT, // radius 64*FRACUNIT, // height 100, // mass 0, // damage sfxenum_t.sfx_None, // activesound MF_SOLID|MF_SPAWNCEILING|MF_NOGRAVITY, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_MISC83 78, // doomednum statenum_t.S_HANGTNOBRAIN, // spawnstate 1000, // spawnhealth statenum_t.S_NULL, // seestate sfxenum_t.sfx_None, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_NULL, // painstate 0, // painchance sfxenum_t.sfx_None, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_NULL, // missilestate statenum_t.S_NULL, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_None, // deathsound 0, // speed 16*FRACUNIT, // radius 64*FRACUNIT, // height 100, // mass 0, // damage sfxenum_t.sfx_None, // activesound MF_SOLID|MF_SPAWNCEILING|MF_NOGRAVITY, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_MISC84 79, // doomednum statenum_t.S_COLONGIBS, // spawnstate 1000, // spawnhealth statenum_t.S_NULL, // seestate sfxenum_t.sfx_None, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_NULL, // painstate 0, // painchance sfxenum_t.sfx_None, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_NULL, // missilestate statenum_t.S_NULL, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_None, // deathsound 0, // speed 20*FRACUNIT, // radius 16*FRACUNIT, // height 100, // mass 0, // damage sfxenum_t.sfx_None, // activesound MF_NOBLOCKMAP, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_MISC85 80, // doomednum statenum_t.S_SMALLPOOL, // spawnstate 1000, // spawnhealth statenum_t.S_NULL, // seestate sfxenum_t.sfx_None, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_NULL, // painstate 0, // painchance sfxenum_t.sfx_None, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_NULL, // missilestate statenum_t.S_NULL, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_None, // deathsound 0, // speed 20*FRACUNIT, // radius 16*FRACUNIT, // height 100, // mass 0, // damage sfxenum_t.sfx_None, // activesound MF_NOBLOCKMAP, // flags statenum_t.S_NULL // raisestate ), new mobjinfo_t( // MT_MISC86 81, // doomednum statenum_t.S_BRAINSTEM, // spawnstate 1000, // spawnhealth statenum_t.S_NULL, // seestate sfxenum_t.sfx_None, // seesound 8, // reactiontime sfxenum_t.sfx_None, // attacksound statenum_t.S_NULL, // painstate 0, // painchance sfxenum_t.sfx_None, // painsound statenum_t.S_NULL, // meleestate statenum_t.S_NULL, // missilestate statenum_t.S_NULL, // deathstate statenum_t.S_NULL, // xdeathstate sfxenum_t.sfx_None, // deathsound 0, // speed 20*FRACUNIT, // radius 16*FRACUNIT, // height 100, // mass 0, // damage sfxenum_t.sfx_None, // activesound MF_NOBLOCKMAP, // flags statenum_t.S_NULL // raisestate ) }; static { // Need to set them to simulate pointer-like operations. for (int i=0;i<states.length;i++){ states[i].id=i; //states[0]=null; } } } package data; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import w.CacheableDoomObject; import w.DoomBuffer; /** BSP Node structure on-disk */ public class mapnode_t implements CacheableDoomObject { public mapnode_t() { this.bbox = new short[2][4]; this.children = new char[2]; } /** Partition line from (x,y) to x+dx,y+dy) */ public short x, y, dx, dy; /** Bounding box for each child, clip against view frustum. */ public short[][] bbox; /** If NF_SUBSECTOR its a subsector, else it's a node of another subtree. * In simpler words: if the first bit is set, strip it and use the rest * as a subtree index. Else it's a node index. * */ public char[] children = new char[2]; // MAES: used to be unsigned short. public static int sizeOf() { return (8 + 16 + 4); } @Override public void unpack(ByteBuffer buf) throws IOException { buf.order(ByteOrder.LITTLE_ENDIAN); this.x = buf.getShort(); this.y = buf.getShort(); this.dx = buf.getShort(); this.dy = buf.getShort(); DoomBuffer.readShortArray(buf, this.bbox[0], 4); DoomBuffer.readShortArray(buf, this.bbox[1], 4); DoomBuffer.readCharArray(buf, this.children, 2); } } package data; import java.io.DataOutputStream; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import w.CacheableDoomObject; import w.DoomBuffer; import w.IPackableDoomObject; import w.IWritableDoomObject; /** * A LineDef, as used for editing, and as input to the BSP builder. */ public class maplinedef_t implements CacheableDoomObject,IPackableDoomObject,IWritableDoomObject{ public maplinedef_t() { this.sidenum = new char[2]; } public char v1; public char v2; public short flags; public short special; public short tag; /** sidenum[1] will be 0xFFFF if one sided */ public char[] sidenum; public static int sizeOf() { return 14; } @Override public void unpack(ByteBuffer buf) throws IOException { buf.order(ByteOrder.LITTLE_ENDIAN); this.v1 = buf.getChar(); this.v2 = buf.getChar(); this.flags = buf.getShort(); this.special = buf.getShort(); this.tag = buf.getShort(); DoomBuffer.readCharArray(buf, this.sidenum, 2); } @Override public void write(DataOutputStream dos) throws IOException { // More efficient, avoids duplicating code and // handles little endian better. iobuffer.position(0); iobuffer.order(ByteOrder.LITTLE_ENDIAN); this.pack(iobuffer); dos.write(iobuffer.array()); } @Override public void pack(ByteBuffer buf) throws IOException { buf.order(ByteOrder.LITTLE_ENDIAN); buf.putChar(v1); buf.putChar(v2); buf.putShort(flags); buf.putShort(special); buf.putShort(tag); buf.putChar(sidenum[0]); buf.putChar(sidenum[1]); } private static ByteBuffer iobuffer=ByteBuffer.allocate(maplinedef_t.sizeOf()); } package data; import java.io.DataOutputStream; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import w.CacheableDoomObject; import w.IPackableDoomObject; import w.IWritableDoomObject; /** * This is the structure of a map vertex ON DISK: in memory it gets shifted and * expanded to fixed_t. Also, on disk it only exists as part of the VERTEXES * lump: it is not individually cacheable, even though it implements * CacheableDoomObject. */ public class mapvertex_t implements CacheableDoomObject,IWritableDoomObject,IPackableDoomObject { public mapvertex_t(short x, short y) { this.x = x; this.y = y; } public mapvertex_t() { this((short) 0, (short) 0); } public short x; public short y; public static int sizeOf() { return 4; } @Override public void unpack(ByteBuffer buf) throws IOException { buf.order(ByteOrder.LITTLE_ENDIAN); x = buf.getShort(); y = buf.getShort(); } @Override public void pack(ByteBuffer buf) throws IOException { buf.order(ByteOrder.LITTLE_ENDIAN); buf.putShort(x); buf.putShort(y); } @Override public void write(DataOutputStream dos) throws IOException { // More efficient, avoids duplicating code and // handles little endian better. iobuffer.position(0); iobuffer.order(ByteOrder.LITTLE_ENDIAN); this.pack(iobuffer); dos.write(iobuffer.array()); } private static final ByteBuffer iobuffer=ByteBuffer.allocate(mapvertex_t.sizeOf()); } package data; import java.io.DataOutputStream; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import rr.sector_t; import w.CacheableDoomObject; import w.DoomBuffer; import w.IPackableDoomObject; import w.IWritableDoomObject; /** Sector definition, from editing. */ public class mapsector_t implements CacheableDoomObject,IWritableDoomObject, IPackableDoomObject { public mapsector_t(){ } public short floorheight; public short ceilingheight; public String floorpic; public String ceilingpic; public short lightlevel; public short special; public short tag; @Override public void unpack(ByteBuffer buf) throws IOException { buf.order(ByteOrder.LITTLE_ENDIAN); this.floorheight = buf.getShort(); this.ceilingheight = buf.getShort(); this.floorpic=DoomBuffer.getNullTerminatedString(buf,8).toUpperCase(); this.ceilingpic=DoomBuffer.getNullTerminatedString(buf,8).toUpperCase(); this.lightlevel= buf.getShort(); this.special= buf.getShort(); this.tag= buf.getShort(); } public static int sizeOf() { return 26; } @Override public void write(DataOutputStream dos) throws IOException { // More efficient, avoids duplicating code and // handles little endian better. iobuffer.position(0); iobuffer.order(ByteOrder.LITTLE_ENDIAN); this.pack(iobuffer); dos.write(iobuffer.array()); } public void pack(ByteBuffer b) throws IOException { b.order(ByteOrder.LITTLE_ENDIAN); b.putShort(this.floorheight); b.putShort(this.ceilingheight); DoomBuffer.putNullTerminatedString(b, this.floorpic,8); DoomBuffer.putNullTerminatedString(b, this.ceilingpic,8); b.putShort(this.lightlevel); b.putShort(this.special); b.putShort(this.tag); } private static final ByteBuffer iobuffer=ByteBuffer.allocate(mapsector_t.sizeOf()); } package data; public class musicinfo_t { public musicinfo_t() { } public musicinfo_t(String name) { this.name = name; } public musicinfo_t(String name, int lumpnum) { this.name = name; this.lumpnum = lumpnum; } // up to 6-character name public String name; // lump number of music public int lumpnum; // music data public byte[] data; // music handle once registered public int handle; } package data; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import w.CacheableDoomObject; /** SubSector, as generated by BSP. */ public class mapsubsector_t implements CacheableDoomObject{ public mapsubsector_t(){ } public char numsegs; /** Index of first one, segs are stored sequentially. */ public char firstseg; @Override public void unpack(ByteBuffer buf) throws IOException { buf.order(ByteOrder.LITTLE_ENDIAN); this.numsegs = buf.getChar(); this.firstseg = buf.getChar(); } public static int sizeOf(){ return 4; } } package data; /** This is actually used as a data type */ public enum spritenum_t { SPR_TROO, SPR_SHTG, SPR_PUNG, SPR_PISG, SPR_PISF, SPR_SHTF, SPR_SHT2, SPR_CHGG, SPR_CHGF, SPR_MISG, SPR_MISF, SPR_SAWG, SPR_PLSG, SPR_PLSF, SPR_BFGG, SPR_BFGF, SPR_BLUD, SPR_PUFF, SPR_BAL1, SPR_BAL2, SPR_PLSS, SPR_PLSE, SPR_MISL, SPR_BFS1, SPR_BFE1, SPR_BFE2, SPR_TFOG, SPR_IFOG, SPR_PLAY, SPR_POSS, SPR_SPOS, SPR_VILE, SPR_FIRE, SPR_FATB, SPR_FBXP, SPR_SKEL, SPR_MANF, SPR_FATT, SPR_CPOS, SPR_SARG, SPR_HEAD, SPR_BAL7, SPR_BOSS, SPR_BOS2, SPR_SKUL, SPR_SPID, SPR_BSPI, SPR_APLS, SPR_APBX, SPR_CYBR, SPR_PAIN, SPR_SSWV, SPR_KEEN, SPR_BBRN, SPR_BOSF, SPR_ARM1, SPR_ARM2, SPR_BAR1, SPR_BEXP, SPR_FCAN, SPR_BON1, SPR_BON2, SPR_BKEY, SPR_RKEY, SPR_YKEY, SPR_BSKU, SPR_RSKU, SPR_YSKU, SPR_STIM, SPR_MEDI, SPR_SOUL, SPR_PINV, SPR_PSTR, SPR_PINS, SPR_MEGA, SPR_SUIT, SPR_PMAP, SPR_PVIS, SPR_CLIP, SPR_AMMO, SPR_ROCK, SPR_BROK, SPR_CELL, SPR_CELP, SPR_SHEL, SPR_SBOX, SPR_BPAK, SPR_BFUG, SPR_MGUN, SPR_CSAW, SPR_LAUN, SPR_PLAS, SPR_SHOT, SPR_SGN2, SPR_COLU, SPR_SMT2, SPR_GOR1, SPR_POL2, SPR_POL5, SPR_POL4, SPR_POL3, SPR_POL1, SPR_POL6, SPR_GOR2, SPR_GOR3, SPR_GOR4, SPR_GOR5, SPR_SMIT, SPR_COL1, SPR_COL2, SPR_COL3, SPR_COL4, SPR_CAND, SPR_CBRA, SPR_COL6, SPR_TRE1, SPR_TRE2, SPR_ELEC, SPR_CEYE, SPR_FSKU, SPR_COL5, SPR_TBLU, SPR_TGRN, SPR_TRED, SPR_SMBT, SPR_SMGT, SPR_SMRT, SPR_HDB1, SPR_HDB2, SPR_HDB3, SPR_HDB4, SPR_HDB5, SPR_HDB6, SPR_POB1, SPR_POB2, SPR_BRS1, SPR_TLMP, SPR_TLP2, NUMSPRITES }; package data; import java.io.DataOutputStream; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import w.CacheableDoomObject; import w.IPackableDoomObject; import w.IWritableDoomObject; /** mapthing_t ... same on disk AND in memory, wow?! */ public class mapthing_t implements CacheableDoomObject,IPackableDoomObject,IWritableDoomObject,Cloneable{ public short x; public short y; public short angle; public short type; public short options; public mapthing_t() { } public mapthing_t(mapthing_t source) { this.copyFrom(source); } public static int sizeOf() { return 10; } @Override public void unpack(ByteBuffer buf) throws IOException { buf.order(ByteOrder.LITTLE_ENDIAN); this.x = buf.getShort(); this.y = buf.getShort(); this.angle = buf.getShort(); this.type = buf.getShort(); this.options = buf.getShort(); } public void copyFrom(mapthing_t source){ this.x=source.x; this.y=source.y; this.angle=source.angle; this.options=source.options; this.type=source.type; } @Override public void write(DataOutputStream f) throws IOException { // More efficient, avoids duplicating code and // handles little endian better. iobuffer.position(0); iobuffer.order(ByteOrder.LITTLE_ENDIAN); this.pack(iobuffer); f.write(iobuffer.array()); } public void pack(ByteBuffer b) { b.order(ByteOrder.LITTLE_ENDIAN); b.putShort(x); b.putShort(y); b.putShort(angle); b.putShort(type); b.putShort(options); } private static ByteBuffer iobuffer=ByteBuffer.allocate(mapthing_t.sizeOf()); } package data; import static m.fixed_t.FRACUNIT; import static m.fixed_t.MAPFRACUNIT; /** Everything that constitutes a removable limit should go here */ public final class Limits { // Obvious rendering limits public static final int MAXVISPLANES = 128; public static final int MAXSEGS = 32; public static final int MAXVISSPRITES = 128; public static final int MAXDRAWSEGS = 256; // MAES: Moved MAXOPENINGS to renderer state, it's scale dependant. public static final int CEILSPEED = MAPFRACUNIT; public static final int CEILWAIT = 150; public static final int MAXCEILINGS = 30; public static final int MAXANIMS = 32; /** Animating line specials */ public static final int MAXLINEANIMS = 64; // These are only used in the renderer, effectively putting // a limit to the size of lookup tables for screen buffers. public static final int MAXWIDTH = 1600; public static final int MAXHEIGHT = 1200; // Command line/file limits public static final int MAXWADFILES = 20; public static final int MAXARGVS = 100; // The maximum number of players, multiplayer/networking. // Max computers/players in a game. AFFECTS SAVEGAMES. public static final int MAXPLAYERS = 4; public final static int MAXNETNODES = 8; /** Some quirky engine limits */ public static final int MAXEVENTS = 64; /** max # of wall switch TYPES in a level */ public static final int MAXSWITCHES = 50; /** 20 adjoining sectors max! */ public static final int MAX_ADJOINING_SECTORS = 20; // 4 players, 4 buttons each at once, max. public static final int MAXBUTTONS = 16; // 1 second, in ticks. public static final int BUTTONTIME = 35; /** * keep track of special lines as they are hit, but don't process them until * the move is proven valid */ public static final int MAXSPECIALCROSS = 8; public static final int MAXHEALTH = 100; /** * MAXRADIUS is for precalculated sector block boxes the spider demon is * larger, but we do not have any moving sectors nearby */ public static final int MAXRADIUS = 32 * FRACUNIT; public static final int MAXINTERCEPTS = 128; public static final int MAXMOVE = (30 * MAPFRACUNIT); /** Player spawn spots for deathmatch. */ public static final int MAX_DM_STARTS = 10; // C's "chars" are actually Java signed bytes. public static final byte MAXCHAR = ((byte) 0x7f); public static final byte MINCHAR = ((byte) 0x80); // 16-bit integers... public static final short MAXSHORT = ((short) 0x7fff); public static final short MINSHORT = ((short) 0x8000); // Max pos 32-bit int. public static final int MAXINT = ((int) 0x7fffffff); public static final long MAXLONG = ((long) 0x7fffffff); // Max negative 32-bit integer. These are considered to be the same. public static final int MININT = ((int) 0x80000000); public static final long MINLONG = ((long) 0x80000000); // Buffering/memory limits. public static final int SAVEGAMESIZE = 0x2c000; public static final int SAVESTRINGSIZE = 24; public static final int VERSIONSIZE = 16; public static final int PLATWAIT = 3; public static final int PLATSPEED = MAPFRACUNIT; public static final int MAXPLATS = 30; public static final int MAXSKULLS = 20; public static final int NUMBRAINTARGETS=32; public static final int NUMMOBJTYPES=mobjtype_t.NUMMOBJTYPES.ordinal(); } package data; import static data.Tables.FINEANGLES; /** Sine and Cosine. * Java can't have that mush initialization data in one file, so I had to separate it. * * @author Maes * */ public class SineCosine { // MAES: original size was 10240, but includes 5PI/4 periods. // We can get away with ints on this one because of the small range. // MAES: WTF? -64 ~ 64K range... so 17-bit accuracy? heh. /** * Original size was 10240, but includes 5PI/4 periods. * We can get away with ints on this one because of the small range. * MAES: WTF? -64 ~ 64K range... so 17-bit accuracy? heh. */ public static int[] finesine= { 25,75,125,175,226,276,326,376, 427,477,527,578,628,678,728,779, 829,879,929,980,1030,1080,1130,1181, 1231,1281,1331,1382,1432,1482,1532,1583, 1633,1683,1733,1784,1834,1884,1934,1985, 2035,2085,2135,2186,2236,2286,2336,2387, 2437,2487,2537,2587,2638,2688,2738,2788, 2839,2889,2939,2989,3039,3090,3140,3190, 3240,3291,3341,3391,3441,3491,3541,3592, 3642,3692,3742,3792,3843,3893,3943,3993, 4043,4093,4144,4194,4244,4294,4344,4394, 4445,4495,4545,4595,4645,4695,4745,4796, 4846,4896,4946,4996,5046,5096,5146,5197, 5247,5297,5347,5397,5447,5497,5547,5597, 5647,5697,5748,5798,5848,5898,5948,5998, 6048,6098,6148,6198,6248,6298,6348,6398, 6448,6498,6548,6598,6648,6698,6748,6798, 6848,6898,6948,6998,7048,7098,7148,7198, 7248,7298,7348,7398,7448,7498,7548,7598, 7648,7697,7747,7797,7847,7897,7947,7997, 8047,8097,8147,8196,8246,8296,8346,8396, 8446,8496,8545,8595,8645,8695,8745,8794, 8844,8894,8944,8994,9043,9093,9143,9193, 9243,9292,9342,9392,9442,9491,9541,9591, 9640,9690,9740,9790,9839,9889,9939,9988, 10038,10088,10137,10187,10237,10286,10336,10386, 10435,10485,10534,10584,10634,10683,10733,10782, 10832,10882,10931,10981,11030,11080,11129,11179, 11228,11278,11327,11377,11426,11476,11525,11575, 11624,11674,11723,11773,11822,11872,11921,11970, 12020,12069,12119,12168,12218,12267,12316,12366, 12415,12464,12514,12563,12612,12662,12711,12760, 12810,12859,12908,12957,13007,13056,13105,13154, 13204,13253,13302,13351,13401,13450,13499,13548, 13597,13647,13696,13745,13794,13843,13892,13941, 13990,14040,14089,14138,14187,14236,14285,14334, 14383,14432,14481,14530,14579,14628,14677,14726, 14775,14824,14873,14922,14971,15020,15069,15118, 15167,15215,15264,15313,15362,15411,15460,15509, 15557,15606,15655,15704,15753,15802,15850,15899, 15948,15997,16045,16094,16143,16191,16240,16289, 16338,16386,16435,16484,16532,16581,16629,16678, 16727,16775,16824,16872,16921,16970,17018,17067, 17115,17164,17212,17261,17309,17358,17406,17455, 17503,17551,17600,17648,17697,17745,17793,17842, 17890,17939,17987,18035,18084,18132,18180,18228, 18277,18325,18373,18421,18470,18518,18566,18614, 18663,18711,18759,18807,18855,18903,18951,19000, 19048,19096,19144,19192,19240,19288,19336,19384, 19432,19480,19528,19576,19624,19672,19720,19768, 19816,19864,19912,19959,20007,20055,20103,20151, 20199,20246,20294,20342,20390,20438,20485,20533, 20581,20629,20676,20724,20772,20819,20867,20915, 20962,21010,21057,21105,21153,21200,21248,21295, 21343,21390,21438,21485,21533,21580,21628,21675, 21723,21770,21817,21865,21912,21960,22007,22054, 22102,22149,22196,22243,22291,22338,22385,22433, 22480,22527,22574,22621,22668,22716,22763,22810, 22857,22904,22951,22998,23045,23092,23139,23186, 23233,23280,23327,23374,23421,23468,23515,23562, 23609,23656,23703,23750,23796,23843,23890,23937, 23984,24030,24077,24124,24171,24217,24264,24311, 24357,24404,24451,24497,24544,24591,24637,24684, 24730,24777,24823,24870,24916,24963,25009,25056, 25102,25149,25195,25241,25288,25334,25381,25427, 25473,25520,25566,25612,25658,25705,25751,25797, 25843,25889,25936,25982,26028,26074,26120,26166, 26212,26258,26304,26350,26396,26442,26488,26534, 26580,26626,26672,26718,26764,26810,26856,26902, 26947,26993,27039,27085,27131,27176,27222,27268, 27313,27359,27405,27450,27496,27542,27587,27633, 27678,27724,27770,27815,27861,27906,27952,27997, 28042,28088,28133,28179,28224,28269,28315,28360, 28405,28451,28496,28541,28586,28632,28677,28722, 28767,28812,28858,28903,28948,28993,29038,29083, 29128,29173,29218,29263,29308,29353,29398,29443, 29488,29533,29577,29622,29667,29712,29757,29801, 29846,29891,29936,29980,30025,30070,30114,30159, 30204,30248,30293,30337,30382,30426,30471,30515, 30560,30604,30649,30693,30738,30782,30826,30871, 30915,30959,31004,31048,31092,31136,31181,31225, 31269,31313,31357,31402,31446,31490,31534,31578, 31622,31666,31710,31754,31798,31842,31886,31930, 31974,32017,32061,32105,32149,32193,32236,32280, 32324,32368,32411,32455,32499,32542,32586,32630, 32673,32717,32760,32804,32847,32891,32934,32978, 33021,33065,33108,33151,33195,33238,33281,33325, 33368,33411,33454,33498,33541,33584,33627,33670, 33713,33756,33799,33843,33886,33929,33972,34015, 34057,34100,34143,34186,34229,34272,34315,34358, 34400,34443,34486,34529,34571,34614,34657,34699, 34742,34785,34827,34870,34912,34955,34997,35040, 35082,35125,35167,35210,35252,35294,35337,35379, 35421,35464,35506,35548,35590,35633,35675,35717, 35759,35801,35843,35885,35927,35969,36011,36053, 36095,36137,36179,36221,36263,36305,36347,36388, 36430,36472,36514,36555,36597,36639,36681,36722, 36764,36805,36847,36889,36930,36972,37013,37055, 37096,37137,37179,37220,37262,37303,37344,37386, 37427,37468,37509,37551,37592,37633,37674,37715, 37756,37797,37838,37879,37920,37961,38002,38043, 38084,38125,38166,38207,38248,38288,38329,38370, 38411,38451,38492,38533,38573,38614,38655,38695, 38736,38776,38817,38857,38898,38938,38979,39019, 39059,39100,39140,39180,39221,39261,39301,39341, 39382,39422,39462,39502,39542,39582,39622,39662, 39702,39742,39782,39822,39862,39902,39942,39982, 40021,40061,40101,40141,40180,40220,40260,40300, 40339,40379,40418,40458,40497,40537,40576,40616, 40655,40695,40734,40773,40813,40852,40891,40931, 40970,41009,41048,41087,41127,41166,41205,41244, 41283,41322,41361,41400,41439,41478,41517,41556, 41595,41633,41672,41711,41750,41788,41827,41866, 41904,41943,41982,42020,42059,42097,42136,42174, 42213,42251,42290,42328,42366,42405,42443,42481, 42520,42558,42596,42634,42672,42711,42749,42787, 42825,42863,42901,42939,42977,43015,43053,43091, 43128,43166,43204,43242,43280,43317,43355,43393, 43430,43468,43506,43543,43581,43618,43656,43693, 43731,43768,43806,43843,43880,43918,43955,43992, 44029,44067,44104,44141,44178,44215,44252,44289, 44326,44363,44400,44437,44474,44511,44548,44585, 44622,44659,44695,44732,44769,44806,44842,44879, 44915,44952,44989,45025,45062,45098,45135,45171, 45207,45244,45280,45316,45353,45389,45425,45462, 45498,45534,45570,45606,45642,45678,45714,45750, 45786,45822,45858,45894,45930,45966,46002,46037, 46073,46109,46145,46180,46216,46252,46287,46323, 46358,46394,46429,46465,46500,46536,46571,46606, 46642,46677,46712,46747,46783,46818,46853,46888, 46923,46958,46993,47028,47063,47098,47133,47168, 47203,47238,47273,47308,47342,47377,47412,47446, 47481,47516,47550,47585,47619,47654,47688,47723, 47757,47792,47826,47860,47895,47929,47963,47998, 48032,48066,48100,48134,48168,48202,48237,48271, 48305,48338,48372,48406,48440,48474,48508,48542, 48575,48609,48643,48676,48710,48744,48777,48811, 48844,48878,48911,48945,48978,49012,49045,49078, 49112,49145,49178,49211,49244,49278,49311,49344, 49377,49410,49443,49476,49509,49542,49575,49608, 49640,49673,49706,49739,49771,49804,49837,49869, 49902,49935,49967,50000,50032,50065,50097,50129, 50162,50194,50226,50259,50291,50323,50355,50387, 50420,50452,50484,50516,50548,50580,50612,50644, 50675,50707,50739,50771,50803,50834,50866,50898, 50929,50961,50993,51024,51056,51087,51119,51150, 51182,51213,51244,51276,51307,51338,51369,51401, 51432,51463,51494,51525,51556,51587,51618,51649, 51680,51711,51742,51773,51803,51834,51865,51896, 51926,51957,51988,52018,52049,52079,52110,52140, 52171,52201,52231,52262,52292,52322,52353,52383, 52413,52443,52473,52503,52534,52564,52594,52624, 52653,52683,52713,52743,52773,52803,52832,52862, 52892,52922,52951,52981,53010,53040,53069,53099, 53128,53158,53187,53216,53246,53275,53304,53334, 53363,53392,53421,53450,53479,53508,53537,53566, 53595,53624,53653,53682,53711,53739,53768,53797, 53826,53854,53883,53911,53940,53969,53997,54026, 54054,54082,54111,54139,54167,54196,54224,54252, 54280,54308,54337,54365,54393,54421,54449,54477, 54505,54533,54560,54588,54616,54644,54672,54699, 54727,54755,54782,54810,54837,54865,54892,54920, 54947,54974,55002,55029,55056,55084,55111,55138, 55165,55192,55219,55246,55274,55300,55327,55354, 55381,55408,55435,55462,55489,55515,55542,55569, 55595,55622,55648,55675,55701,55728,55754,55781, 55807,55833,55860,55886,55912,55938,55965,55991, 56017,56043,56069,56095,56121,56147,56173,56199, 56225,56250,56276,56302,56328,56353,56379,56404, 56430,56456,56481,56507,56532,56557,56583,56608, 56633,56659,56684,56709,56734,56760,56785,56810, 56835,56860,56885,56910,56935,56959,56984,57009, 57034,57059,57083,57108,57133,57157,57182,57206, 57231,57255,57280,57304,57329,57353,57377,57402, 57426,57450,57474,57498,57522,57546,57570,57594, 57618,57642,57666,57690,57714,57738,57762,57785, 57809,57833,57856,57880,57903,57927,57950,57974, 57997,58021,58044,58067,58091,58114,58137,58160, 58183,58207,58230,58253,58276,58299,58322,58345, 58367,58390,58413,58436,58459,58481,58504,58527, 58549,58572,58594,58617,58639,58662,58684,58706, 58729,58751,58773,58795,58818,58840,58862,58884, 58906,58928,58950,58972,58994,59016,59038,59059, 59081,59103,59125,59146,59168,59190,59211,59233, 59254,59276,59297,59318,59340,59361,59382,59404, 59425,59446,59467,59488,59509,59530,59551,59572, 59593,59614,59635,59656,59677,59697,59718,59739, 59759,59780,59801,59821,59842,59862,59883,59903, 59923,59944,59964,59984,60004,60025,60045,60065, 60085,60105,60125,60145,60165,60185,60205,60225, 60244,60264,60284,60304,60323,60343,60363,60382, 60402,60421,60441,60460,60479,60499,60518,60537, 60556,60576,60595,60614,60633,60652,60671,60690, 60709,60728,60747,60766,60785,60803,60822,60841, 60859,60878,60897,60915,60934,60952,60971,60989, 61007,61026,61044,61062,61081,61099,61117,61135, 61153,61171,61189,61207,61225,61243,61261,61279, 61297,61314,61332,61350,61367,61385,61403,61420, 61438,61455,61473,61490,61507,61525,61542,61559, 61577,61594,61611,61628,61645,61662,61679,61696, 61713,61730,61747,61764,61780,61797,61814,61831, 61847,61864,61880,61897,61913,61930,61946,61963, 61979,61995,62012,62028,62044,62060,62076,62092, 62108,62125,62141,62156,62172,62188,62204,62220, 62236,62251,62267,62283,62298,62314,62329,62345, 62360,62376,62391,62407,62422,62437,62453,62468, 62483,62498,62513,62528,62543,62558,62573,62588, 62603,62618,62633,62648,62662,62677,62692,62706, 62721,62735,62750,62764,62779,62793,62808,62822, 62836,62850,62865,62879,62893,62907,62921,62935, 62949,62963,62977,62991,63005,63019,63032,63046, 63060,63074,63087,63101,63114,63128,63141,63155, 63168,63182,63195,63208,63221,63235,63248,63261, 63274,63287,63300,63313,63326,63339,63352,63365, 63378,63390,63403,63416,63429,63441,63454,63466, 63479,63491,63504,63516,63528,63541,63553,63565, 63578,63590,63602,63614,63626,63638,63650,63662, 63674,63686,63698,63709,63721,63733,63745,63756, 63768,63779,63791,63803,63814,63825,63837,63848, 63859,63871,63882,63893,63904,63915,63927,63938, 63949,63960,63971,63981,63992,64003,64014,64025, 64035,64046,64057,64067,64078,64088,64099,64109, 64120,64130,64140,64151,64161,64171,64181,64192, 64202,64212,64222,64232,64242,64252,64261,64271, 64281,64291,64301,64310,64320,64330,64339,64349, 64358,64368,64377,64387,64396,64405,64414,64424, 64433,64442,64451,64460,64469,64478,64487,64496, 64505,64514,64523,64532,64540,64549,64558,64566, 64575,64584,64592,64601,64609,64617,64626,64634, 64642,64651,64659,64667,64675,64683,64691,64699, 64707,64715,64723,64731,64739,64747,64754,64762, 64770,64777,64785,64793,64800,64808,64815,64822, 64830,64837,64844,64852,64859,64866,64873,64880, 64887,64895,64902,64908,64915,64922,64929,64936, 64943,64949,64956,64963,64969,64976,64982,64989, 64995,65002,65008,65015,65021,65027,65033,65040, 65046,65052,65058,65064,65070,65076,65082,65088, 65094,65099,65105,65111,65117,65122,65128,65133, 65139,65144,65150,65155,65161,65166,65171,65177, 65182,65187,65192,65197,65202,65207,65212,65217, 65222,65227,65232,65237,65242,65246,65251,65256, 65260,65265,65270,65274,65279,65283,65287,65292, 65296,65300,65305,65309,65313,65317,65321,65325, 65329,65333,65337,65341,65345,65349,65352,65356, 65360,65363,65367,65371,65374,65378,65381,65385, 65388,65391,65395,65398,65401,65404,65408,65411, 65414,65417,65420,65423,65426,65429,65431,65434, 65437,65440,65442,65445,65448,65450,65453,65455, 65458,65460,65463,65465,65467,65470,65472,65474, 65476,65478,65480,65482,65484,65486,65488,65490, 65492,65494,65496,65497,65499,65501,65502,65504, 65505,65507,65508,65510,65511,65513,65514,65515, 65516,65518,65519,65520,65521,65522,65523,65524, 65525,65526,65527,65527,65528,65529,65530,65530, 65531,65531,65532,65532,65533,65533,65534,65534, 65534,65535,65535,65535,65535,65535,65535,65535, 65535,65535,65535,65535,65535,65535,65535,65534, 65534,65534,65533,65533,65532,65532,65531,65531, 65530,65530,65529,65528,65527,65527,65526,65525, 65524,65523,65522,65521,65520,65519,65518,65516, 65515,65514,65513,65511,65510,65508,65507,65505, 65504,65502,65501,65499,65497,65496,65494,65492, 65490,65488,65486,65484,65482,65480,65478,65476, 65474,65472,65470,65467,65465,65463,65460,65458, 65455,65453,65450,65448,65445,65442,65440,65437, 65434,65431,65429,65426,65423,65420,65417,65414, 65411,65408,65404,65401,65398,65395,65391,65388, 65385,65381,65378,65374,65371,65367,65363,65360, 65356,65352,65349,65345,65341,65337,65333,65329, 65325,65321,65317,65313,65309,65305,65300,65296, 65292,65287,65283,65279,65274,65270,65265,65260, 65256,65251,65246,65242,65237,65232,65227,65222, 65217,65212,65207,65202,65197,65192,65187,65182, 65177,65171,65166,65161,65155,65150,65144,65139, 65133,65128,65122,65117,65111,65105,65099,65094, 65088,65082,65076,65070,65064,65058,65052,65046, 65040,65033,65027,65021,65015,65008,65002,64995, 64989,64982,64976,64969,64963,64956,64949,64943, 64936,64929,64922,64915,64908,64902,64895,64887, 64880,64873,64866,64859,64852,64844,64837,64830, 64822,64815,64808,64800,64793,64785,64777,64770, 64762,64754,64747,64739,64731,64723,64715,64707, 64699,64691,64683,64675,64667,64659,64651,64642, 64634,64626,64617,64609,64600,64592,64584,64575, 64566,64558,64549,64540,64532,64523,64514,64505, 64496,64487,64478,64469,64460,64451,64442,64433, 64424,64414,64405,64396,64387,64377,64368,64358, 64349,64339,64330,64320,64310,64301,64291,64281, 64271,64261,64252,64242,64232,64222,64212,64202, 64192,64181,64171,64161,64151,64140,64130,64120, 64109,64099,64088,64078,64067,64057,64046,64035, 64025,64014,64003,63992,63981,63971,63960,63949, 63938,63927,63915,63904,63893,63882,63871,63859, 63848,63837,63825,63814,63803,63791,63779,63768, 63756,63745,63733,63721,63709,63698,63686,63674, 63662,63650,63638,63626,63614,63602,63590,63578, 63565,63553,63541,63528,63516,63504,63491,63479, 63466,63454,63441,63429,63416,63403,63390,63378, 63365,63352,63339,63326,63313,63300,63287,63274, 63261,63248,63235,63221,63208,63195,63182,63168, 63155,63141,63128,63114,63101,63087,63074,63060, 63046,63032,63019,63005,62991,62977,62963,62949, 62935,62921,62907,62893,62879,62865,62850,62836, 62822,62808,62793,62779,62764,62750,62735,62721, 62706,62692,62677,62662,62648,62633,62618,62603, 62588,62573,62558,62543,62528,62513,62498,62483, 62468,62453,62437,62422,62407,62391,62376,62360, 62345,62329,62314,62298,62283,62267,62251,62236, 62220,62204,62188,62172,62156,62141,62125,62108, 62092,62076,62060,62044,62028,62012,61995,61979, 61963,61946,61930,61913,61897,61880,61864,61847, 61831,61814,61797,61780,61764,61747,61730,61713, 61696,61679,61662,61645,61628,61611,61594,61577, 61559,61542,61525,61507,61490,61473,61455,61438, 61420,61403,61385,61367,61350,61332,61314,61297, 61279,61261,61243,61225,61207,61189,61171,61153, 61135,61117,61099,61081,61062,61044,61026,61007, 60989,60971,60952,60934,60915,60897,60878,60859, 60841,60822,60803,60785,60766,60747,60728,60709, 60690,60671,60652,60633,60614,60595,60576,60556, 60537,60518,60499,60479,60460,60441,60421,60402, 60382,60363,60343,60323,60304,60284,60264,60244, 60225,60205,60185,60165,60145,60125,60105,60085, 60065,60045,60025,60004,59984,59964,59944,59923, 59903,59883,59862,59842,59821,59801,59780,59759, 59739,59718,59697,59677,59656,59635,59614,59593, 59572,59551,59530,59509,59488,59467,59446,59425, 59404,59382,59361,59340,59318,59297,59276,59254, 59233,59211,59190,59168,59146,59125,59103,59081, 59059,59038,59016,58994,58972,58950,58928,58906, 58884,58862,58840,58818,58795,58773,58751,58729, 58706,58684,58662,58639,58617,58594,58572,58549, 58527,58504,58481,58459,58436,58413,58390,58367, 58345,58322,58299,58276,58253,58230,58207,58183, 58160,58137,58114,58091,58067,58044,58021,57997, 57974,57950,57927,57903,57880,57856,57833,57809, 57785,57762,57738,57714,57690,57666,57642,57618, 57594,57570,57546,57522,57498,57474,57450,57426, 57402,57377,57353,57329,57304,57280,57255,57231, 57206,57182,57157,57133,57108,57083,57059,57034, 57009,56984,56959,56935,56910,56885,56860,56835, 56810,56785,56760,56734,56709,56684,56659,56633, 56608,56583,56557,56532,56507,56481,56456,56430, 56404,56379,56353,56328,56302,56276,56250,56225, 56199,56173,56147,56121,56095,56069,56043,56017, 55991,55965,55938,55912,55886,55860,55833,55807, 55781,55754,55728,55701,55675,55648,55622,55595, 55569,55542,55515,55489,55462,55435,55408,55381, 55354,55327,55300,55274,55246,55219,55192,55165, 55138,55111,55084,55056,55029,55002,54974,54947, 54920,54892,54865,54837,54810,54782,54755,54727, 54699,54672,54644,54616,54588,54560,54533,54505, 54477,54449,54421,54393,54365,54337,54308,54280, 54252,54224,54196,54167,54139,54111,54082,54054, 54026,53997,53969,53940,53911,53883,53854,53826, 53797,53768,53739,53711,53682,53653,53624,53595, 53566,53537,53508,53479,53450,53421,53392,53363, 53334,53304,53275,53246,53216,53187,53158,53128, 53099,53069,53040,53010,52981,52951,52922,52892, 52862,52832,52803,52773,52743,52713,52683,52653, 52624,52594,52564,52534,52503,52473,52443,52413, 52383,52353,52322,52292,52262,52231,52201,52171, 52140,52110,52079,52049,52018,51988,51957,51926, 51896,51865,51834,51803,51773,51742,51711,51680, 51649,51618,51587,51556,51525,51494,51463,51432, 51401,51369,51338,51307,51276,51244,51213,51182, 51150,51119,51087,51056,51024,50993,50961,50929, 50898,50866,50834,50803,50771,50739,50707,50675, 50644,50612,50580,50548,50516,50484,50452,50420, 50387,50355,50323,50291,50259,50226,50194,50162, 50129,50097,50065,50032,50000,49967,49935,49902, 49869,49837,49804,49771,49739,49706,49673,49640, 49608,49575,49542,49509,49476,49443,49410,49377, 49344,49311,49278,49244,49211,49178,49145,49112, 49078,49045,49012,48978,48945,48911,48878,48844, 48811,48777,48744,48710,48676,48643,48609,48575, 48542,48508,48474,48440,48406,48372,48338,48304, 48271,48237,48202,48168,48134,48100,48066,48032, 47998,47963,47929,47895,47860,47826,47792,47757, 47723,47688,47654,47619,47585,47550,47516,47481, 47446,47412,47377,47342,47308,47273,47238,47203, 47168,47133,47098,47063,47028,46993,46958,46923, 46888,46853,46818,46783,46747,46712,46677,46642, 46606,46571,46536,46500,46465,46429,46394,46358, 46323,46287,46252,46216,46180,46145,46109,46073, 46037,46002,45966,45930,45894,45858,45822,45786, 45750,45714,45678,45642,45606,45570,45534,45498, 45462,45425,45389,45353,45316,45280,45244,45207, 45171,45135,45098,45062,45025,44989,44952,44915, 44879,44842,44806,44769,44732,44695,44659,44622, 44585,44548,44511,44474,44437,44400,44363,44326, 44289,44252,44215,44178,44141,44104,44067,44029, 43992,43955,43918,43880,43843,43806,43768,43731, 43693,43656,43618,43581,43543,43506,43468,43430, 43393,43355,43317,43280,43242,43204,43166,43128, 43091,43053,43015,42977,42939,42901,42863,42825, 42787,42749,42711,42672,42634,42596,42558,42520, 42481,42443,42405,42366,42328,42290,42251,42213, 42174,42136,42097,42059,42020,41982,41943,41904, 41866,41827,41788,41750,41711,41672,41633,41595, 41556,41517,41478,41439,41400,41361,41322,41283, 41244,41205,41166,41127,41088,41048,41009,40970, 40931,40891,40852,40813,40773,40734,40695,40655, 40616,40576,40537,40497,40458,40418,40379,40339, 40300,40260,40220,40180,40141,40101,40061,40021, 39982,39942,39902,39862,39822,39782,39742,39702, 39662,39622,39582,39542,39502,39462,39422,39382, 39341,39301,39261,39221,39180,39140,39100,39059, 39019,38979,38938,38898,38857,38817,38776,38736, 38695,38655,38614,38573,38533,38492,38451,38411, 38370,38329,38288,38248,38207,38166,38125,38084, 38043,38002,37961,37920,37879,37838,37797,37756, 37715,37674,37633,37592,37551,37509,37468,37427, 37386,37344,37303,37262,37220,37179,37137,37096, 37055,37013,36972,36930,36889,36847,36805,36764, 36722,36681,36639,36597,36556,36514,36472,36430, 36388,36347,36305,36263,36221,36179,36137,36095, 36053,36011,35969,35927,35885,35843,35801,35759, 35717,35675,35633,35590,35548,35506,35464,35421, 35379,35337,35294,35252,35210,35167,35125,35082, 35040,34997,34955,34912,34870,34827,34785,34742, 34699,34657,34614,34571,34529,34486,34443,34400, 34358,34315,34272,34229,34186,34143,34100,34057, 34015,33972,33929,33886,33843,33799,33756,33713, 33670,33627,33584,33541,33498,33454,33411,33368, 33325,33281,33238,33195,33151,33108,33065,33021, 32978,32934,32891,32847,32804,32760,32717,32673, 32630,32586,32542,32499,32455,32411,32368,32324, 32280,32236,32193,32149,32105,32061,32017,31974, 31930,31886,31842,31798,31754,31710,31666,31622, 31578,31534,31490,31446,31402,31357,31313,31269, 31225,31181,31136,31092,31048,31004,30959,30915, 30871,30826,30782,30738,30693,30649,30604,30560, 30515,30471,30426,30382,30337,30293,30248,30204, 30159,30114,30070,30025,29980,29936,29891,29846, 29801,29757,29712,29667,29622,29577,29533,29488, 29443,29398,29353,29308,29263,29218,29173,29128, 29083,29038,28993,28948,28903,28858,28812,28767, 28722,28677,28632,28586,28541,28496,28451,28405, 28360,28315,28269,28224,28179,28133,28088,28042, 27997,27952,27906,27861,27815,27770,27724,27678, 27633,27587,27542,27496,27450,27405,27359,27313, 27268,27222,27176,27131,27085,27039,26993,26947, 26902,26856,26810,26764,26718,26672,26626,26580, 26534,26488,26442,26396,26350,26304,26258,26212, 26166,26120,26074,26028,25982,25936,25889,25843, 25797,25751,25705,25658,25612,25566,25520,25473, 25427,25381,25334,25288,25241,25195,25149,25102, 25056,25009,24963,24916,24870,24823,24777,24730, 24684,24637,24591,24544,24497,24451,24404,24357, 24311,24264,24217,24171,24124,24077,24030,23984, 23937,23890,23843,23796,23750,23703,23656,23609, 23562,23515,23468,23421,23374,23327,23280,23233, 23186,23139,23092,23045,22998,22951,22904,22857, 22810,22763,22716,22668,22621,22574,22527,22480, 22433,22385,22338,22291,22243,22196,22149,22102, 22054,22007,21960,21912,21865,21817,21770,21723, 21675,21628,21580,21533,21485,21438,21390,21343, 21295,21248,21200,21153,21105,21057,21010,20962, 20915,20867,20819,20772,20724,20676,20629,20581, 20533,20485,20438,20390,20342,20294,20246,20199, 20151,20103,20055,20007,19959,19912,19864,19816, 19768,19720,19672,19624,19576,19528,19480,19432, 19384,19336,19288,19240,19192,19144,19096,19048, 19000,18951,18903,18855,18807,18759,18711,18663, 18614,18566,18518,18470,18421,18373,18325,18277, 18228,18180,18132,18084,18035,17987,17939,17890, 17842,17793,17745,17697,17648,17600,17551,17503, 17455,17406,17358,17309,17261,17212,17164,17115, 17067,17018,16970,16921,16872,16824,16775,16727, 16678,16629,16581,16532,16484,16435,16386,16338, 16289,16240,16191,16143,16094,16045,15997,15948, 15899,15850,15802,15753,15704,15655,15606,15557, 15509,15460,15411,15362,15313,15264,15215,15167, 15118,15069,15020,14971,14922,14873,14824,14775, 14726,14677,14628,14579,14530,14481,14432,14383, 14334,14285,14236,14187,14138,14089,14040,13990, 13941,13892,13843,13794,13745,13696,13646,13597, 13548,13499,13450,13401,13351,13302,13253,13204, 13154,13105,13056,13007,12957,12908,12859,12810, 12760,12711,12662,12612,12563,12514,12464,12415, 12366,12316,12267,12218,12168,12119,12069,12020, 11970,11921,11872,11822,11773,11723,11674,11624, 11575,11525,11476,11426,11377,11327,11278,11228, 11179,11129,11080,11030,10981,10931,10882,10832, 10782,10733,10683,10634,10584,10534,10485,10435, 10386,10336,10286,10237,10187,10137,10088,10038, 9988,9939,9889,9839,9790,9740,9690,9640, 9591,9541,9491,9442,9392,9342,9292,9243, 9193,9143,9093,9043,8994,8944,8894,8844, 8794,8745,8695,8645,8595,8545,8496,8446, 8396,8346,8296,8246,8196,8147,8097,8047, 7997,7947,7897,7847,7797,7747,7697,7648, 7598,7548,7498,7448,7398,7348,7298,7248, 7198,7148,7098,7048,6998,6948,6898,6848, 6798,6748,6698,6648,6598,6548,6498,6448, 6398,6348,6298,6248,6198,6148,6098,6048, 5998,5948,5898,5848,5798,5748,5697,5647, 5597,5547,5497,5447,5397,5347,5297,5247, 5197,5146,5096,5046,4996,4946,4896,4846, 4796,4745,4695,4645,4595,4545,4495,4445, 4394,4344,4294,4244,4194,4144,4093,4043, 3993,3943,3893,3843,3792,3742,3692,3642, 3592,3541,3491,3441,3391,3341,3291,3240, 3190,3140,3090,3039,2989,2939,2889,2839, 2788,2738,2688,2638,2587,2537,2487,2437, 2387,2336,2286,2236,2186,2135,2085,2035, 1985,1934,1884,1834,1784,1733,1683,1633, 1583,1532,1482,1432,1382,1331,1281,1231, 1181,1130,1080,1030,980,929,879,829, 779,728,678,628,578,527,477,427, 376,326,276,226,175,125,75,25, -25,-75,-125,-175,-226,-276,-326,-376, -427,-477,-527,-578,-628,-678,-728,-779, -829,-879,-929,-980,-1030,-1080,-1130,-1181, -1231,-1281,-1331,-1382,-1432,-1482,-1532,-1583, -1633,-1683,-1733,-1784,-1834,-1884,-1934,-1985, -2035,-2085,-2135,-2186,-2236,-2286,-2336,-2387, -2437,-2487,-2537,-2588,-2638,-2688,-2738,-2788, -2839,-2889,-2939,-2989,-3039,-3090,-3140,-3190, -3240,-3291,-3341,-3391,-3441,-3491,-3541,-3592, -3642,-3692,-3742,-3792,-3843,-3893,-3943,-3993, -4043,-4093,-4144,-4194,-4244,-4294,-4344,-4394, -4445,-4495,-4545,-4595,-4645,-4695,-4745,-4796, -4846,-4896,-4946,-4996,-5046,-5096,-5146,-5197, -5247,-5297,-5347,-5397,-5447,-5497,-5547,-5597, -5647,-5697,-5748,-5798,-5848,-5898,-5948,-5998, -6048,-6098,-6148,-6198,-6248,-6298,-6348,-6398, -6448,-6498,-6548,-6598,-6648,-6698,-6748,-6798, -6848,-6898,-6948,-6998,-7048,-7098,-7148,-7198, -7248,-7298,-7348,-7398,-7448,-7498,-7548,-7598, -7648,-7697,-7747,-7797,-7847,-7897,-7947,-7997, -8047,-8097,-8147,-8196,-8246,-8296,-8346,-8396, -8446,-8496,-8545,-8595,-8645,-8695,-8745,-8794, -8844,-8894,-8944,-8994,-9043,-9093,-9143,-9193, -9243,-9292,-9342,-9392,-9442,-9491,-9541,-9591, -9640,-9690,-9740,-9790,-9839,-9889,-9939,-9988, -10038,-10088,-10137,-10187,-10237,-10286,-10336,-10386, -10435,-10485,-10534,-10584,-10634,-10683,-10733,-10782, -10832,-10882,-10931,-10981,-11030,-11080,-11129,-11179, -11228,-11278,-11327,-11377,-11426,-11476,-11525,-11575, -11624,-11674,-11723,-11773,-11822,-11872,-11921,-11970, -12020,-12069,-12119,-12168,-12218,-12267,-12316,-12366, -12415,-12464,-12514,-12563,-12612,-12662,-12711,-12760, -12810,-12859,-12908,-12957,-13007,-13056,-13105,-13154, -13204,-13253,-13302,-13351,-13401,-13450,-13499,-13548, -13597,-13647,-13696,-13745,-13794,-13843,-13892,-13941, -13990,-14040,-14089,-14138,-14187,-14236,-14285,-14334, -14383,-14432,-14481,-14530,-14579,-14628,-14677,-14726, -14775,-14824,-14873,-14922,-14971,-15020,-15069,-15118, -15167,-15215,-15264,-15313,-15362,-15411,-15460,-15509, -15557,-15606,-15655,-15704,-15753,-15802,-15850,-15899, -15948,-15997,-16045,-16094,-16143,-16191,-16240,-16289, -16338,-16386,-16435,-16484,-16532,-16581,-16629,-16678, -16727,-16775,-16824,-16872,-16921,-16970,-17018,-17067, -17115,-17164,-17212,-17261,-17309,-17358,-17406,-17455, -17503,-17551,-17600,-17648,-17697,-17745,-17793,-17842, -17890,-17939,-17987,-18035,-18084,-18132,-18180,-18228, -18277,-18325,-18373,-18421,-18470,-18518,-18566,-18614, -18663,-18711,-18759,-18807,-18855,-18903,-18951,-19000, -19048,-19096,-19144,-19192,-19240,-19288,-19336,-19384, -19432,-19480,-19528,-19576,-19624,-19672,-19720,-19768, -19816,-19864,-19912,-19959,-20007,-20055,-20103,-20151, -20199,-20246,-20294,-20342,-20390,-20438,-20485,-20533, -20581,-20629,-20676,-20724,-20772,-20819,-20867,-20915, -20962,-21010,-21057,-21105,-21153,-21200,-21248,-21295, -21343,-21390,-21438,-21485,-21533,-21580,-21628,-21675, -21723,-21770,-21817,-21865,-21912,-21960,-22007,-22054, -22102,-22149,-22196,-22243,-22291,-22338,-22385,-22433, -22480,-22527,-22574,-22621,-22668,-22716,-22763,-22810, -22857,-22904,-22951,-22998,-23045,-23092,-23139,-23186, -23233,-23280,-23327,-23374,-23421,-23468,-23515,-23562, -23609,-23656,-23703,-23750,-23796,-23843,-23890,-23937, -23984,-24030,-24077,-24124,-24171,-24217,-24264,-24311, -24357,-24404,-24451,-24497,-24544,-24591,-24637,-24684, -24730,-24777,-24823,-24870,-24916,-24963,-25009,-25056, -25102,-25149,-25195,-25241,-25288,-25334,-25381,-25427, -25473,-25520,-25566,-25612,-25658,-25705,-25751,-25797, -25843,-25889,-25936,-25982,-26028,-26074,-26120,-26166, -26212,-26258,-26304,-26350,-26396,-26442,-26488,-26534, -26580,-26626,-26672,-26718,-26764,-26810,-26856,-26902, -26947,-26993,-27039,-27085,-27131,-27176,-27222,-27268, -27313,-27359,-27405,-27450,-27496,-27542,-27587,-27633, -27678,-27724,-27770,-27815,-27861,-27906,-27952,-27997, -28042,-28088,-28133,-28179,-28224,-28269,-28315,-28360, -28405,-28451,-28496,-28541,-28586,-28632,-28677,-28722, -28767,-28812,-28858,-28903,-28948,-28993,-29038,-29083, -29128,-29173,-29218,-29263,-29308,-29353,-29398,-29443, -29488,-29533,-29577,-29622,-29667,-29712,-29757,-29801, -29846,-29891,-29936,-29980,-30025,-30070,-30114,-30159, -30204,-30248,-30293,-30337,-30382,-30426,-30471,-30515, -30560,-30604,-30649,-30693,-30738,-30782,-30826,-30871, -30915,-30959,-31004,-31048,-31092,-31136,-31181,-31225, -31269,-31313,-31357,-31402,-31446,-31490,-31534,-31578, -31622,-31666,-31710,-31754,-31798,-31842,-31886,-31930, -31974,-32017,-32061,-32105,-32149,-32193,-32236,-32280, -32324,-32368,-32411,-32455,-32499,-32542,-32586,-32630, -32673,-32717,-32760,-32804,-32847,-32891,-32934,-32978, -33021,-33065,-33108,-33151,-33195,-33238,-33281,-33325, -33368,-33411,-33454,-33498,-33541,-33584,-33627,-33670, -33713,-33756,-33799,-33843,-33886,-33929,-33972,-34015, -34057,-34100,-34143,-34186,-34229,-34272,-34315,-34358, -34400,-34443,-34486,-34529,-34571,-34614,-34657,-34699, -34742,-34785,-34827,-34870,-34912,-34955,-34997,-35040, -35082,-35125,-35167,-35210,-35252,-35294,-35337,-35379, -35421,-35464,-35506,-35548,-35590,-35633,-35675,-35717, -35759,-35801,-35843,-35885,-35927,-35969,-36011,-36053, -36095,-36137,-36179,-36221,-36263,-36305,-36347,-36388, -36430,-36472,-36514,-36555,-36597,-36639,-36681,-36722, -36764,-36805,-36847,-36889,-36930,-36972,-37013,-37055, -37096,-37137,-37179,-37220,-37262,-37303,-37344,-37386, -37427,-37468,-37509,-37551,-37592,-37633,-37674,-37715, -37756,-37797,-37838,-37879,-37920,-37961,-38002,-38043, -38084,-38125,-38166,-38207,-38248,-38288,-38329,-38370, -38411,-38451,-38492,-38533,-38573,-38614,-38655,-38695, -38736,-38776,-38817,-38857,-38898,-38938,-38979,-39019, -39059,-39100,-39140,-39180,-39221,-39261,-39301,-39341, -39382,-39422,-39462,-39502,-39542,-39582,-39622,-39662, -39702,-39742,-39782,-39822,-39862,-39902,-39942,-39982, -40021,-40061,-40101,-40141,-40180,-40220,-40260,-40299, -40339,-40379,-40418,-40458,-40497,-40537,-40576,-40616, -40655,-40695,-40734,-40773,-40813,-40852,-40891,-40931, -40970,-41009,-41048,-41087,-41127,-41166,-41205,-41244, -41283,-41322,-41361,-41400,-41439,-41478,-41517,-41556, -41595,-41633,-41672,-41711,-41750,-41788,-41827,-41866, -41904,-41943,-41982,-42020,-42059,-42097,-42136,-42174, -42213,-42251,-42290,-42328,-42366,-42405,-42443,-42481, -42520,-42558,-42596,-42634,-42672,-42711,-42749,-42787, -42825,-42863,-42901,-42939,-42977,-43015,-43053,-43091, -43128,-43166,-43204,-43242,-43280,-43317,-43355,-43393, -43430,-43468,-43506,-43543,-43581,-43618,-43656,-43693, -43731,-43768,-43806,-43843,-43880,-43918,-43955,-43992, -44029,-44067,-44104,-44141,-44178,-44215,-44252,-44289, -44326,-44363,-44400,-44437,-44474,-44511,-44548,-44585, -44622,-44659,-44695,-44732,-44769,-44806,-44842,-44879, -44915,-44952,-44989,-45025,-45062,-45098,-45135,-45171, -45207,-45244,-45280,-45316,-45353,-45389,-45425,-45462, -45498,-45534,-45570,-45606,-45642,-45678,-45714,-45750, -45786,-45822,-45858,-45894,-45930,-45966,-46002,-46037, -46073,-46109,-46145,-46180,-46216,-46252,-46287,-46323, -46358,-46394,-46429,-46465,-46500,-46536,-46571,-46606, -46642,-46677,-46712,-46747,-46783,-46818,-46853,-46888, -46923,-46958,-46993,-47028,-47063,-47098,-47133,-47168, -47203,-47238,-47273,-47308,-47342,-47377,-47412,-47446, -47481,-47516,-47550,-47585,-47619,-47654,-47688,-47723, -47757,-47792,-47826,-47860,-47895,-47929,-47963,-47998, -48032,-48066,-48100,-48134,-48168,-48202,-48236,-48271, -48304,-48338,-48372,-48406,-48440,-48474,-48508,-48542, -48575,-48609,-48643,-48676,-48710,-48744,-48777,-48811, -48844,-48878,-48911,-48945,-48978,-49012,-49045,-49078, -49112,-49145,-49178,-49211,-49244,-49278,-49311,-49344, -49377,-49410,-49443,-49476,-49509,-49542,-49575,-49608, -49640,-49673,-49706,-49739,-49771,-49804,-49837,-49869, -49902,-49935,-49967,-50000,-50032,-50065,-50097,-50129, -50162,-50194,-50226,-50259,-50291,-50323,-50355,-50387, -50420,-50452,-50484,-50516,-50548,-50580,-50612,-50644, -50675,-50707,-50739,-50771,-50803,-50834,-50866,-50898, -50929,-50961,-50993,-51024,-51056,-51087,-51119,-51150, -51182,-51213,-51244,-51276,-51307,-51338,-51369,-51401, -51432,-51463,-51494,-51525,-51556,-51587,-51618,-51649, -51680,-51711,-51742,-51773,-51803,-51834,-51865,-51896, -51926,-51957,-51988,-52018,-52049,-52079,-52110,-52140, -52171,-52201,-52231,-52262,-52292,-52322,-52353,-52383, -52413,-52443,-52473,-52503,-52534,-52564,-52594,-52624, -52653,-52683,-52713,-52743,-52773,-52803,-52832,-52862, -52892,-52922,-52951,-52981,-53010,-53040,-53069,-53099, -53128,-53158,-53187,-53216,-53246,-53275,-53304,-53334, -53363,-53392,-53421,-53450,-53479,-53508,-53537,-53566, -53595,-53624,-53653,-53682,-53711,-53739,-53768,-53797, -53826,-53854,-53883,-53911,-53940,-53969,-53997,-54026, -54054,-54082,-54111,-54139,-54167,-54196,-54224,-54252, -54280,-54308,-54337,-54365,-54393,-54421,-54449,-54477, -54505,-54533,-54560,-54588,-54616,-54644,-54672,-54699, -54727,-54755,-54782,-54810,-54837,-54865,-54892,-54920, -54947,-54974,-55002,-55029,-55056,-55084,-55111,-55138, -55165,-55192,-55219,-55246,-55274,-55300,-55327,-55354, -55381,-55408,-55435,-55462,-55489,-55515,-55542,-55569, -55595,-55622,-55648,-55675,-55701,-55728,-55754,-55781, -55807,-55833,-55860,-55886,-55912,-55938,-55965,-55991, -56017,-56043,-56069,-56095,-56121,-56147,-56173,-56199, -56225,-56250,-56276,-56302,-56328,-56353,-56379,-56404, -56430,-56456,-56481,-56507,-56532,-56557,-56583,-56608, -56633,-56659,-56684,-56709,-56734,-56760,-56785,-56810, -56835,-56860,-56885,-56910,-56935,-56959,-56984,-57009, -57034,-57059,-57083,-57108,-57133,-57157,-57182,-57206, -57231,-57255,-57280,-57304,-57329,-57353,-57377,-57402, -57426,-57450,-57474,-57498,-57522,-57546,-57570,-57594, -57618,-57642,-57666,-57690,-57714,-57738,-57762,-57785, -57809,-57833,-57856,-57880,-57903,-57927,-57950,-57974, -57997,-58021,-58044,-58067,-58091,-58114,-58137,-58160, -58183,-58207,-58230,-58253,-58276,-58299,-58322,-58345, -58367,-58390,-58413,-58436,-58459,-58481,-58504,-58527, -58549,-58572,-58594,-58617,-58639,-58662,-58684,-58706, -58729,-58751,-58773,-58795,-58818,-58840,-58862,-58884, -58906,-58928,-58950,-58972,-58994,-59016,-59038,-59059, -59081,-59103,-59125,-59146,-59168,-59190,-59211,-59233, -59254,-59276,-59297,-59318,-59340,-59361,-59382,-59404, -59425,-59446,-59467,-59488,-59509,-59530,-59551,-59572, -59593,-59614,-59635,-59656,-59677,-59697,-59718,-59739, -59759,-59780,-59801,-59821,-59842,-59862,-59883,-59903, -59923,-59944,-59964,-59984,-60004,-60025,-60045,-60065, -60085,-60105,-60125,-60145,-60165,-60185,-60205,-60225, -60244,-60264,-60284,-60304,-60323,-60343,-60363,-60382, -60402,-60421,-60441,-60460,-60479,-60499,-60518,-60537, -60556,-60576,-60595,-60614,-60633,-60652,-60671,-60690, -60709,-60728,-60747,-60766,-60785,-60803,-60822,-60841, -60859,-60878,-60897,-60915,-60934,-60952,-60971,-60989, -61007,-61026,-61044,-61062,-61081,-61099,-61117,-61135, -61153,-61171,-61189,-61207,-61225,-61243,-61261,-61279, -61297,-61314,-61332,-61350,-61367,-61385,-61403,-61420, -61438,-61455,-61473,-61490,-61507,-61525,-61542,-61559, -61577,-61594,-61611,-61628,-61645,-61662,-61679,-61696, -61713,-61730,-61747,-61764,-61780,-61797,-61814,-61831, -61847,-61864,-61880,-61897,-61913,-61930,-61946,-61963, -61979,-61995,-62012,-62028,-62044,-62060,-62076,-62092, -62108,-62125,-62141,-62156,-62172,-62188,-62204,-62220, -62236,-62251,-62267,-62283,-62298,-62314,-62329,-62345, -62360,-62376,-62391,-62407,-62422,-62437,-62453,-62468, -62483,-62498,-62513,-62528,-62543,-62558,-62573,-62588, -62603,-62618,-62633,-62648,-62662,-62677,-62692,-62706, -62721,-62735,-62750,-62764,-62779,-62793,-62808,-62822, -62836,-62850,-62865,-62879,-62893,-62907,-62921,-62935, -62949,-62963,-62977,-62991,-63005,-63019,-63032,-63046, -63060,-63074,-63087,-63101,-63114,-63128,-63141,-63155, -63168,-63182,-63195,-63208,-63221,-63235,-63248,-63261, -63274,-63287,-63300,-63313,-63326,-63339,-63352,-63365, -63378,-63390,-63403,-63416,-63429,-63441,-63454,-63466, -63479,-63491,-63504,-63516,-63528,-63541,-63553,-63565, -63578,-63590,-63602,-63614,-63626,-63638,-63650,-63662, -63674,-63686,-63698,-63709,-63721,-63733,-63745,-63756, -63768,-63779,-63791,-63803,-63814,-63825,-63837,-63848, -63859,-63871,-63882,-63893,-63904,-63915,-63927,-63938, -63949,-63960,-63971,-63981,-63992,-64003,-64014,-64025, -64035,-64046,-64057,-64067,-64078,-64088,-64099,-64109, -64120,-64130,-64140,-64151,-64161,-64171,-64181,-64192, -64202,-64212,-64222,-64232,-64242,-64252,-64261,-64271, -64281,-64291,-64301,-64310,-64320,-64330,-64339,-64349, -64358,-64368,-64377,-64387,-64396,-64405,-64414,-64424, -64433,-64442,-64451,-64460,-64469,-64478,-64487,-64496, -64505,-64514,-64523,-64532,-64540,-64549,-64558,-64566, -64575,-64584,-64592,-64601,-64609,-64617,-64626,-64634, -64642,-64651,-64659,-64667,-64675,-64683,-64691,-64699, -64707,-64715,-64723,-64731,-64739,-64747,-64754,-64762, -64770,-64777,-64785,-64793,-64800,-64808,-64815,-64822, -64830,-64837,-64844,-64852,-64859,-64866,-64873,-64880, -64887,-64895,-64902,-64908,-64915,-64922,-64929,-64936, -64943,-64949,-64956,-64963,-64969,-64976,-64982,-64989, -64995,-65002,-65008,-65015,-65021,-65027,-65033,-65040, -65046,-65052,-65058,-65064,-65070,-65076,-65082,-65088, -65094,-65099,-65105,-65111,-65117,-65122,-65128,-65133, -65139,-65144,-65150,-65155,-65161,-65166,-65171,-65177, -65182,-65187,-65192,-65197,-65202,-65207,-65212,-65217, -65222,-65227,-65232,-65237,-65242,-65246,-65251,-65256, -65260,-65265,-65270,-65274,-65279,-65283,-65287,-65292, -65296,-65300,-65305,-65309,-65313,-65317,-65321,-65325, -65329,-65333,-65337,-65341,-65345,-65349,-65352,-65356, -65360,-65363,-65367,-65371,-65374,-65378,-65381,-65385, -65388,-65391,-65395,-65398,-65401,-65404,-65408,-65411, -65414,-65417,-65420,-65423,-65426,-65429,-65431,-65434, -65437,-65440,-65442,-65445,-65448,-65450,-65453,-65455, -65458,-65460,-65463,-65465,-65467,-65470,-65472,-65474, -65476,-65478,-65480,-65482,-65484,-65486,-65488,-65490, -65492,-65494,-65496,-65497,-65499,-65501,-65502,-65504, -65505,-65507,-65508,-65510,-65511,-65513,-65514,-65515, -65516,-65518,-65519,-65520,-65521,-65522,-65523,-65524, -65525,-65526,-65527,-65527,-65528,-65529,-65530,-65530, -65531,-65531,-65532,-65532,-65533,-65533,-65534,-65534, -65534,-65535,-65535,-65535,-65535,-65535,-65535,-65535, -65535,-65535,-65535,-65535,-65535,-65535,-65535,-65534, -65534,-65534,-65533,-65533,-65532,-65532,-65531,-65531, -65530,-65530,-65529,-65528,-65527,-65527,-65526,-65525, -65524,-65523,-65522,-65521,-65520,-65519,-65518,-65516, -65515,-65514,-65513,-65511,-65510,-65508,-65507,-65505, -65504,-65502,-65501,-65499,-65497,-65496,-65494,-65492, -65490,-65488,-65486,-65484,-65482,-65480,-65478,-65476, -65474,-65472,-65470,-65467,-65465,-65463,-65460,-65458, -65455,-65453,-65450,-65448,-65445,-65442,-65440,-65437, -65434,-65431,-65429,-65426,-65423,-65420,-65417,-65414, -65411,-65408,-65404,-65401,-65398,-65395,-65391,-65388, -65385,-65381,-65378,-65374,-65371,-65367,-65363,-65360, -65356,-65352,-65349,-65345,-65341,-65337,-65333,-65329, -65325,-65321,-65317,-65313,-65309,-65305,-65300,-65296, -65292,-65287,-65283,-65279,-65274,-65270,-65265,-65260, -65256,-65251,-65246,-65242,-65237,-65232,-65227,-65222, -65217,-65212,-65207,-65202,-65197,-65192,-65187,-65182, -65177,-65171,-65166,-65161,-65155,-65150,-65144,-65139, -65133,-65128,-65122,-65117,-65111,-65105,-65099,-65094, -65088,-65082,-65076,-65070,-65064,-65058,-65052,-65046, -65040,-65033,-65027,-65021,-65015,-65008,-65002,-64995, -64989,-64982,-64976,-64969,-64963,-64956,-64949,-64943, -64936,-64929,-64922,-64915,-64908,-64902,-64895,-64887, -64880,-64873,-64866,-64859,-64852,-64844,-64837,-64830, -64822,-64815,-64808,-64800,-64793,-64785,-64777,-64770, -64762,-64754,-64747,-64739,-64731,-64723,-64715,-64707, -64699,-64691,-64683,-64675,-64667,-64659,-64651,-64642, -64634,-64626,-64617,-64609,-64601,-64592,-64584,-64575, -64566,-64558,-64549,-64540,-64532,-64523,-64514,-64505, -64496,-64487,-64478,-64469,-64460,-64451,-64442,-64433, -64424,-64414,-64405,-64396,-64387,-64377,-64368,-64358, -64349,-64339,-64330,-64320,-64310,-64301,-64291,-64281, -64271,-64261,-64252,-64242,-64232,-64222,-64212,-64202, -64192,-64181,-64171,-64161,-64151,-64140,-64130,-64120, -64109,-64099,-64088,-64078,-64067,-64057,-64046,-64035, -64025,-64014,-64003,-63992,-63981,-63971,-63960,-63949, -63938,-63927,-63915,-63904,-63893,-63882,-63871,-63859, -63848,-63837,-63825,-63814,-63803,-63791,-63779,-63768, -63756,-63745,-63733,-63721,-63709,-63698,-63686,-63674, -63662,-63650,-63638,-63626,-63614,-63602,-63590,-63578, -63565,-63553,-63541,-63528,-63516,-63504,-63491,-63479, -63466,-63454,-63441,-63429,-63416,-63403,-63390,-63378, -63365,-63352,-63339,-63326,-63313,-63300,-63287,-63274, -63261,-63248,-63235,-63221,-63208,-63195,-63182,-63168, -63155,-63141,-63128,-63114,-63101,-63087,-63074,-63060, -63046,-63032,-63019,-63005,-62991,-62977,-62963,-62949, -62935,-62921,-62907,-62893,-62879,-62865,-62850,-62836, -62822,-62808,-62793,-62779,-62764,-62750,-62735,-62721, -62706,-62692,-62677,-62662,-62648,-62633,-62618,-62603, -62588,-62573,-62558,-62543,-62528,-62513,-62498,-62483, -62468,-62453,-62437,-62422,-62407,-62391,-62376,-62360, -62345,-62329,-62314,-62298,-62283,-62267,-62251,-62236, -62220,-62204,-62188,-62172,-62156,-62141,-62125,-62108, -62092,-62076,-62060,-62044,-62028,-62012,-61995,-61979, -61963,-61946,-61930,-61913,-61897,-61880,-61864,-61847, -61831,-61814,-61797,-61780,-61764,-61747,-61730,-61713, -61696,-61679,-61662,-61645,-61628,-61611,-61594,-61577, -61559,-61542,-61525,-61507,-61490,-61473,-61455,-61438, -61420,-61403,-61385,-61367,-61350,-61332,-61314,-61297, -61279,-61261,-61243,-61225,-61207,-61189,-61171,-61153, -61135,-61117,-61099,-61081,-61062,-61044,-61026,-61007, -60989,-60971,-60952,-60934,-60915,-60897,-60878,-60859, -60841,-60822,-60803,-60785,-60766,-60747,-60728,-60709, -60690,-60671,-60652,-60633,-60614,-60595,-60576,-60556, -60537,-60518,-60499,-60479,-60460,-60441,-60421,-60402, -60382,-60363,-60343,-60323,-60304,-60284,-60264,-60244, -60225,-60205,-60185,-60165,-60145,-60125,-60105,-60085, -60065,-60045,-60025,-60004,-59984,-59964,-59944,-59923, -59903,-59883,-59862,-59842,-59821,-59801,-59780,-59759, -59739,-59718,-59697,-59677,-59656,-59635,-59614,-59593, -59572,-59551,-59530,-59509,-59488,-59467,-59446,-59425, -59404,-59382,-59361,-59340,-59318,-59297,-59276,-59254, -59233,-59211,-59189,-59168,-59146,-59125,-59103,-59081, -59059,-59038,-59016,-58994,-58972,-58950,-58928,-58906, -58884,-58862,-58840,-58818,-58795,-58773,-58751,-58729, -58706,-58684,-58662,-58639,-58617,-58594,-58572,-58549, -58527,-58504,-58481,-58459,-58436,-58413,-58390,-58367, -58345,-58322,-58299,-58276,-58253,-58230,-58207,-58183, -58160,-58137,-58114,-58091,-58067,-58044,-58021,-57997, -57974,-57950,-57927,-57903,-57880,-57856,-57833,-57809, -57785,-57762,-57738,-57714,-57690,-57666,-57642,-57618, -57594,-57570,-57546,-57522,-57498,-57474,-57450,-57426, -57402,-57377,-57353,-57329,-57304,-57280,-57255,-57231, -57206,-57182,-57157,-57133,-57108,-57083,-57059,-57034, -57009,-56984,-56959,-56935,-56910,-56885,-56860,-56835, -56810,-56785,-56760,-56734,-56709,-56684,-56659,-56633, -56608,-56583,-56557,-56532,-56507,-56481,-56456,-56430, -56404,-56379,-56353,-56328,-56302,-56276,-56250,-56225, -56199,-56173,-56147,-56121,-56095,-56069,-56043,-56017, -55991,-55965,-55938,-55912,-55886,-55860,-55833,-55807, -55781,-55754,-55728,-55701,-55675,-55648,-55622,-55595, -55569,-55542,-55515,-55489,-55462,-55435,-55408,-55381, -55354,-55327,-55300,-55274,-55246,-55219,-55192,-55165, -55138,-55111,-55084,-55056,-55029,-55002,-54974,-54947, -54920,-54892,-54865,-54837,-54810,-54782,-54755,-54727, -54699,-54672,-54644,-54616,-54588,-54560,-54533,-54505, -54477,-54449,-54421,-54393,-54365,-54337,-54308,-54280, -54252,-54224,-54196,-54167,-54139,-54111,-54082,-54054, -54026,-53997,-53969,-53940,-53911,-53883,-53854,-53826, -53797,-53768,-53739,-53711,-53682,-53653,-53624,-53595, -53566,-53537,-53508,-53479,-53450,-53421,-53392,-53363, -53334,-53304,-53275,-53246,-53216,-53187,-53158,-53128, -53099,-53069,-53040,-53010,-52981,-52951,-52922,-52892, -52862,-52832,-52803,-52773,-52743,-52713,-52683,-52653, -52624,-52594,-52564,-52534,-52503,-52473,-52443,-52413, -52383,-52353,-52322,-52292,-52262,-52231,-52201,-52171, -52140,-52110,-52079,-52049,-52018,-51988,-51957,-51926, -51896,-51865,-51834,-51803,-51773,-51742,-51711,-51680, -51649,-51618,-51587,-51556,-51525,-51494,-51463,-51432, -51401,-51369,-51338,-51307,-51276,-51244,-51213,-51182, -51150,-51119,-51087,-51056,-51024,-50993,-50961,-50929, -50898,-50866,-50834,-50803,-50771,-50739,-50707,-50675, -50644,-50612,-50580,-50548,-50516,-50484,-50452,-50420, -50387,-50355,-50323,-50291,-50259,-50226,-50194,-50162, -50129,-50097,-50065,-50032,-50000,-49967,-49935,-49902, -49869,-49837,-49804,-49771,-49739,-49706,-49673,-49640, -49608,-49575,-49542,-49509,-49476,-49443,-49410,-49377, -49344,-49311,-49278,-49244,-49211,-49178,-49145,-49112, -49078,-49045,-49012,-48978,-48945,-48911,-48878,-48844, -48811,-48777,-48744,-48710,-48676,-48643,-48609,-48575, -48542,-48508,-48474,-48440,-48406,-48372,-48338,-48305, -48271,-48237,-48202,-48168,-48134,-48100,-48066,-48032, -47998,-47963,-47929,-47895,-47860,-47826,-47792,-47757, -47723,-47688,-47654,-47619,-47585,-47550,-47516,-47481, -47446,-47412,-47377,-47342,-47307,-47273,-47238,-47203, -47168,-47133,-47098,-47063,-47028,-46993,-46958,-46923, -46888,-46853,-46818,-46783,-46747,-46712,-46677,-46642, -46606,-46571,-46536,-46500,-46465,-46429,-46394,-46358, -46323,-46287,-46251,-46216,-46180,-46145,-46109,-46073, -46037,-46002,-45966,-45930,-45894,-45858,-45822,-45786, -45750,-45714,-45678,-45642,-45606,-45570,-45534,-45498, -45462,-45425,-45389,-45353,-45316,-45280,-45244,-45207, -45171,-45135,-45098,-45062,-45025,-44989,-44952,-44915, -44879,-44842,-44806,-44769,-44732,-44695,-44659,-44622, -44585,-44548,-44511,-44474,-44437,-44400,-44363,-44326, -44289,-44252,-44215,-44178,-44141,-44104,-44067,-44029, -43992,-43955,-43918,-43880,-43843,-43806,-43768,-43731, -43693,-43656,-43618,-43581,-43543,-43506,-43468,-43430, -43393,-43355,-43317,-43280,-43242,-43204,-43166,-43128, -43091,-43053,-43015,-42977,-42939,-42901,-42863,-42825, -42787,-42749,-42711,-42672,-42634,-42596,-42558,-42520, -42481,-42443,-42405,-42366,-42328,-42290,-42251,-42213, -42174,-42136,-42097,-42059,-42020,-41982,-41943,-41904, -41866,-41827,-41788,-41750,-41711,-41672,-41633,-41595, -41556,-41517,-41478,-41439,-41400,-41361,-41322,-41283, -41244,-41205,-41166,-41127,-41087,-41048,-41009,-40970, -40931,-40891,-40852,-40813,-40773,-40734,-40695,-40655, -40616,-40576,-40537,-40497,-40458,-40418,-40379,-40339, -40299,-40260,-40220,-40180,-40141,-40101,-40061,-40021, -39982,-39942,-39902,-39862,-39822,-39782,-39742,-39702, -39662,-39622,-39582,-39542,-39502,-39462,-39422,-39382, -39341,-39301,-39261,-39221,-39180,-39140,-39100,-39059, -39019,-38979,-38938,-38898,-38857,-38817,-38776,-38736, -38695,-38655,-38614,-38573,-38533,-38492,-38451,-38411, -38370,-38329,-38288,-38248,-38207,-38166,-38125,-38084, -38043,-38002,-37961,-37920,-37879,-37838,-37797,-37756, -37715,-37674,-37633,-37592,-37550,-37509,-37468,-37427, -37386,-37344,-37303,-37262,-37220,-37179,-37137,-37096, -37055,-37013,-36972,-36930,-36889,-36847,-36805,-36764, -36722,-36681,-36639,-36597,-36556,-36514,-36472,-36430, -36388,-36347,-36305,-36263,-36221,-36179,-36137,-36095, -36053,-36011,-35969,-35927,-35885,-35843,-35801,-35759, -35717,-35675,-35633,-35590,-35548,-35506,-35464,-35421, -35379,-35337,-35294,-35252,-35210,-35167,-35125,-35082, -35040,-34997,-34955,-34912,-34870,-34827,-34785,-34742, -34699,-34657,-34614,-34571,-34529,-34486,-34443,-34400, -34358,-34315,-34272,-34229,-34186,-34143,-34100,-34057, -34015,-33972,-33929,-33886,-33843,-33799,-33756,-33713, -33670,-33627,-33584,-33541,-33498,-33454,-33411,-33368, -33325,-33281,-33238,-33195,-33151,-33108,-33065,-33021, -32978,-32934,-32891,-32847,-32804,-32760,-32717,-32673, -32630,-32586,-32542,-32499,-32455,-32411,-32368,-32324, -32280,-32236,-32193,-32149,-32105,-32061,-32017,-31974, -31930,-31886,-31842,-31798,-31754,-31710,-31666,-31622, -31578,-31534,-31490,-31446,-31402,-31357,-31313,-31269, -31225,-31181,-31136,-31092,-31048,-31004,-30959,-30915, -30871,-30826,-30782,-30738,-30693,-30649,-30604,-30560, -30515,-30471,-30426,-30382,-30337,-30293,-30248,-30204, -30159,-30114,-30070,-30025,-29980,-29936,-29891,-29846, -29801,-29757,-29712,-29667,-29622,-29577,-29533,-29488, -29443,-29398,-29353,-29308,-29263,-29218,-29173,-29128, -29083,-29038,-28993,-28948,-28903,-28858,-28812,-28767, -28722,-28677,-28632,-28586,-28541,-28496,-28451,-28405, -28360,-28315,-28269,-28224,-28179,-28133,-28088,-28042, -27997,-27952,-27906,-27861,-27815,-27770,-27724,-27678, -27633,-27587,-27542,-27496,-27450,-27405,-27359,-27313, -27268,-27222,-27176,-27131,-27085,-27039,-26993,-26947, -26902,-26856,-26810,-26764,-26718,-26672,-26626,-26580, -26534,-26488,-26442,-26396,-26350,-26304,-26258,-26212, -26166,-26120,-26074,-26028,-25982,-25936,-25889,-25843, -25797,-25751,-25705,-25658,-25612,-25566,-25520,-25473, -25427,-25381,-25334,-25288,-25241,-25195,-25149,-25102, -25056,-25009,-24963,-24916,-24870,-24823,-24777,-24730, -24684,-24637,-24591,-24544,-24497,-24451,-24404,-24357, -24311,-24264,-24217,-24171,-24124,-24077,-24030,-23984, -23937,-23890,-23843,-23796,-23750,-23703,-23656,-23609, -23562,-23515,-23468,-23421,-23374,-23327,-23280,-23233, -23186,-23139,-23092,-23045,-22998,-22951,-22904,-22857, -22810,-22763,-22716,-22668,-22621,-22574,-22527,-22480, -22432,-22385,-22338,-22291,-22243,-22196,-22149,-22102, -22054,-22007,-21960,-21912,-21865,-21817,-21770,-21723, -21675,-21628,-21580,-21533,-21485,-21438,-21390,-21343, -21295,-21248,-21200,-21153,-21105,-21057,-21010,-20962, -20915,-20867,-20819,-20772,-20724,-20676,-20629,-20581, -20533,-20485,-20438,-20390,-20342,-20294,-20246,-20199, -20151,-20103,-20055,-20007,-19959,-19912,-19864,-19816, -19768,-19720,-19672,-19624,-19576,-19528,-19480,-19432, -19384,-19336,-19288,-19240,-19192,-19144,-19096,-19048, -19000,-18951,-18903,-18855,-18807,-18759,-18711,-18663, -18614,-18566,-18518,-18470,-18421,-18373,-18325,-18277, -18228,-18180,-18132,-18084,-18035,-17987,-17939,-17890, -17842,-17793,-17745,-17697,-17648,-17600,-17551,-17503, -17455,-17406,-17358,-17309,-17261,-17212,-17164,-17115, -17067,-17018,-16970,-16921,-16872,-16824,-16775,-16727, -16678,-16629,-16581,-16532,-16484,-16435,-16386,-16338, -16289,-16240,-16191,-16143,-16094,-16045,-15997,-15948, -15899,-15850,-15802,-15753,-15704,-15655,-15606,-15557, -15509,-15460,-15411,-15362,-15313,-15264,-15215,-15167, -15118,-15069,-15020,-14971,-14922,-14873,-14824,-14775, -14726,-14677,-14628,-14579,-14530,-14481,-14432,-14383, -14334,-14285,-14236,-14187,-14138,-14089,-14040,-13990, -13941,-13892,-13843,-13794,-13745,-13696,-13647,-13597, -13548,-13499,-13450,-13401,-13351,-13302,-13253,-13204, -13154,-13105,-13056,-13007,-12957,-12908,-12859,-12810, -12760,-12711,-12662,-12612,-12563,-12514,-12464,-12415, -12366,-12316,-12267,-12217,-12168,-12119,-12069,-12020, -11970,-11921,-11872,-11822,-11773,-11723,-11674,-11624, -11575,-11525,-11476,-11426,-11377,-11327,-11278,-11228, -11179,-11129,-11080,-11030,-10981,-10931,-10882,-10832, -10782,-10733,-10683,-10634,-10584,-10534,-10485,-10435, -10386,-10336,-10286,-10237,-10187,-10137,-10088,-10038, -9988,-9939,-9889,-9839,-9790,-9740,-9690,-9640, -9591,-9541,-9491,-9442,-9392,-9342,-9292,-9243, -9193,-9143,-9093,-9043,-8994,-8944,-8894,-8844, -8794,-8745,-8695,-8645,-8595,-8545,-8496,-8446, -8396,-8346,-8296,-8246,-8196,-8147,-8097,-8047, -7997,-7947,-7897,-7847,-7797,-7747,-7697,-7648, -7598,-7548,-7498,-7448,-7398,-7348,-7298,-7248, -7198,-7148,-7098,-7048,-6998,-6948,-6898,-6848, -6798,-6748,-6698,-6648,-6598,-6548,-6498,-6448, -6398,-6348,-6298,-6248,-6198,-6148,-6098,-6048, -5998,-5948,-5898,-5848,-5798,-5747,-5697,-5647, -5597,-5547,-5497,-5447,-5397,-5347,-5297,-5247, -5197,-5146,-5096,-5046,-4996,-4946,-4896,-4846, -4796,-4745,-4695,-4645,-4595,-4545,-4495,-4445, -4394,-4344,-4294,-4244,-4194,-4144,-4093,-4043, -3993,-3943,-3893,-3843,-3792,-3742,-3692,-3642, -3592,-3541,-3491,-3441,-3391,-3341,-3291,-3240, -3190,-3140,-3090,-3039,-2989,-2939,-2889,-2839, -2788,-2738,-2688,-2638,-2588,-2537,-2487,-2437, -2387,-2336,-2286,-2236,-2186,-2135,-2085,-2035, -1985,-1934,-1884,-1834,-1784,-1733,-1683,-1633, -1583,-1532,-1482,-1432,-1382,-1331,-1281,-1231, -1181,-1130,-1080,-1030,-980,-929,-879,-829, -779,-728,-678,-628,-578,-527,-477,-427, -376,-326,-276,-226,-175,-125,-75,-25 }; /* MAES: Java can't use that much direct init data in a single class (finesine alone is well beyond that) * so a first workaround is to generate the data procedurally. * If this proves to cause accuracy problems, an external data file could be used. * Another workaround is to define the int table in its own file and then import or "implement" it. */ // Re-use data, is just PI/2 phase shift. public static final int[] finecosine=new int[FINEANGLES]; /* MAES: Java can't use that much direct init data in a single class so a first workaround * s to generate the data procedurally. * If this proves to cause accuracy problems, an external data file could be used. * Another workaround is to define the int table in its own file and then import * or "implement" it. */ static{ System.arraycopy(finesine, FINEANGLES/4,finecosine, 0, FINEANGLES); } private SineCosine(){ } } package data; public enum mobjtype_t { MT_PLAYER, MT_POSSESSED, MT_SHOTGUY, MT_VILE, MT_FIRE, MT_UNDEAD, MT_TRACER, MT_SMOKE, MT_FATSO, MT_FATSHOT, MT_CHAINGUY, MT_TROOP, MT_SERGEANT, MT_SHADOWS, MT_HEAD, MT_BRUISER, MT_BRUISERSHOT, MT_KNIGHT, MT_SKULL, MT_SPIDER, MT_BABY, MT_CYBORG, MT_PAIN, MT_WOLFSS, MT_KEEN, MT_BOSSBRAIN, MT_BOSSSPIT, MT_BOSSTARGET, MT_SPAWNSHOT, MT_SPAWNFIRE, MT_BARREL, MT_TROOPSHOT, MT_HEADSHOT, MT_ROCKET, MT_PLASMA, MT_BFG, MT_ARACHPLAZ, MT_PUFF, MT_BLOOD, MT_TFOG, MT_IFOG, MT_TELEPORTMAN, MT_EXTRABFG, MT_MISC0, MT_MISC1, MT_MISC2, MT_MISC3, MT_MISC4, MT_MISC5, MT_MISC6, MT_MISC7, MT_MISC8, MT_MISC9, MT_MISC10, MT_MISC11, MT_MISC12, MT_INV, MT_MISC13, MT_INS, MT_MISC14, MT_MISC15, MT_MISC16, MT_MEGA, MT_CLIP, MT_MISC17, MT_MISC18, MT_MISC19, MT_MISC20, MT_MISC21, MT_MISC22, MT_MISC23, MT_MISC24, MT_MISC25, MT_CHAINGUN, MT_MISC26, MT_MISC27, MT_MISC28, MT_SHOTGUN, MT_SUPERSHOTGUN, MT_MISC29, MT_MISC30, MT_MISC31, MT_MISC32, MT_MISC33, MT_MISC34, MT_MISC35, MT_MISC36, MT_MISC37, MT_MISC38, MT_MISC39, MT_MISC40, MT_MISC41, MT_MISC42, MT_MISC43, MT_MISC44, MT_MISC45, MT_MISC46, MT_MISC47, MT_MISC48, MT_MISC49, MT_MISC50, MT_MISC51, MT_MISC52, MT_MISC53, MT_MISC54, MT_MISC55, MT_MISC56, MT_MISC57, MT_MISC58, MT_MISC59, MT_MISC60, MT_MISC61, MT_MISC62, MT_MISC63, MT_MISC64, MT_MISC65, MT_MISC66, MT_MISC67, MT_MISC68, MT_MISC69, MT_MISC70, MT_MISC71, MT_MISC72, MT_MISC73, MT_MISC74, MT_MISC75, MT_MISC76, MT_MISC77, MT_MISC78, MT_MISC79, MT_MISC80, MT_MISC81, MT_MISC82, MT_MISC83, MT_MISC84, MT_MISC85, MT_MISC86, NUMMOBJTYPES }; package data; import p.ActionType1; import p.ActionType2; import p.ActionTypeSS; import doom.think_t; import defines.statenum_t; import static data.Defines.TIC_MUL; public class state_t { public state_t(){ } public state_t(spritenum_t sprite, int frame, int tics, think_t action, statenum_t nextstate, int misc1, int misc2) { this.sprite = sprite; this.frame = frame; this.tics = tics*TIC_MUL; this.action = action; this.nextstate = nextstate; this.misc1 = misc1; this.misc2 = misc2; } public spritenum_t sprite; /** The frame should indicate which one of the frames available in the * available spritenum should be used. This can also be flagged with * 0x8000 indicating bright sprites. */ public int frame; public int tics; //TODO: proper implementation of (*action) // MAES: was actionp_t... which is typedeffed to think_t anyway, // and this is the only place it's invoked explicitly. /** OK...this is the most infamous part of Doom to implement in Java. * We can't have proper "function pointers" in java without either losing a LOT * of speed (through reflection) or cluttering syntax and heap significantly * (callback objects, which also need to be aware of context). * Therefore, I decided to implement an "action dispatcher". * This a * */ public think_t action; public ActionType1 acp1; public ActionType2 acp2; public ActionTypeSS acpss; public statenum_t nextstate; public int misc1, misc2; /** relative index in state array. Needed sometimes. */ public int id; public String toString(){ sb.setLength(0); sb.append(this.getClass().getName()); sb.append(" sprite "); sb.append(this.sprite.name()); sb.append(" frame "); sb.append(this.frame); return sb.toString(); } protected static StringBuilder sb=new StringBuilder(); /*@Override public void read(DoomFile f) throws IOException { this.sprite = spritenum_t.values()[f.readLEInt()]; this.frame = f.readLEInt(); this.tics = f.readLong(); this.action = think_t.values()[f.readInt()]; this.nextstate = statenum_t.values()[f.readInt()]; this.misc1 = f.readInt(); this.misc2 = f.readInt(); } */ } package data; // Emacs style mode select -*- C++ -*- //----------------------------------------------------------------------------- // // $Id: sounds.java,v 1.1 2010/06/30 08:58:51 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. // // $Log: sounds.java,v $ // Revision 1.1 2010/06/30 08:58:51 velktron // Let's see if this stuff will finally commit.... // // // Most stuff is still being worked on. For a good place to start and get an idea of what is being done, I suggest checking out the "testers" package. // // Revision 1.1 2010/06/29 11:07:34 velktron // Release often, release early they say... // // Commiting ALL stuff done so far. A lot of stuff is still broken/incomplete, and there's still mixed C code in there. I suggest you load everything up in Eclpise and see what gives from there. // // A good place to start is the testers/ directory, where you can get an idea of how a few of the implemented stuff works. // // // DESCRIPTION: // Created by a sound utility. // Kept as a sample, DOOM2 sounds. // //----------------------------------------------------------------------------- public class sounds{ // // Information about all the music // public static musicinfo_t[] S_music= { new musicinfo_t(null), new musicinfo_t( "e1m1", 0 ), new musicinfo_t( "e1m2", 0 ), new musicinfo_t( "e1m3", 0 ), new musicinfo_t( "e1m4", 0 ), new musicinfo_t( "e1m5", 0 ), new musicinfo_t( "e1m6", 0 ), new musicinfo_t( "e1m7", 0 ), new musicinfo_t( "e1m8", 0 ), new musicinfo_t( "e1m9", 0 ), new musicinfo_t( "e2m1", 0 ), new musicinfo_t( "e2m2", 0 ), new musicinfo_t( "e2m3", 0 ), new musicinfo_t( "e2m4", 0 ), new musicinfo_t( "e2m5", 0 ), new musicinfo_t( "e2m6", 0 ), new musicinfo_t( "e2m7", 0 ), new musicinfo_t( "e2m8", 0 ), new musicinfo_t( "e2m9", 0 ), new musicinfo_t( "e3m1", 0 ), new musicinfo_t( "e3m2", 0 ), new musicinfo_t( "e3m3", 0 ), new musicinfo_t( "e3m4", 0 ), new musicinfo_t( "e3m5", 0 ), new musicinfo_t( "e3m6", 0 ), new musicinfo_t( "e3m7", 0 ), new musicinfo_t( "e3m8", 0 ), new musicinfo_t( "e3m9", 0 ), new musicinfo_t( "inter", 0 ), new musicinfo_t( "intro", 0 ), new musicinfo_t( "bunny", 0 ), new musicinfo_t( "victor", 0 ), new musicinfo_t( "introa", 0 ), new musicinfo_t( "runnin", 0 ), new musicinfo_t( "stalks", 0 ), new musicinfo_t( "countd", 0 ), new musicinfo_t( "betwee", 0 ), new musicinfo_t( "doom", 0 ), new musicinfo_t( "the_da", 0 ), new musicinfo_t( "shawn", 0 ), new musicinfo_t( "ddtblu", 0 ), new musicinfo_t( "in_cit", 0 ), new musicinfo_t( "dead", 0 ), new musicinfo_t( "stlks2", 0 ), new musicinfo_t( "theda2", 0 ), new musicinfo_t( "doom2", 0 ), new musicinfo_t( "ddtbl2", 0 ), new musicinfo_t( "runni2", 0 ), new musicinfo_t( "dead2", 0 ), new musicinfo_t( "stlks3", 0 ), new musicinfo_t( "romero", 0 ), new musicinfo_t( "shawn2", 0 ), new musicinfo_t( "messag", 0 ), new musicinfo_t( "count2", 0 ), new musicinfo_t( "ddtbl3", 0 ), new musicinfo_t( "ampie", 0 ), new musicinfo_t( "theda3", 0 ), new musicinfo_t( "adrian", 0 ), new musicinfo_t( "messg2", 0 ), new musicinfo_t( "romer2", 0 ), new musicinfo_t( "tense", 0 ), new musicinfo_t( "shawn3", 0 ), new musicinfo_t( "openin", 0 ), new musicinfo_t( "evil", 0 ), new musicinfo_t( "ultima", 0 ), new musicinfo_t( "read_m", 0 ), new musicinfo_t( "dm2ttl", 0 ), new musicinfo_t( "dm2int", 0 ) }; // // Information about all the sfx // public static sfxinfo_t nullSfxLink; public static sfxinfo_t[] S_sfx = { // S_sfx[0] needs to be a dummy for odd reasons. new sfxinfo_t( "none", false, 0, -1, -1, 0 ), new sfxinfo_t( "pistol", false, 64, -1, -1, 0 ), new sfxinfo_t( "shotgn", false, 64, -1, -1, 0 ), new sfxinfo_t( "sgcock", false, 64, -1, -1, 0 ), new sfxinfo_t( "dshtgn", false, 64, -1, -1, 0 ), new sfxinfo_t( "dbopn", false, 64, -1, -1, 0 ), new sfxinfo_t( "dbcls", false, 64, -1, -1, 0 ), new sfxinfo_t( "dbload", false, 64, -1, -1, 0 ), new sfxinfo_t( "plasma", false, 64, -1, -1, 0 ), new sfxinfo_t( "bfg", false, 64, -1, -1, 0 ), new sfxinfo_t( "sawup", false, 64, -1, -1, 0 ), new sfxinfo_t( "sawidl", false, 118, -1, -1, 0 ), new sfxinfo_t( "sawful", false, 64, -1, -1, 0 ), new sfxinfo_t( "sawhit", false, 64, -1, -1, 0 ), new sfxinfo_t( "rlaunc", false, 64, -1, -1, 0 ), new sfxinfo_t( "rxplod", false, 70, -1, -1, 0 ), new sfxinfo_t( "firsht", false, 70, -1, -1, 0 ), new sfxinfo_t( "firxpl", false, 70, -1, -1, 0 ), new sfxinfo_t( "pstart", false, 100, -1, -1, 0 ), new sfxinfo_t( "pstop", false, 100, -1, -1, 0 ), new sfxinfo_t( "doropn", false, 100, -1, -1, 0 ), new sfxinfo_t( "dorcls", false, 100, -1, -1, 0 ), new sfxinfo_t( "stnmov", false, 119, -1, -1, 0 ), new sfxinfo_t( "swtchn", false, 78, -1, -1, 0 ), new sfxinfo_t( "swtchx", false, 78, -1, -1, 0 ), new sfxinfo_t( "plpain", false, 96, -1, -1, 0 ), new sfxinfo_t( "dmpain", false, 96, -1, -1, 0 ), new sfxinfo_t( "popain", false, 96, -1, -1, 0 ), new sfxinfo_t( "vipain", false, 96, -1, -1, 0 ), new sfxinfo_t( "mnpain", false, 96, -1, -1, 0 ), new sfxinfo_t( "pepain", false, 96, -1, -1, 0 ), new sfxinfo_t( "slop", false, 78, -1, -1, 0 ), new sfxinfo_t( "itemup", true, 78, -1, -1, 0 ), new sfxinfo_t( "wpnup", true, 78, -1, -1, 0 ), new sfxinfo_t( "oof", false, 96, -1, -1, 0 ), new sfxinfo_t( "telept", false, 32, -1, -1, 0 ), new sfxinfo_t( "posit1", true, 98, -1, -1, 0 ), new sfxinfo_t( "posit2", true, 98, -1, -1, 0 ), new sfxinfo_t( "posit3", true, 98, -1, -1, 0 ), new sfxinfo_t( "bgsit1", true, 98, -1, -1, 0 ), new sfxinfo_t( "bgsit2", true, 98, -1, -1, 0 ), new sfxinfo_t( "sgtsit", true, 98, -1, -1, 0 ), new sfxinfo_t( "cacsit", true, 98, -1, -1, 0 ), new sfxinfo_t( "brssit", true, 94, -1, -1, 0 ), new sfxinfo_t( "cybsit", true, 92, -1, -1, 0 ), new sfxinfo_t( "spisit", true, 90, -1, -1, 0 ), new sfxinfo_t( "bspsit", true, 90, -1, -1, 0 ), new sfxinfo_t( "kntsit", true, 90, -1, -1, 0 ), new sfxinfo_t( "vilsit", true, 90, -1, -1, 0 ), new sfxinfo_t( "mansit", true, 90, -1, -1, 0 ), new sfxinfo_t( "pesit", true, 90, -1, -1, 0 ), new sfxinfo_t( "sklatk", false, 70, -1, -1, 0 ), new sfxinfo_t( "sgtatk", false, 70, -1, -1, 0 ), new sfxinfo_t( "skepch", false, 70, -1, -1, 0 ), new sfxinfo_t( "vilatk", false, 70, -1, -1, 0 ), new sfxinfo_t( "claw", false, 70, -1, -1, 0 ), new sfxinfo_t( "skeswg", false, 70, -1, -1, 0 ), new sfxinfo_t( "pldeth", false, 32, -1, -1, 0 ), new sfxinfo_t( "pdiehi", false, 32, -1, -1, 0 ), new sfxinfo_t( "podth1", false, 70, -1, -1, 0 ), new sfxinfo_t( "podth2", false, 70, -1, -1, 0 ), new sfxinfo_t( "podth3", false, 70, -1, -1, 0 ), new sfxinfo_t( "bgdth1", false, 70, -1, -1, 0 ), new sfxinfo_t( "bgdth2", false, 70, -1, -1, 0 ), new sfxinfo_t( "sgtdth", false, 70, -1, -1, 0 ), new sfxinfo_t( "cacdth", false, 70, -1, -1, 0 ), new sfxinfo_t( "skldth", false, 70, -1, -1, 0 ), new sfxinfo_t( "brsdth", false, 32, -1, -1, 0 ), new sfxinfo_t( "cybdth", false, 32, -1, -1, 0 ), new sfxinfo_t( "spidth", false, 32, -1, -1, 0 ), new sfxinfo_t( "bspdth", false, 32, -1, -1, 0 ), new sfxinfo_t( "vildth", false, 32, -1, -1, 0 ), new sfxinfo_t( "kntdth", false, 32, -1, -1, 0 ), new sfxinfo_t( "pedth", false, 32, -1, -1, 0 ), new sfxinfo_t( "skedth", false, 32, -1, -1, 0 ), new sfxinfo_t( "posact", true, 120, -1, -1, 0 ), new sfxinfo_t( "bgact", true, 120, -1, -1, 0 ), new sfxinfo_t( "dmact", true, 120, -1, -1, 0 ), new sfxinfo_t( "bspact", true, 100, -1, -1, 0 ), new sfxinfo_t( "bspwlk", true, 100, -1, -1, 0 ), new sfxinfo_t( "vilact", true, 100, -1, -1, 0 ), new sfxinfo_t( "noway", false, 78, -1, -1, 0 ), new sfxinfo_t( "barexp", false, 60, -1, -1, 0 ), new sfxinfo_t( "punch", false, 64, -1, -1, 0 ), new sfxinfo_t( "hoof", false, 70, -1, -1, 0 ), new sfxinfo_t( "metal", false, 70, -1, -1, 0 ), // MAES: here C referenced a field before it was defined. // We'll make do by defining a new "linked" boolean field, and // handling special cases in a separate initializer. new sfxinfo_t( "chgun", false, 64, true, 150, 0, 0 ), new sfxinfo_t( "tink", false, 60, -1, -1, 0 ), new sfxinfo_t( "bdopn", false, 100, -1, -1, 0 ), new sfxinfo_t( "bdcls", false, 100, -1, -1, 0 ), new sfxinfo_t( "itmbk", false, 100, -1, -1, 0 ), new sfxinfo_t( "flame", false, 32, -1, -1, 0 ), new sfxinfo_t( "flamst", false, 32, -1, -1, 0 ), new sfxinfo_t( "getpow", false, 60, -1, -1, 0 ), new sfxinfo_t( "bospit", false, 70, -1, -1, 0 ), new sfxinfo_t( "boscub", false, 70, -1, -1, 0 ), new sfxinfo_t( "bossit", false, 70, -1, -1, 0 ), new sfxinfo_t( "bospn", false, 70, -1, -1, 0 ), new sfxinfo_t( "bosdth", false, 70, -1, -1, 0 ), new sfxinfo_t( "manatk", false, 70, -1, -1, 0 ), new sfxinfo_t( "mandth", false, 70, -1, -1, 0 ), new sfxinfo_t( "sssit", false, 70, -1, -1, 0 ), new sfxinfo_t( "ssdth", false, 70, -1, -1, 0 ), new sfxinfo_t( "keenpn", false, 70, -1, -1, 0 ), new sfxinfo_t( "keendt", false, 70, -1, -1, 0 ), new sfxinfo_t( "skeact", false, 70, -1, -1, 0 ), new sfxinfo_t( "skesit", false, 70, -1, -1, 0 ), new sfxinfo_t( "skeatk", false, 70, -1, -1, 0 ), new sfxinfo_t( "radio", false, 60, -1, -1, 0 ) }; /** MAES: This method is here to handle exceptions in definitions of static sfx. * Only the chaingun sound needs to be cross-linked to the pistol sound, but in * Java you can't do that until the array is actually created. So remember to run * this explicitly, and add any further rules you want! * */ public static void init(){ for (int i=0;i<S_sfx.length;i++){ if (S_sfx[i].linked){ // MAES: Rule for chgun if (S_sfx[i].name.compareToIgnoreCase("chgun")==0) { S_sfx[i].link=S_sfx[sfxenum_t.sfx_pistol.ordinal()]; } } } } public static enum musicenum_t { mus_None, mus_e1m1, mus_e1m2, mus_e1m3, mus_e1m4, mus_e1m5, mus_e1m6, mus_e1m7, mus_e1m8, mus_e1m9, mus_e2m1, mus_e2m2, mus_e2m3, mus_e2m4, mus_e2m5, mus_e2m6, mus_e2m7, mus_e2m8, mus_e2m9, mus_e3m1, mus_e3m2, mus_e3m3, mus_e3m4, mus_e3m5, mus_e3m6, mus_e3m7, mus_e3m8, mus_e3m9, mus_inter, mus_intro, mus_bunny, mus_victor, mus_introa, mus_runnin, mus_stalks, mus_countd, mus_betwee, mus_doom, mus_the_da, mus_shawn, mus_ddtblu, mus_in_cit, mus_dead, mus_stlks2, mus_theda2, mus_doom2, mus_ddtbl2, mus_runni2, mus_dead2, mus_stlks3, mus_romero, mus_shawn2, mus_messag, mus_count2, mus_ddtbl3, mus_ampie, mus_theda3, mus_adrian, mus_messg2, mus_romer2, mus_tense, mus_shawn3, mus_openin, mus_evil, mus_ultima, mus_read_m, mus_dm2ttl, mus_dm2int, NUMMUSIC }; public static enum sfxenum_t { sfx_None, sfx_pistol, sfx_shotgn, sfx_sgcock, sfx_dshtgn, sfx_dbopn, sfx_dbcls, sfx_dbload, sfx_plasma, sfx_bfg, sfx_sawup, sfx_sawidl, sfx_sawful, sfx_sawhit, sfx_rlaunc, sfx_rxplod, sfx_firsht, sfx_firxpl, sfx_pstart, sfx_pstop, sfx_doropn, sfx_dorcls, sfx_stnmov, sfx_swtchn, sfx_swtchx, sfx_plpain, sfx_dmpain, sfx_popain, sfx_vipain, sfx_mnpain, sfx_pepain, sfx_slop, sfx_itemup, sfx_wpnup, sfx_oof, sfx_telept, sfx_posit1, sfx_posit2, sfx_posit3, sfx_bgsit1, sfx_bgsit2, sfx_sgtsit, sfx_cacsit, sfx_brssit, sfx_cybsit, sfx_spisit, sfx_bspsit, sfx_kntsit, sfx_vilsit, sfx_mansit, sfx_pesit, sfx_sklatk, sfx_sgtatk, sfx_skepch, sfx_vilatk, sfx_claw, sfx_skeswg, sfx_pldeth, sfx_pdiehi, sfx_podth1, sfx_podth2, sfx_podth3, sfx_bgdth1, sfx_bgdth2, sfx_sgtdth, sfx_cacdth, sfx_skldth, sfx_brsdth, sfx_cybdth, sfx_spidth, sfx_bspdth, sfx_vildth, sfx_kntdth, sfx_pedth, sfx_skedth, sfx_posact, sfx_bgact, sfx_dmact, sfx_bspact, sfx_bspwlk, sfx_vilact, sfx_noway, sfx_barexp, sfx_punch, sfx_hoof, sfx_metal, sfx_chgun, sfx_tink, sfx_bdopn, sfx_bdcls, sfx_itmbk, sfx_flame, sfx_flamst, sfx_getpow, sfx_bospit, sfx_boscub, sfx_bossit, sfx_bospn, sfx_bosdth, sfx_manatk, sfx_mandth, sfx_sssit, sfx_ssdth, sfx_keenpn, sfx_keendt, sfx_skeact, sfx_skesit, sfx_skeatk, sfx_radio, NUMSFX }; } package data; import static data.Tables.FINEANGLES; import static data.Tables.SLOPERANGE; import static data.Tables.tantoangle; import static m.fixed_t.*; // Emacs style mode select -*- C++ -*- //----------------------------------------------------------------------------- // // $Id: Tables.java,v 1.22 2011/05/06 09:21:59 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. // // $Log: Tables.java,v $ // Revision 1.22 2011/05/06 09:21:59 velktron // Cleaned up and reorganized common renderer code. // // Revision 1.21 2011/02/11 00:11:13 velktron // A MUCH needed update to v1.3. // // Revision 1.20 2010/12/20 17:15:08 velktron // Made the renderer more OO -> TextureManager and other changes as well. // // Revision 1.19 2010/12/10 17:38:56 velktron // pspritescale fixed, weapon actions won't crash (but not work either). // // Revision 1.18 2010/11/25 20:12:44 velktron // Fixed blockmap bug and viewangletox overflow bug. // // Revision 1.17 2010/11/22 01:17:16 velktron // Fixed blockmap (for the most part), some actions implemented and functional, ambient animation/lighting functional. // // Revision 1.16 2010/11/17 23:55:06 velktron // Kind of playable/controllable. // // Revision 1.15 2010/11/15 17:15:54 velktron // Fixed masked columns rendering, introduced unrolled span and column functions from Boom (thanks, Lee Killough :-) // // Revision 1.14 2010/11/14 20:00:21 velktron // Bleeding floor bug fixed! // // Revision 1.13 2010/11/12 13:37:25 velktron // Rationalized the LUT system - now it's 100% procedurally generated. // // Revision 1.12 2010/11/03 16:48:04 velktron // "Bling" view angles fixed (perhaps related to the "bleeding line bug"?) // // Revision 1.11 2010/10/14 18:37:14 velktron // Rendering kinda works. Wow. // // Revision 1.10 2010/10/08 16:55:50 velktron // Duh // // Revision 1.9 2010/10/01 16:47:51 velktron // Fixed tab interception. // // Revision 1.8 2010/09/27 15:07:44 velktron // meh // // Revision 1.7 2010/09/27 02:27:29 velktron // BEASTLY update // // Revision 1.6 2010/09/22 16:40:02 velktron // MASSIVE changes in the status passing model. // DoomMain and DoomGame unified. // Doomstat merged into DoomMain (now status and game functions are one). // // Most of DoomMain implemented. Possible to attempt a "classic type" start but will stop when reading sprites. // // Revision 1.5 2010/09/21 15:53:37 velktron // Split the Map ...somewhat... // // Revision 1.4 2010/09/16 00:16:27 velktron // Velvet FM 96.8 // // Revision 1.3 2010/09/15 16:17:38 velktron // Arithmetic // // Revision 1.2 2010/09/09 16:09:09 velktron // Yer more enhancements to the display system... // // Revision 1.1 2010/07/05 16:18:40 velktron // YOU DON'T WANNA KNOW // // Revision 1.1 2010/06/30 08:58:51 velktron // Let's see if this stuff will finally commit.... // // // Most stuff is still being worked on. For a good place to start and get an idea of what is being done, I suggest checking out the "testers" package. // // Revision 1.1 2010/06/29 11:07:34 velktron // Release often, release early they say... // // Commiting ALL stuff done so far. A lot of stuff is still broken/incomplete, and there's still mixed C code in there. I suggest you load everything up in Eclpise and see what gives from there. // // A good place to start is the testers/ directory, where you can get an idea of how a few of the implemented stuff works. // // // DESCRIPTION: // Lookup tables. // Do not try to look them up :-). // In the order of appearance: // // int finetangent[4096] - Tangens LUT. // Should work with BAM fairly well (12 of 16bit, // effectively, by shifting). // // int finesine[10240] - Sine lookup. // Guess what, serves as cosine, too. // Remarkable thing is, how to use BAMs with this? // // int tantoangle[2049] - ArcTan LUT, // maps tan(angle) to angle fast. Gotta search. // // //----------------------------------------------------------------------------- public final class Tables { public static final String rcsid="$Id:"; public static final double PI = 3.141592657; /** Normally set to 12, and this sets the value of other constants too. * Howevever changing it will also distort the view, resulting in a * nightmare-like vision. There are some practical minimum and * maximums as well. * * */ public static final int BITSPRECISION = 12; public static final int FINEANGLES = 2<<BITSPRECISION; public static final int FINETANS = FINEANGLES/2; // 4096 for normal precision. public static final int QUARTERMARK = 2<<BITSPRECISION-2; public static final int FINEMASK = (FINEANGLES-1); /** Mod long angle_t's with this value to cut off rollover */ public static final long ANGLEMODULE = 0x100000000L; /** AND with this to remove unwanted sign extensions */ public static final long BITS32 = 0x00000000FFFFFFFFL; /** Sign elimination */ public static final int BITS31 = 0x7FFFFFFF; // Maes: we have to procedurally generate finesine/finecosine, else we run into a Java static limit. // Either that, or I split the files. Guess what I did. // public static int PRECISION = 10240 ; /** 0x100000000 to 0x2000 */ public static final int ANGLETOFINESHIFT = 31-BITSPRECISION; /* Binary Angle Measurement. * Some maths: their definition means that a range of 2pi is actually * mapped to 2^32 values!!! But the lookup tables are only 8K (2^13) * long (for sine/cosine), which means that we're 19 bits too precise * -> ergo, >>ANGLETOFINESHIFT must be applied. * * Also, the original angle_t type was "unsigned int", so we should be * using longs here. However, as BAM is used only after shifting, so * using ints doesn't cause a problem for LUT access. * * However, some problems may arise with comparisons and ordinary arithmetic: * e.g. ANG270 is supposed to be larger not only than ANG180, but also from * ANG45, which does not hold true if those constants were stored as ints. * * As a rule of thumb, whenever you need to store JUST a BAM index, then * ints are ok (actually, you'll have to cast down to int anyway). * * Whenever you need to add or compare angles directly however, you need * longs. Furthermore, you must account for possible rollovers by modding * with 0x100000000 or else long ints will store angles exceeding 360 degrees! * Under no circumstances the value actually stored in the "long angles" should * exceed 0xFFFFFFFF. * * An example: comparing any two long angles directly is OK, provided they were * constructed correctly. * * Adding, subtracting, multiplying etc. with two or more angles however requires * rollover compensation (e.g. result=(a+b+c) is wrong, result=(a+b+c)%0xFFFFFFFF * is correct and will produce an angle you can "trust". * * */ /** Doom angle constants. */ public static final long ANG45 = 0x20000000L, ANG90 = 0x40000000L, ANG180 = 0x80000000L, ANG270 = 0xc0000000L; public static final int SLOPERANGE = (2<<(BITSPRECISION-2)); // Normally 2048. public static final int SLOPEBITS = BITSPRECISION-1; public static final int DBITS=FRACBITS-SLOPEBITS; // typedef unsigned angle_t; // Effective size is 2049; // The +1 size is to handle the case when x==y // without additional checking. //extern angle_t tantoangle[SLOPERANGE+1]; /** * Original size was 10240, but includes 5PI/4 periods. We can get away with * ints on this one because of the small range. MAES: WTF? -64 ~ 64K * range... so 17-bit accuracy? heh. */ public static final int[] finesine=new int[FINEANGLES+QUARTERMARK]; public static final int[] finecosine=new int[FINEANGLES]; /** Any denominator smaller than 512 will result in * maximum slope (45 degrees, or an index into tantoangle of 2048) * The original method used unsigned args. So if this returns NEGATIVES * in any way, it means you fucked up. Another "consistency" for Doom. * Even though it was called upon fixed_t signed numbers. * */ public static final int SlopeDiv ( long num, long den) { int ans; if (den < 512) return SLOPERANGE; ans = (int) ((num<<3)/(den>>>8)); return ans <= SLOPERANGE ? ans : SLOPERANGE; } /** Finetangent table. It only has 4096 values corresponding roughly * to -90/+90 angles, with values between are -/+ 2607 for "infinity". * * Since in vanilla accesses to the table can overflow way beyond 4096 * indexes, the access index must be clipped to 4K tops via an accessor, * or, in order to simulate some aspects of vanilla overflowing, replicate * 4K of finesine's values AFTER the 4K index. This removes the need * for access checking, at the cost of some extra memory. It also allows * a small degree of "vanilla like" compatibility. * * */ public final static int[] finetangent=new int[2*FINETANS]; // MAES: original range 2049 // This obviously // Range goes from 0x00000000 to 0x20000000, so in theory plain ints should be enough... /** This maps a value 0-2048 to a BAM unsigned integer angle, ranging from 0x0 to 0x2000000: * * In practice, this means there are only tangent values for angles up to 45 degrees. * * These values are valid BAM measurements in the first quadrant * * */ public static final int[] tantoangle=new int[SLOPERANGE+1]; /** Use this to get a value from the finesine table. It will be automatically shifte, * Equivalent to finesine[angle>>>ANGLETOFINESHIFT] * * @param angle in BAM units * @return */ public static final int finesine(int angle){ return finesine[angle>>>ANGLETOFINESHIFT]; } /** Use this to get a value from the finesine table using a long argument. * It will automatically shift, apply rollover module and cast. * * Equivalent to finesine[(int) ((angle>>ANGLETOFINESHIFT)%ANGLEMODULE)]; * * @param angle in BAM units * @return */ public static final int finesine(long angle){ return finesine[(int) ((angle&BITS32)>>>ANGLETOFINESHIFT)]; } /** Use this to get a value from the finecosine table. It will be automatically shifted, * Equivalent to finecosine[angle>>>ANGLETOFINESHIFT] * @param angle in BAM units * @return */ public static final int finecosine(int angle){ return finecosine[angle>>>ANGLETOFINESHIFT]; } /** Use this to get a value from the finecosine table. * It will automatically shift, apply rollover module and cast. * * Equivalent to finecosine[(int) ((angle&BITS32)>>>ANGLETOFINESHIFT)] * @param angle in BAM units * @return */ public static final int finecosine(long angle){ return finecosine[(int) ((angle&BITS32)>>>ANGLETOFINESHIFT)]; } /** Compare BAM angles in 32-bit format * "Greater or Equal" bam0>bam1 * */ public static final boolean GE(int bam0, int bam1){ // Handle easy case. if (bam0==bam1) return true; // bam0 is greater than 180 degrees. if (bam0<0 && bam1>=0) return true; // bam1 is greater than 180 degrees. if (bam0>=0 && bam1<0) return false; // Both "greater than 180", No other way to compare. bam0&=BITS31; bam1&=BITS31; return bam0>bam1; } public static final boolean GT(int bam0, int bam1){ // bam0 is greater than 180 degrees. if (bam0<0 && bam1>=0) return true; // bam1 is greater than 180 degrees. if (bam0>=0 && bam1<0) return false; // Both "greater than 180", No other way to compare. bam0&=BITS31; bam1&=BITS31; return bam0>bam1; } public static final int BAMDiv(int bam0, int bam1){ // bam0 is greater than 180 degrees. if (bam0>=0) return bam0/bam1; // bam0 is greater than 180 degrees. // We have to make is so that ANG270 0xC0000000 becomes ANG135, aka 60000000 if (bam1>=0) return (int) ((long)(0x0FFFFFFFFL&bam0)/bam1); return (int) ((long)(0x0FFFFFFFFL&bam0)/(0x0FFFFFFFFL&bam1)); } /** Converts a long angle to a BAM LUT-ready angle (13 bits, between 0-8191). * Cuts away rollover. * * @param angle * @return */ public static final int toBAMIndex(long angle){ return (int) ((angle&BITS32)>>>ANGLETOFINESHIFT); } /** Converts a long angle to a TAN BAM LUT-ready angle (12 bits, between 0-4195). * Cuts away rollover. * * @param angle * @return */ public static final int toFineTanIndex(long angle){ return (int) ((angle&BITS31)>>>ANGLETOFINESHIFT); } /** Converts an 32-bit int angle to a BAM LUT-ready angle (13 bits, between 0-8192). * * @param angle * @return */ public static final int toBAMIndex(int angle){ return angle>>>ANGLETOFINESHIFT; } /** Add two long angles and correct for overflow */ public static final long addAngles(long a, long b) { return ((a+b)&BITS32); } /** MAES: I brought this function "back from the dead" since * Java has some pretty low static limits for statically defined LUTs. * In order to keep the codebase clutter and static allocation to a minimum, * I decided to procedurally generate the tables during runtime, * using the original functions. * * The code has been thoroughly checked in both Sun's JDK and GCC and was * found to, indeed, produce the same values found in the finesine/finecosine * and finetangent tables, at least on Intel. * * The "tantoangle" table is also generated procedurally, but since there * was no "dead code" to build upon, it was recreated through reverse * engineering and also found to be 100% faithful to the original data. * * */ public static void InitTables(){ int i; float a; float fv; int t; // viewangle tangent table for (i=0 ; i<FINEANGLES/2 ; i++) { a = (float)((i-FINEANGLES/4+0.5)*PI*2)/FINEANGLES; fv = (float)(FRACUNIT*Math.tan (a)); t = (int)fv; finetangent[i] = t; } // finesine table for (i=0 ; i<FINEANGLES+QUARTERMARK ; i++) { // OPTIMIZE: mirror... a = (float)((i+0.5)*PI*2)/FINEANGLES; t = (int)(FRACUNIT*Math.sin (a)); finesine[i] = t; if (i>=QUARTERMARK){ finecosine[i-QUARTERMARK] = t; } } // HACK: replicate part of finesine after finetangent, to // simulate overflow behavior and remove need for capping // indexes // viewangle tangent table for (i=FINEANGLES/2 ; i<FINEANGLES ; i++) { finetangent[i] = finesine[i-FINEANGLES/2]; } /* tantoangle table * There was actually no dead code for that one, so this is a close recreation. * Since there are 2049 values, and the maximum angle considered is 536870912 (0x20000000) * which is 45 degrees in BAM, we have to fill in the atan for values up to 45 degrees. * Since the argument is a slope ranging from 0...2048, we have 2049 equally spaced (?) * values, with 2048 being being the unitary slope (0x10000 in fixed_t). That value is only * accessed in special cases (overflow) so we only need to consider 0..2047 aka 11 bits. * So: we take "minislopes" 0-2048, we blow them up to a full fixed_t unit with <<5. * We make this into a float (?), then use trigonometric ATAN, and then go to BAM. * * Any questions? * */ /* This is the recreated code for (i=0 ; i<SLOPERANGE+1 ; i++) { a=(float)((i<<DBITS)/65536.0); t=(int)((float)(2*Math.atan(a)/PI)*0x40000000); tantoangle[i] = t; } */ // This is the original R_InitPointToAngle code that created this table. for (i=0 ; i<=SLOPERANGE ; i++) { a = (float) (Math.atan( (double)i/SLOPERANGE )/(3.141592657*2)); t = (int) (0xffffffffL*a); tantoangle[i] = (int) t; } } private Tables(){ } } package data; import java.io.DataOutputStream; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import w.CacheableDoomObject; import w.DoomBuffer; import w.IPackableDoomObject; import w.IWritableDoomObject; /** * A SideDef, defining the visual appearance of a wall, by setting textures and * offsets. ON-DISK. */ public class mapsidedef_t implements CacheableDoomObject,IWritableDoomObject,IPackableDoomObject{ public mapsidedef_t() { } public short textureoffset; public short rowoffset; // 8-char strings. public String toptexture; public String bottomtexture; public String midtexture; /** Front sector, towards viewer. */ public short sector; public static int sizeOf() { return 30; } @Override public void unpack(ByteBuffer buf) throws IOException { buf.order(ByteOrder.LITTLE_ENDIAN); this.textureoffset = buf.getShort(); this.rowoffset = buf.getShort(); this.toptexture=DoomBuffer.getNullTerminatedString(buf,8).toUpperCase(); this.bottomtexture=DoomBuffer.getNullTerminatedString(buf,8).toUpperCase(); this.midtexture=DoomBuffer.getNullTerminatedString(buf,8).toUpperCase(); this.sector = buf.getShort(); } @Override public void pack(ByteBuffer buf) throws IOException { buf.order(ByteOrder.LITTLE_ENDIAN); buf.putShort(textureoffset); buf.putShort(rowoffset); DoomBuffer.putNullTerminatedString(buf,toptexture,8); DoomBuffer.putNullTerminatedString(buf,bottomtexture,8); DoomBuffer.putNullTerminatedString(buf,midtexture,8); buf.putShort(sector); } @Override public void write(DataOutputStream dos) throws IOException { // More efficient, avoids duplicating code and // handles little endian better. iobuffer.position(0); iobuffer.order(ByteOrder.LITTLE_ENDIAN); this.pack(iobuffer); dos.write(iobuffer.array()); } private static ByteBuffer iobuffer=ByteBuffer.allocate(mapsidedef_t.sizeOf()); } package i; import doom.ticcmd_t; public interface IDoomSystem { public void AllocLow(int length); public void BeginRead(); public void EndRead(); public void WaitVBL(int count); public byte[] ZoneBase(int size); public int GetHeapSize(); public void Tactile(int on, int off, int total); public void Quit(); public ticcmd_t BaseTiccmd(); public void Error(String error, Object ... args); void Error(String error); void Init(); /** Generate a blocking alert with the intention of continuing or aborting * a certain game-altering action. E.g. loading PWADs, or upon critical * level loading failures. This can be either a popup panel or console * message. * * @param cause Provide a clear string explaining why the alert was generated * @return true if we should continue, false if an alternate action should be taken. */ boolean GenerateAlert(String title,String cause); } package i; import doom.DoomStatus; import rr.patch_t; import v.DoomVideoRenderer; import v.IVideoScale; import w.IWadLoader; public class DiskDrawer implements IDiskDrawer,DoomStatusAware { private patch_t disk; private IWadLoader W; private DoomVideoRenderer<?,?> V; private IVideoScale VS; private int timer=0; private String diskname; public static final String STDISK="STDISK"; public static final String STCDROM="STCDROM"; public DiskDrawer(DoomStatus<?,?> DM,String icon){ this.updateStatus(DM); this.diskname=icon; } @Override public void Init(){ this.disk=W.CachePatchName(diskname); } @Override public void Drawer() { if (timer>0){ if (timer%2==0) V.DrawScaledPatch(304,184,DoomVideoRenderer.SCREEN_FG,VS, disk); } if (timer>=0) timer--; } @Override public void updateStatus(DoomStatus<?,?> DC) { this.W=DC.W; this.V=DC.V; } @Override public void setVideoScale(IVideoScale vs) { this.VS = vs; } @Override public void initScaling() { } @Override public void setReading(int reading) { timer=reading; } @Override public boolean isReading() { return timer>0; } @Override public boolean justDoneReading() { return timer==0; } } // Emacs style mode select -*- C++ -*- //----------------------------------------------------------------------------- // // $Id: system.java,v 1.5 2011/02/11 00:11:13 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. // // $Log: system.java,v $ // Revision 1.5 2011/02/11 00:11:13 velktron // A MUCH needed update to v1.3. // // Revision 1.1 2010/06/30 08:58:50 velktron // Let's see if this stuff will finally commit.... // // // Most stuff is still being worked on. For a good place to start and get an idea of what is being done, I suggest checking out the "testers" package. // // Revision 1.1 2010/06/29 11:07:34 velktron // Release often, release early they say... // // Commiting ALL stuff done so far. A lot of stuff is still broken/incomplete, and there's still mixed C code in there. I suggest you load everything up in Eclpise and see what gives from there. // // A good place to start is the testers/ directory, where you can get an idea of how a few of the implemented stuff works. // // // DESCRIPTION: // //----------------------------------------------------------------------------- package i; public class system{ /* #include <stdlib.h> #include <stdio.h> #include <string.h> #include <stdarg.h> #include <sys/time.h> #include <unistd.h> #include "doomdef.h" #include "m_misc.h" #include "i_video.h" #include "i_sound.h" #include "d_net.h" #include "g_game.h" #ifdef __GNUG__ #pragma implementation "i_system.h" #endif #include "i_system.h" */ static int mb_used = 6; public void Tactile ( int on, int off, int total ) { // UNUSED. on = off = total = 0; } /* ticcmd_t emptycmd; ticcmd_t* I_BaseTiccmd(void) { return &emptycmd; } */ public static int GetHeapSize () { return mb_used*1024*1024; } /* byte* I_ZoneBase (int* size) { *size = mb_used*1024*1024; return (byte *) malloc (*size); } */ // // I_GetTime // returns time in 1/70th second tics // /* int I_GetTime () { struct timeval tp; struct timezone tzp; int newtics; static int basetime=0; gettimeofday(&tp, &tzp); if (!basetime) basetime = tp.tv_sec; newtics = (tp.tv_sec-basetime)*TICRATE + tp.tv_usec*TICRATE/1000000; return newtics; } */ // // I_Init // /* void I_Init (void) { I_InitSound(); // I_InitGraphics(); } // // I_Quit // void I_Quit (void) { D_QuitNetGame (); I_ShutdownSound(); I_ShutdownMusic(); M_SaveDefaults (); I_ShutdownGraphics(); exit(0); } void I_WaitVBL(int count) { #ifdef SGI sginap(1); #else #ifdef SUN sleep(0); #else usleep (count * (1000000/70) ); #endif #endif } void I_BeginRead(void) { } void I_EndRead(void) { } byte* I_AllocLow(int length) { byte* mem; mem = (byte *)malloc (length); memset (mem,0,length); return mem; } */ // // I_Error // public static boolean demorecording; public static void Error (String error, Object ... args) { //va_list argptr; // Message first. //va_start (argptr,error); System.err.print("Error: "); System.err.printf(error,args); System.err.print("\n"); //va_end (argptr); //fflush( stderr ); // Shutdown. Here might be other errors. //if (demorecording) //G_CheckDemoStatus(); //D_QuitNetGame (); //I_ShutdownGraphics(); System.exit(-1); } } package i; /** Interface for Doom-to-System event handling methods * * @author Velktron * */ public interface DoomEventInterface { /** The implementation is windowing subsystem-specific * e.g. DOS, XServer, AWT or Swing or whatever. * */ public void GetEvent(); } package i; import v.IVideoScaleAware; public interface IDiskDrawer extends IDrawer,IVideoScaleAware{ /** Set a timeout (in tics) for displaying the disk icon * * @param timeout */ void setReading(int reading); /** Disk displayer is currently active * * @return */ boolean isReading(); /** Only call after the Wadloader is instantiated and * initialized itself. * */ void Init(); /**Status only valid after the last tic has been drawn. * Use to know when to redraw status bar. * * @return */ boolean justDoneReading(); } package i; public final class Strings { public static final String MOCHA_DOOM_TITLE="Mocha Doom Alpha 1.6"; public static final String MODIFIED_GAME= ("===========================================================================\n"+ "ATTENTION: This version of DOOM has been modified. If you would like to\n"+ "get a copy of the original game, call 1-800-IDGAMES or see the readme file.\n"+ " You will not receive technical support for modified games.\n"+ " press enter to continue\n"+ "===========================================================================\n"); public static final String MODIFIED_GAME_TITLE="Modified game alert"; public static final String MODIFIED_GAME_DIALOG= ("<html><center>"+ "===========================================================================<br>"+ "ATTENTION: This version of DOOM has been modified. If you would like to<br>"+ "get a copy of the original game, call 1-800-IDGAMES or see the readme file.<br>"+ " You will not receive technical support for modified games.<br>"+ " press OK to continue<br>"+ "===========================================================================<br>"+ "</center></html>"); public static final String LEVEL_FAILURE_TITLE="Level loading failure"; public static final String LEVEL_FAILURE_CAUSE= ("<html><center>"+ "Level loading failed!<br>"+ "Press OK to end this game without exiting, or cancel to quit Doom."+ "</center></html>"); } package i; import doom.ticcmd_t; public class DummySystem implements IDoomSystem{ @Override public void AllocLow(int length) { // TODO Auto-generated method stub } @Override public void BeginRead() { // TODO Auto-generated method stub } @Override public void EndRead() { // TODO Auto-generated method stub } @Override public void WaitVBL(int count) { // TODO Auto-generated method stub } @Override public byte[] ZoneBase(int size) { // TODO Auto-generated method stub return null; } @Override public int GetHeapSize() { // TODO Auto-generated method stub return 0; } @Override public void Tactile(int on, int off, int total) { // TODO Auto-generated method stub } @Override public void Quit() { // TODO Auto-generated method stub } @Override public ticcmd_t BaseTiccmd() { // TODO Auto-generated method stub return null; } @Override public void Error(String error, Object... args) { // TODO Auto-generated method stub } @Override public void Error(String error) { // TODO Auto-generated method stub } @Override public void Init() { // TODO Auto-generated method stub } @Override public boolean GenerateAlert(String title, String cause) { // TODO Auto-generated method stub return false; } } package i; // Emacs style mode select -*- C++ -*- //----------------------------------------------------------------------------- // // $Id: DoomSoundInterface.java,v 1.3 2011/02/11 00:11:13 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: // System interface, sound. // //----------------------------------------------------------------------------- import data.sfxinfo_t; /* // UNIX hack, to be removed. #ifdef SNDSERV #include <stdio.h> extern FILE* sndserver; extern char* sndserver_filename; #endif*/ public interface DoomSoundInterface{ // Init at program start... public void I_InitSound(); // ... update sound buffer and audio device at runtime... public void I_UpdateSound(); public void I_SubmitSound(); // ... shut down and relase at program termination. public void I_ShutdownSound(); // // SFX I/O // // Initialize channels? void I_SetChannels(); // Get raw data lump index for sound descriptor. public int I_GetSfxLumpNum (sfxinfo_t sfxinfo ); // Starts a sound in a particular sound channel. public int I_StartSound ( int id, int vol, int sep, int pitch, int priority ); // Stops a sound channel. public void I_StopSound(int handle); // Called by S_*() functions // to see if a channel is still playing. // Returns 0 if no longer playing, 1 if playing. public boolean I_SoundIsPlaying(int handle); // Updates the volume, separation, // and pitch of a sound channel. public void I_UpdateSoundParams ( int handle, int vol, int sep, int pitch ); // // MUSIC I/O // public void I_InitMusic(); public void I_ShutdownMusic(); // Volume. public void I_SetMusicVolume(int volume); // PAUSE game handling. public void I_PauseSong(int handle); public void I_ResumeSong(int handle); // Registers a song handle to song data. public int I_RegisterSong(byte[] data); // Called by anything that wishes to start music. // plays a song, and when the song is done, // starts playing it again in an endless loop. // Horrible thing to do, considering. public void I_PlaySong ( int handle, int looping ); // Stops a song over 3 seconds. public void I_StopSong(int handle); // See above (register), then think backwards public void I_UnRegisterSong(int handle); } package i; public enum BppMode { HiColor, Indexed, TrueColor, TrueColor32 } package i; public interface IDrawer { public void Drawer(); } package i; import doom.DoomStatus; public interface DoomStatusAware { public void updateStatus(DoomStatus<?,?> DC); } package i; /** Methods specific to Doom-System video interfacing. * In essence, whatever you are using as a final system-specific way to display * the screens, should be able to respond to these commands. In particular, * screen update requests must be honored, and palette/gamma request changes * must be intercepted before they are forwarded to the renderers (in case they * are system-specific, rather than renderer-specific). * * The idea is that the final screen rendering module sees/handles as less as * possible, and only gets a screen to render, no matter what depth it is. * * */ public interface DoomVideoInterface<K> { public void StartFrame(); public void StartTic(); public void SetPalette(int palette); public void InitGraphics(); public void FinishUpdate(); public void UpdateNoBlit(); public void ShutdownGraphics(); public void SetGamma(int gammalevel); public void ReadScreen(K linear); } // Emacs style mode select -*- C++ -*- //----------------------------------------------------------------------------- // // $Id: DoomSystem.java,v 1.18 2013/06/04 11:29:39 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. // // $Log: DoomSystem.java,v $ // Revision 1.18 2013/06/04 11:29:39 velktron // Shut up logger // // Revision 1.17 2013/06/03 10:54:11 velktron // System interface allows for a logging subsystem, but implementation details may very well vary. // // Revision 1.16.2.1 2012/11/19 22:14:35 velktron // Sync tooling shutdown. // // Revision 1.16 2012/11/06 16:05:29 velktron // Variables manager now part of Main. // // Revision 1.15 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.14.2.2 2012/09/17 15:57:53 velktron // Aware of IVariablesManager // // Revision 1.14.2.1 2012/06/14 22:38:06 velktron // Update to handle new disk flasher // // Revision 1.14 2011/10/24 02:11:27 velktron // Stream compliancy // // Revision 1.13 2011/09/29 15:16:04 velktron // Modal popup generation moved here. // // Revision 1.12 2011/06/12 21:54:31 velktron // Separate music + sound closing. // // Revision 1.11 2011/06/05 22:52:28 velktron // Proper audio subsystem shutdown. // // Revision 1.10 2011/05/29 22:15:32 velktron // Introduced IRandom interface. // // Revision 1.9 2011/05/26 17:56:32 velktron // Removed ticker functionality, moved to ITicker interface. // // Revision 1.8 2011/05/18 16:53:29 velktron // Implements IDoomSystem now. // // Revision 1.7 2011/05/17 16:54:09 velktron // Switched to DoomStatus // // Revision 1.6 2011/05/13 17:44:24 velktron // Global error function, shutdown on demos. // // Revision 1.5 2011/02/11 00:11:13 velktron // A MUCH needed update to v1.3. // // Revision 1.4 2010/12/15 16:12:19 velktron // Changes in Wiper code and alternate timing method, hoping to fix the Athlon X2 // // Revision 1.3 2010/09/24 17:58:39 velktron // Menus and HU functional -mostly. // // Revision 1.2 2010/09/23 20:36:45 velktron // *** empty log message *** // // Revision 1.1 2010/09/23 15:11:57 velktron // A bit closer... // // Revision 1.3 2010/09/07 16:23:00 velktron // *** empty log message *** // // Revision 1.2 2010/08/30 15:53:19 velktron // Screen wipes work...Finale coded but untested. // GRID.WAD included for testing. // // Revision 1.1 2010/06/30 08:58:50 velktron // Let's see if this stuff will finally commit.... // // // Most stuff is still being worked on. For a good place to start and get an idea of what is being done, I suggest checking out the "testers" package. // // Revision 1.1 2010/06/29 11:07:34 velktron // Release often, release early they say... // // Commiting ALL stuff done so far. A lot of stuff is still broken/incomplete, and there's still mixed C code in there. I suggest you load everything up in Eclpise and see what gives from there. // // A good place to start is the testers/ directory, where you can get an idea of how a few of the implemented stuff works. // // // DESCRIPTION: // //----------------------------------------------------------------------------- package i; import java.io.IOException; import sun.misc.VM; import awt.MsgBox; import m.MenuMisc; import doom.DoomMain; import doom.DoomStatus; import doom.ticcmd_t; import static data.Defines.TICRATE; public class DoomSystem implements IDoomSystem, DoomStatusAware{ static int mb_used = 6; // Even the SYSTEM needs to know about DOOM!!!! protected DoomMain<?,?> DM; @Override public void Tactile ( int on, int off, int total ) { // UNUSED. on = off = total = 0; } public ticcmd_t emptycmd; @Override public ticcmd_t BaseTiccmd() { return emptycmd; } @Override public int GetHeapSize () { return mb_used*1024*1024; } @Override public byte[] ZoneBase (int size) { return (new byte[mb_used*1024*1024]); } // //I_Quit // @Override public void Quit () { //DM.CheckDemoStatus(); try { DM.QuitNetGame (); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } //DM.debugEnd(); DM.ISND.ShutdownSound(); DM.IMUS.ShutdownMusic(); DM.commit(); DM.VM.SaveDefaults(DM.VM.getDefaultFile()); DM.VI.ShutdownGraphics(); System.exit(0); } /** * I_Init */ @Override public void Init () { //TODO: InitSound(); //TODO: InitGraphics(); } @Override public void WaitVBL(int count) { try { Thread.sleep(count*1000/70); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } @Override public void BeginRead() { if (DM.DD!=null) if (!DM.DD.isReading()) { // Set 8 tick reading time DM.DD.setReading(8); } } @Override public void EndRead() { } @Override public void AllocLow(int length) { ; // Dummy } // // I_Error // @Override public void Error (String error, Object ... args) { System.err.print("Error: "); System.err.printf(error,args); System.err.print("\n"); //va_end (argptr); //fflush( stderr ); // Shutdown. Here might be other errors. if (DM.demorecording) DM.DG.CheckDemoStatus(); if (DM.VI!=null) DM.VI.ShutdownGraphics(); try { DM.QuitNetGame (); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } // DM.VI.ShutdownGraphics(); System.exit(-1); } @Override public void Error (String error) { //va_list argptr; // Message first. //va_start (argptr,error); System.err.print("Error: "); System.err.printf(error); System.err.print("\n"); //va_end (argptr); //fflush( stderr ); // Shutdown. Here might be other errors. //if (demorecording) //G_CheckDemoStatus(); //D_QuitNetGame (); //I_ShutdownGraphics(); System.exit(-1); } public DoomSystem(){ emptycmd=new ticcmd_t(); } public static void MiscError(String error, Object ... args) { System.err.print("Error: "); System.err.printf(error); System.err.print("\n"); } @Override public void updateStatus(DoomStatus DS) { this.DM=DS.DM; } // This particular implementation will generate a popup box.// @Override public boolean GenerateAlert(String title,String cause) { MsgBox alert=new MsgBox(null, title, cause, true); return alert.isOk(); } } package i; import java.io.IOException; import m.IVariablesManager; import m.VarsManager; import rr.LightsAndColors; import v.VideoScaleInfo; import doom.CommandLine; import doom.DoomMain; import doom.ICommandLineManager; //Emacs style mode select -*- C++ -*- //----------------------------------------------------------------------------- // //$Id: Main.java,v 1.14 2016/06/06 14:22:17 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: //Main program, simply calls D_DoomMain high level loop after loading //some essential settings and determining what "flavor" we're going to run. // //----------------------------------------------------------------------------- public class Main { static final String rcsid = "$Id: Main.java,v 1.14 2016/06/06 14:22:17 velktron Exp $"; /** Global setting for initializing the renderer's bitplane mode */ public static BppMode bpp; /** Global setting for initializing the renderer's light levels */ public static int lightlevels; public static void main(String[] argv) throws IOException{ //First, get the command line parameters. ICommandLineManager CLM=new CommandLine(argv); // Handles variables and settings from default.cfg IVariablesManager VM=new VarsManager(CLM); // load before initting other systems, but don't apply them yet. System.out.print ("M_LoadDefaults: Load system defaults.\n"); VM.LoadDefaults (VM.getDefaultFile()); bpp= BppMode.Indexed; lightlevels=5; //if (VM.getSetting(name), value)) if (VM.isSettingLiteral("color_depth","hicolor")) bpp=BppMode.HiColor; if (VM.isSettingLiteral("color_depth","truecolor")) bpp=BppMode.TrueColor; if (CLM.CheckParmBool("-hicolor")) bpp=BppMode.HiColor; else if (CLM.CheckParmBool("-truecolor")) bpp=BppMode.TrueColor; // Here we create DOOM DoomMain<?, ?> DM=null; // Create a dummy. This will force static init to run. switch(bpp){ case Indexed: System.out.println("Indexed 8-bit mode selected..."); DM=new DoomMain.Indexed(); break; case HiColor: System.out.println("HiColor (Alpha) 16-bit mode selected..."); DM=new DoomMain.HiColor(); break; case TrueColor: case TrueColor32: System.out.println("TrueColor (extended colormaps) 24-bit mode selected..."); DM=new DoomMain.TrueColor(); break; } DM.setCommandLineArgs(CLM); DM.registerVariableManager(VM); DM.Init(); DM.Start(); return; } } package i; // Emacs style mode select -*- C++ -*- //----------------------------------------------------------------------------- // // $Id: SystemSoundInterface.java,v 1.2 2011/05/17 16:51:20 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: // System interface, sound. // //----------------------------------------------------------------------------- import data.sfxinfo_t; /* // UNIX hack, to be removed. #ifdef SNDSERV #include <stdio.h> extern FILE* sndserver; extern char* sndserver_filename; #endif*/ public interface SystemSoundInterface{ // Init at program start... public void InitSound(); // ... update sound buffer and audio device at runtime... public void UpdateSound(); public void SubmitSound(); // ... shut down and relase at program termination. public void ShutdownSound(); // // SFX I/O // // Initialize channels? void SetChannels(); // Get raw data lump index for sound descriptor. public int GetSfxLumpNum (sfxinfo_t sfxinfo ); // Starts a sound in a particular sound channel. public int StartSound ( int id, int vol, int sep, int pitch, int priority ); // Stops a sound channel. public void StopSound(int handle); // Called by S_*() functions // to see if a channel is still playing. // Returns 0 if no longer playing, 1 if playing. public boolean SoundIsPlaying(int handle); // Updates the volume, separation, // and pitch of a sound channel. public void UpdateSoundParams ( int handle, int vol, int sep, int pitch ); // // MUSIC I/O // public void InitMusic(); public void ShutdownMusic(); // Volume. public void SetMusicVolume(int volume); // PAUSE game handling. public void PauseSong(int handle); public void ResumeSong(int handle); // Registers a song handle to song data. public int RegisterSong(byte[] data); // Called by anything that wishes to start music. // plays a song, and when the song is done, // starts playing it again in an endless loop. // Horrible thing to do, considering. public void PlaySong ( int handle, int looping ); // Stops a song over 3 seconds. public void StopSong(int handle); // See above (register), then think backwards public void UnRegisterSong(int handle); } /** Blatantly ripped off Jake 2. Sieg heil! */ /* Copyright (C) 1997-2001 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. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package i; import java.awt.event.*; import java.util.LinkedList; import doom.event_t; import doom.evtype_t; /** * InputListener */ public final class InputListener implements KeyListener, MouseListener, MouseMotionListener, ComponentListener, MouseWheelListener { // modifications of eventQueue must be thread safe! private static LinkedList<event_t> eventQueue = new LinkedList<event_t>(); static void addEvent(event_t ev) { synchronized (eventQueue) { eventQueue.addLast(ev); } } public static event_t nextEvent() { event_t ev; synchronized (eventQueue) { ev = (!eventQueue.isEmpty())?(event_t)eventQueue.removeFirst():null; } return ev; } public void keyPressed(KeyEvent e) { if (!((e.getModifiersEx() & InputEvent.ALT_GRAPH_DOWN_MASK) != 0)) { addEvent(new event_t(evtype_t.ev_keydown, e.getKeyCode())); } } public void keyReleased(KeyEvent e) { addEvent(new event_t(evtype_t.ev_keyup, e.getKeyCode())); } public void keyTyped(KeyEvent e) { if ((e.getModifiersEx() & InputEvent.ALT_GRAPH_DOWN_MASK) != 0) { addEvent(new event_t(evtype_t.ev_keydown, e.getKeyCode())); addEvent(new event_t(evtype_t.ev_keyup, e.getKeyCode())); } } public void mouseClicked(MouseEvent e) { } public void mouseEntered(MouseEvent e) { } public void mouseExited(MouseEvent e) { } public void mousePressed(MouseEvent e) { addEvent(new event_t(evtype_t.ev_mouse, e.getButton())); } public void mouseReleased(MouseEvent e) { addEvent(new event_t(evtype_t.ev_mouse, e.getButton())); } public void mouseDragged(MouseEvent e) { addEvent(new event_t(evtype_t.ev_mouse, e.getButton(),e.getX(),e.getY())); } public void mouseMoved(MouseEvent e) { addEvent(new event_t(evtype_t.ev_mouse, e.getButton(),e.getX(),e.getY())); } public void mouseWheelMoved(MouseWheelEvent e) { addEvent(new event_t(evtype_t.ev_mousewheel, e.getWheelRotation())); } // Don't listen? public void componentHidden(ComponentEvent e) { } public void componentMoved(ComponentEvent e) { // addEvent(new event_t(event_t.ConfigureNotify, e)); } public void componentResized(ComponentEvent e) { // addEvent(new event_t(event_t.ConfigureNotify, e)); } public void componentShown(ComponentEvent e) { // JOGLKBD.c = e.getComponent(); // addEvent(new event_t(event_t.CreateNotify, e)); } } package rr; import java.io.DataInputStream; import java.io.IOException; import java.nio.ByteBuffer; import p.Resettable; import static m.fixed_t.FRACBITS; import w.DoomIO; import w.IPackableDoomObject; import w.IReadableDoomObject; /** * The SideDef. * * @author admin */ public class side_t implements IReadableDoomObject, IPackableDoomObject, Resettable { /** (fixed_t) add this to the calculated texture column */ public int textureoffset; /** (fixed_t) add this to the calculated texture top */ public int rowoffset; /** * Texture indices. We do not maintain names here. */ public short toptexture; public short bottomtexture; public short midtexture; /** Sector the SideDef is facing. MAES: pointer */ public sector_t sector; public int sectorid; public int special; public side_t() { } public side_t(int textureoffset, int rowoffset, short toptexture, short bottomtexture, short midtexture, sector_t sector) { super(); this.textureoffset = textureoffset; this.rowoffset = rowoffset; this.toptexture = toptexture; this.bottomtexture = bottomtexture; this.midtexture = midtexture; this.sector = sector; } @Override public void read(DataInputStream f) throws IOException { this.textureoffset = DoomIO.readLEShort(f) << FRACBITS; this.rowoffset = DoomIO.readLEShort(f) << FRACBITS; this.toptexture = DoomIO.readLEShort(f); this.bottomtexture = DoomIO.readLEShort(f); this.midtexture = DoomIO.readLEShort(f); // this.sectorid=f.readLEInt(); } @Override public void pack(ByteBuffer buffer) { buffer.putShort((short) (textureoffset >> FRACBITS)); buffer.putShort((short) (rowoffset >> FRACBITS)); buffer.putShort(toptexture); buffer.putShort(bottomtexture); buffer.putShort(midtexture); } @Override public void reset() { textureoffset = 0; rowoffset = 0; toptexture = 0; bottomtexture = 0; midtexture = 0; sector = null; sectorid = 0; special = 0; } } package rr; public interface IDetailAware { public static int HIGH_DETAIL=0; public static int LOW_DETAIL=1; void setDetail(int detailshift); } package rr; import static data.Tables.ANG180; import static data.Tables.ANG270; import static data.Tables.ANG90; import static data.Tables.SlopeDiv; import static data.Tables.tantoangle; import utils.C2JUtils; import doom.player_t; public class ViewVars { // Found in draw_c. Only ever used in renderer. public int windowx; public int windowy; public int width; public int height; // MAES: outsiders have no business peeking into this. // Or...well..maybe they do. It's only used to center the "pause" X // position. // TODO: get rid of this? public int scaledwidth; public int centerx; public int centery; /** Used to determine the view center and projection in view units fixed_t */ public int centerxfrac, centeryfrac, projection; /** fixed_t */ public int x, y, z; // MAES: an exception to strict type safety. These are used only in here, // anyway (?) and have no special functions. // Plus I must use them as indexes. angle_t public long angle; /** fixed */ public int cos, sin; public player_t player; /** Heretic/freeview stuff? */ public int lookdir; // 0 = high, 1 = low. Normally only the menu and the interface can change // that. public int detailshift; public int WEAPONADJUST; public int BOBADJUST; /** * constant arrays used for psprite clipping and initializing clipping */ public short[] negonearray; // MAES: in scaling public short[] screenheightarray;// MAES: in scaling /** Mirrors the one in renderer... */ public long[] xtoviewangle; public final void initNegOneArray(int screenwidth){ C2JUtils.memset(negonearray, (short)-1,screenwidth); } public final long PointToAngle(int x, int y) { // MAES: note how we don't use &BITS32 here. That is because // we know that the maximum possible value of tantoangle is angle // This way, we are actually working with vectors emanating // from our current position. x -= this.x; y -= this.y; if ((x == 0) && (y == 0)) return 0; if (x >= 0) { // x >=0 if (y >= 0) { // y>= 0 if (x > y) { // octant 0 return tantoangle[SlopeDiv(y, x)]; } else { // octant 1 return (ANG90 - 1 - tantoangle[SlopeDiv(x, y)]); } } else { // y<0 y = -y; if (x > y) { // octant 8 return (-tantoangle[SlopeDiv(y, x)]); } else { // octant 7 return (ANG270 + tantoangle[SlopeDiv(x, y)]); } } } else { // x<0 x = -x; if (y >= 0) { // y>= 0 if (x > y) { // octant 3 return (ANG180 - 1 - tantoangle[SlopeDiv(y, x)]); } else { // octant 2 return (ANG90 + tantoangle[SlopeDiv(x, y)]); } } else { // y<0 y = -y; if (x > y) { // octant 4 return (ANG180 + tantoangle[SlopeDiv(y, x)]); } else { // octant 5 return (ANG270 - 1 - tantoangle[SlopeDiv(x, y)]); } } } // This is actually unreachable. // return 0; } public final int getViewWindowX(){ return windowx; } public final int getViewWindowY(){ return windowy; } public final int getScaledViewWidth(){ return scaledwidth; } public final int getScaledViewHeight() { return height; } } package rr; public interface planefunction_t { } package rr; import java.io.IOException; import rr.parallel.IGetSmpColumn; /** All texture, flat and sprite management operations should be handled * by an implementing class. As of now, the renderer does both, though it's * not really the most ideal. * * @author Velktron * */ public interface TextureManager<T> extends IGetColumn<T>, IGetCachedColumn<T>,IGetSmpColumn{ public final static String[] texturelumps={"TEXTURE1","TEXTURE2"}; public final static int NUMTEXLUMPS=texturelumps.length; public final static int TEXTURE1=0; public final static int TEXTURE2=1; int TextureNumForName(String texname); /**The "num" expected here is the internal flat number, * not the absolute lump number. So impement accordingly. * * @param flatname * @return */ int FlatNumForName(String flatname); void PrecacheLevel() throws IOException; void GenerateComposite(int tex); int getTextureheight(int texnum); int getTextureTranslation(int texnum); int getFlatTranslation(int flatnum); void setTextureTranslation(int texnum, int amount); void setFlatTranslation(int flatnum,int amount); int CheckTextureNumForName(String texnamem); String CheckTextureNameForNum(int texnum); int getTexturewidthmask(int tex); int getTextureColumnLump(int tex, int col); char getTextureColumnOfs(int tex, int col); T[] getTextureComposite(int tex); T getTextureComposite(int tex, int col); void InitFlats(); void InitTextures() throws IOException; //int getFirstFlat(); int getSkyTextureMid(); int getSkyFlatNum(); int getSkyTexture(); void setSkyTexture(int skytexture); int InitSkyMap(); void setSkyFlatNum(int skyflatnum); void GenerateLookup(int texnum) throws IOException; int getFlatLumpNum(int flatnum); T getRogueColumn(int lump, int column); patch_t getMaskedComposite(int tex); void GenerateMaskedComposite(int texnum); /** Return a "sanitized" patch. If data is insufficient, return * a default patch or attempt a partial draw. * * @param patchnum * @return */ public T getSafeFlat(int flatnum); column_t GetColumnStruct(int tex, int col); void setSMPVars(int nUMMASKEDTHREADS); } package rr; import static rr.line_t.*; import static data.Defines.ANGLETOSKYSHIFT; import static data.Defines.NF_SUBSECTOR; import static data.Defines.PU_CACHE; import static data.Defines.SIL_BOTH; import static data.Defines.SIL_BOTTOM; import static data.Defines.SIL_TOP; import static data.Limits.MAXHEIGHT; import static data.Limits.MAXSEGS; import static data.Limits.MAXWIDTH; import static data.Tables.ANG180; import static data.Tables.ANG270; import static data.Tables.ANG90; import static data.Tables.ANGLETOFINESHIFT; import static data.Tables.BITS32; import static data.Tables.DBITS; import static data.Tables.FINEANGLES; import static data.Tables.QUARTERMARK; import static data.Tables.SlopeDiv; import static data.Tables.addAngles; import static data.Tables.finecosine; import static data.Tables.finesine; import static data.Tables.finetangent; import static data.Tables.tantoangle; import static m.BBox.BOXBOTTOM; import static m.BBox.BOXLEFT; import static m.BBox.BOXRIGHT; import static m.BBox.BOXTOP; import static m.fixed_t.FRACBITS; import static m.fixed_t.FRACUNIT; import static m.fixed_t.FixedDiv; import static m.fixed_t.FixedMul; import java.io.IOException; import java.util.ArrayList; import java.util.List; import m.IDoomMenu; import m.MenuMisc; import p.AbstractLevelLoader; import p.UnifiedGameMap; import p.mobj_t; import rr.UnifiedRenderer.Segs; import rr.drawfuns.ColFuncs; import rr.drawfuns.ColVars; import rr.drawfuns.DoomColumnFunction; import rr.drawfuns.DoomSpanFunction; import rr.drawfuns.R_DrawColumnBoom; import rr.drawfuns.R_DrawColumnBoomLow; import rr.drawfuns.R_DrawColumnBoomOpt; import rr.drawfuns.R_DrawColumnBoomOptLow; import rr.drawfuns.R_DrawFuzzColumn; import rr.drawfuns.R_DrawFuzzColumnLow; import rr.drawfuns.R_DrawSpanLow; import rr.drawfuns.R_DrawSpanUnrolled; import rr.drawfuns.R_DrawTLColumn; import rr.drawfuns.R_DrawTranslatedColumn; import rr.drawfuns.R_DrawTranslatedColumnLow; import rr.drawfuns.SpanVars; import i.IDoomSystem; import utils.C2JUtils; import v.DoomVideoRenderer; import v.IVideoScale; import v.IVideoScaleAware; import w.IWadLoader; import data.Defines; import data.Tables; import doom.DoomMain; import doom.DoomStatus; import doom.IDoomGameNetworking; import doom.player_t; import doom.think_t; import doom.thinker_t; import static rr.LightsAndColors.*; /** * Most shared -essential- status information, methods and classes related to * the software rendering subsystem are found here, shared between the various * implementations of the Doom's renderer. Not the cleanest or more OO way * possible, but still a good way to avoid duplicating common code. Some stuff * like Texture, Flat and Sprite management are also found -or at least * implemented temporarily- here, until a cleaner split can be made. This is a * kind of "Jack of all trades" class, but hopefully not for long. * * @author velktron */ public abstract class RendererState<T, V> implements Renderer<T, V>, ILimitResettable { protected static final boolean DEBUG = false; protected static final boolean DEBUG2 = false; // ///////////////////// STATUS //////////////////////// protected DoomMain<T, V> DM; protected IDoomGameNetworking DGN; protected AbstractLevelLoader LL; protected ISegDrawer MySegs; protected IDoomMenu Menu; protected BSP MyBSP; protected PlaneDrawer<T, V> MyPlanes; protected IMaskedDrawer<T, V> MyThings; protected DoomVideoRenderer<T, V> V; protected UnifiedGameMap P; public IWadLoader W; public ISpriteManager SM; public IVisSpriteManagement<V> VIS; public IDoomSystem I; protected TextureManager<T> TexMan; public ViewVars view; public LightsAndColors<V> colormaps; public SegVars seg_vars; public Visplanes vp_vars; // Rendering subsystems that are detailshift-aware protected List<IDetailAware> detailaware; // The only reason to query scaledviewwidth from outside the renderer, is // this. public boolean isFullHeight() { return (view.height == SCREENHEIGHT); } public boolean isFullWidth() { return (view.scaledwidth == SCREENWIDTH); } public boolean isFullScreen() { return isFullWidth() && isFullHeight(); } /** * Increment every time a check is made For some reason, this needs to be * visible even by enemies thinking :-S */ protected int validcount = 1; /** Who can set this? A: The Menu. */ protected boolean setsizeneeded; protected int setblocks; protected int setdetail; // private BSPVars bspvars; /** * R_SetViewSize Do not really change anything here, because it might be in * the middle of a refresh. The change will take effect next refresh. * * @param blocks * 11 is full screen, 9 default. * @param detail * 0= high, 1 =low. */ public void SetViewSize(int blocks, int detail) { // System.out.println("SetViewSize"); setsizeneeded = true; setblocks = blocks; setdetail = detail; for (IDetailAware d : detailaware) { d.setDetail(setdetail); } } /** * R_SetupFrame */ public void SetupFrame(player_t player) { int i; view.player = player; view.x = player.mo.x; view.y = player.mo.y; // viewangle = addAngles(player.mo.angle , viewangleoffset); view.angle = player.mo.angle & BITS32; // With 32 colormaps, a bump of 1 or 2 is normal. // With more than 32, it should be obviously higher. int bumplight = Math.max(LBITS - 5, 0); // Be a bit more generous, otherwise the effect is not // as evident with truecolor maps. bumplight += (bumplight > 0) ? 1 : 0; colormaps.extralight = player.extralight << bumplight; view.z = player.viewz; view.lookdir = (int) player.lookdir; int tempCentery; // MAES: hacks based on Heretic. Weapon movement needs to be compensated if (setblocks == 11) tempCentery = (view.height / 2) + (int) (view.lookdir * SCREEN_MUL * setblocks) / 11; else tempCentery = (view.height / 2) + (int) (view.lookdir * SCREEN_MUL * setblocks) / 10; if (view.centery != tempCentery) { view.centery = tempCentery; view.centeryfrac = view.centery << FRACBITS; int yslope[] = vp_vars.yslope; for (i = 0; i < view.height; i++) { yslope[i] = FixedDiv( (view.width << view.detailshift) / 2 * FRACUNIT, Math.abs(((i - view.centery) << FRACBITS) + FRACUNIT / 2)); } skydcvars.centery = maskedcvars.centery = dcvars.centery = view.centery; } view.sin = Tables.finesine(view.angle); view.cos = Tables.finecosine(view.angle); sscount = 0; if (player.fixedcolormap != 0) { colormaps.fixedcolormap = colormaps.colormaps[player.fixedcolormap]; // Offset by fixedcolomap // pfixedcolormap =player.fixedcolormap*256; colormaps.walllights = colormaps.scalelightfixed; for (i = 0; i < MAXLIGHTSCALE; i++) colormaps.scalelightfixed[i] = colormaps.fixedcolormap; } else colormaps.fixedcolormap = null; framecount++; validcount++; } /** * R_SetupFrame for a particular actor. */ public void SetupFrame(mobj_t actor) { // viewplayer = player; view.x = actor.x; view.y = actor.y; // viewangle = addAngles(player.mo.angle , viewangleoffset); view.angle = actor.angle & BITS32; // extralight = actor.extralight; view.z = actor.z + actor.height; view.sin = finesine(view.angle); view.cos = finecosine(view.angle); sscount = 0; framecount++; validcount++; } public RendererState(DoomStatus<T, V> DS) { this.updateStatus(DS); // These don't change between implementations, yet. this.MyBSP = new BSP(); this.view = new ViewVars(); this.seg_vars = new SegVars(); this.dcvars=new ColVars<T,V>(); this.dsvars=new SpanVars<T,V>(); this.maskedcvars=new ColVars<T,V>(); this.skydcvars=new ColVars<T,V>(); this.colfunclow=new ColFuncs<T,V>(); this.colfunchi=new ColFuncs<T,V>(); this.detailaware = new ArrayList<IDetailAware>(); this.colormaps=new LightsAndColors<V>(); // It's better to construct this here this.TexMan = (TextureManager<T>) new SimpleTextureManager(DS); this.SM=new SpriteManager(DS); // Visplane variables this.vp_vars = new Visplanes(view, TexMan); // Initialize array of minus ones for sprite clipping view.initNegOneArray(SCREENWIDTH); // Set rendering functions only after screen sizes // and stuff have been set. this.MyPlanes = new Planes(this); this.VIS=new VisSprites<V>(this); this.MyThings = new SimpleThings<T,V>(this); } @SuppressWarnings("unchecked") @Override public void updateStatus(DoomStatus<?, ?> DC) { this.DM = (DoomMain<T, V>) DC.DM; this.DGN = DC.DGN; this.LL = DC.LL; this.W = DC.W; this.P = DC.P; // We must also connect screen to V. Don't forget it. Do it in Init(), // OK? this.V = (DoomVideoRenderer<T, V>) DC.V; this.SM = DC.SM; this.I = DC.I; if (VIS != null) this.VIS.updateStatus(this); } // ////////////////////////////// THINGS //////////////////////////////// protected final class BSP extends BSPVars { /** newend is one past the last valid seg (cliprange_t) */ int newend; cliprange_t[] solidsegs; public BSP() { solidsegs = new cliprange_t[MAXSEGS + 1]; C2JUtils.initArrayOfObjects(solidsegs); } /** * R_ClipSolidWallSegment Does handle solid walls, single sided LineDefs * (middle texture) that entirely block the view VERTICALLY. Handles * "clipranges" for a solid wall, aka where it blocks the view. * * @param first * starting y coord? * @param last * ending y coord? */ private void ClipSolidWallSegment(int first, int last) { int next; int start; // int maxlast=Integer.MIN_VALUE; start = 0; // within solidsegs // Find the first cliprange that touches the range. // Actually, the first one not completely hiding it (its last must // be lower than first. while (solidsegs[start].last < first - 1) start++; // If the post begins above the lastly found cliprange... if (first < solidsegs[start].first) { // ..and ends above it, too (no overlapping) if (last < solidsegs[start].first - 1) { // ... then the post is entirely visible (above start), // so insert a new clippost. Calling this function // tells the renderer that there is an obstruction. MySegs.StoreWallRange(first, last); // Newend should have a value of 2 if we are at the // beginning of a new frame. next = newend; newend++; if (next >= solidsegs.length) ResizeSolidSegs(); while (next != start) { // *next=*(next-1); /* * MAES: I think this is supposed to copy the structs * solidsegs[next] = solidsegs[next-1].clone(); OK, so * basically the last solidseg copies its previous, and * so on until we reach the start. This means that at * some point, the value of the start solidseg is * duplicated. */ solidsegs[next].copy(solidsegs[next - 1]); next--; } // At this point, next points at start. // Therefore, start solidsegs[next].first = first; solidsegs[next].last = last; return; } // There is a fragment above *start. This can occur if it a // post does start before another, but its lower edge overlaps // (partial, upper occlusion) MySegs.StoreWallRange(first, solidsegs[start].first - 1); // Now adjust the clip size. solidsegs[start].first = first; } // We can reach this only if a post starts AFTER another // Bottom contained in start? Obviously it won't be visible. if (last <= solidsegs[start].last) return; next = start; while (last >= solidsegs[(next + 1)].first - 1) { // There is a fragment between two posts. MySegs.StoreWallRange(solidsegs[next].last + 1, solidsegs[next + 1].first - 1); next++; if (last <= solidsegs[next].last) { // Bottom is contained in next. // Adjust the clip size. solidsegs[start].last = solidsegs[next].last; // goto crunch; { // crunch code if (next == start) { // Post just extended past the bottom of one post. return; } while (next++ != newend) { // Remove a post. // MAES: this is a struct copy. if (next >= solidsegs.length) ResizeSolidSegs(); solidsegs[++start].copy(solidsegs[next]); } newend = start + 1; return; } } } // There is a fragment after *next. MySegs.StoreWallRange(solidsegs[next].last + 1, last); // Adjust the clip size. solidsegs[start].last = last; // Remove start+1 to next from the clip list, // because start now covers their area. { // crunch code if (next == start) { // Post just extended past the bottom of one post. return; } while (next++ != newend) { // Remove a post. // MAES: this is a struct copy. // MAES: this can overflow, breaking e.g. MAP30 of Final // Doom. if (next >= solidsegs.length) ResizeSolidSegs(); solidsegs[++start].copy(solidsegs[next]); } newend = start + 1; return; } } protected final void ResizeSolidSegs() { solidsegs = C2JUtils.resize(solidsegs, solidsegs.length * 2); } // // R_ClipPassWallSegment // Clips the given range of columns, // but does not includes it in the clip list. // Does handle windows, // e.g. LineDefs with upper and lower texture. // private void ClipPassWallSegment(int first, int last) { // Find the first range that touches the range // (adjacent pixels are touching). int start = 0; while (solidsegs[start].last < first - 1) start++; if (first < solidsegs[start].first) { if (last < solidsegs[start].first - 1) { // Post is entirely visible (above start). MySegs.StoreWallRange(first, last); return; } // There is a fragment above *start. MySegs.StoreWallRange(first, solidsegs[start].first - 1); } // Bottom contained in start? if (last <= solidsegs[start].last) return; // MAES: Java absolutely can't do without a sanity check here. // if (startptr>=MAXSEGS-2) return; while (last >= solidsegs[start + 1].first - 1) { // There is a fragment between two posts. MySegs.StoreWallRange(solidsegs[start].last + 1, solidsegs[start + 1].first - 1); start++; // if (startptr>=MAXSEGS-2) return; // start=solidsegs[startptr]; if (last <= solidsegs[start].last) return; } // There is a fragment after *next. MySegs.StoreWallRange(solidsegs[start].last + 1, last); } /** * R_ClearClipSegs Clears the clipping segs list. The list is actually * fixed size for efficiency reasons, so it just tells Doom to use the * first two solidsegs, which are "neutered". It's interesting to note * how the solidsegs begin and end just "outside" the visible borders of * the screen. */ public void ClearClipSegs() { solidsegs[0].first = -0x7fffffff; solidsegs[0].last = -1; solidsegs[1].first = view.width; solidsegs[1].last = 0x7fffffff; newend = 2; // point so solidsegs[2]; } /** * R_AddLine Called after a SubSector BSP trasversal ends up in a * "final" subsector. Clips the given segment and adds any visible * pieces to the line list. It also determines what kind of boundary * (line) visplane clipping should be performed. E.g. window, final * 1-sided line, closed door etc.) CAREFUL: was the source of much * frustration with visplanes... */ private void AddLine(seg_t line) { if (DEBUG) System.out.println("Entered AddLine for " + line); int x1; int x2; long angle1; long angle2; long span; long tspan; curline = line; // OPTIMIZE: quickly reject orthogonal back sides. angle1 = view.PointToAngle(line.v1x, line.v1y); angle2 = view.PointToAngle(line.v2x, line.v2y); // Clip to view edges. // OPTIMIZE: make constant out of 2*clipangle (FIELDOFVIEW). span = addAngles(angle1, -angle2); // Back side? I.e. backface culling? if (span >= ANG180) return; // Global angle needed by segcalc. MySegs.setGlobalAngle(angle1); angle1 -= view.angle; angle2 -= view.angle; angle1 &= BITS32; angle2 &= BITS32; tspan = addAngles(angle1, clipangle); if (tspan > CLIPANGLE2) { tspan -= CLIPANGLE2; tspan &= BITS32; // Totally off the left edge? if (tspan >= span) return; angle1 = clipangle; } tspan = addAngles(clipangle, -angle2); if (tspan > CLIPANGLE2) { tspan -= CLIPANGLE2; tspan &= BITS32; // Totally off the left edge? if (tspan >= span) return; angle2 = -clipangle; angle2 &= BITS32; } // The seg is in the view range, // but not necessarily visible. angle1 = ((angle1 + ANG90) & BITS32) >>> ANGLETOFINESHIFT; angle2 = ((angle2 + ANG90) & BITS32) >>> ANGLETOFINESHIFT; x1 = viewangletox[(int) angle1]; x2 = viewangletox[(int) angle2]; // Does not cross a pixel? if (x1 == x2) return; backsector = line.backsector; // Single sided line? if (backsector == null) { if (DEBUG) System.out .println("Entering ClipSolidWallSegment SS with params " + x1 + " " + (x2 - 1)); ClipSolidWallSegment(x1, x2 - 1); // to clipsolid if (DEBUG) System.out.println("Exiting ClipSolidWallSegment"); return; } // Closed door. if (backsector.ceilingheight <= frontsector.floorheight || backsector.floorheight >= frontsector.ceilingheight) { if (DEBUG) System.out .println("Entering ClipSolidWallSegment Closed door with params " + x1 + " " + (x2 - 1)); ClipSolidWallSegment(x1, x2 - 1); ; // to clipsolid return; } // Window. This includes same-level floors with different textures if (backsector.ceilingheight != frontsector.ceilingheight || backsector.floorheight != frontsector.floorheight) { if (DEBUG) System.out .println("Entering ClipSolidWallSegment window with params " + x1 + " " + (x2 - 1)); ClipPassWallSegment(x1, x2 - 1); // to clippass return; } // Reject empty lines used for triggers // and special events. // Identical floor and ceiling on both sides, // identical light levels on both sides, // and no middle texture. if (backsector.ceilingpic == frontsector.ceilingpic && backsector.floorpic == frontsector.floorpic && backsector.lightlevel == frontsector.lightlevel && curline.sidedef.midtexture == 0) { return; } // If nothing of the previous holds, then we are // treating the case of same-level, differently // textured floors. ACHTUNG, this caused the "bleeding floor" // bug, which is now fixed. // Fucking GOTOs.... ClipPassWallSegment(x1, x2 - 1); // to clippass if (DEBUG) System.out.println("Exiting AddLine for " + line); } // // R_CheckBBox // Checks BSP node/subtree bounding box. // Returns true // if some part of the bbox might be visible. // private int[][] checkcoord = { { 3, 0, 2, 1 }, { 3, 0, 2, 0 }, { 3, 1, 2, 0 }, { 0 }, { 2, 0, 2, 1 }, { 0, 0, 0, 0 }, { 3, 1, 3, 0 }, { 0 }, { 2, 0, 3, 1 }, { 2, 1, 3, 1 }, { 2, 1, 3, 0 } }; /** * @param bspcoord * (fixed_t* as bbox) * @return */ public boolean CheckBBox(int[] bspcoord) { int boxx; int boxy; int boxpos; // fixed_t int x1; int y1; int x2; int y2; // angle_t long angle1; long angle2; long span; long tspan; cliprange_t start; int sx1; int sx2; // Find the corners of the box // that define the edges from current viewpoint. if (view.x <= bspcoord[BOXLEFT]) boxx = 0; else if (view.x < bspcoord[BOXRIGHT]) boxx = 1; else boxx = 2; if (view.y >= bspcoord[BOXTOP]) boxy = 0; else if (view.y > bspcoord[BOXBOTTOM]) boxy = 1; else boxy = 2; boxpos = (boxy << 2) + boxx; if (boxpos == 5) return true; x1 = bspcoord[checkcoord[boxpos][0]]; y1 = bspcoord[checkcoord[boxpos][1]]; x2 = bspcoord[checkcoord[boxpos][2]]; y2 = bspcoord[checkcoord[boxpos][3]]; // check clip list for an open space angle1 = view.PointToAngle(x1, y1) - view.angle; angle2 = view.PointToAngle(x2, y2) - view.angle; angle1 &= BITS32; angle2 &= BITS32; span = angle1 - angle2; span &= BITS32; // Sitting on a line? if (span >= ANG180) return true; tspan = angle1 + clipangle; tspan &= BITS32; if (tspan > CLIPANGLE2) { tspan -= CLIPANGLE2; tspan &= BITS32; // Totally off the left edge? if (tspan >= span) return false; angle1 = clipangle; } tspan = (clipangle - angle2) & BITS32; ; if (tspan > CLIPANGLE2) { tspan -= CLIPANGLE2; tspan &= BITS32; // Totally off the left edge? if (tspan >= span) return false; angle2 = -clipangle; angle2 &= BITS32; } // Find the first clippost // that touches the source post // (adjacent pixels are touching). angle1 = ((angle1 + ANG90) & BITS32) >>> ANGLETOFINESHIFT; angle2 = ((angle2 + ANG90) & BITS32) >>> ANGLETOFINESHIFT; sx1 = viewangletox[(int) angle1]; sx2 = viewangletox[(int) angle2]; // Does not cross a pixel. if (sx1 == sx2) return false; sx2--; int pstart = 0; start = solidsegs[pstart]; // FIXME: possible solidseg overflow here overflows while (start.last < sx2 && pstart < MAXSEGS) start = solidsegs[pstart++]; if (sx1 >= start.first && sx2 <= start.last) { // The clippost contains the new span. return false; } return true; } /** * R_Subsector Determine floor/ceiling planes. Add sprites of things in * sector. Draw one or more line segments. It also alters the visplane * list! * * @param num * Subsector from subsector_t list in Lever Loader. */ private void Subsector(int num) { if (DEBUG) System.out.println("\t\tSubSector " + num + " to render"); int count; int line; // pointer into a list of segs instead of seg_t subsector_t sub; if (RANGECHECK) { if (num >= LL.numsubsectors) I.Error("R_Subsector: ss %d with numss = %d", num, LL.numsubsectors); } sscount++; sub = LL.subsectors[num]; frontsector = sub.sector; if (DEBUG) System.out.println("Frontsector to render :" + frontsector); count = sub.numlines; // line = LL.segs[sub.firstline]; line = sub.firstline; if (DEBUG) System.out .println("Trying to find an existing FLOOR visplane..."); if (frontsector.floorheight < view.z) { vp_vars.floorplane = vp_vars.FindPlane(frontsector.floorheight, frontsector.floorpic, frontsector.lightlevel); } else // FIXME: unclear what would happen with a null visplane used // It's never checked explicitly for either condition, just // called straight. vp_vars.floorplane = -1; // in lieu of NULL // System.out.println("Trying to find an existing CEILING visplane..."); if (frontsector.ceilingheight > view.z || frontsector.ceilingpic == TexMan.getSkyFlatNum()) { vp_vars.ceilingplane = vp_vars.FindPlane(frontsector.ceilingheight, frontsector.ceilingpic, frontsector.lightlevel); } else vp_vars.ceilingplane = -1; // In lieu of NULL. Will bomb if // actually // used. VIS.AddSprites(frontsector); if (DEBUG) System.out.println("Enter Addline for SubSector " + num + " count " + count); while (count-- > 0) { AddLine(LL.segs[line]); line++; } if (DEBUG) System.out.println("Exit Addline for SubSector " + num); } /** * RenderBSPNode Renders all subsectors below a given node, traversing * subtree recursively. Just call with BSP root. */ public void RenderBSPNode(int bspnum) { if (DEBUG) System.out.println("Processing BSP Node " + bspnum); node_t bsp; int side; // Found a subsector? Then further decisions are taken, in, well, // SubSector. if (C2JUtils.flags(bspnum, NF_SUBSECTOR)) { if (DEBUG) System.out.println("Subsector found."); if (bspnum == -1) Subsector(0); else Subsector(bspnum & (~NF_SUBSECTOR)); return; } bsp = LL.nodes[bspnum]; // Decide which side the view point is on. side = bsp.PointOnSide(view.x, view.y); if (DEBUG) System.out.println("\tView side: " + side); // Recursively divide front space. if (DEBUG) System.out.println("\tEnter Front space of " + bspnum); RenderBSPNode(bsp.children[side]); if (DEBUG) System.out.println("\tReturn Front space of " + bspnum); // Possibly divide back space. if (CheckBBox(bsp.bbox[side ^ 1].bbox)) { if (DEBUG) System.out.println("\tEnter Back space of " + bspnum); RenderBSPNode(bsp.children[side ^ 1]); if (DEBUG) System.out.println("\tReturn Back space of " + bspnum); } } } protected abstract class SegDrawer implements ISegDrawer { protected static final int HEIGHTBITS = 12; protected static final int HEIGHTUNIT = (1 << HEIGHTBITS); protected final Visplanes vp_vars; protected final SegVars seg_vars; // Fast blanking buffers. protected short[] BLANKFLOORCLIP; protected short[] BLANKCEILINGCLIP; @Override public short[] getBLANKFLOORCLIP() { return BLANKFLOORCLIP; } @Override public short[] getBLANKCEILINGCLIP() { return BLANKCEILINGCLIP; } /** fixed_t */ protected int pixhigh, pixlow, pixhighstep, pixlowstep, topfrac, topstep, bottomfrac, bottomstep; protected int worldtop, worldbottom, worldhigh, worldlow; /** True if any of the segs textures might be visible. */ protected boolean segtextured; /** * Clip values are the solid pixel bounding the range. floorclip starts * out SCREENHEIGHT ceilingclip starts out -1 */ protected short[] floorclip, ceilingclip; public final short[] getFloorClip() { return floorclip; } public final short[] getCeilingClip() { return ceilingclip; } /** False if the back side is the same plane. */ protected boolean markfloor, markceiling; protected boolean maskedtexture; protected int toptexture; protected int bottomtexture; protected int midtexture; /** angle_t, used after adding ANG90 in StoreWallRange */ protected long rw_normalangle; /** angle to line origin */ protected long rw_angle1; // // regular wall // protected int rw_x; protected int rw_stopx; protected long rw_centerangle; // angle_t /** fixed_t */ protected int rw_offset, rw_distance, rw_scale, rw_scalestep, rw_midtexturemid, rw_toptexturemid, rw_bottomtexturemid; public void resetLimits() { drawseg_t[] tmp = new drawseg_t[seg_vars.MAXDRAWSEGS]; System.arraycopy(seg_vars.drawsegs, 0, tmp, 0, seg_vars.MAXDRAWSEGS); // Now, that was quite a haircut!. seg_vars.drawsegs = tmp; // System.out.println("Drawseg buffer cut back to original limit of "+MAXDRAWSEGS); } public void sync(){ // Nothing required if serial. } /** * R_StoreWallRange A wall segment will be drawn between start and stop * pixels (inclusive). This is the only place where * markceiling/markfloor can be set. Can only be called from * ClipSolidWallSegment and ClipPassWallSegment. * * @throws IOException */ public void StoreWallRange(int start, int stop) { if (DEBUG2) System.out.println("\t\t\t\tStorewallrange called between " + start + " and " + stop); int hyp; // fixed_t int sineval; // fixed_t int distangle; long offsetangle; // angle_t int vtop; // fixed_t int lightnum; drawseg_t seg; // don't overflow and crash if (seg_vars.ds_p == seg_vars.drawsegs.length) seg_vars.ResizeDrawsegs(); if (RANGECHECK) { if (start >= view.width || start > stop) I.Error("Bad R_RenderWallRange: %d to %d", start, stop); } seg = seg_vars.drawsegs[seg_vars.ds_p]; MyBSP.sidedef = MyBSP.curline.sidedef; MyBSP.linedef = MyBSP.curline.linedef; // mark the segment as visible for auto map MyBSP.linedef.flags |= ML_MAPPED; // calculate rw_distance for scale calculation rw_normalangle = addAngles(MyBSP.curline.angle, ANG90); /* * MAES: ok, this is a tricky spot. angle_t's are supposed to be * always positive 32-bit unsigned integers, so a subtraction should * be always positive by definition, right? WRONG: this fucking spot * caused "blind spots" at certain angles because ONLY HERE angles * are supposed to be treated as SIGNED and result in differences * <180 degrees -_- The only way to coerce this behavior is to cast * both as signed ints. */ offsetangle = Math.abs((int) rw_normalangle - (int) rw_angle1); if (offsetangle > ANG90) offsetangle = ANG90; // It should fit even in a signed int, by now. distangle = (int) (ANG90 - offsetangle); hyp = PointToDist(MyBSP.curline.v1x, MyBSP.curline.v1y); sineval = finesine(distangle); rw_distance = FixedMul(hyp, sineval); seg.x1 = rw_x = start; seg.x2 = stop; seg.curline = MyBSP.curline; /* * This is the only place it's ever explicitly assigned. Therefore * it always starts at stop+1. */ rw_stopx = stop + 1; // calculate scale at both ends and step // this is the ONLY place where rw_scale is set. seg.scale1 = rw_scale = ScaleFromGlobalAngle((view.angle + xtoviewangle[start])); if (stop > start) { seg.scale2 = ScaleFromGlobalAngle(view.angle + xtoviewangle[stop]); seg.scalestep = rw_scalestep = (seg.scale2 - rw_scale) / (stop - start); } else { // UNUSED: try to fix the stretched line bug /* * #if 0 if (rw_distance < FRACUNIT/2) { fixed_t trx,try; * fixed_t gxt,gyt; trx = curline.v1.x - viewx; try = * curline.v1.y - viewy; gxt = FixedMul(trx,viewcos); gyt = * -FixedMul(try,viewsin); seg.scale1 = FixedDiv(projection, * gxt-gyt)<<detailshift; } #endif */ seg.scale2 = seg.scale1; } // calculate texture boundaries // and decide if floor / ceiling marks are needed worldtop = MyBSP.frontsector.ceilingheight - view.z; worldbottom = MyBSP.frontsector.floorheight - view.z; midtexture = toptexture = bottomtexture = 0; maskedtexture = false; seg.setMaskedTextureCol(null, 0); // seg.maskedtexturecol = null; if (MyBSP.backsector == null) { // single sided line midtexture = TexMan.getTextureTranslation(MyBSP.sidedef.midtexture); // a single sided line is terminal, so it must mark ends markfloor = markceiling = true; if ((MyBSP.linedef.flags & ML_DONTPEGBOTTOM) != 0) { vtop = MyBSP.frontsector.floorheight + TexMan.getTextureheight(MyBSP.sidedef.midtexture); // bottom of texture at bottom rw_midtexturemid = vtop - view.z; } else { // top of texture at top rw_midtexturemid = worldtop; } rw_midtexturemid += MyBSP.sidedef.rowoffset; seg.silhouette = SIL_BOTH; seg.setSprTopClip(view.screenheightarray, 0); seg.setSprBottomClip(view.negonearray, 0); seg.bsilheight = Integer.MAX_VALUE; seg.tsilheight = Integer.MIN_VALUE; } else { // two sided line seg.setSprTopClip(null, 0); seg.setSprBottomClip(null, 0); seg.silhouette = 0; if (MyBSP.frontsector.floorheight > MyBSP.backsector.floorheight) { seg.silhouette = SIL_BOTTOM; seg.bsilheight = MyBSP.frontsector.floorheight; } else if (MyBSP.backsector.floorheight > view.z) { seg.silhouette = SIL_BOTTOM; seg.bsilheight = Integer.MAX_VALUE; // seg.sprbottomclip = negonearray; } if (MyBSP.frontsector.ceilingheight < MyBSP.backsector.ceilingheight) { seg.silhouette |= SIL_TOP; seg.tsilheight = MyBSP.frontsector.ceilingheight; } else if (MyBSP.backsector.ceilingheight < view.z) { seg.silhouette |= SIL_TOP; seg.tsilheight = Integer.MIN_VALUE; // seg.sprtopclip = screenheightarray; } if (MyBSP.backsector.ceilingheight <= MyBSP.frontsector.floorheight) { seg.setSprBottomClip(view.negonearray, 0); seg.bsilheight = Integer.MAX_VALUE; seg.silhouette |= SIL_BOTTOM; } if (MyBSP.backsector.floorheight >= MyBSP.frontsector.ceilingheight) { seg.setSprTopClip(view.screenheightarray, 0); seg.tsilheight = Integer.MIN_VALUE; seg.silhouette |= SIL_TOP; } worldhigh = MyBSP.backsector.ceilingheight - view.z; worldlow = MyBSP.backsector.floorheight - view.z; // hack to allow height changes in outdoor areas if (MyBSP.frontsector.ceilingpic == TexMan.getSkyFlatNum() && MyBSP.backsector.ceilingpic == TexMan .getSkyFlatNum()) { worldtop = worldhigh; } if (worldlow != worldbottom || MyBSP.backsector.floorpic != MyBSP.frontsector.floorpic || MyBSP.backsector.lightlevel != MyBSP.frontsector.lightlevel) { markfloor = true; } else { // same plane on both sides markfloor = false; } if (worldhigh != worldtop || MyBSP.backsector.ceilingpic != MyBSP.frontsector.ceilingpic || MyBSP.backsector.lightlevel != MyBSP.frontsector.lightlevel) { markceiling = true; } else { // same plane on both sides markceiling = false; } if (MyBSP.backsector.ceilingheight <= MyBSP.frontsector.floorheight || MyBSP.backsector.floorheight >= MyBSP.frontsector.ceilingheight) { // closed door markceiling = markfloor = true; } if (worldhigh < worldtop) { // top texture toptexture = TexMan.getTextureTranslation(MyBSP.sidedef.toptexture); if ((MyBSP.linedef.flags & ML_DONTPEGTOP) != 0) { // top of texture at top rw_toptexturemid = worldtop; } else { vtop = MyBSP.backsector.ceilingheight + TexMan.getTextureheight(MyBSP.sidedef.toptexture); // bottom of texture rw_toptexturemid = vtop - view.z; } } if (worldlow > worldbottom) { // bottom texture bottomtexture = TexMan.getTextureTranslation(MyBSP.sidedef.bottomtexture); if ((MyBSP.linedef.flags & ML_DONTPEGBOTTOM) != 0) { // bottom of texture at bottom // top of texture at top rw_bottomtexturemid = worldtop; } else // top of texture at top rw_bottomtexturemid = worldlow; } rw_toptexturemid += MyBSP.sidedef.rowoffset; rw_bottomtexturemid += MyBSP.sidedef.rowoffset; // allocate space for masked texture tables if (MyBSP.sidedef.midtexture != 0) { // masked midtexture maskedtexture = true; seg_vars.maskedtexturecol = vp_vars.openings; seg_vars.pmaskedtexturecol = vp_vars.lastopening - rw_x; seg.setMaskedTextureCol(seg_vars.maskedtexturecol, seg_vars.pmaskedtexturecol); vp_vars.lastopening += rw_stopx - rw_x; } } // calculate rw_offset (only needed for textured lines) segtextured = (((midtexture | toptexture | bottomtexture) != 0) | maskedtexture); if (segtextured) { offsetangle = addAngles(rw_normalangle, -rw_angle1); // Another "tricky spot": negative of an unsigned number? if (offsetangle > ANG180) offsetangle = (-(int) offsetangle) & BITS32; if (offsetangle > ANG90) offsetangle = ANG90; sineval = finesine(offsetangle); rw_offset = FixedMul(hyp, sineval); // Another bug: we CAN'T assume that the result won't wrap // around. // If that assumption is made, then texture alignment issues // appear if (((rw_normalangle - rw_angle1) & BITS32) < ANG180) rw_offset = -rw_offset; rw_offset += MyBSP.sidedef.textureoffset + MyBSP.curline.offset; // This is OK, however: we can add as much shit as we want, // as long as we trim it to the 32 LSB. Proof as to why // this is always true is left as an exercise to the reader. rw_centerangle = (ANG90 + view.angle - rw_normalangle) & BITS32; // calculate light table // use different light tables // for horizontal / vertical / diagonal // OPTIMIZE: get rid of LIGHTSEGSHIFT globally if (colormaps.fixedcolormap == null) { lightnum = (MyBSP.frontsector.lightlevel >> LIGHTSEGSHIFT) + colormaps.extralight; if (MyBSP.curline.v1y == MyBSP.curline.v2y) lightnum--; else if (MyBSP.curline.v1x == MyBSP.curline.v2x) lightnum++; if (lightnum < 0) colormaps.walllights = colormaps.scalelight[0]; else if (lightnum >= LIGHTLEVELS) colormaps.walllights = colormaps.scalelight[LIGHTLEVELS - 1]; else colormaps.walllights = colormaps.scalelight[lightnum]; } } // if a floor / ceiling plane is on the wrong side // of the view plane, it is definitely invisible // and doesn't need to be marked. if (MyBSP.frontsector.floorheight >= view.z) { // above view plane markfloor = false; } if (MyBSP.frontsector.ceilingheight <= view.z && MyBSP.frontsector.ceilingpic != TexMan.getSkyFlatNum()) { // below view plane markceiling = false; } // calculate incremental stepping values for texture edges worldtop >>= 4; worldbottom >>= 4; topstep = -FixedMul(rw_scalestep, worldtop); topfrac = (view.centeryfrac >> 4) - FixedMul(worldtop, rw_scale); bottomstep = -FixedMul(rw_scalestep, worldbottom); bottomfrac = (view.centeryfrac >> 4) - FixedMul(worldbottom, rw_scale); if (MyBSP.backsector != null) { worldhigh >>= 4; worldlow >>= 4; if (worldhigh < worldtop) { pixhigh = (view.centeryfrac >> 4) - FixedMul(worldhigh, rw_scale); pixhighstep = -FixedMul(rw_scalestep, worldhigh); } if (worldlow > worldbottom) { pixlow = (view.centeryfrac >> 4) - FixedMul(worldlow, rw_scale); pixlowstep = -FixedMul(rw_scalestep, worldlow); } } // render it if (markceiling) { // System.out.println("Markceiling"); vp_vars.ceilingplane = vp_vars.CheckPlane(vp_vars.ceilingplane, rw_x, rw_stopx - 1); } if (markfloor) { // System.out.println("Markfloor"); vp_vars.floorplane = vp_vars.CheckPlane(vp_vars.floorplane, rw_x, rw_stopx - 1); } RenderSegLoop(); // After rendering is actually performed, clipping is set. // save sprite clipping info ... no top clipping? if ((C2JUtils.flags(seg.silhouette, SIL_TOP) || maskedtexture) && seg.nullSprTopClip()) { // memcpy (lastopening, ceilingclip+start, 2*(rw_stopx-start)); System.arraycopy(ceilingclip, start, vp_vars.openings, vp_vars.lastopening, rw_stopx - start); seg.setSprTopClip(vp_vars.openings, vp_vars.lastopening - start); // seg.setSprTopClipPointer(); vp_vars.lastopening += rw_stopx - start; } // no floor clipping? if ((C2JUtils.flags(seg.silhouette, SIL_BOTTOM) || maskedtexture) && seg.nullSprBottomClip()) { // memcpy (lastopening, floorclip+start, 2*(rw_stopx-start)); System.arraycopy(floorclip, start, vp_vars.openings, vp_vars.lastopening, rw_stopx - start); seg.setSprBottomClip(vp_vars.openings, vp_vars.lastopening - start); vp_vars.lastopening += rw_stopx - start; } if (maskedtexture && C2JUtils.flags(seg.silhouette, SIL_TOP)) { seg.silhouette |= SIL_TOP; seg.tsilheight = Integer.MIN_VALUE; } if (maskedtexture && (seg.silhouette & SIL_BOTTOM) == 0) { seg.silhouette |= SIL_BOTTOM; seg.bsilheight = Integer.MAX_VALUE; } seg_vars.ds_p++; } /** * R_RenderSegLoop Draws zero, one, or two textures (and possibly a * masked texture) for walls. Can draw or mark the starting pixel of * floor and ceiling textures. Also sets the actual sprite clipping info * (where sprites should be cut) Since rw_x ranges are non-overlapping, * rendering all walls means completing the clipping list as well. The * only difference between the parallel and the non-parallel version is * that the parallel doesn't draw immediately but rather, generates * RWIs. This can surely be unified to avoid replicating code. CALLED: * CORE LOOPING ROUTINE. */ protected void RenderSegLoop() { int angle; // angle_t int index; int yl; // low int yh; // hight int mid; int texturecolumn = 0; // fixed_t int top; int bottom; for (; rw_x < rw_stopx; rw_x++) { // mark floor / ceiling areas yl = (topfrac + HEIGHTUNIT - 1) >> HEIGHTBITS; // no space above wall? if (yl < ceilingclip[rw_x] + 1) yl = ceilingclip[rw_x] + 1; if (markceiling) { top = ceilingclip[rw_x] + 1; bottom = yl - 1; if (bottom >= floorclip[rw_x]) bottom = floorclip[rw_x] - 1; if (top <= bottom) { vp_vars.visplanes[vp_vars.ceilingplane].setTop(rw_x, (char) top); vp_vars.visplanes[vp_vars.ceilingplane].setBottom(rw_x, (char) bottom); } } yh = bottomfrac >> HEIGHTBITS; if (yh >= floorclip[rw_x]) yh = floorclip[rw_x] - 1; // A particular seg has been identified as a floor marker. if (markfloor) { top = yh + 1; bottom = floorclip[rw_x] - 1; if (top <= ceilingclip[rw_x]) top = ceilingclip[rw_x] + 1; if (top <= bottom) { vp_vars.visplanes[vp_vars.floorplane].setTop(rw_x, (char) top); vp_vars.visplanes[vp_vars.floorplane].setBottom(rw_x, (char) bottom); } } // texturecolumn and lighting are independent of wall tiers if (segtextured) { // calculate texture offset // CAREFUL: a VERY anomalous point in the code. Their sum is // supposed // to give an angle not exceeding 45 degrees (or an index of // 0x0FFF after // shifting). If added with pure unsigned rules, this // doesn't hold anymore, // not even if accounting for overflow. angle = Tables.toBAMIndex(rw_centerangle + (int) xtoviewangle[rw_x]); // FIXME: We are accessing finetangent here, the code seems // pretty confident in that angle won't exceed 4K no matter // what. // But xtoviewangle alone can yield 8K when shifted. // This usually only overflows if we idclip and look at // certain directions (probably angles get fucked up), // however it seems rare // enough to just "swallow" the exception. You can eliminate // it by anding // with 0x1FFF if you're so inclined. // FIXED by allowing overflow. See Tables for details. texturecolumn = rw_offset - FixedMul(finetangent[angle], rw_distance); texturecolumn >>= FRACBITS; // calculate lighting index = rw_scale >> LIGHTSCALESHIFT; if (index >= MAXLIGHTSCALE) index = MAXLIGHTSCALE - 1; dcvars.dc_colormap = colormaps.walllights[index]; dcvars.dc_x = rw_x; dcvars.dc_iscale = (int) (0xffffffffL / rw_scale); } // draw the wall tiers if (midtexture != 0) { // single sided line dcvars.dc_yl = yl; dcvars.dc_yh = yh; dcvars.dc_texheight = TexMan.getTextureheight(midtexture) >> FRACBITS; // killough dcvars.dc_texturemid = rw_midtexturemid; dcvars.dc_source_ofs = 0; dcvars.dc_source = TexMan.GetCachedColumn(midtexture, texturecolumn); CompleteColumn(); ceilingclip[rw_x] = (short) view.height; floorclip[rw_x] = -1; } else { // two sided line if (toptexture != 0) { // top wall mid = pixhigh >> HEIGHTBITS; pixhigh += pixhighstep; if (mid >= floorclip[rw_x]) mid = floorclip[rw_x] - 1; if (mid >= yl) { dcvars.dc_yl = yl; dcvars.dc_yh = mid; dcvars.dc_texturemid = rw_toptexturemid; dcvars.dc_texheight = TexMan.getTextureheight(toptexture) >> FRACBITS; dcvars.dc_source = (T) TexMan.GetCachedColumn(toptexture, texturecolumn); dcvars.dc_source_ofs = 0; if (dcvars.dc_colormap == null) System.out.println("Two-sided"); CompleteColumn(); ceilingclip[rw_x] = (short) mid; } else ceilingclip[rw_x] = (short) (yl - 1); } else { // no top wall if (markceiling) ceilingclip[rw_x] = (short) (yl - 1); } if (bottomtexture != 0) { // bottom wall mid = (pixlow + HEIGHTUNIT - 1) >> HEIGHTBITS; pixlow += pixlowstep; // no space above wall? if (mid <= ceilingclip[rw_x]) mid = ceilingclip[rw_x] + 1; if (mid <= yh) { dcvars.dc_yl = mid; dcvars.dc_yh = yh; dcvars.dc_texturemid = rw_bottomtexturemid; dcvars.dc_texheight = TexMan.getTextureheight(bottomtexture) >> FRACBITS; dcvars.dc_source = (T) TexMan.GetCachedColumn(bottomtexture, texturecolumn); dcvars.dc_source_ofs = 0; CompleteColumn(); floorclip[rw_x] = (short) mid; } else floorclip[rw_x] = (short) (yh + 1); } else { // no bottom wall if (markfloor) floorclip[rw_x] = (short) (yh + 1); } if (maskedtexture) { // save texturecol // for backdrawing of masked mid texture seg_vars.maskedtexturecol[seg_vars.pmaskedtexturecol + rw_x] = (short) texturecolumn; } } rw_scale += rw_scalestep; topfrac += topstep; bottomfrac += bottomstep; } } @Override public void ClearClips() { System.arraycopy(BLANKFLOORCLIP, 0, floorclip, 0, view.width); System.arraycopy(BLANKCEILINGCLIP, 0, ceilingclip, 0, view.width); } /** * Called from RenderSegLoop. This should either invoke the column * function, or store a wall rendering instruction in the parallel * version. It's the only difference between the parallel and serial * renderer, BTW. So override and implement accordingly. */ protected abstract void CompleteColumn(); @Override public void ExecuteSetViewSize(int viewwidth) { for (int i = 0; i < viewwidth; i++) { BLANKFLOORCLIP[i] = (short) view.height; BLANKCEILINGCLIP[i] = -1; } } public void CompleteRendering(){ // Nothing to do for serial. } protected column_t col; public SegDrawer(Renderer<?, ?> R) { this.vp_vars = R.getVPVars(); this.seg_vars = R.getSegVars(); col = new column_t(); seg_vars.drawsegs = new drawseg_t[seg_vars.MAXDRAWSEGS]; C2JUtils.initArrayOfObjects(seg_vars.drawsegs); } /** * R_ScaleFromGlobalAngle Returns the texture mapping scale for the * current line (horizontal span) at the given angle. rw_distance must * be calculated first. */ protected final int ScaleFromGlobalAngle(long visangle) { int scale; // fixed_t long anglea; long angleb; int sinea; int sineb; int num; // fixed_t int den; // UNUSED /* * { fixed_t dist; fixed_t z; fixed_t sinv; fixed_t cosv; sinv = * finesine[(visangle-rw_normalangle)>>ANGLETOFINESHIFT]; dist = * FixedDiv (rw_distance, sinv); cosv = * finecosine[(viewangle-visangle)>>ANGLETOFINESHIFT]; z = * abs(FixedMul (dist, cosv)); scale = FixedDiv(projection, z); * return scale; } */ anglea = (ANG90 + visangle - view.angle) & BITS32; angleb = (ANG90 + visangle - rw_normalangle) & BITS32; // both sines are allways positive sinea = finesine(anglea); sineb = finesine(angleb); num = FixedMul(view.projection, sineb) << view.detailshift; den = FixedMul(rw_distance, sinea); if (den > num >> 16) { scale = FixedDiv(num, den); if (scale > 64 * FRACUNIT) scale = 64 * FRACUNIT; else if (scale < 256) scale = 256; } else scale = 64 * FRACUNIT; return scale; } public void setGlobalAngle(long angle) { this.rw_angle1 = angle; } public void initScaling() { this.floorclip = new short[vs.getScreenWidth()]; this.ceilingclip = new short[vs.getScreenWidth()]; BLANKFLOORCLIP = new short[vs.getScreenWidth()]; BLANKCEILINGCLIP = new short[vs.getScreenWidth()]; } @Override public void setVideoScale(IVideoScale vs) { this.vs = vs; } IVideoScale vs; } protected interface IPlaneDrawer extends IVideoScaleAware { void InitPlanes(); void MapPlane(int y, int x1, int x2); void DrawPlanes(); int[] getDistScale(); /** Sync up in case there's concurrent planes/walls rendering */ public void sync(); } protected interface ISegDrawer extends IVideoScaleAware, ILimitResettable { public void ClearClips(); short[] getBLANKCEILINGCLIP(); short[] getBLANKFLOORCLIP(); short[] getFloorClip(); short[] getCeilingClip(); void ExecuteSetViewSize(int viewwidth); public void setGlobalAngle(long angle1); public void StoreWallRange(int first, int last); /** If there is anything to do beyond the BPS traversal, * e.g. parallel rendering */ public void CompleteRendering(); /** Sync up in case there's concurrent planes/walls rendering */ public void sync(); } protected final class Planes extends PlaneDrawer<T, V> { public Planes(RendererState<T, V> R) { super(R); } /** * R_DrawPlanes At the end of each frame. This also means that visplanes * must have been set BEFORE we called this function. Therefore, look * for errors behind. * * @throws IOException */ @Override public final void DrawPlanes() { if (DEBUG) System.out.println(" >>>>>>>>>>>>>>>>>>>>> DrawPlanes: " + vp_vars.lastvisplane); visplane_t pln = null; // visplane_t int light; int x; int stop; int angle; if (RANGECHECK) { rangeCheckErrors(); } for (int pl = 0; pl < vp_vars.lastvisplane; pl++) { pln = vp_vars.visplanes[pl]; if (DEBUG2) System.out.println(pln); if (pln.minx > pln.maxx) continue; // sky flat if (pln.picnum == TexMan.getSkyFlatNum()) { // Cache skytexture stuff here. They aren't going to change // while // being drawn, after all, are they? int skytexture = TexMan.getSkyTexture(); skydcvars.dc_texheight = TexMan.getTextureheight(skytexture) >> FRACBITS; skydcvars.dc_iscale = vpvars.getSkyScale() >> view.detailshift; /* * Sky is allways drawn full bright, i.e. colormaps[0] is * used. Because of this hack, sky is not affected by INVUL * inverse mapping. */ skydcvars.dc_colormap = colormap.colormaps[0]; skydcvars.dc_texturemid = TexMan.getSkyTextureMid(); for (x = pln.minx; x <= pln.maxx; x++) { skydcvars.dc_yl = pln.getTop(x); skydcvars.dc_yh = pln.getBottom(x); if (skydcvars.dc_yl <= skydcvars.dc_yh) { angle = (int) (addAngles(view.angle, xtoviewangle[x]) >>> ANGLETOSKYSHIFT); skydcvars.dc_x = x; // Optimized: texheight is going to be the same // during normal skies drawing...right? skydcvars.dc_source = TexMan.GetCachedColumn(skytexture, angle); colfunc.sky.invoke(); } } continue; } // regular flat dsvars.ds_source = TexMan.getSafeFlat(pln.picnum); planeheight = Math.abs(pln.height - view.z); light = (pln.lightlevel >> LIGHTSEGSHIFT) + colormap.extralight; if (light >= LIGHTLEVELS) light = LIGHTLEVELS - 1; if (light < 0) light = 0; planezlight = colormap.zlight[light]; // We set those values at the border of a plane's top to a // "sentinel" value...ok. pln.setTop(pln.maxx + 1, visplane_t.SENTINEL); pln.setTop(pln.minx - 1, visplane_t.SENTINEL); stop = pln.maxx + 1; for (x = pln.minx; x <= stop; x++) { MakeSpans(x, pln.getTop(x - 1), pln.getBottom(x - 1), pln.getTop(x), pln.getBottom(x)); } // Z_ChangeTag (ds_source, PU_CACHE); } } } // End Plane class // /////////////////////// LIGHTS, POINTERS, COLORMAPS ETC. //////////////// // /// FROM R_DATA, R_MAIN , R_DRAW ////////// /** * OK< this is supposed to "peg" into screen buffer 0. It will work AS LONG * AS SOMEONE FUCKING ACTUALLY SETS IT !!!! */ protected V screen; protected static final boolean RANGECHECK = false; /** * These are actually offsets inside screen 0 (or any screen). Therefore * anything using them should "draw" inside screen 0 */ protected int[] ylookup = new int[MAXHEIGHT]; /** Columns offset to set where?! */ protected int[] columnofs = new int[MAXWIDTH]; /** * General purpose. Used for solid walls and as an intermediary for * threading */ protected ColVars<T, V> dcvars; /** Used for spans */ protected SpanVars<T, V> dsvars; // Used for sky drawer, to avoid clashing with shared dcvars protected ColVars<T, V> skydcvars; /** * Masked drawing functions get "pegged" to this set of dcvars, passed upon * initialization. However, multi-threaded vars are better off carrying each * their own ones. */ protected ColVars<T, V> maskedcvars; /** * e6y: wide-res Borrowed from PrBoom+; */ /* * protected int wide_centerx, wide_ratio, wide_offsetx, wide_offset2x, * wide_offsety, wide_offset2y; protected final base_ratio_t[] * BaseRatioSizes = { new base_ratio_t(960, 600, 0, 48, 1.333333f), // 4:3 * new base_ratio_t(1280, 450, 0, 48 * 3 / 4, 1.777777f), // 16:9 new * base_ratio_t(1152, 500, 0, 48 * 5 / 6, 1.6f), // 16:10 new * base_ratio_t(960, 600, 0, 48, 1.333333f), new base_ratio_t(960, 640, * (int) (6.5 * FRACUNIT), 48 * 15 / 16, 1.25f) // 5:4 }; */ /** just for profiling purposes */ protected int framecount; protected int sscount; protected int linecount; protected int loopcount; // // precalculated math tables // protected long clipangle; // Set to 2*clipangle later. protected long CLIPANGLE2; // The viewangletox[viewangle + FINEANGLES/4] lookup // maps the visible view angles to screen X coordinates, // flattening the arc to a flat projection plane. // There will be many angles mapped to the same X. protected final int[] viewangletox = new int[FINEANGLES / 2]; /** * The xtoviewangle[] table maps a screen pixel to the lowest viewangle that * maps back to x ranges from clipangle to -clipangle. */ protected long[] xtoviewangle;// MAES: to resize // UNUSED. // The finetangentgent[angle+FINEANGLES/4] table // holds the fixed_t tangent values for view angles, // ranging from MININT to 0 to MAXINT. // fixed_t finetangent[FINEANGLES/2]; // fixed_t finesine[5*FINEANGLES/4]; // MAES: uh oh. So now all these ints must become finesines? fuck that. // Also wtf @ this hack....this points to approx 1/4th of the finesine // table, but what happens if I read past it? // int[] finecosine = finesine[FINEANGLES/4]; /* * MAES: what's going on with light tables here. OK...so these should be * "unsigned bytes", since, after all, they'll be used as pointers inside an * array to finally pick a color, so they should be expanded to shorts. */ // //////////// SOME UTILITY METHODS ///////////// /** * Assigns a point of view before calling PointToAngle CAREFUL: this isn't a * pure function, as it alters the renderer's state! */ public final long PointToAngle2(int x1, int y1, int x2, int y2) { // Careful with assignments... view.x = x1; view.y = y1; return view.PointToAngle(x2, y2); } // // R_InitPointToAngle // /* * protected final void InitPointToAngle () { // UNUSED - now getting from * tables.c if (false){ int i; long t; float f; // // slope (tangent) to * angle lookup // for (i=0 ; i<=SLOPERANGE ; i++) { f = (float) Math.atan( * (double)(i/SLOPERANGE )/(3.141592657*2)); t = (long) (0xffffffffL*f); * tantoangle[i] = (int) t; } } } */ /** * Public, static, stateless version of PointToAngle2. Call this one when * "renderless" use of PointToAngle2 is required. */ public final static long PointToAngle(int viewx, int viewy, int x, int y) { // MAES: note how we don't use &BITS32 here. That is because // we know that the maximum possible value of tantoangle is angle // This way, we are actually working with vectors emanating // from our current position. x -= viewx; y -= viewy; if ((x == 0) && (y == 0)) return 0; if (x >= 0) { // x >=0 if (y >= 0) { // y>= 0 if (x > y) { // octant 0 return tantoangle[SlopeDiv(y, x)]; } else { // octant 1 return (ANG90 - 1 - tantoangle[SlopeDiv(x, y)]); } } else { // y<0 y = -y; if (x > y) { // octant 8 return (-tantoangle[SlopeDiv(y, x)]); } else { // octant 7 return (ANG270 + tantoangle[SlopeDiv(x, y)]); } } } else { // x<0 x = -x; if (y >= 0) { // y>= 0 if (x > y) { // octant 3 return (ANG180 - 1 - tantoangle[SlopeDiv(y, x)]); } else { // octant 2 return (ANG90 + tantoangle[SlopeDiv(x, y)]); } } else { // y<0 y = -y; if (x > y) { // octant 4 return (ANG180 + tantoangle[SlopeDiv(y, x)]); } else { // octant 5 return (ANG270 - 1 - tantoangle[SlopeDiv(x, y)]); } } } // This is actually unreachable. // return 0; } // // R_InitTables // protected final void InitTables() { // UNUSED: now getting from tables.c /* * int i; float a; float fv; int t; // viewangle tangent table for (i=0 * ; i<FINEANGLES/2 ; i++) { a = (i-FINEANGLES/4+0.5)*PI*2/FINEANGLES; * fv = FRACUNIT*tan (a); t = fv; finetangent[i] = t; } // finesine * table for (i=0 ; i<5*FINEANGLES/4 ; i++) { // OPTIMIZE: mirro.. a = * (i+0.5)*PI*2/FINEANGLES; t = FRACUNIT*sin (a); finesine[i] = t; } */ } /** * R_PointToDist * * @param x * fixed_t * @param y * fixed_t * @return */ protected final int PointToDist(int x, int y) { int angle; int dx; int dy; int temp; int dist; dx = Math.abs(x - view.x); dy = Math.abs(y - view.y); // If something is farther north/south than west/east, it gets swapped. // Probably as a crude way to avoid divisions by zero. This divides // the field into octants, rather than quadrants, where the biggest // angle to // consider is 45...right? So dy/dx can never exceed 1.0, in theory. if (dy > dx) { temp = dx; dx = dy; dy = temp; } // If one or both of the distances are *exactly* zero at this point, // then this means that the wall is in your face anyway, plus we want to // avoid a division by zero. So you get zero. if (dx == 0) return 0; /* * If dx is zero, this is going to bomb. Fixeddiv will return MAXINT aka * 7FFFFFFF, >> DBITS will make it 3FFFFFF, which is more than enough to * break tantoangle[]. In the original C code, this probably didn't * matter: there would probably be garbage orientations thrown all * around. However this is unacceptable in Java. OK, so the safeguard * above prevents that. Still... this method is only called once per * visible wall per frame, so one check more or less at this point won't * change much. It's better to be safe than sorry. */ // This effectively limits the angle to // angle = Math.max(FixedDiv(dy, dx), 2048) >> DBITS; angle = (FixedDiv(dy, dx) & 0x1FFFF) >> DBITS; // Since the division will be 0xFFFF at most, DBITS will restrict // the maximum angle index to 7FF, about 45, so adding ANG90 with // no other safeguards is OK. angle = (int) ((tantoangle[angle] + ANG90) >> ANGLETOFINESHIFT); // use as cosine dist = FixedDiv(dx, finesine[angle]); return dist; } // //////////// COMMON RENDERING GLOBALS //////////////// // //////////////// COLUMN AND SPAN FUNCTIONS ////////////// protected ColFuncs<T, V> colfunc; // Keep two sets of functions. protected ColFuncs<T, V> colfunchi; protected ColFuncs<T, V> colfunclow; protected final void setHiColFuns() { colfunchi.main = colfunchi.base = DrawColumn; colfunchi.masked = DrawColumnMasked; colfunchi.fuzz = DrawFuzzColumn; colfunchi.trans = DrawTranslatedColumn; colfunchi.glass = DrawTLColumn; colfunchi.player = DrawColumnPlayer; colfunchi.sky = DrawColumnSkies; } protected final void setLowColFuns() { colfunclow.main = colfunclow.base = DrawColumnLow; colfunclow.masked = DrawColumnMaskedLow; colfunclow.fuzz = DrawFuzzColumnLow; colfunclow.trans = DrawTranslatedColumnLow; colfunclow.glass = DrawTLColumn; colfunclow.player = DrawColumnMaskedLow; colfunclow.sky = DrawColumnSkiesLow; } public ColFuncs<T,V> getColFuncsHi(){ return this.colfunchi; } public ColFuncs<T,V> getColFuncsLow(){ return this.colfunclow; } public ColVars<T, V> getMaskedDCVars(){ return this.maskedcvars; } // These column functions are "fixed" for a given renderer, and are // not used directly, but only after passing them to colfuncs protected DoomColumnFunction<T, V> DrawTranslatedColumn; protected DoomColumnFunction<T, V> DrawTranslatedColumnLow; protected DoomColumnFunction<T, V> DrawColumnPlayer; protected DoomColumnFunction<T, V> DrawColumnSkies; protected DoomColumnFunction<T, V> DrawColumnSkiesLow; protected DoomColumnFunction<T, V> DrawFuzzColumn; protected DoomColumnFunction<T, V> DrawFuzzColumnLow; protected DoomColumnFunction<T, V> DrawColumn; protected DoomColumnFunction<T, V> DrawColumnLow; protected DoomColumnFunction<T, V> DrawColumnMasked; protected DoomColumnFunction<T, V> DrawColumnMaskedLow; protected DoomColumnFunction<T, V> DrawTLColumn; /** to be set in UnifiedRenderer */ protected DoomSpanFunction<T, V> DrawSpan, DrawSpanLow; // ////////////// r_draw methods ////////////// /** * Copy a screen buffer. Actually, it's hardcoded to copy stuff from screen * 1 to screen 0. Used to overlay stuff like beveled edges that don't need * to be updated that often. * LFB copy. This might not be a good idea if * memcpy is not optiomal, e.g. byte by byte on a 32bit CPU, as GNU * GCC/Linux libc did at one point. */ public void VideoErase(int ofs, int count) { // memcpy (screens[0]+ofs, screens[1]+ofs, count); System.arraycopy(V.getScreen(DoomVideoRenderer.SCREEN_BG), ofs, V.getScreen(DoomVideoRenderer.SCREEN_FG), ofs, count); } /* * R_DrawViewBorder Draws the border around the view for different size * windows? */ public void DrawViewBorder() { int top; int side; int ofs; int i; if (view.scaledwidth == SCREENWIDTH) return; top = ((SCREENHEIGHT - DM.ST.getHeight()) - view.height) / 2; side = (SCREENWIDTH - view.scaledwidth) / 2; // copy top and one line of left side this.VideoErase(0, top * SCREENWIDTH + side); // copy one line of right side and bottom ofs = (view.height + top) * SCREENWIDTH - side; this.VideoErase(ofs, top * SCREENWIDTH + side); // copy sides using wraparound ofs = top * SCREENWIDTH + SCREENWIDTH - side; side <<= 1; for (i = 1; i < view.height; i++) { this.VideoErase(ofs, side); ofs += SCREENWIDTH; } // ? V.MarkRect(0, 0, SCREENWIDTH, SCREENHEIGHT - DM.ST.getHeight()); } public void ExecuteSetViewSize() { int cosadj; int dy; int i; int j; int level; int startmap; setsizeneeded = false; // 11 Blocks means "full screen" if (setblocks == 11) { view.scaledwidth = SCREENWIDTH; view.height = SCREENHEIGHT; } else { view.scaledwidth = setblocks * (SCREENWIDTH / 10); // Height can only be a multiple of 8. view.height = (short) ((setblocks * (SCREENHEIGHT - DM.ST.getHeight()) / 10) & ~7); } skydcvars.viewheight = maskedcvars.viewheight = dcvars.viewheight = view.height; view.detailshift = setdetail; view.width = view.scaledwidth >> view.detailshift; view.centery = view.height / 2; view.centerx = view.width / 2; view.centerxfrac = (view.centerx << FRACBITS); view.centeryfrac = (view.centery << FRACBITS); view.projection = view.centerxfrac; skydcvars.centery = maskedcvars.centery = dcvars.centery = view.centery; // High detail if (view.detailshift == 0) { colfunc = colfunchi; dsvars.spanfunc = DrawSpan; } else { // Low detail colfunc = colfunclow; dsvars.spanfunc = DrawSpanLow; } InitBuffer(view.scaledwidth, view.height); InitTextureMapping(); // psprite scales // pspritescale = FRACUNIT*viewwidth/SCREENWIDTH; // pspriteiscale = FRACUNIT*SCREENWIDTH/viewwidth; MyThings.setPspriteScale((int) (FRACUNIT * ((float) SCREEN_MUL * view.width) / SCREENWIDTH)); MyThings.setPspriteIscale((int) (FRACUNIT * (SCREENWIDTH / (view.width * (float) SCREEN_MUL)))); vp_vars.setSkyScale((int) (FRACUNIT * (SCREENWIDTH / (view.width * (float) SCREEN_MUL)))); view.BOBADJUST = (int) (this.vs.getSafeScaling() << 15); view.WEAPONADJUST = (int) ((SCREENWIDTH / (2 * SCREEN_MUL)) * FRACUNIT); // thing clipping for (i = 0; i < view.width; i++) view.screenheightarray[i] = (short) view.height; // planes for (i = 0; i < view.height; i++) { dy = ((i - view.height / 2) << FRACBITS) + FRACUNIT / 2; dy = Math.abs(dy); vp_vars.yslope[i] = FixedDiv((view.width << view.detailshift) / 2 * FRACUNIT, dy); // MyPlanes.yslopef[i] = ((viewwidth<<detailshift)/2)/ dy; } // double cosadjf; for (i = 0; i < view.width; i++) { // MAES: In this spot we must interpet it as SIGNED, else it's // pointless, right? // MAES: this spot caused the "warped floor bug", now fixed. Don't // forget xtoviewangle[i]! cosadj = Math.abs(finecosine(xtoviewangle[i])); // cosadjf = // Math.abs(Math.cos((double)xtoviewangle[i]/(double)0xFFFFFFFFL)); MyPlanes.getDistScale()[i] = FixedDiv(FRACUNIT, cosadj); // MyPlanes.distscalef[i] = (float) (1.0/cosadjf); } // Calculate the light levels to use // for each level / scale combination. for (i = 0; i < LIGHTLEVELS; i++) { startmap = ((LIGHTLEVELS-LIGHTBRIGHT-i)*2)*NUMCOLORMAPS/LIGHTLEVELS; for (j = 0; j < MAXLIGHTSCALE; j++) { level = startmap - j/ DISTMAP; if (level < 0) level = 0; if (level >= NUMCOLORMAPS) level = NUMCOLORMAPS - 1; colormaps.scalelight[i][j] = colormaps.colormaps[level]; } } MySegs.ExecuteSetViewSize(view.width); } /** * R_FillBackScreen Fills the back screen with a pattern for variable screen * sizes Also draws a beveled edge. This is actually stored in screen 1, and * is only OCCASIONALLY written to screen 0 (the visible one) by calling * R_VideoErase. */ public void FillBackScreen() { flat_t src; int x; int y; patch_t patch; // DOOM border patch. String name1 = "FLOOR7_2"; // DOOM II border patch. String name2 = "GRNROCK"; String name; if (view.scaledwidth == SCREENWIDTH) return; if (DM.isCommercial()) name = name2; else name = name1; /* This is a flat we're reading here */ src = (flat_t) (W.CacheLumpName(name, PU_CACHE, flat_t.class)); /* * This part actually draws the border itself, without bevels MAES: * improved drawing routine for extended bit-depth compatibility. */ for (y = 0; y < SCREENHEIGHT - DM.ST.getHeight(); y += 64) { int y_maxdraw = Math.min(SCREENHEIGHT - DM.ST.getHeight() - y, 64); // Draw whole blocks. for (x = 0; x < SCREENWIDTH; x += 64) { int x_maxdraw = Math.min(SCREENWIDTH - x, 64); V.DrawBlock(x, y, DoomVideoRenderer.SCREEN_BG, x_maxdraw, y_maxdraw, (T) src.data); } } patch = (patch_t) W.CachePatchName("BRDR_T", PU_CACHE); for (x = 0; x < view.scaledwidth; x += 8) V.DrawPatch(view.windowx + x, view.windowy - 8, 1, patch); patch = (patch_t) W.CachePatchName("BRDR_B", PU_CACHE); for (x = 0; x < view.scaledwidth; x += 8) V.DrawPatch(view.windowx + x, view.windowy + view.height, 1, patch); patch = (patch_t) W.CachePatchName("BRDR_L", PU_CACHE); for (y = 0; y < view.height; y += 8) V.DrawPatch(view.windowx - 8, view.windowy + y, 1, patch); patch = (patch_t) W.CachePatchName("BRDR_R", PU_CACHE); for (y = 0; y < view.height; y += 8) V.DrawPatch(view.windowx + view.scaledwidth, view.windowy + y, 1, patch); // Draw beveled edge. Top-left V.DrawPatch(view.windowx - 8, view.windowy - 8, 1, (patch_t) W.CachePatchName("BRDR_TL", PU_CACHE)); // Top-right. V.DrawPatch(view.windowx + view.scaledwidth, view.windowy - 8, 1, (patch_t) W.CachePatchName("BRDR_TR", PU_CACHE)); // Bottom-left V.DrawPatch(view.windowx - 8, view.windowy + view.height, 1, (patch_t) W.CachePatchName("BRDR_BL", PU_CACHE)); // Bottom-right. V.DrawPatch(view.windowx + view.width, view.windowy + view.height, 1, (patch_t) W.CachePatchName("BRDR_BR", PU_CACHE)); } /** * R_Init */ public void Init() { // Any good reason for this to be here? // drawsegs=new drawseg_t[MAXDRAWSEGS]; // C2JUtils.initArrayOfObjects(drawsegs); // DON'T FORGET ABOUT MEEEEEE!!!11!!! this.screen = this.V.getScreen(DoomVideoRenderer.SCREEN_FG); System.out.print("\nR_InitData"); InitData(); // InitPointToAngle (); System.out.print("\nR_InitPointToAngle"); // ds.DM.viewwidth / ds.viewheight / detailLevel are set by the defaults System.out.print("\nR_InitTables"); InitTables(); SetViewSize(DM.M.getScreenBlocks(), DM.M.getDetailLevel()); System.out.print("\nR_InitPlanes"); MyPlanes.InitPlanes(); System.out.print("\nR_InitLightTables"); InitLightTables(); System.out.print("\nR_InitSkyMap: " + TexMan.InitSkyMap()); System.out.print("\nR_InitTranslationsTables"); InitTranslationTables(); System.out.print("\nR_InitTranMap: "); R_InitTranMap(0); System.out.print("\nR_InitDrawingFunctions: "); R_InitDrawingFunctions(); framecount = 0; } /** * R_InitBuffer Creates lookup tables that avoid multiplies and other * hazzles for getting the framebuffer address of a pixel to draw. MAES: * this is "pinned" to screen[0] of a Video Renderer. We will handle this * differently elsewhere... */ protected final void InitBuffer(int width, int height) { int i; // Handle resize, // e.g. smaller view windows // with border and/or status bar. view.windowx = (SCREENWIDTH - width) >> 1; // Column offset. For windows. for (i = 0; i < width; i++) columnofs[i] = view.windowx + i; // SamE with base row offset. if (width == SCREENWIDTH) view.windowy = 0; else view.windowy = (SCREENHEIGHT - DM.ST.getHeight() - height) >> 1; // Preclaculate all row offsets. for (i = 0; i < height; i++) ylookup[i] = /* screens[0] + */(i + view.windowy) * SCREENWIDTH; } /** * R_InitTextureMapping Not moved into the TextureManager because it's * tighly coupled to the visuals, rather than textures. Perhaps the name is * not the most appropriate. */ protected final void InitTextureMapping() { int i, x, t; int focallength; // fixed_t int fov = FIELDOFVIEW; // For widescreen displays, increase the FOV so that the middle part of // the // screen that would be visible on a 4:3 display has the requested FOV. /* * UNUSED if (wide_centerx != centerx) { // wide_centerx is what centerx * would be // if the display was not widescreen fov = (int) * (Math.atan((double) centerx Math.tan((double) fov * Math.PI / * FINEANGLES) / (double) wide_centerx) FINEANGLES / Math.PI); if (fov > * 130 * FINEANGLES / 360) fov = 130 * FINEANGLES / 360; } */ // Use tangent table to generate viewangletox: // viewangletox will give the next greatest x // after the view angle. // // Calc focallength // so FIELDOFVIEW angles covers SCREENWIDTH. focallength = FixedDiv(view.centerxfrac, finetangent[QUARTERMARK + FIELDOFVIEW / 2]); for (i = 0; i < FINEANGLES / 2; i++) { if (finetangent[i] > FRACUNIT * 2) t = -1; else if (finetangent[i] < -FRACUNIT * 2) t = view.width + 1; else { t = FixedMul(finetangent[i], focallength); t = (view.centerxfrac - t + FRACUNIT - 1) >> FRACBITS; if (t < -1) t = -1; else if (t > view.width + 1) t = view.width + 1; } viewangletox[i] = t; } // Scan viewangletox[] to generate xtoviewangle[]: // xtoviewangle will give the smallest view angle // that maps to x. for (x = 0; x <= view.width; x++) { i = 0; while (viewangletox[i] > x) i++; xtoviewangle[x] = addAngles((i << ANGLETOFINESHIFT), -ANG90); } // Take out the fencepost cases from viewangletox. for (i = 0; i < FINEANGLES / 2; i++) { t = FixedMul(finetangent[i], focallength); t = view.centerx - t; if (viewangletox[i] == -1) viewangletox[i] = 0; else if (viewangletox[i] == view.width + 1) viewangletox[i] = view.width; } clipangle = xtoviewangle[0]; // OPTIMIZE: assign constant for optimization. CLIPANGLE2 = (2 * clipangle) & BITS32; } // // R_InitLightTables // Only inits the zlight table, // because the scalelight table changes with view size. // protected final static int DISTMAP = 2; protected final void InitLightTables() { int i; int j; int startmap; int scale; // Calculate the light levels to use // for each level / distance combination. for (i = 0; i < LIGHTLEVELS; i++) { startmap = ((LIGHTLEVELS - LIGHTBRIGHT - i) * 2) * NUMCOLORMAPS / LIGHTLEVELS; for (j = 0; j < MAXLIGHTZ; j++) { // CPhipps - use 320 here instead of SCREENWIDTH, otherwise hires is // brighter than normal res scale = FixedDiv((320 / 2 * FRACUNIT), (j + 1) << LIGHTZSHIFT); int t, level = startmap - (scale >>= LIGHTSCALESHIFT)/DISTMAP; if (level < 0) level = 0; if (level >= NUMCOLORMAPS) level = NUMCOLORMAPS - 1; // zlight[i][j] = colormaps + level*256; colormaps.zlight[i][j] = colormaps.colormaps[level]; } } } protected static final int TSC = 12; /* * number of fixed point digits in * filter percent */ byte[] main_tranmap; /** * A faster implementation of the tranmap calculations. Almost 10x faster * than the old one! * * @param progress */ protected void R_InitTranMap(int progress) { int lump = W.CheckNumForName("TRANMAP"); long ta = System.nanoTime(); int p; String tranmap; // PRIORITY: a map file has been specified from commandline. Try to read // it. If OK, this trumps even those specified in lumps. if ((p = DM.CM.CheckParm("-tranmap")) != 0) { if ((tranmap = DM.CM.getArgv(p + 1)) != null) { if (C2JUtils.testReadAccess(tranmap)) { System.out .printf( "Translucency map file %s specified in -tranmap arg. Attempting to use...\n", tranmap); main_tranmap = new byte[256 * 256]; // killough 4/11/98 int result = MenuMisc.ReadFile(tranmap, main_tranmap); if (result > 0) return; System.out.print("...failure.\n"); } } } // Next, if a tranlucency filter map lump is present, use it if (lump != -1) { // Set a pointer to the translucency filter maps. System.out .print("Translucency map found in lump. Attempting to use..."); // main_tranmap=new byte[256*256]; // killough 4/11/98 main_tranmap = W.CacheLumpNumAsRawBytes(lump, Defines.PU_STATIC); // killough // 4/11/98 // Tolerate 64K or more. if (main_tranmap.length >= 0x10000) return; System.out.print("...failure.\n"); // Not good, try something else. } // A default map file already exists. Try to read it. if (C2JUtils.testReadAccess("tranmap.dat")) { System.out .print("Translucency map found in default tranmap.dat file. Attempting to use..."); main_tranmap = new byte[256 * 256]; // killough 4/11/98 int result = MenuMisc.ReadFile("tranmap.dat", main_tranmap); if (result > 0) return; // Something went wrong, so fuck that. } // Nothing to do, so we must synthesize it from scratch. And, boy, is it // slooow. { // Compose a default transparent filter map based on PLAYPAL. System.out .print("Computing translucency map from scratch...that's gonna be SLOW..."); byte[] playpal = W.CacheLumpNameAsRawBytes("PLAYPAL", Defines.PU_STATIC); main_tranmap = new byte[256 * 256]; // killough 4/11/98 int[] basepal = new int[3 * 256]; int[] mixedpal = new int[3 * 256 * 256]; main_tranmap = new byte[256 * 256]; // Init array of base colors. for (int i = 0; i < 256; i++) { basepal[3 * i] = 0Xff & playpal[i * 3]; basepal[1 + 3 * i] = 0Xff & playpal[1 + i * 3]; basepal[2 + 3 * i] = 0Xff & playpal[2 + i * 3]; } // Init array of mixed colors. These are true RGB. // The diagonal of this array will be the original colors. for (int i = 0; i < 256 * 3; i += 3) { for (int j = 0; j < 256 * 3; j += 3) { mixColors(basepal, basepal, mixedpal, i, j, j * 256 + i); } } // Init distance map. Every original palette colour has a // certain distance from all the others. The diagonal is zero. // The interpretation is that e.g. the mixture of color 2 and 8 will // have a RGB value, which is closest to euclidean distance to // e.g. original color 9. Therefore we should put "9" in the (2,8) // and (8,2) cells of the tranmap. final float[] tmpdist = new float[256]; for (int a = 0; a < 256; a++) { for (int b = a; b < 256; b++) { // We evaluate the mixture of a and b // Construct distance table vs all of the ORIGINAL colors. for (int k = 0; k < 256; k++) { tmpdist[k] = colorDistance(mixedpal, basepal, 3 * (a + b * 256), k * 3); } main_tranmap[(a << 8) | b] = (byte) findMin(tmpdist); main_tranmap[(b << 8) | a] = main_tranmap[(a << 8) | b]; } } System.out.print("...done\n"); if (MenuMisc.WriteFile("tranmap.dat", main_tranmap, main_tranmap.length)) System.out .print("TRANMAP.DAT saved to disk for your convenience! Next time will be faster.\n"); } long b = System.nanoTime(); System.out.printf("Tranmap %d\n", (b - ta) / 1000000); } /** Mixes two RGB colors. Nuff said */ protected final void mixColors(int[] a, int[] b, int[] c, int pa, int pb, int pc) { c[pc] = (a[pa] + b[pb]) / 2; c[pc + 1] = (a[pa + 1] + b[pb + 1]) / 2; c[pc + 2] = (a[pa + 2] + b[pb + 2]) / 2; } /** Returns the euclidean distance of two RGB colors. Nuff said */ protected final float colorDistance(int[] a, int[] b, int pa, int pb) { return (float) Math.sqrt((a[pa] - b[pb]) * (a[pa] - b[pb]) + (a[pa + 1] - b[pb + 1]) * (a[pa + 1] - b[pb + 1]) + (a[pa + 2] - b[pb + 2]) * (a[pa + 2] - b[pb + 2])); } /** Stuff that is trivially initializable, even with generics, * but is only safe to do after all constructors have completed. */ protected final void completeInit(){ this.detailaware.add(MyThings); } protected final int findMin(float[] a) { int minindex = 0; float min = Float.POSITIVE_INFINITY; for (int i = 0; i < a.length; i++) if (a[i] < min) { min = a[i]; minindex = i; } return minindex; } /** * R_DrawMaskedColumnSinglePost. Used to handle some special cases where * cached columns get used as "masked" middle textures. Will be treated as a * single-run post of capped length. */ /* * protected final void DrawCompositeColumnPost(byte[] column) { int * topscreen; int bottomscreen; int basetexturemid; // fixed_t int * topdelta=0; // Fixed value int length; basetexturemid = dc_texturemid; // * That's true for the whole column. dc_source = column; // for each post... * while (topdelta==0) { // calculate unclipped screen coordinates // for * post topscreen = sprtopscreen + spryscale * 0; length = column.length; * bottomscreen = topscreen + spryscale * length; dc_yl = (topscreen + * FRACUNIT - 1) >> FRACBITS; dc_yh = (bottomscreen - 1) >> FRACBITS; if * (dc_yh >= mfloorclip[p_mfloorclip + dc_x]) dc_yh = * mfloorclip[p_mfloorclip + dc_x] - 1; if (dc_yl <= * mceilingclip[p_mceilingclip + dc_x]) dc_yl = mceilingclip[p_mceilingclip * + dc_x] + 1; // killough 3/2/98, 3/27/98: Failsafe against * overflow/crash: if (dc_yl <= dc_yh && dc_yh < viewheight) { // Set * pointer inside column to current post's data // Rremember, it goes * {postlen}{postdelta}{pad}[data]{pad} dc_source_ofs = 0; // pointer + 3; * dc_texturemid = basetexturemid - (topdelta << FRACBITS); // Drawn by * either R_DrawColumn // or (SHADOW) R_DrawFuzzColumn. dc_texheight=0; // * Killough try { maskedcolfunc.invoke(); } catch (Exception e){ * System.err.printf("Error rendering %d %d %d\n", dc_yl,dc_yh,dc_yh-dc_yl); * } } topdelta--; } dc_texturemid = basetexturemid; } */ protected abstract void InitColormaps() throws IOException; // Only used b protected byte[] BLURRY_MAP; /** * R_InitData Locates all the lumps that will be used by all views Must be * called after W_Init. */ public void InitData() { try { System.out.print("\nInit Texture and Flat Manager"); TexMan = this.DM.TM; System.out.print("\nInitTextures"); TexMan.InitTextures(); System.out.print("\nInitFlats"); TexMan.InitFlats(); System.out.print("\nInitSprites"); SM.InitSpriteLumps(); MyThings.cacheSpriteManager(SM); VIS.cacheSpriteManager(SM); System.out.print("\nInitColormaps"); InitColormaps(); } catch (IOException e) { e.printStackTrace(); } } protected int spritememory; /** * To be called right after PrecacheLevel from SetupLevel in LevelLoader. * It's an ugly hack, in that it must communicate with the "Game map" class * and determine what kinds of monsters are actually in the level and * whether it should load their graphics or not. Whenever we implement it, * it's going to be ugly and not neatly separated anyway. * * @return */ public void PreCacheThinkers() { boolean[] spritepresent; thinker_t th; spriteframe_t sf; int i, j, k; int lump; final spritedef_t[] sprites = SM.getSprites(); final int numsprites = SM.getNumSprites(); spritepresent = new boolean[numsprites]; for (th = P.getThinkerCap().next; th != P.getThinkerCap(); th = th.next) { if (th.function == think_t.P_MobjThinker) spritepresent[((mobj_t) th).sprite.ordinal()] = true; } spritememory = 0; for (i = 0; i < numsprites; i++) { if (!spritepresent[i]) continue; for (j = 0; j < sprites[i].numframes; j++) { sf = sprites[i].spriteframes[j]; for (k = 0; k < 8; k++) { lump = SM.getFirstSpriteLump() + sf.lump[k]; spritememory += W.GetLumpInfo(lump).size; W.CacheLumpNum(lump, PU_CACHE, patch_t.class); } } } } /** * R_InitTranslationTables Creates the translation tables to map the green * color ramp to gray, brown, red. Assumes a given structure of the PLAYPAL. * Could be read from a lump instead. */ protected final void InitTranslationTables() { int i; final int TR_COLORS = 28; // translationtables = Z_Malloc (256*3+255, PU_STATIC, 0); // translationtables = (byte *)(( (int)translationtables + 255 )& ~255); byte[][] translationtables = colormaps.translationtables = new byte[TR_COLORS][256]; // translate just the 16 green colors for (i = 0; i < 256; i++) { translationtables[0][i] = (byte) i; if (i >= 0x70 && i <= 0x7f) { // Remap green range to other ranges. translationtables[1][i] = (byte) (0x60 + (i & 0xf)); // gray translationtables[2][i] = (byte) (0x40 + (i & 0xf)); // brown translationtables[3][i] = (byte) (0x20 + (i & 0xf)); // red translationtables[4][i] = (byte) (0x10 + (i & 0xf)); // pink translationtables[5][i] = (byte) (0x30 + (i & 0xf)); // skin translationtables[6][i] = (byte) (0x50 + (i & 0xf)); // metal translationtables[7][i] = (byte) (0x80 + (i & 0xf)); // copper translationtables[8][i] = (byte) (0xB0 + (i & 0xf)); // b.red translationtables[9][i] = (byte) (0xC0 + (i & 0xf)); // electric // blue translationtables[10][i] = (byte) (0xD0 + (i & 0xf)); // guantanamo // "Halfhue" colors for which there are only 8 distinct hues translationtables[11][i] = (byte) (0x90 + (i & 0xf) / 2); // brown2 translationtables[12][i] = (byte) (0x98 + (i & 0xf) / 2); // gray2 translationtables[13][i] = (byte) (0xA0 + (i & 0xf) / 2); // piss translationtables[14][i] = (byte) (0xA8 + (i & 0xf) / 2); // gay translationtables[15][i] = (byte) (0xE0 + (i & 0xf) / 2); // yellow translationtables[16][i] = (byte) (0xE8 + (i & 0xf) / 2); // turd translationtables[17][i] = (byte) (0xF0 + (i & 0xf) / 2); // compblue translationtables[18][i] = (byte) (0xF8 + (i & 0xf) / 2); // whore translationtables[19][i] = (byte) (0x05 + (i & 0xf) / 2); // nigga // "Pimped up" colors, using mixed hues. translationtables[20][i] = (byte) (0x90 + (i & 0xf)); // soldier translationtables[21][i] = (byte) (0xA0 + (i & 0xf)); // drag // queen translationtables[22][i] = (byte) (0xE0 + (i & 0xf)); // shit & // piss translationtables[23][i] = (byte) (0xF0 + (i & 0xf)); // raver translationtables[24][i] = (byte) (0x70 + (0xf - i & 0xf)); // inv.marine translationtables[25][i] = (byte) (0xF0 + (0xf - i & 0xf)); // inv.raver translationtables[26][i] = (byte) (0xE0 + (0xf - i & 0xf)); // piss // & // shit translationtables[27][i] = (byte) (0xA0 + (i & 0xf)); // shitty // gay } else { for (int j = 1; j < TR_COLORS; j++) // Keep all other colors as is. translationtables[j][i] = (byte) i; } } } // ///////////////// Generic rendering methods ///////////////////// public IMaskedDrawer<T, V> getThings() { return this.MyThings; } /** * e6y: this is a precalculated value for more precise flats drawing (see * R_MapPlane) "Borrowed" from PrBoom+ */ protected float viewfocratio; protected int projectiony; // Some more isolation methods.... public final int getValidCount() { return validcount; } public final void increaseValidCount(int amount) { validcount += amount; } public boolean getSetSizeNeeded() { return setsizeneeded; } @Override public TextureManager<T> getTextureManager() { return TexMan; } public PlaneDrawer<T, V> getPlaneDrawer() { return this.MyPlanes; } public ViewVars getView() { return this.view; } public SpanVars<T, V> getDSVars() { return this.dsvars; } public LightsAndColors<V> getColorMap() { return this.colormaps; } public IDoomSystem getDoomSystem() { return this.I; } public Visplanes getVPVars() { return this.vp_vars; } @Override public SegVars getSegVars() { return this.seg_vars; } public IWadLoader getWadLoader() { return this.W; } public ISpriteManager getSpriteManager(){ return this.SM; } public BSPVars getBSPVars(){ return this.MyBSP; } public IVisSpriteManagement<V> getVisSpriteManager(){ return this.VIS; } // //////////////VIDEO SCALE STUFF /////////////////////// protected int SCREENWIDTH; protected int SCREENHEIGHT; protected float SCREEN_MUL; 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.SCREEN_MUL = vs.getScreenMul(); // Pre-scale stuff. view.negonearray = new short[SCREENWIDTH]; // MAES: in scaling view.screenheightarray = new short[SCREENWIDTH];// MAES: in scaling // Mirror in view view.xtoviewangle = xtoviewangle = new long[SCREENWIDTH + 1]; // Initialize children objects\ MySegs.setVideoScale(vs); MyPlanes.setVideoScale(vs); vp_vars.setVideoScale(vs); MyThings.setVideoScale(vs); MySegs.initScaling(); MyPlanes.initScaling(); vp_vars.initScaling(); MyThings.initScaling(); } /** * Initializes the various drawing functions. They are all "pegged" to the * same dcvars/dsvars object. Any initializations of e.g. parallel renderers * and their supporting subsystems should occur here. */ protected void R_InitDrawingFunctions(){ this.setHiColFuns(); this.setLowColFuns(); } // //////////////////////////// LIMIT RESETTING ////////////////// @Override public void resetLimits() { // Call it only at the beginning of new levels. VIS.resetLimits(); MySegs.resetLimits(); } /** * R_RenderView As you can guess, this renders the player view of a * particular player object. In practice, it could render the view of any * mobj too, provided you adapt the SetupFrame method (where the viewing * variables are set). This is the "vanilla" implementation which just works * for most cases. */ public void RenderPlayerView(player_t player) { // Viewing variables are set according to the player's mobj. Interesting // hacks like // free cameras or monster views can be done. /* Uncommenting this will result in a very existential experience. * Basically, the player's view will randomly switch between different mobj_t's in the map, * "possessing" them at the same time and turning them into (partial) Doomguys. Limited * control is possible (e.g. moving) but trying to use things or shoot while in this state will * result in a NPE. */ /* if (Math.random()>0.99){ thinker_t shit=P.getRandomThinker(); try { mobj_t crap=(mobj_t)shit; // It works, because practically all thinkers are also mobj_t's. There are no "bodyless" thinkers player.mo=crap; SetupFrame(crap); } catch (ClassCastException e){ } } else{ SetupFrame(player); } */ // Clear buffers. MyBSP.ClearClipSegs(); seg_vars.ClearDrawSegs(); vp_vars.ClearPlanes(); MySegs.ClearClips(); VIS.ClearSprites(); // Check for new console commands. DGN.NetUpdate(); // The head node is the last node output. MyBSP.RenderBSPNode(LL.numnodes - 1); // Check for new console commands. DGN.NetUpdate(); // FIXME: "Warped floor" fixed, now to fix same-height visplane // bleeding. MyPlanes.DrawPlanes(); // Check for new console commands. DGN.NetUpdate(); MyThings.DrawMasked(); colfunc.main = colfunc.base; // Check for new console commands. DGN.NetUpdate(); } } package rr; import static m.fixed_t.FRACBITS; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import p.Resettable; import w.CacheableDoomObject; /** This is the vertex structure used IN MEMORY with fixed-point arithmetic. * It's DIFFERENT than the one used on disk, which has 16-bit signed shorts. * However, it must be parsed. * */ public class vertex_t implements CacheableDoomObject, Resettable{ public vertex_t(){ } /** treat as (fixed_t) */ public int x,y; /** Notice how we auto-expand to fixed_t */ @Override public void unpack(ByteBuffer buf) throws IOException { buf.order(ByteOrder.LITTLE_ENDIAN); this.x=buf.getShort()<<FRACBITS; this.y=buf.getShort()<<FRACBITS; } @Override public void reset() { x=0; y=0; } public static int sizeOf() { return 4; } } package rr; import java.io.ByteArrayOutputStream; import java.util.ArrayList; import java.util.Arrays; import java.util.List; /** * Utilities to synthesize patch_t format images from multiple patches * (with transparency). * * * @author velktron * */ public class MultiPatchSynthesizer { static class PixelRange { public PixelRange(int start, int end) { this.start=start; this.end=end; } public int getLength(){ return (end-start+1); } public int start; public int end; } public static patch_t synthesizePatchFromFlat(String name,byte[] flat, int width, int height){ byte[] expected=new byte[width *height]; byte[][] pixels=new byte[width][height]; boolean[][] solid=new boolean[width][height]; // Copy as much data as possible. System.arraycopy(flat,0,expected,0,Math.min(flat.length,expected.length)); for (int i=0;i<width;i++){ Arrays.fill(solid[i],true); for (int j=0;j<height;j++){ pixels[i][j]=expected[i+j*width]; } } patch_t result=new patch_t(name,width,height,0,0); column_t[] columns=new column_t[width]; for (int x=0;x<width;x++) columns[x]=getColumnStream(pixels[x],solid[x],height); result.columns=columns; return result; } public static patch_t synthesize(String name,byte[][] pixels, boolean[][] solid, int width, int height){ patch_t result=new patch_t(name,width,height,0,0); column_t[] columns=new column_t[width]; for (int x=0;x<width;x++) columns[x]=getColumnStream(pixels[x],solid[x],height); result.columns=columns; return result; } public static column_t getColumnStream(byte[] pixels, boolean[] solid, int height){ column_t result=new column_t(); int start=-1; int end=-1; List<PixelRange> ranges=new ArrayList<PixelRange>(); // Scan column for continuous pixel ranges for (int i=0;i<height;i++){ // Encountered solid start. if (solid[i] && start==-1){ start=i; // mark start } // Last solid pixel if (solid[i] && i==height-1 && start!=-1 ){ end=i; ranges.add(new PixelRange(start,end)); start=end=-1; // reset start/end } // Start defined and ending not yet detected if (!solid[i] && start!=-1 && end ==-1){ end=i-1; // Single-pixel runs would be e.g. 1-2 -> 1-1 } if (start!=-1 && end!=-1){ // Range complete. ranges.add(new PixelRange(start,end)); start=end=-1; // reset start/end } } // There should be at least an empty post if (ranges.size()==0){ ranges.add(new PixelRange(0,-1)); } // Ideal for this use, since we don't know how big the patch is going to be a-priori ByteArrayOutputStream file=new ByteArrayOutputStream(); int topdelta=0; int n=ranges.size(); short topdeltas[]=new short[n]; int postofs[]=new int[n]; short postlens[]=new short[n]; for (int i=0;i<n;i++){ PixelRange pr=ranges.get(i); topdelta=pr.start; // cumulative top delta // Precomputed column data postofs[i]=(short) file.size()+3; // Last written post +3, pointing at first pixel of data. topdeltas[i]=(short) topdelta; postlens[i]=(short) (pr.getLength()); // Post lengths are at net of padding file.write(topdeltas[i]); file.write(postlens[i]); file.write(0); // padding file.write(pixels,pr.start,pr.getLength()); // data file.write(0); // padding } file.write(0xFF); // Terminator result.data=file.toByteArray(); result.postdeltas=topdeltas; result.postlen=postlens; result.postofs=postofs; result.posts=ranges.size(); // The ranges tell us where continuous posts return result; } /* public static patch_t synthesize(byte[][] pixels, boolean[][] solid, int width, int height, int picture_top, int picture_left){ // Ideal for this use, since we don't know how big the patch is going to be a-priori ByteArrayOutputStream file=new ByteArrayOutputStream(); int offset; int[] columnofs=new int[width]; // Patch header file.write(width); file.write(height); file.write(picture_top); file.write(picture_left); int column_array=0; int x=0,y; byte dummy_value; while (x<width){ //write memory buffer position to end of column_array columnofs[column_array]=file.size(); column_array++; y = 0; boolean operator = true; int pixel_count = 0; while (y < height){ byte val=pixels[x][y]; boolean transparent=!solid[x][y]; // Pixel is transparent if (transparent && !operator ) { dummy_value = 0; file.write(dummy_value); operator = true; } else //Pixel not transparent, and operator condition set. if (!transparent && operator){ int row_start = y; pixel_count = 0; dummy_value = 0; // write above post data to memory buffer offset = file.size(); //current post position in memory buffer operator = false; } else if (!transparent && !operator){ pixel_count++; // increment current post pixel_count } if (offset > 0 && pixel_count > 0){ previous_offset = current post position seek back in memory buffer by offset - 2 write pixel_count to memory buffer seek back to previous_offset end block write pixel to memory buffer end block increment y by 1 end block if operator = true or y = height then Pixel = 0 write Pixel to memory buffer rowstart = 255 write rowstart to memory buffer end block increment x by 1 end block seek memory buffer position to 0 block_size = picture_width * size of dword allocate block_memory, filled with 0's, with block_size write block_memory to file, using block_size as size offset = current file_position free block_memory seek to position 8 in file from start for loop, count = 0, break on count = number of elements in column_array column_offset = column_array[count] + offset write column_offset to file end block write memory buffer to file } } */ } package rr; /** Interface for sprite managers. Handles loading sprites, fixing * rotations etc. and helping retrieving spritedefs when required. * * @author velktron. * */ public interface ISpriteManager { /** Default known sprite names for DOOM */ public final static String[] doomsprnames = { "TROO","SHTG","PUNG","PISG","PISF","SHTF","SHT2","CHGG","CHGF","MISG", "MISF","SAWG","PLSG","PLSF","BFGG","BFGF","BLUD","PUFF","BAL1","BAL2", "PLSS","PLSE","MISL","BFS1","BFE1","BFE2","TFOG","IFOG","PLAY","POSS", "SPOS","VILE","FIRE","FATB","FBXP","SKEL","MANF","FATT","CPOS","SARG", "HEAD","BAL7","BOSS","BOS2","SKUL","SPID","BSPI","APLS","APBX","CYBR", "PAIN","SSWV","KEEN","BBRN","BOSF","ARM1","ARM2","BAR1","BEXP","FCAN", "BON1","BON2","BKEY","RKEY","YKEY","BSKU","RSKU","YSKU","STIM","MEDI", "SOUL","PINV","PSTR","PINS","MEGA","SUIT","PMAP","PVIS","CLIP","AMMO", "ROCK","BROK","CELL","CELP","SHEL","SBOX","BPAK","BFUG","MGUN","CSAW", "LAUN","PLAS","SHOT","SGN2","COLU","SMT2","GOR1","POL2","POL5","POL4", "POL3","POL1","POL6","GOR2","GOR3","GOR4","GOR5","SMIT","COL1","COL2", "COL3","COL4","CAND","CBRA","COL6","TRE1","TRE2","ELEC","CEYE","FSKU", "COL5","TBLU","TGRN","TRED","SMBT","SMGT","SMRT","HDB1","HDB2","HDB3", "HDB4","HDB5","HDB6","POB1","POB2","BRS1","TLMP","TLP2" }; void InitSpriteLumps(); int getNumSprites(); int getFirstSpriteLump(); spritedef_t[] getSprites(); spritedef_t getSprite(int index); int[] getSpriteWidth(); int[] getSpriteOffset(); int[] getSpriteTopOffset(); int getSpriteWidth(int index); int getSpriteOffset(int index); int getSpriteTopOffset(int index); void InitSprites(String[] namelist); } package rr; import p.Resettable; /** * * A SubSector. References a Sector. Basically, this is a list of LineSegs, * indicating the visible walls that define (all or some) sides of a convex BSP * leaf. * * @author admin */ public class subsector_t implements Resettable{ public subsector_t() { this(null, 0, 0); } public subsector_t(sector_t sector, int numlines, int firstline) { this.sector = sector; this.numlines = numlines; this.firstline = firstline; } // Maes: single pointer public sector_t sector; // e6y: support for extended nodes // 'int' instead of 'short' public int numlines; public int firstline; public String toString(){ sb.setLength(0); sb.append("Subsector"); sb.append('\t'); sb.append("Sector: "); sb.append(sector); sb.append('\t'); sb.append("numlines "); sb.append(numlines); sb.append('\t'); sb.append("firstline "); sb.append(firstline); return sb.toString(); } private static StringBuilder sb=new StringBuilder(); @Override public void reset() { sector=null; firstline=numlines=0; } } package rr; /** * A single patch from a texture definition, * basically a rectangular area within * the texture rectangle. * @author admin * */ public class texpatch_t { // Block origin (allways UL), // which has allready accounted // for the internal origin of the patch. int originx; int originy; int patch; public void copyFromMapPatch(mappatch_t mpp) { this.originx=mpp.originx; this.originy=mpp.originy; this.patch=mpp.patch; } } package rr; import static data.Defines.FF_FRAMEMASK; import static data.Defines.FF_FULLBRIGHT; import static data.Limits.MAXVISSPRITES; import static data.Tables.ANG45; import static data.Tables.BITS32; import static m.fixed_t.FRACBITS; import static m.fixed_t.FRACUNIT; import static m.fixed_t.FixedDiv; import static m.fixed_t.FixedMul; import static p.mobj_t.MF_SHADOW; import static rr.LightsAndColors.*; import static rr.Renderer.MINZ; import i.IDoomSystem; import java.util.Arrays; import p.mobj_t; import utils.C2JUtils; /** Visualized sprite manager. Depends on: SpriteManager, DoomSystem, * Colormaps, Current View. * * @author velktron * * @param <V> */ public final class VisSprites<V> implements IVisSpriteManagement<V> { private final static boolean DEBUG = false; private final static boolean RANGECHECK = false; protected IDoomSystem I; protected ISpriteManager SM; protected ViewVars view; protected LightsAndColors<V> colormaps; protected RendererState<?, V> R; public VisSprites(RendererState<?, V> R) { updateStatus(R); vissprite_t<V> tmp = new vissprite_t<V>(); vissprites = C2JUtils.createArrayOfObjects(tmp, MAXVISSPRITES); } public void updateStatus(RendererState<?, V> R) { this.R = R; this.view = R.view; this.I = R.I; this.SM = R.SM; this.colormaps = R.colormaps; } protected vissprite_t<V>[] vissprites; protected int vissprite_p; protected int newvissprite; // UNUSED // private final vissprite_t unsorted; // private final vissprite_t vsprsortedhead; // Cache those you get from the sprite manager protected int[] spritewidth, spriteoffset, spritetopoffset; /** * R_AddSprites During BSP traversal, this adds sprites by sector. */ @Override public void AddSprites(sector_t sec) { if (DEBUG) System.out.println("AddSprites"); mobj_t thing; int lightnum; // BSP is traversed by subsector. // A sector might have been split into several // subsectors during BSP building. // Thus we check whether its already added. if (sec.validcount == R.getValidCount()) return; // Well, now it will be done. sec.validcount = R.getValidCount(); lightnum = (sec.lightlevel >> LIGHTSEGSHIFT) + colormaps.extralight; if (lightnum < 0) colormaps.spritelights = colormaps.scalelight[0]; else if (lightnum >= LIGHTLEVELS) colormaps.spritelights = colormaps.scalelight[LIGHTLEVELS - 1]; else colormaps.spritelights = colormaps.scalelight[lightnum]; // Handle all things in sector. for (thing = sec.thinglist; thing != null; thing = (mobj_t) thing.snext) ProjectSprite(thing); } /** * R_ProjectSprite Generates a vissprite for a thing if it might be visible. * * @param thing */ protected final void ProjectSprite(mobj_t thing) { int tr_x, tr_y; int gxt, gyt; int tx, tz; int xscale, x1, x2; spritedef_t sprdef; spriteframe_t sprframe; int lump; int rot; boolean flip; int index; vissprite_t<V> vis; long ang; int iscale; // transform the origin point tr_x = thing.x - view.x; tr_y = thing.y - view.y; gxt = FixedMul(tr_x, view.cos); gyt = -FixedMul(tr_y, view.sin); tz = gxt - gyt; // thing is behind view plane? if (tz < MINZ) return; /* MAES: so projection/tz gives horizontal scale */ xscale = FixedDiv(view.projection, tz); gxt = -FixedMul(tr_x, view.sin); gyt = FixedMul(tr_y, view.cos); tx = -(gyt + gxt); // too far off the side? if (Math.abs(tx) > (tz << 2)) return; // decide which patch to use for sprite relative to player if (RANGECHECK) { if (thing.sprite.ordinal() >= SM.getNumSprites()) I.Error("R_ProjectSprite: invalid sprite number %d ", thing.sprite); } sprdef = SM.getSprite(thing.sprite.ordinal()); if (RANGECHECK) { if ((thing.frame & FF_FRAMEMASK) >= sprdef.numframes) I.Error("R_ProjectSprite: invalid sprite frame %d : %d ", thing.sprite, thing.frame); } sprframe = sprdef.spriteframes[thing.frame & FF_FRAMEMASK]; if (sprframe.rotate != 0) { // choose a different rotation based on player view ang = view.PointToAngle(thing.x, thing.y); rot = (int) ((ang - thing.angle + (ANG45 * 9) / 2) & BITS32) >>> 29; lump = sprframe.lump[rot]; flip = (boolean) (sprframe.flip[rot] != 0); } else { // use single rotation for all views lump = sprframe.lump[0]; flip = (boolean) (sprframe.flip[0] != 0); } // calculate edges of the shape tx -= spriteoffset[lump]; x1 = (view.centerxfrac + FixedMul(tx, xscale)) >> FRACBITS; // off the right side? if (x1 > view.width) return; tx += spritewidth[lump]; x2 = ((view.centerxfrac + FixedMul(tx, xscale)) >> FRACBITS) - 1; // off the left side if (x2 < 0) return; // store information in a vissprite vis = NewVisSprite(); vis.mobjflags = thing.flags; vis.scale = xscale << view.detailshift; vis.gx = thing.x; vis.gy = thing.y; vis.gz = thing.z; vis.gzt = thing.z + spritetopoffset[lump]; vis.texturemid = vis.gzt - view.z; vis.x1 = x1 < 0 ? 0 : x1; vis.x2 = x2 >= view.width ? view.width - 1 : x2; /* * This actually determines the general sprite scale) iscale = 1/xscale, * if this was floating point. */ iscale = FixedDiv(FRACUNIT, xscale); if (flip) { vis.startfrac = spritewidth[lump] - 1; vis.xiscale = -iscale; } else { vis.startfrac = 0; vis.xiscale = iscale; } if (vis.x1 > x1) vis.startfrac += vis.xiscale * (vis.x1 - x1); vis.patch = lump; // get light level if ((thing.flags & MF_SHADOW) != 0) { // shadow draw vis.colormap = null; } else if (colormaps.fixedcolormap != null) { // fixed map vis.colormap = (V) colormaps.fixedcolormap; // vis.pcolormap=0; } else if ((thing.frame & FF_FULLBRIGHT) != 0) { // full bright vis.colormap = (V) colormaps.colormaps[0]; // vis.pcolormap=0; } else { // diminished light index = xscale >> (LIGHTSCALESHIFT - view.detailshift); if (index >= MAXLIGHTSCALE) index = MAXLIGHTSCALE - 1; vis.colormap = colormaps.spritelights[index]; // vis.pcolormap=index; } } /** * R_NewVisSprite Returns either a "new" sprite (actually, reuses a pool), * or a special "overflow sprite" which just gets overwritten with bogus * data. It's a bit of dumb thing to do, since the overflow sprite is never * rendered but we have to copy data over it anyway. Would make more sense * to check for it specifically and avoiding copying data, which should be * more time consuming. Fixed by making this fully limit-removing. * * @return */ protected final vissprite_t<V> NewVisSprite() { if (vissprite_p == (vissprites.length - 1)) { ResizeSprites(); } // return overflowsprite; vissprite_p++; return vissprites[vissprite_p - 1]; } @Override public void cacheSpriteManager(ISpriteManager SM) { this.spritewidth = SM.getSpriteWidth(); this.spriteoffset = SM.getSpriteOffset(); this.spritetopoffset = SM.getSpriteTopOffset(); } /** * R_ClearSprites Called at frame start. */ @Override public void ClearSprites() { // vissprite_p = vissprites; vissprite_p = 0; } // UNUSED private final vissprite_t overflowsprite = new vissprite_t(); protected final void ResizeSprites() { vissprites = C2JUtils.resize(vissprites[0], vissprites, vissprites.length * 2); // Bye // bye, // old // vissprites. } /** * R_SortVisSprites UNUSED more efficient Comparable sorting + built-in * Arrays.sort function used. */ @Override public final void SortVisSprites() { Arrays.sort(vissprites, 0, vissprite_p); // Maes: got rid of old vissprite sorting code. Java's is better // Hell, almost anything was better than that. } @Override public int getNumVisSprites() { return vissprite_p; } @Override public vissprite_t<V>[] getVisSprites() { return vissprites; } public void resetLimits() { vissprite_t<V>[] tmp = C2JUtils.createArrayOfObjects(vissprites[0], MAXVISSPRITES); System.arraycopy(vissprites, 0, tmp, 0, MAXVISSPRITES); // Now, that was quite a haircut!. vissprites = tmp; } } package rr; import static m.fixed_t.FRACBITS; import static m.fixed_t.FixedMul; import p.Resettable; /** * The LineSeg. Must be built from on-disk mapsegs_t, which are much simpler. * * @author Maes */ public class seg_t implements Resettable { /** To be used as references */ public vertex_t v1, v2; /** Local caching. Spares us using one extra reference level */ public int v1x, v1y, v2x, v2y; /** (fixed_t) */ public int offset; /** (angle_t) */ public long angle; // MAES: all were single pointers. public side_t sidedef; public line_t linedef; /** * Sector references. Could be retrieved from linedef, too. backsector is * NULL for one sided lines */ public sector_t frontsector, backsector; // Boom stuff public boolean miniseg; public float length; /** proff 11/05/2000: needed for OpenGL */ public int iSegID; public void assignVertexValues() { this.v1x = v1.x; this.v1y = v1.y; this.v2x = v2.x; this.v2y = v2.y; } /** * R_PointOnSegSide * * @param x * @param y * @param line * @return */ public static int PointOnSegSide(int x, int y, seg_t line) { int lx; int ly; int ldx; int ldy; int dx; int dy; int left; int right; lx = line.v1x; ly = line.v1y; ldx = line.v2x - lx; ldy = line.v2y - ly; if (ldx == 0) { if (x <= lx) return (ldy > 0) ? 1 : 0; return (ldy < 0) ? 1 : 0; } if (ldy == 0) { if (y <= ly) return (ldx < 0) ? 1 : 0; return (ldx > 0) ? 1 : 0; } dx = x - lx; dy = y - ly; // Try to quickly decide by looking at sign bits. if (((ldy ^ ldx ^ dx ^ dy) & 0x80000000) != 0) { if (((ldy ^ dx) & 0x80000000) != 0) { // (left is negative) return 1; } return 0; } left = FixedMul(ldy >> FRACBITS, dx); right = FixedMul(dy, ldx >> FRACBITS); if (right < left) { // front side return 0; } // back side return 1; } /** * R_PointOnSegSide * * @param x * @param y * @param line * @return */ public int PointOnSegSide(int x, int y) { int lx; int ly; int ldx; int ldy; int dx; int dy; int left; int right; lx = this.v1x; ly = this.v1y; ldx = this.v2x - lx; ldy = this.v2y - ly; if (ldx == 0) { if (x <= lx) return (ldy > 0) ? 1 : 0; return (ldy < 0) ? 1 : 0; } if (ldy == 0) { if (y <= ly) return (ldx < 0) ? 1 : 0; return (ldx > 0) ? 1 : 0; } dx = x - lx; dy = y - ly; // Try to quickly decide by looking at sign bits. if (((ldy ^ ldx ^ dx ^ dy) & 0x80000000) != 0) { if (((ldy ^ dx) & 0x80000000) != 0) { // (left is negative) return 1; } return 0; } left = FixedMul(ldy >> FRACBITS, dx); right = FixedMul(dy, ldx >> FRACBITS); if (right < left) { // front side return 0; } // back side return 1; } public String toString() { return String .format( "Seg %d\n\tFrontsector: %s\n\tBacksector: %s\n\tVertexes: %x %x %x %x", iSegID, frontsector, backsector, v1x, v1y, v2x, v2y); } @Override public void reset() { v1 = v2 = null; v1x = v1y = v2x = v2y = 0; angle = 0; frontsector = backsector = null; iSegID = 0; linedef = null; miniseg = false; offset = 0; length = 0; } } package rr; import static data.Tables.ANGLETOFINESHIFT; import static data.Tables.BITS32; import static data.Tables.finecosine; import static data.Tables.finesine; import static m.fixed_t.FixedMul; import static rr.LightsAndColors.*; import i.IDoomSystem; import rr.RendererState.IPlaneDrawer; import rr.drawfuns.SpanVars; import v.IVideoScale; public abstract class PlaneDrawer<T,V> implements IPlaneDrawer{ private static final boolean DEBUG2=false; protected final boolean RANGECHECK = false; // // spanstart holds the start of a plane span // initialized to 0 at start // protected int[] spanstart, spanstop; // // texture mapping // protected V[] planezlight; // The distance lighting effect you see /** To treat as fixed_t */ protected int planeheight; /** To treat as fixed_t */ protected int[] distscale; /** To treat as fixed_t */ protected int[] cacheddistance, cachedxstep, cachedystep; protected final ViewVars view; protected final SegVars seg_vars; protected final SpanVars<T,V> dsvars; /** The visplane data. Set separately. For threads, use the same for * everyone. */ protected Visplanes vpvars; protected final LightsAndColors<V> colormap; protected final TextureManager<T> TexMan; protected final IDoomSystem I; protected PlaneDrawer(Renderer<T,V> R){ this.view=R.getView(); this.vpvars=R.getVPVars(); this.dsvars=R.getDSVars(); this.seg_vars=R.getSegVars(); this.colormap=R.getColorMap(); this.TexMan=R.getTextureManager(); this.I=R.getDoomSystem(); } /** * R_MapPlane * * Called only by R_MakeSpans. * * This is where the actual span drawing function is called. * * Uses global vars: planeheight ds_source -> flat data has already been * set. basexscale -> actual drawing angle and position is computed from * these baseyscale viewx viewy * * BASIC PRIMITIVE */ public void MapPlane(int y, int x1, int x2) { // MAES: angle_t int angle; // fixed_t int distance; int length; int index; if (RANGECHECK) { rangeCheck(x1,x2,y); } if (planeheight != vpvars.cachedheight[y]) { vpvars.cachedheight[y] = planeheight; distance = cacheddistance[y] = FixedMul(planeheight, vpvars.yslope[y]); dsvars.ds_xstep = cachedxstep[y] = FixedMul(distance, vpvars.basexscale); dsvars.ds_ystep = cachedystep[y] = FixedMul(distance, vpvars.baseyscale); } else { distance = cacheddistance[y]; dsvars.ds_xstep = cachedxstep[y]; dsvars.ds_ystep = cachedystep[y]; } length = FixedMul(distance, distscale[x1]); angle = (int) (((view.angle + view.xtoviewangle[x1]) & BITS32) >>> ANGLETOFINESHIFT); dsvars.ds_xfrac = view.x + FixedMul(finecosine[angle], length); dsvars.ds_yfrac = -view.y - FixedMul(finesine[angle], length); if (colormap.fixedcolormap != null) dsvars.ds_colormap = colormap.fixedcolormap; else { index = distance >>> LIGHTZSHIFT; if (index >= MAXLIGHTZ) index = MAXLIGHTZ - 1; dsvars.ds_colormap = planezlight[index]; } dsvars.ds_y = y; dsvars.ds_x1 = x1; dsvars.ds_x2 = x2; // high or low detail dsvars.spanfunc.invoke(); } protected final void rangeCheck(int x1,int x2,int y) { if (x2 < x1 || x1 < 0 || x2 >= view.width || y > view.height) I.Error("%s: %d, %d at %d",this.getClass().getName(), x1, x2, y); } /** * R_MakeSpans * * Called only by DrawPlanes. If you wondered where the actual * boundaries for the visplane flood-fill are laid out, this is it. * * The system of coords seems to be defining a sort of cone. * * * @param x * Horizontal position * @param t1 * Top-left y coord? * @param b1 * Bottom-left y coord? * @param t2 * Top-right y coord ? * @param b2 * Bottom-right y coord ? * */ protected void MakeSpans(int x, int t1, int b1, int t2, int b2) { // If t1 = [sentinel value] then this part won't be executed. while (t1 < t2 && t1 <= b1) { this.MapPlane(t1, spanstart[t1], x - 1); t1++; } while (b1 > b2 && b1 >= t1) { this.MapPlane(b1, spanstart[b1], x - 1); b1--; } // So...if t1 for some reason is < t2, we increase t2 AND store the // current x // at spanstart [t2] :-S while (t2 < t1 && t2 <= b2) { // System.out.println("Increasing t2"); spanstart[t2] = x; t2++; } // So...if t1 for some reason b2 > b1, we decrease b2 AND store the // current x // at spanstart [t2] :-S while (b2 > b1 && b2 >= t2) { // System.out.println("Decreasing b2"); spanstart[b2] = x; b2--; } } /** * R_InitPlanes Only at game startup. */ public void InitPlanes() { // Doh! } /////////// VIDEO SCALE STUFF /////////// protected int SCREENWIDTH; protected int SCREENHEIGHT; protected IVideoScale vs; @Override public void setVideoScale(IVideoScale vs) { this.vs = vs; } @Override public void initScaling() { this.SCREENHEIGHT = vs.getScreenHeight(); this.SCREENWIDTH = vs.getScreenWidth(); // Pre-scale stuff. spanstart = new int[SCREENHEIGHT]; spanstop = new int[SCREENHEIGHT]; distscale = new int[SCREENWIDTH]; cacheddistance = new int[SCREENHEIGHT]; cachedxstep = new int[SCREENHEIGHT]; cachedystep = new int[SCREENHEIGHT]; // HACK: visplanes are initialized globally. visplane_t.setVideoScale(vs); visplane_t.initScaling(); vpvars.initVisplanes(); } protected final void rangeCheckErrors(){ if (seg_vars.ds_p > seg_vars.MAXDRAWSEGS) I.Error("R_DrawPlanes: drawsegs overflow (%d)", seg_vars.ds_p); if (vpvars.lastvisplane > vpvars.MAXVISPLANES) I.Error(" R_DrawPlanes: visplane overflow (%d)", vpvars.lastvisplane); if (vpvars.lastopening > vpvars.MAXOPENINGS) I.Error("R_DrawPlanes: opening overflow (%d)", vpvars.lastopening); } /** Default implementation which DOES NOTHING. MUST OVERRIDE */ public void DrawPlanes(){ } public void sync(){ // Nothing required if serial. } /////////////// VARIOUS BORING GETTERS //////////////////// @Override public int[] getDistScale() { return distscale; } } package rr; import static data.Defines.PU_CACHE; import static data.Defines.PU_STATIC; import static data.Defines.SKYFLATNAME; import static m.fixed_t.FRACBITS; import static m.fixed_t.FRACUNIT; import i.IDoomSystem; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.Arrays; import java.util.Enumeration; import java.util.HashMap; import java.util.Hashtable; import p.AbstractLevelLoader; import doom.DoomStatus; import w.DoomBuffer; import w.IWadLoader; import w.li_namespace; import w.lumpinfo_t; /** An attempt to separate texture mapping functionality from * the rest of the rendering. Seems to work like a charm, and * it makes it clearer what needs and what doesn't need to be * exposed. * * @author Maes * */ public class SimpleTextureManager implements TextureManager<byte[]> { IWadLoader W; IDoomSystem I; AbstractLevelLoader LL; DoomStatus<?,?> DM; // // Graphics. // DOOM graphics for walls and sprites // is stored in vertical runs of opaque pixels (posts). // A column is composed of zero or more posts, // a patch or sprite is composed of zero or more columns. // protected int firstflat; protected int lastflat; protected int numflats; /** HACK */ protected flat_t[] flats; //protected int firstpatch; //protected int lastpatch; protected int numpatches; protected int numtextures; /** The unchached textures themselves, stored just as patch lists and various properties */ protected texture_t[] textures; /** Width per texture? */ protected int[] texturewidthmask; /** fixed_t[] needed for texture pegging */ /** How tall each composite texture is supposed to be */ protected int[] textureheight; /** How large each composite texture is supposed to be */ protected int[] texturecompositesize; /** Tells us which patch lump covers which column of which texture */ protected short[][] texturecolumnlump; /** This is supposed to store indexes into a patch_t lump which point to the columns themselves * Instead, we're going to return indexes to columns inside a particular patch. * In the case of patches inside a non-cached multi-patch texture (e.g. those made of non-overlapping * patches), we're storing indexes INSIDE A PARTICULAR PATCH. E.g. for STARTAN1, which is made of two * 32-px wide patches, it should go something like 0, 1,2 ,3...31, 0,1,2,....31. * * */ protected char[][] texturecolumnofs; /** couple with texturecomposite */ protected char texturecoloffset; //short[][] texturecolumnindexes; /** Stores [textures][columns][data]. */ protected byte[][][] texturecomposite; /** HACK to store "composite masked textures", a Boomism. */ protected patch_t[] patchcomposite; /** for global animation. Storage stores actual lumps, translation is a relative -> relative map */ protected int[] flattranslation, flatstorage,texturetranslation; // This is also in DM, but one is enough, really. protected int skytexture,skytexturemid,skyflatnum; public SimpleTextureManager(DoomStatus<?,?> DC) { this.DM=DC; this.W=DM.W; this.I=DM.I; this.LL=DM.LL; FlatPatchCache=new Hashtable<Integer, patch_t>(); } /** Hash table used for matching flat <i>lump</i> to flat <i>num</i> */ Hashtable<Integer, Integer> FlatCache; Hashtable<Integer, patch_t> FlatPatchCache; /** * R_CheckTextureNumForName Check whether texture is available. Filter out * NoTexture indicator. Can be sped up with a hash table, but it's pointless. */ @Override public int CheckTextureNumForName(String name) { Integer i; // "NoTexture" marker. if (name.charAt(0) == '-') return 0; i=TextureCache.get(name); if (i==null) return -1; else return i; /* for (i = 0; i < numtextures; i++) if (textures[i].name.compareToIgnoreCase(name) == 0) return i; return -1; */ } /** Hash table used for fast texture lookup */ Hashtable<String, Integer> TextureCache; /** * R_TextureNumForName * Calls R_CheckTextureNumForName, * aborts with error message. */ public int TextureNumForName(String name) { int i; i = CheckTextureNumForName(name); if (i == -1) { I.Error("R_TextureNumForName: %s not found", name); } return i; } /** * R_InitTextures * Initializes the texture list * with the textures from the world map. */ public void InitTextures () throws IOException { // This drives the rest maptexture_t mtexture = new maptexture_t(); texture_t texture; mappatch_t[] mpatch; texpatch_t[] patch; ByteBuffer[] maptex = new ByteBuffer[texturelumps.length]; int[] patchlookup; int totalwidth; int offset; int[] maxoff = new int[texturelumps.length]; int[] _numtextures = new int[texturelumps.length]; int directory = 1; int texset=TEXTURE1; // Load the patch names from pnames.lmp. //name[8] = 0; patchlookup=loadPatchNames("PNAMES"); // Load the map texture definitions from textures.lmp. // The data is contained in one or two lumps, // TEXTURE1 for shareware, plus TEXTURE2 for commercial. for (int i=0;i<texturelumps.length;i++){ String TEXTUREx=texturelumps[i]; if (W.CheckNumForName (TEXTUREx) != -1){ maptex[i] = W.CacheLumpName (TEXTUREx, PU_STATIC).getBuffer(); maptex[i].rewind(); maptex[i].order(ByteOrder.LITTLE_ENDIAN); _numtextures[i] = maptex[i].getInt(); maxoff[i] = W.LumpLength (W.GetNumForName (TEXTUREx)); if (_numtextures[i]!= (maxoff[i]-4)/8) { System.err.printf("Warning! Possibly malformed TEXTURE%d lump. Stated number of textures: %d Possible: %d\n",i, _numtextures[i] , (maxoff[i]-4)/8); } } } // Total number of textures. numtextures = _numtextures[0] + _numtextures[1]; textures = new texture_t[numtextures]; // MAES: Texture hashtable. TextureCache=new Hashtable<String, Integer>(numtextures); texturecolumnlump = new short[numtextures][]; texturecolumnofs = new char[numtextures][]; patchcomposite=new patch_t[numtextures]; texturecomposite = new byte[numtextures][][]; texturecompositesize = new int[numtextures]; texturewidthmask = new int[numtextures]; textureheight = new int[numtextures]; totalwidth = 0; // Really complex printing shit... System.out.print("["); for (int i=0 ; i<numtextures ; i++,directory++) { if ((i&63)==0) System.out.print ('.'); if (i == _numtextures[TEXTURE1]) { // Start looking in second texture file. texset=TEXTURE2; directory = 1; // offset "1" inside maptex buffer //System.err.print("Starting looking into TEXTURE2\n"); } offset = maptex[texset].getInt(directory<<2); if (offset > maxoff[texset]) I.Error("R_InitTextures: bad texture directory"); maptex[texset].position(offset); // Read "maptexture", which is the on-disk form. mtexture.unpack(maptex[texset]); // MAES: the HashTable only needs to know the correct names. TextureCache.put(mtexture.name.toUpperCase(), new Integer(i)); // We don't need to manually copy trivial fields. textures[i]=new texture_t(); textures[i].copyFromMapTexture(mtexture); texture = textures[i]; // However we do need to correct the "patch.patch" field through the patchlookup mpatch = mtexture.patches; patch = texture.patches; for (int j=0 ; j<texture.patchcount ; j++) { //System.err.printf("Texture %d name %s patch %d lookup %d\n",i,mtexture.name,j,mpatch[j].patch); patch[j].patch = patchlookup[mpatch[j].patch%patchlookup.length]; if (patch[j].patch == -1) { I.Error ("R_InitTextures: Missing patch in texture %s", texture.name); } } // Columns and offsets of taxture = textures[i] texturecolumnlump[i] = new short[texture.width]; //C2JUtils.initArrayOfObjects( texturecolumnlump[i], column_t.class); texturecolumnofs[i] = new char[texture.width]; int j = 1; while (j*2 <= texture.width) j<<=1; texturewidthmask[i] = j-1; textureheight[i] = texture.height<<FRACBITS; totalwidth += texture.width; } // Precalculate whatever possible. for (int i=0 ; i<numtextures ; i++) GenerateLookup (i); // Create translation table for global animation. texturetranslation = new int[numtextures]; for (int i=0 ; i<numtextures ; i++) texturetranslation[i] = i; } /** Assigns proper lumpnum to patch names. Check whether flats and patches of the same name coexist. * If yes, priority should go to patches. Otherwise, it's a "flats on walls" case. * * @param pnames * @return * @throws IOException */ private int[] loadPatchNames(String pnames) throws IOException { int[] patchlookup; int nummappatches; String name; ByteBuffer names = W.CacheLumpName (pnames, PU_STATIC).getBuffer(); names.order(ByteOrder.LITTLE_ENDIAN); // Number of patches. names.rewind(); nummappatches = names.getInt(); patchlookup = new int[nummappatches]; for (int i=0 ; i<nummappatches ; i++) { // Get a size limited string; name=DoomBuffer.getNullTerminatedString(names, 8).toUpperCase(); // Resolve clashes int[] stuff= W.CheckNumsForName (name); // Move backwards. for (int k=0;k<stuff.length;k++){ // Prefer non-flat, with priority if (W.GetLumpInfo(stuff[k]).namespace != li_namespace.ns_flats) { patchlookup[i]=stuff[k]; break; } // Suck it down :-/ patchlookup[i]=stuff[k]; } } return patchlookup; } private patch_t retrievePatchSafe(int lump){ // If this is a known troublesome lump, get it from the cache. if (FlatPatchCache.containsKey(lump)){ return FlatPatchCache.get(lump); } lumpinfo_t info = W.GetLumpInfo(lump); patch_t realpatch; // Patch is actually a flat or something equally nasty. Ouch. if (info.namespace==li_namespace.ns_flats) { byte[] flat=W.CacheLumpNumAsRawBytes(lump, PU_CACHE); realpatch= MultiPatchSynthesizer.synthesizePatchFromFlat(info.name,flat,64, 64); this.FlatPatchCache.put(lump, realpatch); W.UnlockLumpNum(lump); } else // It's probably safe, at this point. realpatch = (patch_t) W.CacheLumpNum (lump, PU_CACHE,patch_t.class); return realpatch; } /** * R_GenerateLookup * * Creates the lookup tables for a given texture (aka, where inside the texture cache * is the offset for particular column... I think. * * @throws IOException */ @Override public void GenerateLookup (int texnum) throws IOException { texture_t texture; short[] patchcount; //Keeps track of how many patches overlap a column. texpatch_t[] patch; patch_t realpatch = null; int x; int x1; int x2; short[] collump; char[] colofs; texture = textures[texnum]; // Composited texture not created yet. texturecomposite[texnum] = null; // We don't know ho large the texture will be, yet, but it will be a multiple of its height. texturecompositesize[texnum] = 0; // This is the only place where those can be actually modified. // They are still null at this point. collump = texturecolumnlump[texnum]; colofs = texturecolumnofs[texnum]; /* Now count the number of columns that are covered by more * than one patch. Fill in the lump / offset, so columns * with only a single patch are all done. */ patchcount = new short[texture.width]; patch = texture.patches; // for each patch in a texture... for (int i=0; i<texture.patchcount;i++) { // Retrieve patch...if it IS a patch. realpatch=this.retrievePatchSafe(patch[i].patch); x1 = patch[i].originx; x2 = x1 + realpatch.width; // Where does the patch start, inside the compositetexture? if (x1 < 0) x = 0; else x = x1; // Correct, starts at originx. Where does it end? if (x2 > texture.width) x2 = texture.width; for ( ; x<x2 ; x++) { /* Obviously, if a patch starts at x it does cover the x-th column * of a texture, even if transparent. */ patchcount[x]++; // Column "x" of composite texture "texnum" is covered by this patch. collump[x] = (short) patch[i].patch; /* This is supposed to be a raw pointer to the beginning of the column * data, as it appears inside the PATCH. * * Instead, we can return the actual column index (x-x1) * As an example, the second patch of STARTAN1 (width 64) starts * at column 32. Therefore colofs should be something like * 0,1,2,...,31,0,1,....31, indicating that the 32-th column of * STARTAN1 is the 0-th column of the patch that is assigned to that column * (the latter can be looked up in texturecolumnlump[texnum]. * * Any questions? * */ colofs[x] = (char) (x-x1); // This implies that colofs[x] is 0 for a void column? } // end column of patch. } // end patch // Now check all columns again. for ( x=0 ; x<texture.width ; x++) { // Can only occur if a column isn't covered by a patch at all, not even a transparent one. if (patchcount[x]==0) { // TODO: somehow handle this. System.err.print (realpatch.width); System.err.print ("R_GenerateLookup: column without a patch ("+texture.name+")\n"); //return; } // I_Error ("R_GenerateLookup: column without a patch"); // Columns where more than one patch overlaps. if (patchcount[x] > 1) { // Use the cached block. This column won't be read from the wad system. collump[x] = -1; colofs[x] = (char) texturecompositesize[texnum]; /* Do we really mind? if (texturecompositesize[texnum] > 0x10000-texture.height) { I.Error ("R_GenerateLookup: texture no %d (%s) is >64k", texnum,textures[texnum].name); } */ texturecompositesize[texnum] += texture.height; } } } /** * R_GenerateComposite * Using the texture definition, the composite texture is created * from the patches and each column is cached. This method is "lazy" * aka it's only called when a cached/composite texture is needed. * * @param texnum * */ public void GenerateComposite (int texnum) { byte[][] block; texture_t texture; texpatch_t[] patch; patch_t realpatch=null; int x; int x1; int x2; column_t patchcol; short[] collump; char[] colofs; // unsigned short // short[] colidxs; // unsigned short texture = textures[texnum]; // BOth allocate the composite texture, and assign it to block. // texturecompositesize indicates a size in BYTES. We need a number of columns, though. // Now block is divided into columns. We need to allocate enough data for each column block = texturecomposite[texnum]=new byte[texture.width][texture.height]; // Lump where a certain column will be read from (actually, a patch) collump = texturecolumnlump[texnum]; // Offset of said column into the patch. colofs = texturecolumnofs[texnum]; // colidxs = texturecolumnindexes[texnum]; // Composite the columns together. patch = texture.patches; // For each patch in the texture... for (int i=0 ;i<texture.patchcount; i++) { // Retrieve patch...if it IS a patch. realpatch=this.retrievePatchSafe(patch[i].patch); x1 = patch[i].originx; x2 = x1 + realpatch.width; if (x1<0) x = 0; else x = x1; if (x2 > texture.width) x2 = texture.width; for ( ; x<x2 ; x++) { // Column does not have multiple patches? if (collump[x] >= 0) continue; // patchcol = (column_t *)((byte *)realpatch // + LONG(realpatch.columnofs[x-x1])); // We can look this up cleanly in Java. Ha! patchcol=realpatch.columns[x-x1]; DrawColumnInCache (patchcol, block[x], colofs[x], patch[i].originy, texture.height); } } } /** * R_GenerateMaskedComposite * * Generates a "masked composite texture": the result is a MASKED texture * (with see-thru holes), but this time multiple patches can be used to * assemble it, unlike standard Doom where this is not allowed. * * Called only if a request for a texture in the general purpose GetColumn * method (used only for masked renders) turns out not to be pointing to a standard * cached texture, nor to a disk lump(which is the standard Doom way of indicating a * composite single patch texture) but to a cached one which, however, is composite. * * Confusing, huh? * * Normally, this results in a disaster, as the masked rendering methods * don't expect cached/composite textures at all, and you get all sorts of nasty * tutti frutti and medusa effects. Not anymore ;-) * * @param texnum * */ @Override public void GenerateMaskedComposite(int texnum) { byte[][] block; boolean[][] pixmap; // Solidity map texture_t texture; texpatch_t[] patch; patch_t realpatch = null; int x; int x1; int x2; column_t patchcol; short[] collump; char[] colofs; // unsigned short texture = textures[texnum]; // MAES: we don't want to save a solid block this time. Will only use // it for synthesis. block = new byte[texture.width][texture.height]; pixmap = new boolean[texture.width][texture.height]; // True values = solid // Lump where a certain column will be read from (actually, a patch) collump = texturecolumnlump[texnum]; // Offset of said column into the patch. colofs = texturecolumnofs[texnum]; // Composite the columns together. patch = texture.patches; // For each patch in the texture... for (int i = 0; i < texture.patchcount; i++) { realpatch = W.CachePatchNum(patch[i].patch); x1 = patch[i].originx; x2 = x1 + realpatch.width; if (x1 < 0) x = 0; else x = x1; if (x2 > texture.width) x2 = texture.width; for (; x < x2; x++) { // Column does not have multiple patches? if (collump[x] >= 0) continue; // patchcol = (column_t *)((byte *)realpatch // + LONG(realpatch.columnofs[x-x1])); // We can look this up cleanly in Java. Ha! patchcol = realpatch.columns[x - x1]; DrawColumnInCache(patchcol, block[x], pixmap[x], colofs[x], patch[i].originy, texture.height); } } // Patch drawn on cache, synthesize patch_t using it. this.patchcomposite[texnum]=MultiPatchSynthesizer.synthesize(this.CheckTextureNameForNum(texnum),block, pixmap,texture.width,texture.height); } /** * R_DrawColumnInCache * Clip and draw a column from a patch into a cached post. * * This means that columns are effectively "uncompressed" into cache, here, * and that composite textures are generally uncompressed...right? * * Actually: "compressed" or "masked" textures are retrieved in the same way. * There are both "masked" and "unmasked" drawing methods. If a masked * column is passed to a method that expects a full, dense column...well, * it will look fugly/overflow/crash. Vanilla Doom tolerated this, * we're probably going to have more problems. * * @param patch Actually it's a single column to be drawn. May overdraw existing ones or void space. * @param cache the column cache itself. Actually it's the third level [texture][column]->data. * @param offset an offset inside the column cache (UNUSED) * @param originy vertical offset. Caution with masked stuff! * @param cacheheight the maximum height it's supposed to reach when drawing? * */ public void DrawColumnInCache(column_t patch, byte[] cache, int offset, int originy, int cacheheight) { int count; int position; int source = 0; // treat as pointers /* * Iterate inside column. This is starkly different from the C code, * because post positions AND offsets are already precomputed at load * time */ for (int i = 0; i < patch.posts; i++) { // This should position us at the beginning of the next post source = patch.postofs[i]; count = patch.postlen[i]; // length of this particular post position = originy + patch.postdeltas[i]; // Position to draw inside // cache. // Post starts outside of texture's bounds. Adjust offset. if (position < 0) { count += position; // Consider that we have a "drawing debt". position = 0; } // Post will go too far outside. if (position + count > cacheheight) count = cacheheight - position; if (count > 0) // Draw this post. Won't draw posts that start // "outside" // Will start at post's start, but will only draw enough pixels // not to overdraw. System.arraycopy(patch.data, source, cache, position, count); } } // Version also drawing on a supplied transparency map public void DrawColumnInCache(column_t patch, byte[] cache, boolean[] pixmap, int offset, int originy, int cacheheight) { int count; int position; int source = 0; // treat as pointers /* * Iterate inside column. This is starkly different from the C code, * because post positions AND offsets are already precomputed at load * time */ for (int i = 0; i < patch.posts; i++) { // This should position us at the beginning of the next post source = patch.postofs[i]; count = patch.postlen[i]; // length of this particular post position = originy + patch.postdeltas[i]; // Position to draw inside // cache. // Post starts outside of texture's bounds. Adjust offset. if (position < 0) { count += position; // Consider that we have a "drawing debt". position = 0; } // Post will go too far outside. if (position + count > cacheheight) count = cacheheight - position; if (count > 0) { // Draw post, AND fill solidity map System.arraycopy(patch.data, source, cache, position, count); Arrays.fill(pixmap, position, position+count, true); } // Repeat for next post(s), if any. } } /** * R_InitFlats * * Scans WADs for F_START/F_END lumps, and also any additional * F1_ and F2_ pairs. * * Correct behavior would be to detect F_START/F_END lumps, * and skip any marker lumps sandwiched in between. If F_START and F_END are external, * use external override. * * Also, in the presence of external FF_START lumps, merge their contents * with those previously read. * * The method is COMPATIBLE with resource pre-coalesing, however it's not * trivial to change back to the naive code because of the "translationless" * system used (all flats are assumed to lie in a linear space). This * speeds up lookups. * */ @Override public final void InitFlats () { numflats=0; int extendedflatstart=-1; firstflat=W.GetNumForName(LUMPSTART); // This is the start of normal lumps. if (FlatCache==null) FlatCache=new Hashtable<Integer,Integer>(); else FlatCache.clear(); Hashtable<String,Integer> FlatNames=new Hashtable<String,Integer> (); // Store names here. // Normally, if we don't use Boom features, we could look for F_END and that's it. // However we want to avoid using flat translation and instead store absolute lump numbers. // So we need to do a clean parse. // The rule is: we scan from the very first F_START to the very first F_END. // We discard markers, and only assign sequential numbers to valid lumps. // These are the vanilla flats, and will work with fully merged PWADs too. // Normally, this marks the end of regular lumps. However, if DEUTEX extension // are present, it will actually mark the end of the extensions due to lump // priority, so its usefulness as an absolute end-index for regular flats // is dodgy at best. Gotta love the inconsistent mundo hacks! //int lastflatlump=W.GetNumForName(LUMPEND); // int lump=firstflat; int seq=0; String name; while (!(name=W.GetNameForNum(lump)).equalsIgnoreCase(LUMPEND)){ if (!W.isLumpMarker(lump)){ // Not a marker. Put in cache. FlatCache.put(lump, seq); // Save its name too. FlatNames.put(name, lump); seq++; // Advance sequence numflats++; // Total flats do increase } lump++; // Advance lump. } extendedflatstart=W.CheckNumForName(DEUTEX_START); // This is the start of DEUTEX flats. if (extendedflatstart>-1){ // If extended ones are present, then Advance slowly. lump=extendedflatstart; // Safeguard: FF_START without corresponding F_END (e.g. in helltest.wad) name=W.GetNameForNum(lump); // The end of those extended flats is also marked by F_END or FF_END, as noted above. // It can also be non-existent in some broken maps like helltest.wad. Jesus. while (!(name==null || name.equalsIgnoreCase(LUMPEND)||name.equalsIgnoreCase(DEUTEX_END))){ if (!W.isLumpMarker(lump)){ // Not a marker. Check if it's supposed to replace something. if (FlatNames.containsKey(name)){ // Well, it is. Off with its name, save the lump number though. int removed=FlatNames.remove(name); // Put new name in list FlatNames.put(name, lump); // Remove old lump, but keep sequence. int oldseq=FlatCache.remove(removed); // Put new lump number with old sequence. FlatCache.put(lump, oldseq); } else { // Add normally FlatCache.put(lump, seq); // Save its name too. FlatNames.put(name, lump); seq++; // Advance sequence numflats++; // Total flats do increase } } lump++; // Advance lump. name=W.GetNameForNum(lump); } } // So now we have a lump -> sequence number mapping. // Create translation table for global animation. flattranslation = new int[numflats]; flatstorage = new int[numflats]; // MAJOR CHANGE: flattranslation stores absolute lump numbers. Adding // firstlump is not necessary anymore. // Now, we're pretty sure that we have a progressive value mapping. Enumeration<Integer> stuff= FlatCache.keys(); while (stuff.hasMoreElements()){ int nextlump=stuff.nextElement(); flatstorage[FlatCache.get(nextlump)]=nextlump; // Lump is used as the key, while the relative lump number is the value. //FlatCache.put(j, k-1); } for (int i=0;i<numflats;i++){ flattranslation[i]=i; // System.out.printf("Verification: flat[%d] is %s in lump %d\n",i,W.GetNameForNum(flattranslation[i]),flatstorage[i]); } } private final static String LUMPSTART="F_START"; private final static String LUMPEND="F_END"; private final static String DEUTEX_END="FF_END"; private final static String DEUTEX_START="FF_START"; /** * R_PrecacheLevel * Preloads all relevant graphics for the level. * * MAES: Everything except sprites. * A Texturemanager != sprite manager. * So the relevant functionality has been split into * PrecacheThinkers (found in common rendering code). * * */ int flatmemory; int texturememory; public void PrecacheLevel () throws IOException { this.preCacheFlats(); this.preCacheTextures(); // recache sprites. /* MAES: this code into PrecacheThinkers spritepresent = new boolean[numsprites]; for (th = P.thinkercap.next ; th != P.thinkercap ; th=th.next) { if (th.function==think_t.P_MobjThinker) spritepresent[((mobj_t )th).sprite.ordinal()] = true; } spritememory = 0; for (i=0 ; i<numsprites ; i++) { if (!spritepresent[i]) continue; for (j=0 ; j<sprites[i].numframes ; j++) { sf = sprites[i].spriteframes[j]; for (k=0 ; k<8 ; k++) { lump = firstspritelump + sf.lump[k]; spritememory += W.lumpinfo[lump].size; W.CacheLumpNum(lump , PU_CACHE,patch_t.class); } } } */ } protected final void preCacheFlats(){ boolean[] flatpresent; int lump; if (DM.demoplayback) return; // Precache flats. flatpresent = new boolean[numflats]; flats=new flat_t[numflats]; for (int i=0 ; i<LL.numsectors ; i++) { flatpresent[LL.sectors[i].floorpic] = true; flatpresent[LL.sectors[i].ceilingpic] = true; } flatmemory = 0; for (int i=0 ; i<numflats ; i++) { if (flatpresent[i]) { lump = firstflat + i; flatmemory += W.GetLumpInfo(lump).size; flats[i]=(flat_t) W.CacheLumpNum(lump, PU_CACHE,flat_t.class); } } } protected final void preCacheTextures(){ boolean [] texturepresent; texture_t texture; int lump; // Precache textures. texturepresent = new boolean[numtextures]; for (int i=0 ; i<LL.numsides ; i++) { texturepresent[LL.sides[i].toptexture] = true; texturepresent[LL.sides[i].midtexture] = true; texturepresent[LL.sides[i].bottomtexture] = true; } // Sky texture is always present. // Note that F_SKY1 is the name used to // indicate a sky floor/ceiling as a flat, // while the sky texture is stored like // a wall texture, with an episode dependend // name. texturepresent[skytexture] = true; texturememory = 0; for (int i=0 ; i<numtextures ; i++) { if (!texturepresent[i]) continue; texture = textures[i]; for (int j=0 ; j<texture.patchcount ; j++) { lump = texture.patches[j].patch; texturememory += W.GetLumpInfo(lump).size; W.CacheLumpNum(lump , PU_CACHE,patch_t.class); } } } /** * R_FlatNumForName * Retrieval, get a flat number for a flat name. * * Unlike the texture one, this one is not used frequently. Go figure. */ @Override public final int FlatNumForName(String name) { int i; //System.out.println("Checking for "+name); i = W.CheckNumForName(name); //System.out.printf("R_FlatNumForName retrieved lump %d for name %s picnum %d\n",i,name,FlatCache.get(i)); if (i == -1) { I.Error("R_FlatNumForName: %s not found", name); } return FlatCache.get(i); } @Override public final int getTextureColumnLump(int tex, int col) { return texturecolumnlump[tex][col]; } @Override public final char getTextureColumnOfs(int tex, int col) { return texturecolumnofs[tex][col]; } @Override public final int getTexturewidthmask(int tex) { return texturewidthmask[tex]; } @Override public final byte[][] getTextureComposite(int tex) { return texturecomposite[tex]; } @Override public final byte[] getTextureComposite(int tex, int col) { return texturecomposite[tex][col]; } @Override public final patch_t getMaskedComposite(int tex) { return this.patchcomposite[tex]; } @Override public final int getTextureheight(int texnum) { return textureheight[texnum]; } @Override public final int getTextureTranslation(int texnum) { return texturetranslation[texnum]; } /** Returns a flat after it has been modified by the translation table e.g. by animations */ @Override public int getFlatTranslation(int flatnum) { return flatstorage[flattranslation[flatnum]]; } @Override public final void setTextureTranslation(int texnum, int amount) { texturetranslation[texnum]=amount; } /** This affects ONLY THE TRANSLATION TABLE, not the lump storage. * */ @Override public final void setFlatTranslation(int flatnum, int amount) { flattranslation[flatnum]=amount; } //////////////////////////////////From r_sky.c ///////////////////////////////////// //////////////////////////////////From r_sky.c ///////////////////////////////////// /** * R_InitSkyMap * Called whenever the view size changes. */ public int InitSkyMap () { skyflatnum = FlatNumForName ( SKYFLATNAME ); skytexturemid = 100*FRACUNIT; return skyflatnum; } @Override public int getSkyFlatNum() { return skyflatnum; } @Override public void setSkyFlatNum(int skyflatnum) { this.skyflatnum = skyflatnum; } @Override public int getSkyTexture() { return skytexture; } @Override public void setSkyTexture(int skytexture) { this.skytexture = skytexture; } /*@Override public int getFirstFlat() { return firstflat; } */ @Override public int getSkyTextureMid() { return skytexturemid; } @Override public String CheckTextureNameForNum(int texnum) { return textures[texnum].name; } @Override public int getFlatLumpNum(int flatnum) { // TODO Auto-generated method stub return 0; } /** Generates a "cached" masked column against a black background. * Synchronized so concurrency issues won't cause random glitching and * errors. * * @param lump * @param column * @return raw, 0-pointed column data. */ public synchronized byte[] getRogueColumn(int lump, int column) { // If previously requested, speed up gathering. //if (lastrogue==lump) // return rogue[column]; // Not contained? Generate. if (!roguePatches.containsKey(lump)) roguePatches.put(lump,generateRoguePatch(lump)); lastrogue=lump; rogue=roguePatches.get(lump); return rogue[column]; } /** Actually generates a tutti-frutti-safe cached patch out of * a masked or unmasked single-patch lump. * * @param lump * @return */ private byte[][] generateRoguePatch(int lump) { // Retrieve patch...if it IS a patch. patch_t p=this.retrievePatchSafe(lump); // Allocate space for a cached block. byte[][] block=new byte[p.width][p.height]; for (int i=0;i<p.width;i++) DrawColumnInCache(p.columns[i], block[i], i, 0, p.height); // Don't keep this twice in memory. W.UnlockLumpNum(lump); return block; } int lastrogue=-1; byte[][] rogue; HashMap<Integer,byte[][]> roguePatches= new HashMap<Integer,byte[][]> (); class TextureDirectoryEntry implements Comparable<TextureDirectoryEntry>{ /** Where an entry starts within the TEXTUREx lump */ int offset; /** Its implicit position as indicated by the directory's ordering */ int entry; /** Its MAXIMUM possible length, depending on what follows it. * Not trivial to compute without thoroughtly examining the entire lump */ int length; /** Entries are ranked according to actual offset */ @Override public int compareTo(TextureDirectoryEntry o) { if (this.offset<o.offset) return -1; if (this.offset==o.offset) return 0; return 1; } } @Override public byte[] getSafeFlat(int flatnum) { byte[] flat= ((flat_t)W.CacheLumpNum(getFlatTranslation(flatnum), PU_STATIC,flat_t.class)).data; if (flat.length<4096){ System.arraycopy(flat, 0,safepatch,0,flat.length); return safepatch; } return flat; } private final byte[] safepatch=new byte[4096]; // COLUMN GETTING METHODS. No idea why those had to be in the renderer... /** * Special version of GetColumn meant to be called concurrently by different * (MASKED) seg rendering threads, identfiex by index. This serves to avoid stomping * on mutual cached textures and causing crashes. * * Returns column_t, so in theory it could be made data-agnostic. * */ public column_t GetSmpColumn(int tex, int col, int id) { int lump,ofs; col &= getTexturewidthmask(tex); lump = getTextureColumnLump(tex, col); ofs = getTextureColumnOfs(tex, col); // It's always 0 for this kind of access. // Speed-increasing trick: speed up repeated accesses to the same // texture or patch, if they come from the same lump if (tex == smp_lasttex[id] && lump == smp_lastlump[id]) { if (composite) return smp_lastpatch[id].columns[col]; else return smp_lastpatch[id].columns[ofs]; } // If pointing inside a non-zero, positive lump, then it's not a // composite texture. Read it from disk. if (lump > 0) { // This will actually return a pointer to a patch's columns. // That is, to the ONE column exactly.{ // If the caller needs access to a raw column, we must point 3 bytes // "ahead". smp_lastpatch[id] = W.CachePatchNum(lump); smp_lasttex[id] = tex; smp_lastlump[id]=lump; smp_composite[id]=false; // If the column was a disk lump, use ofs. return smp_lastpatch[id].columns[ofs]; } // Problem. Composite texture requested as if it was masked // but it doesn't yet exist. Create it. if (getMaskedComposite(tex) == null){ System.err.printf("Forced generation of composite %s\n",CheckTextureNameForNum(tex),smp_composite[id],col,ofs); GenerateMaskedComposite(tex); System.err.printf("Composite patch %s %d\n",getMaskedComposite(tex).name,getMaskedComposite(tex).columns.length); } // Last resort. smp_lastpatch[id] = getMaskedComposite(tex); smp_lasttex[id]=tex; smp_composite[id]=true; smp_lastlump[id]=0; return lastpatch.columns[col]; } // False: disk-mirrored patch. True: improper "transparent composite". protected boolean[] smp_composite;// = false; protected int[] smp_lasttex;// = -1; protected int[] smp_lastlump;// = -1; protected patch_t[] smp_lastpatch;// = null; ///////////////////////// TEXTURE MANAGEMENT ///////////////////////// /** * R_GetColumn original version: returns raw pointers to byte-based column * data. Works for both masked and unmasked columns, but is not * tutti-frutti-safe. * * Use GetCachedColumn instead, if rendering non-masked stuff, which is also * faster. * * @throws IOException * * */ public byte[] GetColumn(int tex, int col) { int lump,ofs; col &= getTexturewidthmask(tex); lump = getTextureColumnLump(tex, col); ofs = getTextureColumnOfs(tex, col); // It's always 0 for this kind of access. // Speed-increasing trick: speed up repeated accesses to the same // texture or patch, if they come from the same lump if (tex == lasttex && lump == lastlump) { if (composite) return lastpatch.columns[col].data; else return lastpatch.columns[ofs].data; } // If pointing inside a non-zero, positive lump, then it's not a // composite texture. Read it from disk. if (lump > 0) { // This will actually return a pointer to a patch's columns. // That is, to the ONE column exactly.{ // If the caller needs access to a raw column, we must point 3 bytes // "ahead". lastpatch = W.CachePatchNum(lump); lasttex = tex; lastlump=lump; composite=false; // If the column was a disk lump, use ofs. return lastpatch.columns[ofs].data; } // Problem. Composite texture requested as if it was masked // but it doesn't yet exist. Create it. if (getMaskedComposite(tex) == null){ System.err.printf("Forced generation of composite %s\n",CheckTextureNameForNum(tex),composite,col,ofs); GenerateMaskedComposite(tex); System.err.printf("Composite patch %s %d\n",getMaskedComposite(tex).name,getMaskedComposite(tex).columns.length); } // Last resort. lastpatch = getMaskedComposite(tex); lasttex=tex; composite=true; lastlump=0; return lastpatch.columns[col].data; } /** * R_GetColumnStruct: returns actual pointers to columns. * Agnostic of the underlying type. * * Works for both masked and unmasked columns, but is not * tutti-frutti-safe. * * Use GetCachedColumn instead, if rendering non-masked stuff, which is also * faster. * * @throws IOException * * */ @Override public column_t GetColumnStruct(int tex, int col) { int lump,ofs; col &= getTexturewidthmask(tex); lump = getTextureColumnLump(tex, col); ofs = getTextureColumnOfs(tex, col); // Speed-increasing trick: speed up repeated accesses to the same // texture or patch, if they come from the same lump if (tex == lasttex && lump == lastlump) { if (composite) return lastpatch.columns[col]; else return lastpatch.columns[ofs]; } // If pointing inside a non-zero, positive lump, then it's not a // composite texture. Read it from disk. if (lump > 0) { // This will actually return a pointer to a patch's columns. // That is, to the ONE column exactly.{ // If the caller needs access to a raw column, we must point 3 bytes // "ahead". lastpatch = W.CachePatchNum(lump); lasttex = tex; lastlump=lump; composite=false; // If the column was a disk lump, use ofs. return lastpatch.columns[ofs]; } // Problem. Composite texture requested as if it was masked // but it doesn't yet exist. Create it. if (getMaskedComposite(tex) == null){ System.err.printf("Forced generation of composite %s\n",CheckTextureNameForNum(tex),composite,col,ofs); GenerateMaskedComposite(tex); System.err.printf("Composite patch %s %d\n",getMaskedComposite(tex).name,getMaskedComposite(tex).columns.length); } // Last resort. lastpatch = getMaskedComposite(tex); lasttex=tex; composite=true; lastlump=0; return lastpatch.columns[col]; } // False: disk-mirrored patch. True: improper "transparent composite". private boolean composite = false; private int lasttex = -1; private int lastlump = -1; private patch_t lastpatch = null; /** * R_GetColumn variation which is tutti-frutti proof. It only returns cached * columns, and even pre-caches single-patch textures intead of trashing the * WAD manager (should be faster, in theory). * * Cannot be used for drawing masked textures, use classic GetColumn * instead. * * * @throws IOException */ @Override public final byte[] GetCachedColumn(int tex, int col) { int lump, ofs; col &= getTexturewidthmask(tex); lump = getTextureColumnLump(tex, col); ofs = getTextureColumnOfs(tex, col); // In the case of cached columns, this is always 0. // Done externally, for now. //dcvars.dc_source_ofs = 0; // If pointing inside a non-zero, positive lump, then it's not a // composite texture. // Read from disk, and safeguard vs tutti frutti. if (lump > 0) { // This will actually return a pointer to a patch's columns. return getRogueColumn(lump, ofs); } // Texture should be composite, but it doesn't yet exist. Create it. if (getTextureComposite(tex) == null) GenerateComposite(tex); return getTextureComposite(tex, col); } @Override public void setSMPVars(int num_threads) { smp_composite=new boolean[num_threads];// = false; smp_lasttex=new int[num_threads];// = -1; smp_lastlump=new int[num_threads];// = -1; smp_lastpatch=new patch_t[num_threads];// = null; } } package rr; /** Purpose unknown, probably unused. * On a closer examination, it could have been part of a system to * "enqueue" masked draws, not much unlike the current parallel * rendering subsystem, but discarded because of simplifications. * In theory it could be brought back one day if parallel sprite * drawing comes back.. just a thought ;-) * * * @author Maes * */ public class maskdraw_t { public int x1; public int x2; public int column; public int topclip; public int bottomclip; } package rr; import v.IVideoScaleAware; /** Draws any masked stuff -sprites, textures, or special 3D floors */ public interface IMaskedDrawer<T,V> extends IVideoScaleAware,IDetailAware { public static final int BASEYCENTER = 100; /** Cache the sprite manager, if possible */ void cacheSpriteManager(ISpriteManager SM); void DrawMasked(); void setPspriteIscale(int i); void setPspriteScale(int i); /** * For serial masked drawer, just complete the column function. For * parallel version, store rendering instructions and execute later on. * HINT: you need to discern between masked and non-masked draws. */ void completeColumn(); } package rr; /** Stuff used to pass information between the BSP and the SegDrawer */ public class BSPVars{ /** The sectors of the line currently being considered */ public sector_t frontsector, backsector; public seg_t curline; public side_t sidedef; public line_t linedef; } package rr; import static data.Defines.PU_CACHE; import static m.fixed_t.FRACBITS; import i.DoomStatusAware; import i.IDoomSystem; import java.util.ArrayList; import java.util.Arrays; import java.util.Hashtable; import java.util.List; import doom.DoomStatus; import utils.C2JUtils; import w.IWadLoader; import w.lumpinfo_t; /** An stand-alone sprite loader. Surprisingly, it is quite a * separate concern from the renderer, and only needs to communicate * occasionally through its getters with the rest of the stuff. * * Helped lighten up the rendering code a lot, too. * * @author Maes * */ public class SpriteManager implements ISpriteManager, DoomStatusAware{ /** There seems to be an arbitrary limit of 29 distinct frames per THING */ public static final int MAX_SPRITE_FRAMES = 29; private SpriteManager(){ sprtemp = new spriteframe_t[MAX_SPRITE_FRAMES]; C2JUtils.initArrayOfObjects(sprtemp); } public SpriteManager(DoomStatus DC) { this(); this.updateStatus(DC); } // Temporarily contains the frames of a given sprite before they are // registered with the rendering system. Apparently, a maximum of 29 frames // per sprite is allowed. protected spriteframe_t[] sprtemp = new spriteframe_t[29]; protected int maxframe; protected String spritename; // MAES: Shit taken from things protected int firstspritelump; protected int lastspritelump; protected int numspritelumps; // variables used to look up and range check thing_t sprites patches // protected spritedef_t[] sprites; protected int numsprites; /** needed for pre rendering (fixed_t[]) */ protected int[] spritewidth, spriteoffset, spritetopoffset; // // R_InitSpriteDefs // Pass a null terminated list of sprite names // (4 chars exactly) to be used. // // Builds the sprite rotation matrixes to account // for horizontally flipped sprites. // // Will report an error if the lumps are inconsistent. // Only called at startup. // // Sprite lump names are 4 characters for the actor, // a letter for the frame, and a number for the rotation. // // A sprite that is flippable will have an additional // letter/number appended. // // The rotation character can be 0 to signify no rotations. // // 1/25/98, 1/31/98 killough : Rewritten for performance // // Empirically verified to have excellent hash // properties across standard Doom sprites: protected final void InitSpriteDefs(String[] namelist) { int numentries = lastspritelump - firstspritelump + 1; Hashtable<Integer, List<Integer>> hash; int i; if (numentries == 0 || namelist == null) return; // count the number of sprite names i = namelist.length; numsprites = i; sprites = new spritedef_t[numsprites]; C2JUtils.initArrayOfObjects(sprites); // Create hash table based on just the first four letters of each // sprite // killough 1/31/98 // Maes: the idea is to have a chained hastable which can handle // multiple entries (sprites) on the same primary key (the 4 first chars of // the sprite name) hash = new Hashtable<Integer, List<Integer>>(numentries); // allocate // hash // table // We have to trasverse this in the opposite order, so that later // lumps // trump previous ones in order. for (i = numentries - 1; i >= 0; i--) { int hashcode = SpriteNameHash(W.GetLumpInfo(i + firstspritelump).name); // Create chain list for each sprite class (e.g. TROO, POSS, // etc.) // if (!hash.containsKey(hashcode)) { hash.put(hashcode, new ArrayList<Integer>()); } // Store (yet another) lump index for this sprite. hash.get(hashcode).add(i); } // scan all the lump names for each of the names, // noting the highest frame letter. for (i = 0; i < numsprites; i++) { // We only look for entries that are known to be sprites. // The hashtable may contain a lot of other shit, at this point // which will be hopefully ignored. String spritename = namelist[i]; List<Integer> list = hash.get(SpriteNameHash(spritename)); // Well, it may have been something else. Fuck it. if (list != null && !list.isEmpty()) { // Maes: the original code actually set everything to "-1" // here, including the // "boolean" rotate value. The idea was to create a // "tristate" of sorts, where -1 // means a sprite of uncertain status. Goto // InstallSpriteLumps for more. for (int k = 0; k < sprtemp.length; k++) { Arrays.fill(sprtemp[k].flip, (byte) -1); Arrays.fill(sprtemp[k].lump, (short) -1); // This should be INDETERMINATE at this point. sprtemp[k].rotate = -1; } maxframe = -1; // What is stored in the lists are all actual lump numbers // relative // to e.g. TROO. In coalesced lumps, there will be overlap. // This procedure should, in theory, trump older ones. for (Integer j : list) { lumpinfo_t lump = W.GetLumpInfo(j + firstspritelump); // We don't know a-priori which frames exist. // However, we do know how to interpret existing ones, // and have an implicit maximum sequence of 29 Frames. // A frame can also hame multiple rotations. if (lump.name.substring(0, 4).equalsIgnoreCase( spritename.substring(0, 4))) { int frame = lump.name.charAt(4) - 'A'; int rotation = lump.name.charAt(5) - '0'; if (sprtemp[frame].rotate != -1) { // We already encountered this sprite, but we // may need to trump it with something else } InstallSpriteLump(j + firstspritelump, frame, rotation, false); if (lump.name.length() >= 7) { frame = lump.name.charAt(6) - 'A'; rotation = lump.name.charAt(7) - '0'; InstallSpriteLump(j + firstspritelump, frame, rotation, true); } } } // check the frames that were found for completeness if ((sprites[i].numframes = ++maxframe) != 0) // killough // 1/31/98 { int frame; for (frame = 0; frame < maxframe; frame++) switch ((int) sprtemp[frame].rotate) { case -1: // no rotations were found for that frame at all I.Error("R_InitSprites: No patches found for %s frame %c", namelist[i], frame + 'A'); break; case 0: // only the first rotation is needed break; case 1: // must have all 8 frames { int rotation; for (rotation = 0; rotation < 8; rotation++) if (sprtemp[frame].lump[rotation] == -1) I.Error("R_InitSprites: Sprite %s frame %c is missing rotations", namelist[i], frame + 'A'); break; } } // allocate space for the frames present and copy // sprtemp to it // MAES: we can do that elegantly in one line. sprites[i].copy(sprtemp, maxframe); } } } } /** * R_InitSpriteLumps Finds the width and hoffset of all sprites in the wad, * so the sprite does not need to be cached completely just for having the * header info ready during rendering. */ public void InitSpriteLumps() { int i; patch_t patch; firstspritelump = W.GetNumForName("S_START") + 1; lastspritelump = W.GetNumForName("S_END") - 1; numspritelumps = lastspritelump - firstspritelump + 1; spritewidth = new int[numspritelumps]; spriteoffset = new int[numspritelumps]; spritetopoffset = new int[numspritelumps]; for (i = 0; i < numspritelumps; i++) { if ((i & 63) == 0) System.out.print("."); patch = (patch_t) W.CacheLumpNum(firstspritelump + i, PU_CACHE, patch_t.class); spritewidth[i] = patch.width << FRACBITS; spriteoffset[i] = patch.leftoffset << FRACBITS; spritetopoffset[i] = patch.topoffset << FRACBITS; } } /** * R_InstallSpriteLump Local function for R_InitSprites. * * Boom function, more suited to resource coalescing. * */ public final void InstallSpriteLump(int lump, int frame, int rotation, boolean flipped) { if (frame >= MAX_SPRITE_FRAMES || rotation > 8) I.Error("R_InstallSpriteLump: Bad frame characters in lump %d", lump); if ((int) frame > maxframe) maxframe = frame; if (rotation == 0) { // the lump should be used for all rotations int r; for (r = 0; r < 8; r++) if (sprtemp[frame].lump[r] == -1) { sprtemp[frame].lump[r] = lump - firstspritelump; sprtemp[frame].flip[r] = (byte) (flipped ? 1 : 0); sprtemp[frame].rotate = 0; // jff 4/24/98 if any subbed, // rotless } return; } // the lump is only used for one rotation if (sprtemp[frame].lump[--rotation] == -1) { sprtemp[frame].lump[rotation] = lump - firstspritelump; sprtemp[frame].flip[rotation] = (byte) (flipped ? 1 : 0); sprtemp[frame].rotate = 1; // jff 4/24/98 only change if rot // used } } /** * R_InitSprites Called at program start. * */ @Override public void InitSprites(String[] namelist) { InitSpriteDefs(namelist); } protected final int SpriteNameHash(String ss) { return ss.substring(0, 4).hashCode(); } // GETTERS @Override public final int getFirstSpriteLump(){ return firstspritelump; } @Override public final int getNumSprites(){ return numsprites; } @Override public final spritedef_t[] getSprites() { return sprites; } @Override public final spritedef_t getSprite(int index) { return sprites[index]; } @Override public final int[] getSpriteWidth() { return spritewidth; } @Override public final int[] getSpriteOffset() { return spriteoffset; } @Override public final int[] getSpriteTopOffset() { return spritetopoffset; } @Override public final int getSpriteWidth(int index) { return spritewidth[index]; } @Override public final int getSpriteOffset(int index) { return spriteoffset[index]; } @Override public final int getSpriteTopOffset(int index) { return spritetopoffset[index]; } /////////// STATUS /////////// @Override public void updateStatus(DoomStatus DC) { this.W=DC.W; this.I=DC.I; } protected IWadLoader W; protected IDoomSystem I; // Some unused shit /* * R_InstallSpriteLump Local function for R_InitSprites. * * Older function, closer to linuxdoom. Using Boom-derived one instead. */ /* protected final void InstallSpriteLump(int lump, int frame, int rotation, boolean flipped) { // System.out.println("Trying to install "+spritename+" Frame "+ // (char)('A'+frame)+" rot "+(rotation) // +" . Should have rotations: "+sprtemp[frame].rotate); int r; if (frame >= 29 || rotation > 8) I.Error("R_InstallSpriteLump: Bad frame characters in lump %i", lump); if ((int) frame > maxframe) maxframe = frame; // A rotation value of 0 means that we are either checking the first // frame of a sprite that HAS rotations, or something that has no // rotations at all. The value of rotate doesn't really help us // discern here, unless set to "false" a-priori...which can't happen // ?! if (rotation == 0) { // MAES: notice how comparisons are done with strict literals // (true and false) which are actually defined to be 0 and 1, // rather than assuming that true is "any nonzero value". This // happens because rotate's value could be -1 at this point (!), // if a series of circumstances occur. Therefore it's actually a // "tri-state", and the comparison 0==false and // "anything else"==true was not good enough in this case. A // value of -1 doesn't yield either true or false here. // the lump should be used for all rotations if (sprtemp[frame].rotate == 0) { // MAES: Explanation: we stumbled upon this lump before, and // decided that this frame should have no more rotations, // hence we found an error and we bomb everything. I.Error("R_InitSprites: Sprite %s frame %c has multiple rot=0 lump", spritename, 'A' + frame); } // This should NEVER happen! if (sprtemp[frame].rotate == 1) { // MAES: This can only happen if we decided that a sprite's // frame was already decided to have rotations, but now we // stumble upon another occurence of "rotation 0". Or if you // use naive true/false evaluation for .rotate ( -1 is also // an admissible value). I.Error("R_InitSprites: Sprite %s frame %c has rotations and a rot=0 lump", spritename, 'A' + frame); } // Rotation is acknowledged to be totally false at this point. sprtemp[frame].rotate = 0; for (r = 0; r < 8; r++) { sprtemp[frame].lump[r] = (short) (lump - firstspritelump); sprtemp[frame].flip[r] = (byte) (flipped ? 1 : 0); } return; } // the lump is only used for one rotation if (sprtemp[frame].rotate == 0) I.Error("R_InitSprites: Sprite %s frame %c has rotations and a rot=0 lump", spritename, 'A' + frame); sprtemp[frame].rotate = 1; // make 0 based rotation--; if (sprtemp[frame].lump[rotation] == -1) { // FUN FACT: with resource coalesing, this is no longer an // error. // I.Error // ("R_InitSprites: Sprite %s : %c : %c has two lumps mapped to it", // spritename, 'A'+frame, '1'+rotation); // Everything is OK, we can bless the temporary sprite's frame's // rotation. sprtemp[frame].lump[rotation] = (short) (lump - firstspritelump); sprtemp[frame].flip[rotation] = (byte) (flipped ? 1 : 0); sprtemp[frame].rotate = 1; // jff 4/24/98 only change if rot // used } } */ /* * OLDER, UNUSED VERSION * * R_InitSpriteDefs Pass a null terminated list of sprite names (4 chars * exactly) to be used. Builds the sprite rotation matrixes to account * for horizontally flipped sprites. Will report an error if the lumps * are inconsistent. Only called at startup. * * Sprite lump names are 4 characters for the actor, a letter for the * frame, and a number for the rotation. A sprite that is flippable will * have an additional letter/number appended. The rotation character can * be 0 to signify no rotations. */ /* public void InitSpriteDefs2(String[] namelist) { int intname; int frame; int rotation; int start; int end; int patched; if (namelist == null) return; numsprites = namelist.length; if (numsprites == 0) return; sprites = new spritedef_t[numsprites]; C2JUtils.initArrayOfObjects(sprites); start = firstspritelump - 1; end = lastspritelump + 1; // scan all the lump names for each of the names, // noting the highest frame letter. // Just compare 4 characters as ints for (int i = 0; i < numsprites; i++) { // System.out.println("Preparing sprite "+i); spritename = namelist[i]; // The original code actually set everything to "-1" // here, including the "boolean" rotate value. The idea was // to create a "tristate" of sorts, where -1 means a // sprite of uncertain status. Goto InstallSpriteLumps // for more. for (int j = 0; j < sprtemp.length; j++) { Arrays.fill(sprtemp[j].flip, (byte) -1); Arrays.fill(sprtemp[j].lump, (short) -1); // This should be INDETERMINATE at this point. sprtemp[j].rotate = -1; } maxframe = -1; intname = name8.getIntName(namelist[i].toUpperCase()); // scan the lumps, // filling in the frames for whatever is found for (int l = start + 1; l < end; l++) { // We HOPE it has 8 characters. char[] cname = W.GetLumpInfo(l).name.toCharArray(); if (cname.length == 6 || cname.length == 8) // Sprite names // must be this // way // If the check is successful, we keep looking for more // frames // for a particular sprite e.g. TROOAx, TROOHxHy etc. // if (W.GetLumpInfo(l).intname == intname) { frame = cname[4] - 'A'; rotation = cname[5] - '0'; if (DM.modifiedgame) patched = W .GetNumForName(W.GetLumpInfo(l).name); else patched = l; InstallSpriteLump2(patched, frame, rotation, false); // Second set of rotations? if (cname.length > 6 && cname[6] != 0) { frame = cname[6] - 'A'; rotation = cname[7] - '0'; InstallSpriteLump2(l, frame, rotation, true); } } } // check the frames that were found for completeness // This can only be -1 at this point if we didn't install // a single frame successfuly. // if (maxframe == -1) { // System.out.println("Sprite "+spritename+" has no frames!"); getSprites()[i].numframes = 0; // We move on to the next sprite with this one. continue; } maxframe++; for (frame = 0; frame < maxframe; frame++) { switch ((int) sprtemp[frame].rotate) { case -1: // no rotations were found for that frame at all I.Error("R_InitSprites: No patches found for %s frame %c", namelist[i], frame + 'A'); break; case 0: // only the first rotation is needed break; case 1: // must have all 8 frames for (rotation = 0; rotation < 8; rotation++) if (sprtemp[frame].lump[rotation] == -1) I.Error("R_InitSprites: Sprite %s frame %c is missing rotations", namelist[i], frame + 'A'); break; } } // allocate space for the frames present and copy sprtemp to it // MAES: we can do that elegantly in one line. sprites[i].copy(sprtemp, maxframe); // sprites[i].numframes = maxframe; // sprites[i].spriteframes = new spriteframe_t[maxframe]; // C2JUtils.initArrayOfObjects(sprites[i].spriteframes,spriteframe_t.class); // for (int j=0;j<) // System.arraycopy(src, srcPos, dest, destPos, length) // memcpy (sprites[i].spriteframes, sprtemp, // maxframe*sizeof(spriteframe_t)); } } */ } package rr; import java.io.IOException; import java.nio.ByteBuffer; import w.CacheableDoomObject; public class flat_t implements CacheableDoomObject { public static final int FLAT_SIZE=4096; public byte[] data; public flat_t(){ this.data=new byte[FLAT_SIZE]; } public flat_t(int size){ this.data=new byte[size]; } @Override public void unpack(ByteBuffer buf) throws IOException { //buf.get(this.data); this.data=buf.array(); } } package rr; import static data.Tables.FINEANGLES; import static m.fixed_t.FRACUNIT; import rr.drawfuns.ColFuncs; import rr.drawfuns.ColVars; import rr.drawfuns.SpanVars; import i.DoomStatusAware; import i.IDoomSystem; import v.IVideoScaleAware; import w.IWadLoader; import doom.player_t; public interface Renderer<T,V> extends IVideoScaleAware,DoomStatusAware{ /** Fineangles in the SCREENWIDTH wide window. */ public static final int FIELDOFVIEW = FINEANGLES / 4; public static final int MINZ = (FRACUNIT * 4); public static final int FUZZTABLE = 50; /** * killough: viewangleoffset is a legacy from the pre-v1.2 days, when Doom * had Left/Mid/Right viewing. +/-ANG90 offsets were placed here on each * node, by d_net.c, to set up a L/M/R session. */ public static final long viewangleoffset = 0; public void Init(); public void RenderPlayerView(player_t player); public void ExecuteSetViewSize(); public void FillBackScreen(); public void DrawViewBorder(); public void SetViewSize(int size, int detaillevel); public void VideoErase(int offset, int width); public long PointToAngle2(int x1, int y1, int x2, int y2); public void PreCacheThinkers(); public int getValidCount(); public void increaseValidCount(int amount); public boolean isFullHeight(); public void resetLimits(); public boolean getSetSizeNeeded(); public boolean isFullScreen(); // Isolation methods public TextureManager<T> getTextureManager(); public PlaneDrawer<T,V> getPlaneDrawer(); public ViewVars getView(); public SpanVars<T, V> getDSVars(); public LightsAndColors<V> getColorMap(); public IDoomSystem getDoomSystem(); public IWadLoader getWadLoader(); /** Use this to "peg" visplane drawers (even parallel ones) to * the same set of visplane variables. * * @return */ public Visplanes getVPVars(); public SegVars getSegVars(); public ISpriteManager getSpriteManager(); public BSPVars getBSPVars(); public IVisSpriteManagement<V> getVisSpriteManager(); public ColFuncs<T,V> getColFuncsHi(); public ColFuncs<T,V> getColFuncsLow(); public ColVars<T, V> getMaskedDCVars(); //public subsector_t PointInSubsector(int x, int y); } package rr; public class cliprange_t { public cliprange_t(int first, int last) { this.first = first; this.last = last; } public cliprange_t(){ } public int first; public int last; public void copy(cliprange_t from){ this.first=from.first; this.last=from.last; } } package rr; /** * A sprite definition: * a number of animation frames. */ public class spritedef_t { /** the very least, primitive fields won't bomb, * and copy constructors can do their job. */ public spritedef_t(){ } public spritedef_t(int numframes){ this.numframes=numframes; this.spriteframes=new spriteframe_t[numframes]; } public spritedef_t(spriteframe_t[] frames){ this.numframes=frames.length; this.spriteframes=new spriteframe_t[numframes]; // copy shit over... for (int i=0;i<numframes;i++){ spriteframes[i]=frames[i].clone(); } } /** Use this constructor, as we usually need less than 30 frames * It will actually clone the frames. */ public void copy(spriteframe_t[] from, int maxframes){ this.numframes=maxframes; this.spriteframes=new spriteframe_t[maxframes]; // copy shit over... for (int i=0;i<maxframes;i++){ spriteframes[i]=from[i].clone(); } } public int numframes; public spriteframe_t[] spriteframes; }; package rr; /** A sprite manager does everything but drawing the sprites. It creates lists * of sprites-per-sector, sorts them, and stuff like that. * that gory visibiliy * * @author velkton * * @param <V> */ public interface IVisSpriteManagement<V> extends ILimitResettable { void AddSprites(sector_t sec); /** Cache the sprite manager, if possible */ void cacheSpriteManager(ISpriteManager SM); void SortVisSprites(); int getNumVisSprites(); vissprite_t<V>[] getVisSprites(); void ClearSprites(); void updateStatus(RendererState<?,V> R); } package rr; import static rr.LightsAndColors.*; import java.io.IOException; import java.util.ArrayList; import rr.drawfuns.ColVars; import rr.drawfuns.R_DrawColumnBoom; import rr.drawfuns.R_DrawColumnBoomLow; import rr.drawfuns.R_DrawColumnBoomOpt; import rr.drawfuns.R_DrawColumnBoomOptLow; import rr.drawfuns.R_DrawFuzzColumn; import rr.drawfuns.R_DrawFuzzColumnLow; import rr.drawfuns.R_DrawSpanLow; import rr.drawfuns.R_DrawSpanUnrolled; import rr.drawfuns.R_DrawTLColumn; import rr.drawfuns.R_DrawTranslatedColumn; import rr.drawfuns.R_DrawTranslatedColumnLow; import rr.drawfuns.SpanVars; import v.DoomVideoRenderer; import doom.DoomMain; import doom.DoomStatus; public abstract class UnifiedRenderer<T, V> extends RendererState<T,V> { public UnifiedRenderer(DoomStatus<T,V> DS) { super(DS); this.MySegs=new Segs(this); } /** A very simple Seg (Wall) drawer, which just completes abstract SegDrawer * by calling the final column functions. * * TODO: move out of RendererState. * * @author velktron * */ protected final class Segs extends SegDrawer { public Segs(Renderer<?, ?> R) { super(R); } /** For serial version, just complete the call */ @Override protected final void CompleteColumn() { colfunc.main.invoke(); } } ////////////////// The actual rendering calls /////////////////////// public static final class HiColor extends UnifiedRenderer<byte[],short[]> { public HiColor(DoomStatus<byte[],short[]> DM) { super(DM); // Init any video-output dependant stuff // Init light levels colormaps.scalelight = new short[LIGHTLEVELS][MAXLIGHTSCALE][]; colormaps.scalelightfixed = new short[MAXLIGHTSCALE][]; colormaps.zlight = new short[LIGHTLEVELS][MAXLIGHTZ][]; completeInit(); } /** * R_InitColormaps This is VERY different for hicolor. * * @throws IOException */ protected void InitColormaps() throws IOException { colormaps.colormaps = V.getColorMaps(); System.out.println("COLORS15 Colormaps: " + colormaps.colormaps.length); // MAES: blurry effect is hardcoded to this colormap. // Pointless, since we don't use indexes. Instead, a half-brite // processing works just fine. BLURRY_MAP = null;// colormaps[0]; } /** Initializes the various drawing functions. They are all "pegged" to the * same dcvars/dsvars object. Any initializations of e.g. parallel renderers * and their supporting subsystems should occur here. */ @Override protected void R_InitDrawingFunctions(){ // Span functions. Common to all renderers unless overriden // or unused e.g. parallel renderers ignore them. DrawSpan=new R_DrawSpanUnrolled.HiColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,dsvars,screen,I); DrawSpanLow=new R_DrawSpanLow.HiColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,dsvars,screen,I); // Translated columns are usually sprites-only. DrawTranslatedColumn=new R_DrawTranslatedColumn.HiColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I); DrawTranslatedColumnLow=new R_DrawTranslatedColumnLow.HiColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I); DrawTLColumn=new R_DrawTLColumn(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I); // Fuzzy columns. These are also masked. DrawFuzzColumn=new R_DrawFuzzColumn.HiColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I); DrawFuzzColumnLow=new R_DrawFuzzColumnLow.HiColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I); // Regular draw for solid columns/walls. Full optimizations. DrawColumn=new R_DrawColumnBoomOpt.HiColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,dcvars,screen,I); DrawColumnLow=new R_DrawColumnBoomOptLow.HiColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,dcvars,screen,I); // Non-optimized stuff for masked. DrawColumnMasked=new R_DrawColumnBoom.HiColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I); DrawColumnMaskedLow=new R_DrawColumnBoomLow.HiColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I); // Player uses masked DrawColumnPlayer=DrawColumnMasked; // Player normally uses masked. // Skies use their own. This is done in order not to stomp parallel threads. DrawColumnSkies=new R_DrawColumnBoomOpt.HiColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,skydcvars,screen,I); DrawColumnSkiesLow=new R_DrawColumnBoomOptLow.HiColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,skydcvars,screen,I); super.R_InitDrawingFunctions(); } } public static final class Indexed extends UnifiedRenderer<byte[],byte[]> { public Indexed(DoomStatus<byte[],byte[]> DM) { super(DM); // Init light levels colormaps.scalelight = new byte[LIGHTLEVELS][MAXLIGHTSCALE][]; colormaps.scalelightfixed = new byte[MAXLIGHTSCALE][]; colormaps.zlight = new byte[LIGHTLEVELS][MAXLIGHTZ][]; completeInit(); } /** * R_InitColormaps * * @throws IOException */ protected void InitColormaps() throws IOException { int lump, length; // Load in the light tables, // 256 byte align tables. lump = W.GetNumForName("COLORMAP"); length = W.LumpLength(lump) + 256; colormaps.colormaps = new byte[(length / 256)][256]; System.out.println("Colormaps: " + colormaps.colormaps.length); byte[] tmp = new byte[length]; W.ReadLump(lump,tmp); for (int i = 0; i < colormaps.colormaps.length; i++) { System.arraycopy(tmp, i * 256, colormaps.colormaps[i], 0, 256); } // MAES: blurry effect is hardcoded to this colormap. BLURRY_MAP=colormaps.colormaps[6]; // colormaps = (byte *)( ((int)colormaps + 255)&~0xff); } /** Initializes the various drawing functions. They are all "pegged" to the * same dcvars/dsvars object. Any initializations of e.g. parallel renderers * and their supporting subsystems should occur here. */ @Override protected void R_InitDrawingFunctions(){ // Span functions. Common to all renderers unless overriden // or unused e.g. parallel renderers ignore them. DrawSpan=new R_DrawSpanUnrolled.Indexed(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,dsvars,screen,I); DrawSpanLow=new R_DrawSpanLow.Indexed(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,dsvars,screen,I); // Translated columns are usually sprites-only. DrawTranslatedColumn=new R_DrawTranslatedColumn.Indexed(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I); DrawTranslatedColumnLow=new R_DrawTranslatedColumnLow.Indexed(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I); //DrawTLColumn=new R_DrawTLColumn(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I); // Fuzzy columns. These are also masked. DrawFuzzColumn=new R_DrawFuzzColumn.Indexed(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I,BLURRY_MAP); DrawFuzzColumnLow=new R_DrawFuzzColumnLow.Indexed(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I,BLURRY_MAP); // Regular draw for solid columns/walls. Full optimizations. DrawColumn=new R_DrawColumnBoomOpt.Indexed(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,dcvars,screen,I); DrawColumnLow=new R_DrawColumnBoomOptLow.Indexed(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,dcvars,screen,I); // Non-optimized stuff for masked. DrawColumnMasked=new R_DrawColumnBoom.Indexed(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I); DrawColumnMaskedLow=new R_DrawColumnBoomLow.Indexed(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I); // Player uses masked DrawColumnPlayer=DrawColumnMasked; // Player normally uses masked. // Skies use their own. This is done in order not to stomp parallel threads. DrawColumnSkies=new R_DrawColumnBoomOpt.Indexed(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,skydcvars,screen,I); DrawColumnSkiesLow=new R_DrawColumnBoomOptLow.Indexed(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,skydcvars,screen,I); super.R_InitDrawingFunctions(); } } public static final class TrueColor extends UnifiedRenderer<byte[],int[]> { public TrueColor(DoomStatus<byte[],int[]> DM) { super(DM); // Init light levels colormaps.scalelight = new int[LIGHTLEVELS][MAXLIGHTSCALE][]; colormaps.scalelightfixed = new int[MAXLIGHTSCALE][]; colormaps.zlight = new int[LIGHTLEVELS][MAXLIGHTZ][]; completeInit(); } /** * R_InitColormaps This is VERY different for hicolor. * * @throws IOException */ protected void InitColormaps() throws IOException { colormaps.colormaps = V.getColorMaps(); System.out.println("COLORS32 Colormaps: " + colormaps.colormaps.length); BLURRY_MAP = null; } /** Initializes the various drawing functions. They are all "pegged" to the * same dcvars/dsvars object. Any initializations of e.g. parallel renderers * and their supporting subsystems should occur here. */ @Override protected void R_InitDrawingFunctions(){ // Span functions. Common to all renderers unless overriden // or unused e.g. parallel renderers ignore them. DrawSpan=new R_DrawSpanUnrolled.TrueColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,dsvars,screen,I); DrawSpanLow=new R_DrawSpanLow.TrueColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,dsvars,screen,I); // Translated columns are usually sprites-only. DrawTranslatedColumn=new R_DrawTranslatedColumn.TrueColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I); DrawTranslatedColumnLow=new R_DrawTranslatedColumnLow.TrueColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I); //DrawTLColumn=new R_DrawTLColumn.TrueColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I); // Fuzzy columns. These are also masked. DrawFuzzColumn=new R_DrawFuzzColumn.TrueColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I); DrawFuzzColumnLow=new R_DrawFuzzColumnLow.TrueColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I); // Regular draw for solid columns/walls. Full optimizations. DrawColumn=new R_DrawColumnBoomOpt.TrueColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,dcvars,screen,I); DrawColumnLow=new R_DrawColumnBoomOptLow.TrueColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,dcvars,screen,I); // Non-optimized stuff for masked. DrawColumnMasked=new R_DrawColumnBoom.TrueColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I); DrawColumnMaskedLow=new R_DrawColumnBoomLow.TrueColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I); // Player uses masked DrawColumnPlayer=DrawColumnMasked; // Player normally uses masked. // Skies use their own. This is done in order not to stomp parallel threads. DrawColumnSkies=new R_DrawColumnBoomOpt.TrueColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,skydcvars,screen,I); DrawColumnSkiesLow=new R_DrawColumnBoomOptLow.TrueColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,skydcvars,screen,I); super.R_InitDrawingFunctions(); } } } package rr; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.Hashtable; import utils.C2JUtils; import w.CacheableDoomObject; import w.DoomBuffer; //Patches. //A patch holds one or more columns. //Patches are used for sprites and all masked pictures, //and we compose textures from the TEXTURE1/2 lists //of patches. public class patch_t implements /*IReadableDoomObject,*/CacheableDoomObject{ /** bounding box size */ public short width, height; /** pixels to the left of origin */ public short leftoffset; /** pixels below the origin */ public short topoffset; /** This used to be an implicit array pointing to raw posts of data. * TODO: get rid of it? It's never used * only [width] used the [0] is &columnofs[width] */ public int[] columnofs; /** The ACTUAL data is here, nicely deserialized (well, almost) */ public column_t[] columns; /** Added for debug aid purposes */ public String name; /** Synthesizing constructor. * You have to provide the columns yourself, a-posteriori. * * @param name * @param width * @param height * @param leftoffset * @param topoffset */ public patch_t(String name, int width, int height, int leftoffset, int topoffset){ this.name=name; this.width=(short) width; this.height=(short) height; this.leftoffset=(short) leftoffset; this.columns=new column_t[width]; } public patch_t(){ } /* @Override public void read(DoomFile f) throws IOException{ long pos=f.getFilePointer(); this.width=f.readLEShort(); this.height=f.readLEShort(); this.leftoffset=f.readLEShort(); this.topoffset=f.readLEShort(); // As many columns as width...right??? this.columnofs=new int[this.width]; this.columns=new column_t[this.width]; C2JUtils.initArrayOfObjects( this.columns, column_t.class); // Read the column offsets. f.readIntArray(this.columnofs, this.columnofs.length, ByteOrder.LITTLE_ENDIAN); for (int i=0;i<this.width;i++){ // Go to offset. //f.seek(pos+this.columnofs[i]); this.columns[i].read(f); } }*/ /** In the C code, reading is "aided", aka they know how long the header + all * posts/columns actually are on disk, and only "deserialize" them when using them. * Here, we strive to keep stuff as elegant and OO as possible, so each column will get * deserialized one by one. I thought about reading ALL column data as raw data, but * IMO that's shit in the C code, and would be utter shite here too. Ergo, I cleanly * separate columns at the patch level (an advantage is that it's now easy to address * individual columns). However, column data is still read "raw". */ @Override public void unpack(ByteBuffer b) throws IOException { // Remember to reset the ByteBuffer position each time. b.position(0); // In ByteBuffers, the order can be conveniently set beforehand :-o b.order(ByteOrder.LITTLE_ENDIAN); this.width=b.getShort(); this.height=b.getShort(); this.leftoffset=b.getShort(); this.topoffset=b.getShort(); // As many columns as width...right??? this.columnofs=new int[this.width]; this.columns=new column_t[this.width]; C2JUtils.initArrayOfObjects( this.columns, column_t.class); // Compute the ACTUAL full-column sizes. int[] actualsizes=new int[columns.length]; for (int i=0;i<actualsizes.length-1;i++){ actualsizes[i]=columnofs[i+1]-columnofs[i]; } // The offsets. DoomBuffer.readIntArray(b, this.columnofs, this.columnofs.length); for (int i=0;i<this.width;i++){ // Go to offset. b.position(this.columnofs[i]); try { this.columns[i].unpack(b); } catch (Exception e){ // Error during loading of column. // If first column (too bad..) set to special error column. if (i==0) this.columns[i]=getBadColumn(this.height); // Else duplicate previous column. Saves memory, too! else this.columns[i]=this.columns[i-1]; } } } // Special safeguard against badly computed columns. Now they can be any size. private static Hashtable<Integer,column_t> badColumns=new Hashtable<Integer,column_t>(); private static column_t getBadColumn(int size){ if (badColumns.get(size)==null){ column_t tmp=new column_t(); tmp.data=new byte[size+5]; for (int i=3;i<size+3;i++){ tmp.data[i]=(byte) (i-3); } tmp.data[size+4]=(byte) 0xFF; tmp.posts=1; //tmp.length=(short) size; //tmp.topdelta=0; tmp.postofs=new int[]{3}; tmp.postdeltas=new short[]{0}; tmp.postlen=new short[]{(short) (size%256)}; //tmp.setData(); badColumns.put(size, tmp); } return badColumns.get(size); } } package rr; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import utils.C2JUtils; import w.CacheableDoomObject; import w.DoomBuffer; /** Texture definition. * A DOOM wall texture is a list of patches which are to be combined in a predefined order. * This is the ON-DISK structure, to be read from the TEXTURES1 and TEXTURES2 lumps. * In memory, this becomes texture_t. * * @author MAES * */ public class maptexture_t implements CacheableDoomObject{ public String name; /* Fixed, 8-char width */ public boolean masked; public short width; // was signed byte public short height; // was //void**t columndirectory; // OBSOLETE (yeah, but we must read a dummy integer here) public short patchcount; public mappatch_t[] patches; @Override public void unpack(ByteBuffer buf) throws IOException { buf.order(ByteOrder.LITTLE_ENDIAN); name=DoomBuffer.getNullTerminatedString(buf,8); masked=(buf.getInt()!=0); width=buf.getShort(); height=buf.getShort(); buf.getInt(); // read a dummy integer for obsolete columndirectory. patchcount=buf.getShort(); // Simple sanity check. Do not attempt reading more patches than there // are left in the TEXTURE lump. patchcount=(short) Math.min(patchcount,(buf.capacity()-buf.position())/mappatch_t.size()); patches=new mappatch_t[patchcount]; C2JUtils.initArrayOfObjects(patches,mappatch_t.class); DoomBuffer.readObjectArray(buf, patches, patchcount); } }; package rr; import static data.Limits.MAXINT; import static data.Limits.MAX_ADJOINING_SECTORS; import static m.fixed_t.FRACUNIT; import static m.fixed_t.FRACBITS; import static p.DoorDefines.*; import java.io.DataInputStream; import java.io.IOException; import java.nio.ByteBuffer; import java.util.Arrays; import m.IRandom; import doom.think_t; import doom.thinker_t; import p.Resettable; import p.ThinkerList; import p.fireflicker_t; import p.glow_t; import p.lightflash_t; import p.mobj_t; import p.strobe_t; import p.vldoor_e; import p.vldoor_t; import s.degenmobj_t; import w.DoomIO; import w.IPackableDoomObject; import w.IReadableDoomObject; /** * The SECTORS record, at runtime. Stores things/mobjs. Can be * archived/unarchived during savegames. * * @author Maes */ public class sector_t implements IReadableDoomObject, IPackableDoomObject, Resettable { public ThinkerList TL; public IRandom RND; public sector_t() { blockbox = new int[4]; id = -1; } /** (fixed_t) */ public int floorheight, ceilingheight; public short floorpic; public short ceilingpic; public short lightlevel; public short special; public short tag; /** 0 = untraversed, 1,2 = sndlines -1 */ public int soundtraversed; /** thing that made a sound (or null) (MAES: single pointer) */ public mobj_t soundtarget; /** mapblock bounding box for height changes */ public int[] blockbox; /** * origin for any sounds played by the sector. Used to be degenmobj_t, but * that's really a futile distinction. */ public degenmobj_t soundorg; /** if == validcount, already checked */ public int validcount; /** list of mobjs in sector (MAES: it's used as a linked list) */ public mobj_t thinglist; /** * thinker_t for reversable actions. This actually was a void*, and in * practice it could store doors, plats, floors and ceiling objects. */ public SectorAction specialdata; public int linecount; // struct line_s** lines; // [linecount] size // MAES: make this line_t[] for now? public line_t[] lines; /** Use for internal identification */ public int id; /** killough 1/30/98: improves searches for tags. */ public int nexttag,firsttag; public String toString() { String str = String.format("Sector: %d %x %x %d %d %d %d %d", id, floorheight, ceilingheight, floorpic, ceilingpic, lightlevel, special, // needed? tag); // needed? return str; } // // Find minimum light from an adjacent sector // public int FindMinSurroundingLight(int max) { int i; int min; line_t line; sector_t check; min = max; for (i = 0; i < this.linecount; i++) { line = this.lines[i]; check = line.getNextSector(this); if (check == null) continue; if (check.lightlevel < min) min = check.lightlevel; } return min; } // // P_FindLowestFloorSurrounding() // FIND LOWEST FLOOR HEIGHT IN SURROUNDING SECTORS // public int FindLowestFloorSurrounding() { int i; line_t check; sector_t other; int floor = this.floorheight; for (i = 0; i < this.linecount; i++) { check = this.lines[i]; other = check.getNextSector(this); if (other == null) continue; if (other.floorheight < floor) floor = other.floorheight; } return floor; } /** * P_FindHighestFloorSurrounding() FIND HIGHEST FLOOR HEIGHT IN SURROUNDING * SECTORS Compatibility problem: apparently this is hardcoded for vanilla * compatibility (instead of Integer.MIN_VALUE), but it will cause some * "semi-Boom" maps not to work, since it won't be able to lower stuff below * -500 units. The correct fix here would be to allow for -compatlevel style * options. Maybe later. * * @param sec */ public int FindHighestFloorSurrounding() { int i; line_t check; sector_t other; int floor = -500 * FRACUNIT; for (i = 0; i < this.linecount; i++) { check = this.lines[i]; other = check.getNextSector(this); // The compiler nagged about this being unreachable, with // some older 1.6 JDKs, but that's obviously not true. if (other == null) continue; if (other.floorheight > floor) floor = other.floorheight; } return floor; } /** * P_FindNextHighestFloor FIND NEXT HIGHEST FLOOR IN SURROUNDING SECTORS * Note: this should be doable w/o a fixed array. * * @param sec * @param currentheight * @return fixed */ public int FindNextHighestFloor(int currentheight) { int i; int h; int min; line_t check; sector_t other; int height = currentheight; int heightlist[] = new int[MAX_ADJOINING_SECTORS]; for (i = 0, h = 0; i < this.linecount; i++) { check = this.lines[i]; other = check.getNextSector(this); if (other == null) continue; if (other.floorheight > height) heightlist[h++] = other.floorheight; // Check for overflow. Exit. if (h >= MAX_ADJOINING_SECTORS) { System.err .print("Sector with more than 20 adjoining sectors\n"); break; } } // Find lowest height in list if (h == 0) return currentheight; min = heightlist[0]; // Range checking? for (i = 1; i < h; i++) if (heightlist[i] < min) min = heightlist[i]; return min; } // // FIND LOWEST CEILING IN THE SURROUNDING SECTORS // public int FindLowestCeilingSurrounding() { int i; line_t check; sector_t other; int height = MAXINT; for (i = 0; i < this.linecount; i++) { check = this.lines[i]; other = check.getNextSector(this); if (other == null) continue; if (other.ceilingheight < height) height = other.ceilingheight; } return height; } // // FIND HIGHEST CEILING IN THE SURROUNDING SECTORS // public int FindHighestCeilingSurrounding() { int i; line_t check; sector_t other; int height = 0; for (i = 0; i < this.linecount; i++) { check = this.lines[i]; other = check.getNextSector(this); if (other == null) continue; if (other.ceilingheight > height) height = other.ceilingheight; } return height; } // // P_SpawnFireFlicker // public void SpawnFireFlicker() { fireflicker_t flick; // Note that we are resetting sector attributes. // Nothing special about it during gameplay. this.special = 0; flick = new fireflicker_t(RND); flick.function = think_t.T_FireFlicker; TL.AddThinker(flick); flick.sector = this; flick.maxlight = this.lightlevel; flick.minlight = this.FindMinSurroundingLight(this.lightlevel) + 16; flick.count = 4; } /** * Spawn a door that opens after 5 minutes */ public void SpawnDoorRaiseIn5Mins(int secnum) { vldoor_t door; door = new vldoor_t(); this.specialdata = door; this.special = 0; door.function = think_t.T_VerticalDoor; TL.AddThinker(door); door.sector = this; door.direction = 2; door.type = vldoor_e.raiseIn5Mins; door.speed = VDOORSPEED; door.topheight = this.FindLowestCeilingSurrounding(); door.topheight -= 4 * FRACUNIT; door.topwait = VDOORWAIT; door.topcountdown = 5 * 60 * 35; } // // Spawn a door that closes after 30 seconds // public void SpawnDoorCloseIn30() { vldoor_t door; door = new vldoor_t(); this.specialdata = door; this.special = 0; door.function = think_t.T_VerticalDoor; TL.AddThinker(door); door.sector = this; door.direction = 0; door.type = vldoor_e.normal; door.speed = VDOORSPEED; door.topcountdown = 30 * 35; } // // P_SpawnStrobeFlash // After the map has been loaded, scan each sector // for specials that spawn thinkers // public void SpawnStrobeFlash(int fastOrSlow, int inSync) { strobe_t flash; flash = new strobe_t(); flash.sector = this; flash.darktime = fastOrSlow; flash.brighttime = STROBEBRIGHT; flash.function = think_t.T_StrobeFlash; TL.AddThinker(flash); flash.maxlight = this.lightlevel; flash.minlight = this.FindMinSurroundingLight(this.lightlevel); if (flash.minlight == flash.maxlight) flash.minlight = 0; // nothing special about it during gameplay this.special = 0; if (inSync == 0) flash.count = (RND.P_Random() & 7) + 1; else flash.count = 1; } /** * P_SpawnLightFlash After the map has been loaded, scan each sector for * specials that spawn thinkers */ public void SpawnLightFlash() { lightflash_t flash; // nothing special about it during gameplay special = 0; flash = new lightflash_t(RND); flash.function = think_t.T_LightFlash; TL.AddThinker((thinker_t) flash); flash.sector = this; flash.maxlight = lightlevel; flash.minlight = FindMinSurroundingLight(lightlevel); flash.maxtime = 64; flash.mintime = 7; flash.count = (RND.P_Random() & flash.maxtime) + 1; } public void SpawnGlowingLight() { glow_t g; g = new glow_t(); g.sector = this; g.minlight = FindMinSurroundingLight(this.lightlevel); g.maxlight = lightlevel; g.function = think_t.T_Glow; TL.AddThinker(g); g.direction = -1; this.special = 0; } @Override public void read(DataInputStream f) throws IOException { // ACHTUNG: the only situation where we'd // like to read memory-format sector_t's is from // savegames, and in vanilla savegames, not all info // is saved (or read) from disk. this.floorheight = DoomIO.readLEShort(f) << FRACBITS; this.ceilingheight = DoomIO.readLEShort(f) << FRACBITS; // MAES: it may be necessary to apply a hack in order to // read vanilla savegames. this.floorpic = (short) DoomIO.readLEShort(f); this.ceilingpic = (short) DoomIO.readLEShort(f); // f.skipBytes(4); this.lightlevel = DoomIO.readLEShort(f); this.special = DoomIO.readLEShort(f); // needed? this.tag = DoomIO.readLEShort(f); // needed? } @Override public void pack(ByteBuffer b) { b.putShort((short) (floorheight >> FRACBITS)); b.putShort((short) (ceilingheight >> FRACBITS)); // MAES: it may be necessary to apply a hack in order to // read vanilla savegames. b.putShort(floorpic); b.putShort(ceilingpic); // f.skipBytes(4); b.putShort(lightlevel); b.putShort(special); b.putShort(tag); } @Override public void reset() { floorheight = 0; ceilingheight = 0; floorpic = 0; ceilingpic = 0; lightlevel = 0; special = 0; tag = 0; soundtraversed = 0; soundtarget = null; Arrays.fill(blockbox, 0); soundorg = null; validcount = 0; thinglist = null; specialdata = null; linecount = 0; lines = null; id = -1; } } package rr.drawfuns; public class ColFuncs<T,V> { public DoomColumnFunction<T, V> main; public DoomColumnFunction<T, V> base; public DoomColumnFunction<T, V> masked; public DoomColumnFunction<T, V> fuzz; public DoomColumnFunction<T, V> trans; public DoomColumnFunction<T, V> glass; public DoomColumnFunction<T, V> player; public DoomColumnFunction<T, V> sky; } package rr.drawfuns; import static m.fixed_t.FRACBITS; import i.IDoomSystem; public final class R_DrawTLColumn extends DoomColumnFunction<byte[],short[]> { public R_DrawTLColumn(int SCREENWIDTH, int SCREENHEIGHT, int[] ylookup, int[] columnofs, ColVars<byte[],short[]> dcvars, short[] screen, IDoomSystem I) { super(SCREENWIDTH, SCREENHEIGHT, ylookup, columnofs, dcvars, screen, I); this.flags=DcFlags.TRANSPARENT; } public void invoke() { int count; int dest; // killough int frac; // killough final int fracstep; final int dc_source_ofs=dcvars.dc_source_ofs; final byte[] tranmap=dcvars.tranmap; count = dcvars.dc_yh - dcvars.dc_yl + 1; if (count <= 0) // Zero length, column does not exceed a pixel. return; if (RANGECHECK) { performRangeCheck(); } // Framebuffer destination address. // Use ylookup LUT to avoid multiply with ScreenWidth. // Use columnofs LUT for subwindows? dest = computeScreenDest(); // Determine scaling, which is the only mapping to be done. fracstep = dcvars.dc_iscale; frac = dcvars.dc_texturemid + (dcvars.dc_yl - dcvars.centery) * fracstep; // Inner loop that does the actual texture mapping, // e.g. a DDA-lile scaling. // This is as fast as it gets. (Yeah, right!!! -- killough) // // killough 2/1/98: more performance tuning { final byte[] source = dcvars.dc_source; final short[] colormap = dcvars.dc_colormap; int heightmask = dcvars.dc_texheight - 1; if ((dcvars.dc_texheight & heightmask) != 0) // not a power of 2 -- // killough { heightmask++; heightmask <<= FRACBITS; if (frac < 0) while ((frac += heightmask) < 0) ; else while (frac >= heightmask) frac -= heightmask; do { // Re-map color indices from wall texture column // using a lighting/special effects LUT. // heightmask is the Tutti-Frutti fix -- killough screen[dest] = tranmap[0xFF00 & (screen[dest] << 8) | (0x00FF & colormap[0x00FF & source[dc_source_ofs + ((frac >> FRACBITS) & heightmask)]])]; dest += SCREENWIDTH; if ((frac += fracstep) >= heightmask) frac -= heightmask; } while (--count > 0); } else { while ((count -= 4) >= 0) // texture height is a power of 2 // -- killough { // screen[dest] = // main_tranmap[0xFF00&(screen[dest]<<8)|(0x00FF&colormap[0x00FF&source[dc_source_ofs+((frac>>FRACBITS) // & heightmask)]])]; screen[dest] = tranmap[0xFF00 & (screen[dest] << 8) | (0x00FF & colormap[0x00FF & source[dc_source_ofs + ((frac >> FRACBITS) & heightmask)]])]; dest += SCREENWIDTH; frac += fracstep; screen[dest] = tranmap[0xFF00 & (screen[dest] << 8) | (0x00FF & colormap[0x00FF & source[dc_source_ofs + ((frac >> FRACBITS) & heightmask)]])]; dest += SCREENWIDTH; frac += fracstep; screen[dest] = tranmap[0xFF00 & (screen[dest] << 8) | (0x00FF & colormap[0x00FF & source[dc_source_ofs + ((frac >> FRACBITS) & heightmask)]])]; dest += SCREENWIDTH; frac += fracstep; screen[dest] = tranmap[0xFF00 & (screen[dest] << 8) | (0x00FF & colormap[0x00FF & source[dc_source_ofs + ((frac >> FRACBITS) & heightmask)]])]; dest += SCREENWIDTH; frac += fracstep; } if ((count & 1) != 0) screen[dest] = tranmap[0xFF00 & (screen[dest] << 8) | (0x00FF & colormap[0x00FF & source[dc_source_ofs + ((frac >> FRACBITS) & heightmask)]])]; } } } } package rr.drawfuns; import i.IDoomSystem; /** * Framebuffer postprocessing. Creates a fuzzy image by copying pixels from * adjacent ones to left and right. Used with an all black colormap, this * could create the SHADOW effect, i.e. spectres and invisible players. */ public abstract class R_DrawFuzzColumn<T,V> extends DoomColumnFunction<T,V> { public R_DrawFuzzColumn(int SCREENWIDTH, int SCREENHEIGHT, int[] ylookup, int[] columnofs, ColVars<T,V> dcvars, V screen, IDoomSystem I,T BLURRY_MAP) { this(SCREENWIDTH, SCREENHEIGHT, ylookup, columnofs, dcvars, screen, I); this.BLURRY_MAP=BLURRY_MAP; } public R_DrawFuzzColumn(int SCREENWIDTH, int SCREENHEIGHT, int[] ylookup, int[] columnofs, ColVars<T,V> dcvars, V screen, IDoomSystem I) { super(SCREENWIDTH, SCREENHEIGHT, ylookup, columnofs, dcvars, screen, I); this.flags=DcFlags.FUZZY; FUZZOFF = SCREENWIDTH; // Recompute fuzz table fuzzoffset= new int[]{ FUZZOFF, -FUZZOFF, FUZZOFF, -FUZZOFF, FUZZOFF, FUZZOFF, -FUZZOFF, FUZZOFF, FUZZOFF, -FUZZOFF, FUZZOFF, FUZZOFF, FUZZOFF, -FUZZOFF, FUZZOFF, FUZZOFF, FUZZOFF, -FUZZOFF, -FUZZOFF, -FUZZOFF, -FUZZOFF, FUZZOFF, -FUZZOFF, -FUZZOFF, FUZZOFF, FUZZOFF, FUZZOFF, FUZZOFF, -FUZZOFF, FUZZOFF, -FUZZOFF, FUZZOFF, FUZZOFF, -FUZZOFF, -FUZZOFF, FUZZOFF, FUZZOFF, -FUZZOFF, -FUZZOFF, -FUZZOFF, -FUZZOFF, FUZZOFF, FUZZOFF, FUZZOFF, FUZZOFF, -FUZZOFF, FUZZOFF, FUZZOFF, -FUZZOFF, FUZZOFF }; FUZZTABLE=fuzzoffset.length; } protected int fuzzpos; protected final int FUZZTABLE; // // Spectre/Invisibility. // protected final int FUZZOFF; protected final int[] fuzzoffset; public static final class HiColor extends R_DrawFuzzColumn<byte[],short[]>{ public HiColor(int SCREENWIDTH, int SCREENHEIGHT, int[] ylookup, int[] columnofs, ColVars<byte[], short[]> dcvars, short[] screen, IDoomSystem I) { super(SCREENWIDTH, SCREENHEIGHT, ylookup, columnofs, dcvars, screen, I); // TODO Auto-generated constructor stub } public void invoke() { int count; int dest; // Adjust borders. Low... if (dcvars.dc_yl == 0) dcvars.dc_yl = 1; // .. and high. if (dcvars.dc_yh == dcvars.viewheight - 1) dcvars.dc_yh = dcvars.viewheight - 2; count = dcvars.dc_yh - dcvars.dc_yl; // Zero length. if (count < 0) return; if (RANGECHECK) { super.performRangeCheck(); } // Does not work with blocky mode. dest = computeScreenDest(); // Looks like an attempt at dithering, // using the colormap #6 (of 0-31, a bit // brighter than average). if (count>4) // MAES: unroll by 4 do { // Lookup framebuffer, and retrieve // a pixel that is either one column // left or right of the current one. // Add index from colormap to index. screen[dest] = fuzzMix(screen[dest + fuzzoffset[fuzzpos]]); // Clamp table lookup index. if (++fuzzpos == FUZZTABLE) fuzzpos = 0; dest += SCREENWIDTH; screen[dest] = fuzzMix(screen[dest + fuzzoffset[fuzzpos]]); if (++fuzzpos == FUZZTABLE) fuzzpos = 0; dest += SCREENWIDTH; screen[dest] = fuzzMix(screen[dest + fuzzoffset[fuzzpos]]); if (++fuzzpos == FUZZTABLE) fuzzpos = 0; dest += SCREENWIDTH; screen[dest] = fuzzMix(screen[dest + fuzzoffset[fuzzpos]]); if (++fuzzpos == FUZZTABLE) fuzzpos = 0; dest += SCREENWIDTH; } while ((count-=4) > 4); if (count>0) do { screen[dest] = fuzzMix(screen[dest + fuzzoffset[fuzzpos]]); // Clamp table lookup index. if (++fuzzpos == FUZZTABLE) fuzzpos = 0; dest += SCREENWIDTH; } while (count-- > 0); } private final short fuzzMix(short rgb){ // super-fast half-brite trick // 3DEF and >> 1: ok hue, but too dark // 7BDE, no shift: good compromise // 739C, no shift: results in too obvious tinting. return (short) (rgb&0x7BDE); } } public static final class Indexed extends R_DrawFuzzColumn<byte[],byte[]>{ public Indexed(int SCREENWIDTH, int SCREENHEIGHT, int[] ylookup, int[] columnofs, ColVars<byte[], byte[]> dcvars, byte[] screen, IDoomSystem I,byte[] BLURRY_MAP) { super(SCREENWIDTH, SCREENHEIGHT, ylookup, columnofs, dcvars, screen, I,BLURRY_MAP); } public void invoke() { int count; int dest; // Adjust borders. Low... if (dcvars.dc_yl == 0) dcvars.dc_yl = 1; // .. and high. if (dcvars.dc_yh == dcvars.viewheight - 1) dcvars.dc_yh = dcvars.viewheight - 2; count = dcvars.dc_yh - dcvars.dc_yl; // Zero length. if (count < 0) return; if (RANGECHECK) { super.performRangeCheck(); } // Does not work with blocky mode. dest = computeScreenDest(); // Looks like an attempt at dithering, // using the colormap #6 (of 0-31, a bit // brighter than average). if (count>4) // MAES: unroll by 4 do { // Lookup framebuffer, and retrieve // a pixel that is either one column // left or right of the current one. // Add index from colormap to index. screen[dest] = BLURRY_MAP[0x00FF & screen[dest + fuzzoffset[fuzzpos]]]; // Clamp table lookup index. if (++fuzzpos == FUZZTABLE) fuzzpos = 0; dest += SCREENWIDTH; screen[dest] = BLURRY_MAP[0x00FF & screen[dest+ fuzzoffset[fuzzpos]]]; if (++fuzzpos == FUZZTABLE) fuzzpos = 0; dest += SCREENWIDTH; screen[dest] = BLURRY_MAP[0x00FF & screen[dest+ fuzzoffset[fuzzpos]]]; if (++fuzzpos == FUZZTABLE) fuzzpos = 0; dest += SCREENWIDTH; screen[dest] = BLURRY_MAP[0x00FF & screen[dest+ fuzzoffset[fuzzpos]]]; if (++fuzzpos == FUZZTABLE) fuzzpos = 0; dest += SCREENWIDTH; } while ((count-=4) > 4); if (count>0) do { // Lookup framebuffer, and retrieve // a pixel that is either one column // left or right of the current one. // Add index from colormap to index. screen[dest] = BLURRY_MAP[0x00FF & screen[dest + fuzzoffset[fuzzpos]]]; // Clamp table lookup index. if (++fuzzpos == FUZZTABLE) fuzzpos = 0; dest += SCREENWIDTH; } while (count-- > 0); } } public static final class TrueColor extends R_DrawFuzzColumn<byte[],int[]>{ public TrueColor(int SCREENWIDTH, int SCREENHEIGHT, int[] ylookup, int[] columnofs, ColVars<byte[], int[]> dcvars, int[] screen, IDoomSystem I) { super(SCREENWIDTH, SCREENHEIGHT, ylookup, columnofs, dcvars, screen, I); } public void invoke() { int count; int dest; // Adjust borders. Low... if (dcvars.dc_yl == 0) dcvars.dc_yl = 1; // .. and high. if (dcvars.dc_yh == dcvars.viewheight - 1) dcvars.dc_yh = dcvars.viewheight - 2; count = dcvars.dc_yh - dcvars.dc_yl; // Zero length. if (count < 0) return; if (RANGECHECK) { super.performRangeCheck(); } // Does not work with blocky mode. dest = computeScreenDest(); // Looks like an attempt at dithering, // using the colormap #6 (of 0-31, a bit // brighter than average). if (count>4) // MAES: unroll by 4 do { // Lookup framebuffer, and retrieve // a pixel that is either one column // left or right of the current one. // Add index from colormap to index. screen[dest] = fuzzMix(screen[dest + fuzzoffset[fuzzpos]]); // Clamp table lookup index. if (++fuzzpos == FUZZTABLE) fuzzpos = 0; dest += SCREENWIDTH; screen[dest] = fuzzMix(screen[dest + fuzzoffset[fuzzpos]]); if (++fuzzpos == FUZZTABLE) fuzzpos = 0; dest += SCREENWIDTH; screen[dest] = fuzzMix(screen[dest + fuzzoffset[fuzzpos]]); if (++fuzzpos == FUZZTABLE) fuzzpos = 0; dest += SCREENWIDTH; screen[dest] = fuzzMix(screen[dest + fuzzoffset[fuzzpos]]); if (++fuzzpos == FUZZTABLE) fuzzpos = 0; dest += SCREENWIDTH; } while ((count-=4) > 4); if (count>0) do { screen[dest] = fuzzMix(screen[dest + fuzzoffset[fuzzpos]]); // Clamp table lookup index. if (++fuzzpos == FUZZTABLE) fuzzpos = 0; dest += SCREENWIDTH; } while (count-- > 0); } private final int fuzzMix(int rgb){ // Proper half-brite alpha! return rgb&0x10FFFFFF; } } } package rr.drawfuns; import i.IDoomSystem; /** Prototype for * * @author velktron * * @param <T> */ public abstract class DoomColumnFunction<T,V> implements ColumnFunction<T,V>{ protected final boolean RANGECHECK=false; protected final int SCREENWIDTH; protected final int SCREENHEIGHT; protected ColVars<T,V> dcvars; protected final V screen; protected final IDoomSystem I; protected final int[] ylookup; protected final int[] columnofs; protected T BLURRY_MAP; protected int flags; public DoomColumnFunction(int sCREENWIDTH, int sCREENHEIGHT,int[] ylookup, int[] columnofs, ColVars<T,V> dcvars, V screen,IDoomSystem I) { SCREENWIDTH = sCREENWIDTH; SCREENHEIGHT = sCREENHEIGHT; this.ylookup=ylookup; this.columnofs=columnofs; this.dcvars = dcvars; this.screen = screen; this.I=I; this.BLURRY_MAP=null; } public DoomColumnFunction(int sCREENWIDTH, int sCREENHEIGHT,int[] ylookup, int[] columnofs, ColVars<T,V> dcvars, V screen,IDoomSystem I,T BLURRY_MAP) { SCREENWIDTH = sCREENWIDTH; SCREENHEIGHT = sCREENHEIGHT; this.ylookup=ylookup; this.columnofs=columnofs; this.dcvars = dcvars; this.screen = screen; this.I=I; this.BLURRY_MAP=BLURRY_MAP; } protected final void performRangeCheck(){ if (dcvars.dc_x >= SCREENWIDTH || dcvars.dc_yl < 0 || dcvars.dc_yh >= SCREENHEIGHT) I.Error("R_DrawColumn: %d to %d at %d", dcvars.dc_yl, dcvars.dc_yh, dcvars.dc_x); } /** * * Use ylookup LUT to avoid multiply with ScreenWidth. * Use columnofs LUT for subwindows? * * @return Framebuffer destination address. */ protected final int computeScreenDest() { return ylookup[dcvars.dc_yl] + columnofs[dcvars.dc_x]; } protected final int blockyDest1() { return ylookup[dcvars.dc_yl] + columnofs[dcvars.dc_x<<1]; } protected final int blockyDest2() { return ylookup[dcvars.dc_yl] + columnofs[(dcvars.dc_x<<1)+1]; } @Override public final void invoke(ColVars<T,V> dcvars) { this.dcvars=dcvars; invoke(); } public final int getFlags(){ return this.flags; } } package rr.drawfuns; import i.IDoomSystem; /** * EI VITTU, this gives a clean 25% boost. Da fack... * * * @author admin * */ public final class R_DrawColumnUnrolled extends DoomColumnFunction<byte[],short[]> { /* * That's shit, doesn't help. private final int * SCREENWIDTH2=SCREENWIDTH*2; private final int * SCREENWIDTH3=SCREENWIDTH*3; private final int * SCREENWIDTH4=SCREENWIDTH*4; private final int * SCREENWIDTH5=SCREENWIDTH*5; private final int * SCREENWIDTH6=SCREENWIDTH*6; private final int * SCREENWIDTH7=SCREENWIDTH*7; private final int * SCREENWIDTH8=SCREENWIDTH*8; */ public R_DrawColumnUnrolled(int SCREENWIDTH, int SCREENHEIGHT, int[] ylookup, int[] columnofs, ColVars<byte[],short[]> dcvars, short[] screen, IDoomSystem I) { super(SCREENWIDTH, SCREENHEIGHT, ylookup, columnofs, dcvars, screen, I); } public void invoke() { int count,dest; final byte[] source; final short[] colormap; final int dc_source_ofs=dcvars.dc_source_ofs; // These are all "unsigned". Watch out for bit shifts! int frac; final int fracstep, fracstep2, fracstep3, fracstep4; count = dcvars.dc_yh - dcvars.dc_yl + 1; source = dcvars.dc_source; // dc_source_ofs+=15; // ???? WHY colormap = dcvars.dc_colormap; dest = computeScreenDest(); fracstep = dcvars.dc_iscale << 9; frac = (dcvars.dc_texturemid + (dcvars.dc_yl - dcvars.centery) * dcvars.dc_iscale) << 9; fracstep2 = fracstep + fracstep; fracstep3 = fracstep2 + fracstep; fracstep4 = fracstep3 + fracstep; while (count > 8) { screen[dest] = colormap[0x00FF & source[dc_source_ofs + frac >>> 25]]; screen[dest + SCREENWIDTH] = colormap[0x00FF & source[dc_source_ofs + (frac + fracstep) >>> 25]]; screen[dest + SCREENWIDTH * 2] = colormap[0x00FF & source[dc_source_ofs + (frac + fracstep2) >>> 25]]; screen[dest + SCREENWIDTH * 3] = colormap[0x00FF & source[dc_source_ofs + (frac + fracstep3) >>> 25]]; frac += fracstep4; screen[dest + SCREENWIDTH * 4] = colormap[0x00FF & source[dc_source_ofs + frac >>> 25]]; screen[dest + SCREENWIDTH * 5] = colormap[0x00FF & source[dc_source_ofs + (frac + fracstep) >>> 25]]; screen[dest + SCREENWIDTH * 6] = colormap[0x00FF & source[dc_source_ofs + (frac + fracstep2) >>> 25]]; screen[dest + SCREENWIDTH * 7] = colormap[0x00FF & source[dc_source_ofs + (frac + fracstep3) >>> 25]]; frac += fracstep4; dest += SCREENWIDTH * 8; count -= 8; } while (count > 0) { screen[dest] = colormap[0x00FF & source[dc_source_ofs + frac >>> 25]]; dest += SCREENWIDTH; frac += fracstep; count--; } } } package rr.drawfuns; import i.IDoomSystem; /** An unrolled (4x) rendering loop with full quality */ // public final int dumb=63 * 64; public final class R_DrawSpanUnrolled2 extends DoomSpanFunction<byte[],short[]> { public R_DrawSpanUnrolled2(int sCREENWIDTH, int sCREENHEIGHT, int[] ylookup, int[] columnofs, SpanVars<byte[],short[]> dsvars, short[] screen, IDoomSystem I) { super(sCREENWIDTH, sCREENHEIGHT, ylookup, columnofs, dsvars, screen, I); // TODO Auto-generated constructor stub } public void invoke() { final byte[] ds_source= dsvars.ds_source; final short[] ds_colormap= dsvars.ds_colormap; final int ds_xstep=dsvars.ds_xstep; final int ds_ystep=dsvars.ds_ystep; int f_xfrac; // fixed_t int f_yfrac; // fixed_t int dest; int count; int spot; // System.out.println("R_DrawSpan: "+ds_x1+" to "+ds_x2+" at "+ // ds_y); if (RANGECHECK) { doRangeCheck(); // dscount++; } f_xfrac = dsvars.ds_xfrac; f_yfrac = dsvars.ds_yfrac; dest = ylookup[dsvars.ds_y] + columnofs[dsvars.ds_x1]; count = dsvars.ds_x2 - dsvars.ds_x1; while (count >= 4) { // Current texture index in u,v. spot = ((f_yfrac >> (16 - 6)) & (63 * 64)) + ((f_xfrac >> 16) & 63); // Lookup pixel from flat texture tile, // re-index using light/colormap. screen[dest++] = ds_colormap[0x00FF & ds_source[spot]]; // Next step in u,v. f_xfrac += ds_xstep; f_yfrac += ds_ystep; // UNROLL 2 spot = ((f_yfrac >> (16 - 6)) & (63 * 64)) + ((f_xfrac >> 16) & 63); screen[dest++] = ds_colormap[0x00FF & ds_source[spot]]; f_xfrac += ds_xstep; f_yfrac += ds_ystep; // UNROLL 3 spot = ((f_yfrac >> (16 - 6)) & (63 * 64)) + ((f_xfrac >> 16) & 63); screen[dest++] = ds_colormap[0x00FF & ds_source[spot]]; f_xfrac += ds_xstep; f_yfrac += ds_ystep; // UNROLL 4 spot = ((f_yfrac >> (16 - 6)) & (63 * 64)) + ((f_xfrac >> 16) & 63); screen[dest++] = ds_colormap[0x00FF & ds_source[spot]]; f_xfrac += ds_xstep; f_yfrac += ds_ystep; count -= 4; } while (count > 0) { // Current texture index in u,v. spot = ((f_yfrac >> (16 - 6)) & (63 * 64)) + ((f_xfrac >> 16) & 63); // Lookup pixel from flat texture tile, // re-index using light/colormap. screen[dest++] = ds_colormap[0x00FF & ds_source[spot]]; // Next step in u,v. f_xfrac += ds_xstep; f_yfrac += ds_ystep; count--; } } } package rr.drawfuns; import i.IDoomSystem; import static m.fixed_t.FRACBITS; /** * Adapted from Killough's Boom code. Low-detail variation, no DC_SOURCE * optimization. * * @author admin */ public abstract class R_DrawColumnBoomLow<T, V> extends DoomColumnFunction<T, V> { public R_DrawColumnBoomLow(int SCREENWIDTH, int SCREENHEIGHT, int[] ylookup, int[] columnofs, ColVars<T, V> dcvars, V screen, IDoomSystem I) { super(SCREENWIDTH, SCREENHEIGHT, ylookup, columnofs, dcvars, screen, I); this.flags = DcFlags.LOW_DETAIL; } public static final class HiColor extends R_DrawColumnBoomLow<byte[], short[]> { public HiColor(int SCREENWIDTH, int SCREENHEIGHT, int[] ylookup, int[] columnofs, ColVars<byte[], short[]> dcvars, short[] screen, IDoomSystem I) { super(SCREENWIDTH, SCREENHEIGHT, ylookup, columnofs, dcvars, screen, I); } public void invoke() { int count; int dest, dest2; // killough int frac; // killough final int fracstep, dc_source_ofs; count = dcvars.dc_yh - dcvars.dc_yl + 1; dc_source_ofs = dcvars.dc_source_ofs; if (count <= 0) // Zero length, column does not exceed a pixel. return; if (RANGECHECK) { performRangeCheck(); } // Framebuffer destination address. // Use ylookup LUT to avoid multiply with ScreenWidth. // Use columnofs LUT for subwindows? dest = blockyDest1(); dest2 = blockyDest2(); // Determine scaling, which is the only mapping to be done. fracstep = dcvars.dc_iscale; frac = dcvars.dc_texturemid + (dcvars.dc_yl - dcvars.centery) * fracstep; // Inner loop that does the actual texture mapping, // e.g. a DDA-lile scaling. // This is as fast as it gets. (Yeah, right!!! -- killough) // // killough 2/1/98: more performance tuning { final byte[] source = dcvars.dc_source; final short[] colormap = dcvars.dc_colormap; int heightmask = dcvars.dc_texheight - 1; if ((dcvars.dc_texheight & heightmask) != 0) // not a power of 2 // -- // killough { heightmask++; heightmask <<= FRACBITS; if (frac < 0) while ((frac += heightmask) < 0) ; else while (frac >= heightmask) frac -= heightmask; do { // Re-map color indices from wall texture column // using a lighting/special effects LUT. // heightmask is the Tutti-Frutti fix -- killough screen[dest] = screen[dest2] = colormap[0x00FF & source[((frac >> FRACBITS))]]; dest += SCREENWIDTH; dest2 += SCREENWIDTH; if ((frac += fracstep) >= heightmask) frac -= heightmask; } while (--count > 0); } else { while (count >= 4) // texture height is a power of 2 -- // killough { screen[dest] = screen[dest2] = colormap[0x00FF & source[dc_source_ofs + ((frac >> FRACBITS) & heightmask)]]; dest += SCREENWIDTH; dest2 += SCREENWIDTH; frac += fracstep; screen[dest] = screen[dest2] = colormap[0x00FF & source[dc_source_ofs + ((frac >> FRACBITS) & heightmask)]]; dest += SCREENWIDTH; dest2 += SCREENWIDTH; frac += fracstep; screen[dest] = screen[dest2] = colormap[0x00FF & source[dc_source_ofs + ((frac >> FRACBITS) & heightmask)]]; dest += SCREENWIDTH; dest2 += SCREENWIDTH; frac += fracstep; screen[dest] = screen[dest2] = colormap[0x00FF & source[dc_source_ofs + ((frac >> FRACBITS) & heightmask)]]; dest += SCREENWIDTH; dest2 += SCREENWIDTH; frac += fracstep; count -= 4; } while (count > 0) { screen[dest] = screen[dest2] = colormap[0x00FF & source[dc_source_ofs + ((frac >> FRACBITS) & heightmask)]]; dest += SCREENWIDTH; dest2 += SCREENWIDTH; frac += fracstep; count--; } } } } } public static final class Indexed extends R_DrawColumnBoomLow<byte[], byte[]> { public Indexed(int SCREENWIDTH, int SCREENHEIGHT, int[] ylookup, int[] columnofs, ColVars<byte[], byte[]> dcvars, byte[] screen, IDoomSystem I) { super(SCREENWIDTH, SCREENHEIGHT, ylookup, columnofs, dcvars, screen, I); } public void invoke() { int count; int dest, dest2; // killough int frac; // killough final int fracstep, dc_source_ofs; count = dcvars.dc_yh - dcvars.dc_yl + 1; dc_source_ofs = dcvars.dc_source_ofs; if (count <= 0) // Zero length, column does not exceed a pixel. return; if (RANGECHECK) { performRangeCheck(); } // Framebuffer destination address. // Use ylookup LUT to avoid multiply with ScreenWidth. // Use columnofs LUT for subwindows? dest = blockyDest1(); dest2 = blockyDest2(); // Determine scaling, which is the only mapping to be done. fracstep = dcvars.dc_iscale; frac = dcvars.dc_texturemid + (dcvars.dc_yl - dcvars.centery) * fracstep; // Inner loop that does the actual texture mapping, // e.g. a DDA-lile scaling. // This is as fast as it gets. (Yeah, right!!! -- killough) // // killough 2/1/98: more performance tuning { final byte[] source = dcvars.dc_source; final byte[] colormap = dcvars.dc_colormap; int heightmask = dcvars.dc_texheight - 1; if ((dcvars.dc_texheight & heightmask) != 0) // not a power of 2 // -- // killough { heightmask++; heightmask <<= FRACBITS; if (frac < 0) while ((frac += heightmask) < 0) ; else while (frac >= heightmask) frac -= heightmask; do { // Re-map color indices from wall texture column // using a lighting/special effects LUT. // heightmask is the Tutti-Frutti fix -- killough screen[dest] = screen[dest2] = colormap[0x00FF & source[((frac >> FRACBITS))]]; dest += SCREENWIDTH; dest2 += SCREENWIDTH; if ((frac += fracstep) >= heightmask) frac -= heightmask; } while (--count > 0); } else { while (count >= 4) // texture height is a power of 2 -- // killough { screen[dest] = screen[dest2] = colormap[0x00FF & source[dc_source_ofs + ((frac >> FRACBITS) & heightmask)]]; dest += SCREENWIDTH; dest2 += SCREENWIDTH; frac += fracstep; screen[dest] = screen[dest2] = colormap[0x00FF & source[dc_source_ofs + ((frac >> FRACBITS) & heightmask)]]; dest += SCREENWIDTH; dest2 += SCREENWIDTH; frac += fracstep; screen[dest] = screen[dest2] = colormap[0x00FF & source[dc_source_ofs + ((frac >> FRACBITS) & heightmask)]]; dest += SCREENWIDTH; dest2 += SCREENWIDTH; frac += fracstep; screen[dest] = screen[dest2] = colormap[0x00FF & source[dc_source_ofs + ((frac >> FRACBITS) & heightmask)]]; dest += SCREENWIDTH; dest2 += SCREENWIDTH; frac += fracstep; count -= 4; } while (count > 0) { screen[dest] = screen[dest2] = colormap[0x00FF & source[dc_source_ofs + ((frac >> FRACBITS) & heightmask)]]; dest += SCREENWIDTH; dest2 += SCREENWIDTH; frac += fracstep; count--; } } } } } public static final class TrueColor extends R_DrawColumnBoomLow<byte[], int[]> { public TrueColor(int SCREENWIDTH, int SCREENHEIGHT, int[] ylookup, int[] columnofs, ColVars<byte[], int[]> dcvars, int[] screen, IDoomSystem I) { super(SCREENWIDTH, SCREENHEIGHT, ylookup, columnofs, dcvars, screen, I); } public void invoke() { int count; int dest, dest2; // killough int frac; // killough final int fracstep, dc_source_ofs; count = dcvars.dc_yh - dcvars.dc_yl + 1; dc_source_ofs = dcvars.dc_source_ofs; if (count <= 0) // Zero length, column does not exceed a pixel. return; if (RANGECHECK) { performRangeCheck(); } // Framebuffer destination address. // Use ylookup LUT to avoid multiply with ScreenWidth. // Use columnofs LUT for subwindows? dest = blockyDest1(); dest2 = blockyDest2(); // Determine scaling, which is the only mapping to be done. fracstep = dcvars.dc_iscale; frac = dcvars.dc_texturemid + (dcvars.dc_yl - dcvars.centery) * fracstep; // Inner loop that does the actual texture mapping, // e.g. a DDA-lile scaling. // This is as fast as it gets. (Yeah, right!!! -- killough) // // killough 2/1/98: more performance tuning { final byte[] source = dcvars.dc_source; final int[] colormap = dcvars.dc_colormap; int heightmask = dcvars.dc_texheight - 1; if ((dcvars.dc_texheight & heightmask) != 0) // not a power of 2 // -- // killough { heightmask++; heightmask <<= FRACBITS; if (frac < 0) while ((frac += heightmask) < 0) ; else while (frac >= heightmask) frac -= heightmask; do { // Re-map color indices from wall texture column // using a lighting/special effects LUT. // heightmask is the Tutti-Frutti fix -- killough screen[dest] = screen[dest2] = colormap[0x00FF & source[((frac >> FRACBITS))]]; dest += SCREENWIDTH; dest2 += SCREENWIDTH; if ((frac += fracstep) >= heightmask) frac -= heightmask; } while (--count > 0); } else { while (count >= 4) // texture height is a power of 2 -- // killough { screen[dest] = screen[dest2] = colormap[0x00FF & source[dc_source_ofs + ((frac >> FRACBITS) & heightmask)]]; dest += SCREENWIDTH; dest2 += SCREENWIDTH; frac += fracstep; screen[dest] = screen[dest2] = colormap[0x00FF & source[dc_source_ofs + ((frac >> FRACBITS) & heightmask)]]; dest += SCREENWIDTH; dest2 += SCREENWIDTH; frac += fracstep; screen[dest] = screen[dest2] = colormap[0x00FF & source[dc_source_ofs + ((frac >> FRACBITS) & heightmask)]]; dest += SCREENWIDTH; dest2 += SCREENWIDTH; frac += fracstep; screen[dest] = screen[dest2] = colormap[0x00FF & source[dc_source_ofs + ((frac >> FRACBITS) & heightmask)]]; dest += SCREENWIDTH; dest2 += SCREENWIDTH; frac += fracstep; count -= 4; } while (count > 0) { screen[dest] = screen[dest2] = colormap[0x00FF & source[dc_source_ofs + ((frac >> FRACBITS) & heightmask)]]; dest += SCREENWIDTH; dest2 += SCREENWIDTH; frac += fracstep; count--; } } } } } } package rr.drawfuns; import i.IDoomSystem; /** * Draws the actual span. * * ds_frac, ds_yfrac, ds_x2, ds_x1, ds_xstep and ds_ystep must be set. * */ public final class R_DrawSpan extends DoomSpanFunction<byte[],short[]> { public R_DrawSpan(int sCREENWIDTH, int sCREENHEIGHT, int[] ylookup, int[] columnofs, SpanVars<byte[],short[]> dsvars,short[] screen, IDoomSystem I) { super(sCREENWIDTH, sCREENHEIGHT, ylookup, columnofs, dsvars, screen, I); } public void invoke() { int f_xfrac; // fixed_t int f_yfrac; // fixed_t int dest,count,spot; final short[] ds_colormap=dsvars.ds_colormap; final byte[] ds_source=dsvars.ds_source; // System.out.println("R_DrawSpan: "+ds_x1+" to "+ds_x2+" at "+ // ds_y); if (RANGECHECK) { doRangeCheck(); // dscount++; } f_xfrac = dsvars.ds_xfrac; f_yfrac = dsvars.ds_yfrac; dest = ylookup[dsvars.ds_y] + columnofs[dsvars.ds_x1]; // We do not check for zero spans here? count = dsvars.ds_x2 - dsvars.ds_x1; do { // Current texture index in u,v. spot = ((f_yfrac >> (16 - 6)) & (63 * 64)) + ((f_xfrac >> 16) & 63); // Lookup pixel from flat texture tile, // re-index using light/colormap. screen[dest++] = ds_colormap[0x00FF & ds_source[spot]]; // Next step in u,v. f_xfrac += dsvars.ds_xstep; f_yfrac += dsvars.ds_ystep; } while (count-- > 0); } } package rr.drawfuns; import static m.fixed_t.FRACBITS; import i.IDoomSystem; /** * Adapted from Killough's Boom code. There are optimized as well as * low-detail versions of it. * * @author admin * */ /** * A column is a vertical slice/span from a wall texture that, given the * DOOM style restrictions on the view orientation, will always have * constant z depth. Thus a special case loop for very fast rendering can be * used. It has also been used with Wolfenstein 3D. MAES: this is called * mostly from inside Draw and from an external "Renderer" */ public final class R_DrawColumn extends DoomColumnFunction<byte[],short[]> { public R_DrawColumn(int SCREENWIDTH, int SCREENHEIGHT, int[] ylookup, int[] columnofs, ColVars<byte[],short[]> dcvars, short[] screen, IDoomSystem I) { super(SCREENWIDTH, SCREENHEIGHT, ylookup, columnofs, dcvars, screen, I); } public void invoke() { int count; // byte* dest; int dest; // As pointer // fixed_t int frac, fracstep; byte colmask = 127; count = dcvars.dc_yh - dcvars.dc_yl; // How much we should draw // count = Math.min(dc_yh - dc_yl,dc_source.length-dc_source_ofs-1); // colmask = (byte) Math.min(dc_source.length-dc_source_ofs-1,127); // Zero length, column does not exceed a pixel. if (count <= 0) return; if (RANGECHECK) { if (dcvars.dc_x >= SCREENWIDTH || dcvars.dc_yl < 0 || dcvars.dc_yh >= SCREENHEIGHT) I.Error("R_DrawColumn: %i to %i at %i", dcvars.dc_yl, dcvars.dc_yh, dcvars.dc_x); } // Trying to draw a masked column? Then something gross will happen. /* * if (count>=dc_source.length-dc_source_ofs) { int * diff=count-(dc_source.length-dc_source_ofs); * count=dc_source.length-dc_source_ofs-1; dc_source_ofs=0; * //dc_yl=dc_yh-count; gross=true; } */ dest = computeScreenDest(); // Determine scaling, // which is the only mapping to be done. fracstep = dcvars.dc_iscale; frac = dcvars.dc_texturemid + (dcvars.dc_yl - dcvars.centery) * fracstep; // Inner loop that does the actual texture mapping, // e.g. a DDA-lile scaling. // This is as fast as it gets. do { /* * Re-map color indices from wall texture column using a * lighting/special effects LUT. * Q: Where is "*dest"supposed to be pointing? * A: it's pointing inside screen[0] (set long before we came here) * dc_source is a pointer to a decompressed column's data. * Oh Woe if it points at non-pixel data. */ // if (gross) System.out.println(frac >> FRACBITS); screen[dest] = dcvars.dc_colormap[0x00FF & dcvars.dc_source[(dcvars.dc_source_ofs + (frac >> FRACBITS)) & colmask]]; /* * MAES: ok, so we have (from inside out): * * frac is a fixed-point number representing a pointer inside a * column. It gets shifted to an integer, and AND-ed with 128 * (this causes vertical column tiling). */ dest += SCREENWIDTH; frac += fracstep; } while (count-- > 0); } } package rr.drawfuns; import i.IDoomSystem; import static m.fixed_t.FRACBITS; /** * Adapted from Killough's Boom code. Specially super-optimized version assuming * that dc_source_ofs is always 0, AND that frac>>FRACBITS can be eliminated by * doing fracstep>>FRACBITS a-priori. Experimental/untested. * * @author admin * */ public final class R_DrawColumnBoomSuperOpt extends DoomColumnFunction<byte[],short[]> { public R_DrawColumnBoomSuperOpt(int SCREENWIDTH, int SCREENHEIGHT, int[] ylookup, int[] columnofs, ColVars<byte[],short[]> dcvars, short[] screen, IDoomSystem I) { super(SCREENWIDTH, SCREENHEIGHT, ylookup, columnofs, dcvars, screen, I); } public void invoke() { int count; int dest; // killough int frac; // killough final int fracstep; count = dcvars.dc_yh - dcvars.dc_yl + 1; if (count <= 0) // Zero length, column does not exceed a pixel. return; if (RANGECHECK) { performRangeCheck(); } // Framebuffer destination address. // Use ylookup LUT to avoid multiply with ScreenWidth. // Use columnofs LUT for subwindows? dest = computeScreenDest(); // Determine scaling, which is the only mapping to be done. fracstep = dcvars.dc_iscale>>FRACBITS; frac = dcvars.dc_texturemid + (dcvars.dc_yl - dcvars.centery) * fracstep; frac>>=FRACBITS; // Inner loop that does the actual texture mapping, // e.g. a DDA-lile scaling. // This is as fast as it gets. (Yeah, right!!! -- killough) // // killough 2/1/98: more performance tuning { final byte[] source = dcvars.dc_source; final short[] colormap = dcvars.dc_colormap; int heightmask = dcvars.dc_texheight - 1; if ((dcvars.dc_texheight & heightmask) != 0) // not a power of 2 -- // killough { heightmask++; heightmask <<= FRACBITS; if (frac < 0) while ((frac += heightmask) < 0) ; else while (frac >= heightmask) frac -= heightmask; do { // Re-map color indices from wall texture column // using a lighting/special effects LUT. // heightmask is the Tutti-Frutti fix -- killough screen[dest] = colormap[0x00FF & source[frac]]; dest += SCREENWIDTH; if ((frac += fracstep) >= heightmask) frac -= heightmask; } while (--count > 0); } else { while (count >= 4) // texture height is a power of 2 -- // killough { // System.err.println(dest); screen[dest] = colormap[0x00FF & source[frac & heightmask]]; dest += SCREENWIDTH; frac += fracstep; screen[dest] = colormap[0x00FF & source[frac & heightmask]]; dest += SCREENWIDTH; frac += fracstep; screen[dest] = colormap[0x00FF & source[frac & heightmask]]; dest += SCREENWIDTH; frac += fracstep; screen[dest] = colormap[0x00FF & source[frac & heightmask]]; dest += SCREENWIDTH; frac += fracstep; count -= 4; } while (count > 0) { screen[dest] = colormap[0x00FF & source[frac & heightmask]]; dest += SCREENWIDTH; frac += fracstep; count--; } } } } } package rr.drawfuns; import i.IDoomSystem; /** * Drawspan loop unrolled by 4. However it has low rendering quality and bad * distortion. However it does actually does give a small speed boost (120 * -> 130 fps with a Mul of 3.0) * */ public abstract class R_DrawSpanUnrolled<T,V> extends DoomSpanFunction<T,V> { public R_DrawSpanUnrolled(int sCREENWIDTH, int sCREENHEIGHT, int[] ylookup, int[] columnofs, SpanVars<T, V> dsvars, V screen, IDoomSystem I) { super(sCREENWIDTH, sCREENHEIGHT, ylookup, columnofs, dsvars, screen, I); // TODO Auto-generated constructor stub } public static final class HiColor extends R_DrawSpanUnrolled<byte[],short[]>{ public HiColor(int sCREENWIDTH, int sCREENHEIGHT, int[] ylookup, int[] columnofs, SpanVars<byte[], short[]> dsvars, short[] screen, IDoomSystem I) { super(sCREENWIDTH, sCREENHEIGHT, ylookup, columnofs, dsvars, screen, I); // TODO Auto-generated constructor stub } public void invoke() { int position, step; final byte[] source; final short[] colormap; int dest; int count; int spot; int xtemp; int ytemp; position = ((dsvars.ds_xfrac << 10) & 0xffff0000) | ((dsvars.ds_yfrac >> 6) & 0xffff); step = ((dsvars.ds_xstep << 10) & 0xffff0000) | ((dsvars.ds_ystep >> 6) & 0xffff); source = dsvars.ds_source; colormap = dsvars.ds_colormap; dest = ylookup[dsvars.ds_y] + columnofs[dsvars.ds_x1]; count = dsvars.ds_x2 - dsvars.ds_x1 + 1; //int rolls = 0; while (count >= 4) { ytemp = position >> 4; ytemp = ytemp & 0xfc0; xtemp = position >>> 26; spot = xtemp | ytemp; position += step; screen[dest] = colormap[0x00FF & source[spot]]; ytemp = position >> 4; ytemp = ytemp & 0xfc0; xtemp = position >>> 26; spot = xtemp | ytemp; position += step; screen[dest + 1] = colormap[0x00FF & source[spot]]; ytemp = position >> 4; ytemp = ytemp & 0xfc0; xtemp = position >>> 26; spot = xtemp | ytemp; position += step; screen[dest + 2] = colormap[0x00FF & source[spot]]; ytemp = position >> 4; ytemp = ytemp & 0xfc0; xtemp = position >>> 26; spot = xtemp | ytemp; position += step; screen[dest + 3] = colormap[0x00FF & source[spot]]; count -= 4; dest += 4; // Half-assed attempt to fix precision by forced periodic // realignment. /* * if ((rolls++)%64==0){ position = * ((((rolls*4)*ds_xstep+ds_xfrac) << 10) & 0xffff0000) | * ((((rolls*4)*ds_ystep+ds_yfrac) >> 6) & 0xffff); } */ } while (count > 0) { ytemp = position >> 4; ytemp = ytemp & 4032; xtemp = position >>> 26; spot = xtemp | ytemp; position += step; screen[dest++] = colormap[0x00FF & source[spot]]; count--; } } } public static final class Indexed extends R_DrawSpanUnrolled<byte[],byte[]>{ public Indexed(int sCREENWIDTH, int sCREENHEIGHT, int[] ylookup, int[] columnofs, SpanVars<byte[], byte[]> dsvars, byte[] screen, IDoomSystem I) { super(sCREENWIDTH, sCREENHEIGHT, ylookup, columnofs, dsvars, screen, I); // TODO Auto-generated constructor stub } public void invoke() { int position, step; final byte[] source; final byte[] colormap; int dest; int count; int spot; int xtemp; int ytemp; position = ((dsvars.ds_xfrac << 10) & 0xffff0000) | ((dsvars.ds_yfrac >> 6) & 0xffff); step = ((dsvars.ds_xstep << 10) & 0xffff0000) | ((dsvars.ds_ystep >> 6) & 0xffff); source = dsvars.ds_source; colormap = dsvars.ds_colormap; dest = ylookup[dsvars.ds_y] + columnofs[dsvars.ds_x1]; count = dsvars.ds_x2 - dsvars.ds_x1 + 1; //int rolls = 0; while (count >= 4) { ytemp = position >> 4; ytemp = ytemp & 0xfc0; xtemp = position >>> 26; spot = xtemp | ytemp; position += step; screen[dest] = colormap[0x00FF & source[spot]]; ytemp = position >> 4; ytemp = ytemp & 0xfc0; xtemp = position >>> 26; spot = xtemp | ytemp; position += step; screen[dest + 1] = colormap[0x00FF & source[spot]]; ytemp = position >> 4; ytemp = ytemp & 0xfc0; xtemp = position >>> 26; spot = xtemp | ytemp; position += step; screen[dest + 2] = colormap[0x00FF & source[spot]]; ytemp = position >> 4; ytemp = ytemp & 0xfc0; xtemp = position >>> 26; spot = xtemp | ytemp; position += step; screen[dest + 3] = colormap[0x00FF & source[spot]]; count -= 4; dest += 4; // Half-assed attempt to fix precision by forced periodic // realignment. /* * if ((rolls++)%64==0){ position = * ((((rolls*4)*ds_xstep+ds_xfrac) << 10) & 0xffff0000) | * ((((rolls*4)*ds_ystep+ds_yfrac) >> 6) & 0xffff); } */ } while (count > 0) { ytemp = position >> 4; ytemp = ytemp & 4032; xtemp = position >>> 26; spot = xtemp | ytemp; position += step; screen[dest++] = colormap[0x00FF & source[spot]]; count--; } } } public static final class TrueColor extends R_DrawSpanUnrolled<byte[],int[]>{ public TrueColor(int sCREENWIDTH, int sCREENHEIGHT, int[] ylookup, int[] columnofs, SpanVars<byte[], int[]> dsvars, int[] screen, IDoomSystem I) { super(sCREENWIDTH, sCREENHEIGHT, ylookup, columnofs, dsvars, screen, I); } public void invoke() { int position, step; final byte[] source; final int[] colormap; int dest; int count; int spot; int xtemp; int ytemp; position = ((dsvars.ds_xfrac << 10) & 0xffff0000) | ((dsvars.ds_yfrac >> 6) & 0xffff); step = ((dsvars.ds_xstep << 10) & 0xffff0000) | ((dsvars.ds_ystep >> 6) & 0xffff); source = dsvars.ds_source; colormap = dsvars.ds_colormap; dest = ylookup[dsvars.ds_y] + columnofs[dsvars.ds_x1]; count = dsvars.ds_x2 - dsvars.ds_x1 + 1; //int rolls = 0; while (count >= 4) { ytemp = position >> 4; ytemp = ytemp & 0xfc0; xtemp = position >>> 26; spot = xtemp | ytemp; position += step; screen[dest] = colormap[0x00FF & source[spot]]; ytemp = position >> 4; ytemp = ytemp & 0xfc0; xtemp = position >>> 26; spot = xtemp | ytemp; position += step; screen[dest + 1] = colormap[0x00FF & source[spot]]; ytemp = position >> 4; ytemp = ytemp & 0xfc0; xtemp = position >>> 26; spot = xtemp | ytemp; position += step; screen[dest + 2] = colormap[0x00FF & source[spot]]; ytemp = position >> 4; ytemp = ytemp & 0xfc0; xtemp = position >>> 26; spot = xtemp | ytemp; position += step; screen[dest + 3] = colormap[0x00FF & source[spot]]; count -= 4; dest += 4; // Half-assed attempt to fix precision by forced periodic // realignment. /* * if ((rolls++)%64==0){ position = * ((((rolls*4)*ds_xstep+ds_xfrac) << 10) & 0xffff0000) | * ((((rolls*4)*ds_ystep+ds_yfrac) >> 6) & 0xffff); } */ } while (count > 0) { ytemp = position >> 4; ytemp = ytemp & 4032; xtemp = position >>> 26; spot = xtemp | ytemp; position += step; screen[dest++] = colormap[0x00FF & source[spot]]; count--; } } } } package rr.drawfuns; import i.IDoomSystem; /** * Low detail version. Jesus. */ public abstract class R_DrawFuzzColumnLow<T, V> extends DoomColumnFunction<T, V> { public R_DrawFuzzColumnLow(int SCREENWIDTH, int SCREENHEIGHT, int[] ylookup, int[] columnofs, ColVars<T, V> dcvars, V screen, IDoomSystem I, T BLURRY_MAP) { this(SCREENWIDTH, SCREENHEIGHT, ylookup, columnofs, dcvars, screen, I); this.BLURRY_MAP = BLURRY_MAP; } public R_DrawFuzzColumnLow(int SCREENWIDTH, int SCREENHEIGHT, int[] ylookup, int[] columnofs, ColVars<T, V> dcvars, V screen, IDoomSystem I) { super(SCREENWIDTH, SCREENHEIGHT, ylookup, columnofs, dcvars, screen, I); this.flags = DcFlags.LOW_DETAIL | DcFlags.FUZZY; FUZZOFF = SCREENWIDTH; // Recompute fuzz table fuzzoffset = new int[] { FUZZOFF, -FUZZOFF, FUZZOFF, -FUZZOFF, FUZZOFF, FUZZOFF, -FUZZOFF, FUZZOFF, FUZZOFF, -FUZZOFF, FUZZOFF, FUZZOFF, FUZZOFF, -FUZZOFF, FUZZOFF, FUZZOFF, FUZZOFF, -FUZZOFF, -FUZZOFF, -FUZZOFF, -FUZZOFF, FUZZOFF, -FUZZOFF, -FUZZOFF, FUZZOFF, FUZZOFF, FUZZOFF, FUZZOFF, -FUZZOFF, FUZZOFF, -FUZZOFF, FUZZOFF, FUZZOFF, -FUZZOFF, -FUZZOFF, FUZZOFF, FUZZOFF, -FUZZOFF, -FUZZOFF, -FUZZOFF, -FUZZOFF, FUZZOFF, FUZZOFF, FUZZOFF, FUZZOFF, -FUZZOFF, FUZZOFF, FUZZOFF, -FUZZOFF, FUZZOFF }; } protected int fuzzpos; // // Spectre/Invisibility. // protected final int FUZZOFF; protected final int[] fuzzoffset; public static final class HiColor extends R_DrawFuzzColumn<byte[], short[]> { public HiColor(int SCREENWIDTH, int SCREENHEIGHT, int[] ylookup, int[] columnofs, ColVars<byte[], short[]> dcvars, short[] screen, IDoomSystem I) { super(SCREENWIDTH, SCREENHEIGHT, ylookup, columnofs, dcvars, screen, I); // TODO Auto-generated constructor stub } public void invoke() { int count; int dest, dest2; // Adjust borders. Low... if (dcvars.dc_yl == 0) dcvars.dc_yl = 1; // .. and high. if (dcvars.dc_yh == dcvars.viewheight - 1) dcvars.dc_yh = dcvars.viewheight - 2; count = dcvars.dc_yh - dcvars.dc_yl; // Zero length. if (count < 0) return; if (RANGECHECK) { performRangeCheck(); } // The idea is to draw more than one pixel at a time. dest = blockyDest1(); dest2 = blockyDest2(); // Looks like an attempt at dithering, // using the colormap #6 (of 0-31, a bit // brighter than average). if (count > 4) do { // Lookup framebuffer, and retrieve // a pixel that is either one column // left or right of the current one. // Add index from colormap to index. screen[dest] = screen[dest + fuzzoffset[fuzzpos]]; screen[dest2] = screen[dest]; // Ironically, "low detail" fuzziness was not really // low-detail, // as it normally did full-precision calculations. // BLURRY_MAP[0x00FF & screen[dest2+ fuzzoffset[fuzzpos]]]; // Clamp table lookup index. if (++fuzzpos == FUZZTABLE) fuzzpos = 0; dest += SCREENWIDTH; dest2 += SCREENWIDTH; screen[dest] = fuzzMix(screen[dest + fuzzoffset[fuzzpos]]); screen[dest2] = screen[dest]; if (++fuzzpos == FUZZTABLE) fuzzpos = 0; dest += SCREENWIDTH; dest2 += SCREENWIDTH; screen[dest] = fuzzMix(screen[dest + fuzzoffset[fuzzpos]]); screen[dest2] = screen[dest]; if (++fuzzpos == FUZZTABLE) fuzzpos = 0; dest += SCREENWIDTH; dest2 += SCREENWIDTH; screen[dest] = fuzzMix(screen[dest + fuzzoffset[fuzzpos]]); screen[dest2] = screen[dest]; if (++fuzzpos == FUZZTABLE) fuzzpos = 0; dest += SCREENWIDTH; dest2 += SCREENWIDTH; } while ((count -= 4) > 4); if (count > 0) do { screen[dest] = fuzzMix(screen[dest + fuzzoffset[fuzzpos]]); screen[dest2] = screen[dest]; if (++fuzzpos == FUZZTABLE) fuzzpos = 0; dest += SCREENWIDTH; dest2 += SCREENWIDTH; } while (count-- != 0); } private final short fuzzMix(short rgb) { // super-fast half-brite trick // 3DEF and >> 1: ok hue, but too dark // 7BDE, no shift: good compromise // 739C, no shift: results in too obvious tinting. return (short) (rgb & 0x7BDE); } } public static final class Indexed extends R_DrawFuzzColumn<byte[], byte[]> { public Indexed(int SCREENWIDTH, int SCREENHEIGHT, int[] ylookup, int[] columnofs, ColVars<byte[], byte[]> dcvars, byte[] screen, IDoomSystem I, byte[] BLURRY_MAP) { super(SCREENWIDTH, SCREENHEIGHT, ylookup, columnofs, dcvars, screen, I, BLURRY_MAP); } public void invoke() { int count; int dest, dest2; // Adjust borders. Low... if (dcvars.dc_yl == 0) dcvars.dc_yl = 1; // .. and high. if (dcvars.dc_yh == dcvars.viewheight - 1) dcvars.dc_yh = dcvars.viewheight - 2; count = dcvars.dc_yh - dcvars.dc_yl; // Zero length. if (count < 0) return; if (RANGECHECK) { performRangeCheck(); } // The idea is to draw more than one pixel at a time. dest = blockyDest1(); dest2 = blockyDest2(); // Looks like an attempt at dithering, // using the colormap #6 (of 0-31, a bit // brighter than average). if (count > 4) do { // Lookup framebuffer, and retrieve // a pixel that is either one column // left or right of the current one. // Add index from colormap to index. screen[dest] = BLURRY_MAP[0x00FF & screen[dest + fuzzoffset[fuzzpos]]]; screen[dest2] = screen[dest]; // Ironically, "low detail" fuzziness was not really // low-detail, // as it normally did full-precision calculations. // BLURRY_MAP[0x00FF & screen[dest2+ fuzzoffset[fuzzpos]]]; // Clamp table lookup index. if (++fuzzpos == FUZZTABLE) fuzzpos = 0; dest += SCREENWIDTH; dest2 += SCREENWIDTH; screen[dest] = BLURRY_MAP[0x00FF & screen[dest + fuzzoffset[fuzzpos]]]; screen[dest2] = screen[dest]; if (++fuzzpos == FUZZTABLE) fuzzpos = 0; dest += SCREENWIDTH; dest2 += SCREENWIDTH; screen[dest] = BLURRY_MAP[0x00FF & screen[dest + fuzzoffset[fuzzpos]]]; screen[dest2] = screen[dest]; if (++fuzzpos == FUZZTABLE) fuzzpos = 0; dest += SCREENWIDTH; dest2 += SCREENWIDTH; screen[dest] = BLURRY_MAP[0x00FF & screen[dest + fuzzoffset[fuzzpos]]]; screen[dest2] = screen[dest]; if (++fuzzpos == FUZZTABLE) fuzzpos = 0; dest += SCREENWIDTH; dest2 += SCREENWIDTH; } while ((count -= 4) > 4); if (count > 0) do { screen[dest] = BLURRY_MAP[0x00FF & screen[dest + fuzzoffset[fuzzpos]]]; screen[dest2] = screen[dest]; if (++fuzzpos == FUZZTABLE) fuzzpos = 0; dest += SCREENWIDTH; dest2 += SCREENWIDTH; } while (count-- != 0); } } public static final class TrueColor extends R_DrawFuzzColumn<byte[], int[]> { public TrueColor(int SCREENWIDTH, int SCREENHEIGHT, int[] ylookup, int[] columnofs, ColVars<byte[], int[]> dcvars, int[] screen, IDoomSystem I) { super(SCREENWIDTH, SCREENHEIGHT, ylookup, columnofs, dcvars, screen, I); } public void invoke() { int count; int dest, dest2; // Adjust borders. Low... if (dcvars.dc_yl == 0) dcvars.dc_yl = 1; // .. and high. if (dcvars.dc_yh == dcvars.viewheight - 1) dcvars.dc_yh = dcvars.viewheight - 2; count = dcvars.dc_yh - dcvars.dc_yl; // Zero length. if (count < 0) return; if (RANGECHECK) { performRangeCheck(); } // The idea is to draw more than one pixel at a time. dest = blockyDest1(); dest2 = blockyDest2(); // Looks like an attempt at dithering, // using the colormap #6 (of 0-31, a bit // brighter than average). if (count > 4) do { // Lookup framebuffer, and retrieve // a pixel that is either one column // left or right of the current one. // Add index from colormap to index. screen[dest] = screen[dest + fuzzoffset[fuzzpos]]; screen[dest2] = screen[dest]; // Ironically, "low detail" fuzziness was not really // low-detail, // as it normally did full-precision calculations. // BLURRY_MAP[0x00FF & screen[dest2+ fuzzoffset[fuzzpos]]]; // Clamp table lookup index. if (++fuzzpos == FUZZTABLE) fuzzpos = 0; dest += SCREENWIDTH; dest2 += SCREENWIDTH; screen[dest] = fuzzMix(screen[dest + fuzzoffset[fuzzpos]]); screen[dest2] = screen[dest]; if (++fuzzpos == FUZZTABLE) fuzzpos = 0; dest += SCREENWIDTH; dest2 += SCREENWIDTH; screen[dest] = fuzzMix(screen[dest + fuzzoffset[fuzzpos]]); screen[dest2] = screen[dest]; if (++fuzzpos == FUZZTABLE) fuzzpos = 0; dest += SCREENWIDTH; dest2 += SCREENWIDTH; screen[dest] = fuzzMix(screen[dest + fuzzoffset[fuzzpos]]); screen[dest2] = screen[dest]; if (++fuzzpos == FUZZTABLE) fuzzpos = 0; dest += SCREENWIDTH; dest2 += SCREENWIDTH; } while ((count -= 4) > 4); if (count > 0) do { screen[dest] = fuzzMix(screen[dest + fuzzoffset[fuzzpos]]); screen[dest2] = screen[dest]; if (++fuzzpos == FUZZTABLE) fuzzpos = 0; dest += SCREENWIDTH; dest2 += SCREENWIDTH; } while (count-- != 0); } private final int fuzzMix(int rgb) { // super-fast half-brite trick // 3DEF and >> 1: ok hue, but too dark // FF7C7C7C, no shift: good compromise // FF707070, no shift: results in too obvious tinting. return (rgb>>1)&0xFF7F7F7F; } } } package rr.drawfuns; /** Either draws a column or a span * * @author velktron * */ public interface ColumnFunction<T,V> { public void invoke(); public void invoke(ColVars<T,V> dcvars); /** A set of flags that help identifying the type of function */ public int getFlags(); } package rr.drawfuns; import i.IDoomSystem; import static m.fixed_t.FRACBITS; /** * Adapted from Killough's Boom code. Specially optimized version assuming that * dc_source_ofs is always 0. This eliminates it from expressions. * * @author admin */ public abstract class R_DrawColumnBoomOpt<T, V> extends DoomColumnFunction<T, V> { public R_DrawColumnBoomOpt(int sCREENWIDTH, int sCREENHEIGHT, int[] ylookup, int[] columnofs, ColVars<T, V> dcvars, V screen, IDoomSystem I) { super(sCREENWIDTH, sCREENHEIGHT, ylookup, columnofs, dcvars, screen, I); } public static final class HiColor extends R_DrawColumnBoomOpt<byte[], short[]> { public HiColor(int sCREENWIDTH, int sCREENHEIGHT, int[] ylookup, int[] columnofs, ColVars<byte[], short[]> dcvars, short[] screen, IDoomSystem I) { super(sCREENWIDTH, sCREENHEIGHT, ylookup, columnofs, dcvars, screen, I); } public void invoke() { int count; int dest; // killough int frac; // killough final int fracstep; count = dcvars.dc_yh - dcvars.dc_yl + 1; if (count <= 0) // Zero length, column does not exceed a pixel. return; if (RANGECHECK) { performRangeCheck(); } // Framebuffer destination address. // Use ylookup LUT to avoid multiply with ScreenWidth. // Use columnofs LUT for subwindows? dest = computeScreenDest(); // Determine scaling, which is the only mapping to be done. fracstep = dcvars.dc_iscale; frac = dcvars.dc_texturemid + (dcvars.dc_yl - dcvars.centery) * fracstep; // Inner loop that does the actual texture mapping, // e.g. a DDA-lile scaling. // This is as fast as it gets. (Yeah, right!!! -- killough) // // killough 2/1/98: more performance tuning { final byte[] source = dcvars.dc_source; final short[] colormap = dcvars.dc_colormap; int heightmask = dcvars.dc_texheight - 1; if ((dcvars.dc_texheight & heightmask) != 0) // not a power of 2 // -- // killough { heightmask++; heightmask <<= FRACBITS; if (frac < 0) while ((frac += heightmask) < 0) ; else while (frac >= heightmask) frac -= heightmask; do { // Re-map color indices from wall texture column // using a lighting/special effects LUT. // heightmask is the Tutti-Frutti fix -- killough screen[dest] = colormap[0x00FF & source[((frac >> FRACBITS))]]; dest += SCREENWIDTH; if ((frac += fracstep) >= heightmask) frac -= heightmask; } while (--count > 0); } else { while (count >= 4) // texture height is a power of 2 -- // killough { // System.err.println(dest); screen[dest] = colormap[0x00FF & source[((frac >> FRACBITS) & heightmask)]]; dest += SCREENWIDTH; frac += fracstep; screen[dest] = colormap[0x00FF & source[((frac >> FRACBITS) & heightmask)]]; dest += SCREENWIDTH; frac += fracstep; screen[dest] = colormap[0x00FF & source[((frac >> FRACBITS) & heightmask)]]; dest += SCREENWIDTH; frac += fracstep; screen[dest] = colormap[0x00FF & source[((frac >> FRACBITS) & heightmask)]]; dest += SCREENWIDTH; frac += fracstep; count -= 4; } while (count > 0) { screen[dest] = colormap[0x00FF & source[((frac >> FRACBITS) & heightmask)]]; dest += SCREENWIDTH; frac += fracstep; count--; } } } } } public static final class Indexed extends R_DrawColumnBoomOpt<byte[], byte[]> { public Indexed(int sCREENWIDTH, int sCREENHEIGHT, int[] ylookup, int[] columnofs, ColVars<byte[], byte[]> dcvars, byte[] screen, IDoomSystem I) { super(sCREENWIDTH, sCREENHEIGHT, ylookup, columnofs, dcvars, screen, I); } public void invoke() { int count; int dest; // killough int frac; // killough final int fracstep; count = dcvars.dc_yh - dcvars.dc_yl + 1; if (count <= 0) // Zero length, column does not exceed a pixel. return; if (RANGECHECK) { performRangeCheck(); } // Framebuffer destination address. // Use ylookup LUT to avoid multiply with ScreenWidth. // Use columnofs LUT for subwindows? dest = computeScreenDest(); // Determine scaling, which is the only mapping to be done. fracstep = dcvars.dc_iscale; frac = dcvars.dc_texturemid + (dcvars.dc_yl - dcvars.centery) * fracstep; // Inner loop that does the actual texture mapping, // e.g. a DDA-lile scaling. // This is as fast as it gets. (Yeah, right!!! -- killough) // // killough 2/1/98: more performance tuning { final byte[] source = dcvars.dc_source; final byte[] colormap = dcvars.dc_colormap; int heightmask = dcvars.dc_texheight - 1; if ((dcvars.dc_texheight & heightmask) != 0) // not a power of 2 // -- // killough { heightmask++; heightmask <<= FRACBITS; if (frac < 0) while ((frac += heightmask) < 0) ; else while (frac >= heightmask) frac -= heightmask; do { // Re-map color indices from wall texture column // using a lighting/special effects LUT. // heightmask is the Tutti-Frutti fix -- killough screen[dest] = colormap[0x00FF & source[((frac >> FRACBITS))]]; dest += SCREENWIDTH; if ((frac += fracstep) >= heightmask) frac -= heightmask; } while (--count > 0); } else { while (count >= 4) // texture height is a power of 2 -- // killough { // System.err.println(dest); screen[dest] = colormap[0x00FF & source[((frac >> FRACBITS) & heightmask)]]; dest += SCREENWIDTH; frac += fracstep; screen[dest] = colormap[0x00FF & source[((frac >> FRACBITS) & heightmask)]]; dest += SCREENWIDTH; frac += fracstep; screen[dest] = colormap[0x00FF & source[((frac >> FRACBITS) & heightmask)]]; dest += SCREENWIDTH; frac += fracstep; screen[dest] = colormap[0x00FF & source[((frac >> FRACBITS) & heightmask)]]; dest += SCREENWIDTH; frac += fracstep; count -= 4; } while (count > 0) { screen[dest] = colormap[0x00FF & source[((frac >> FRACBITS) & heightmask)]]; dest += SCREENWIDTH; frac += fracstep; count--; } } } } } public static final class TrueColor extends R_DrawColumnBoomOpt<byte[], int[]> { public TrueColor(int sCREENWIDTH, int sCREENHEIGHT, int[] ylookup, int[] columnofs, ColVars<byte[], int[]> dcvars, int[] screen, IDoomSystem I) { super(sCREENWIDTH, sCREENHEIGHT, ylookup, columnofs, dcvars, screen, I); } public void invoke() { int count; int dest; // killough int frac; // killough final int fracstep; count = dcvars.dc_yh - dcvars.dc_yl + 1; if (count <= 0) // Zero length, column does not exceed a pixel. return; if (RANGECHECK) { performRangeCheck(); } // Framebuffer destination address. // Use ylookup LUT to avoid multiply with ScreenWidth. // Use columnofs LUT for subwindows? dest = computeScreenDest(); // Determine scaling, which is the only mapping to be done. fracstep = dcvars.dc_iscale; frac = dcvars.dc_texturemid + (dcvars.dc_yl - dcvars.centery) * fracstep; // Inner loop that does the actual texture mapping, // e.g. a DDA-lile scaling. // This is as fast as it gets. (Yeah, right!!! -- killough) // // killough 2/1/98: more performance tuning { final byte[] source = dcvars.dc_source; final int[] colormap = dcvars.dc_colormap; int heightmask = dcvars.dc_texheight - 1; if ((dcvars.dc_texheight & heightmask) != 0) // not a power of 2 // -- // killough { heightmask++; heightmask <<= FRACBITS; if (frac < 0) while ((frac += heightmask) < 0) ; else while (frac >= heightmask) frac -= heightmask; do { // Re-map color indices from wall texture column // using a lighting/special effects LUT. // heightmask is the Tutti-Frutti fix -- killough screen[dest] = colormap[0x00FF & source[((frac >> FRACBITS))]]; dest += SCREENWIDTH; if ((frac += fracstep) >= heightmask) frac -= heightmask; } while (--count > 0); } else { while (count >= 4) // texture height is a power of 2 -- // killough { // System.err.println(dest); screen[dest] = colormap[0x00FF & source[((frac >> FRACBITS) & heightmask)]]; dest += SCREENWIDTH; frac += fracstep; screen[dest] = colormap[0x00FF & source[((frac >> FRACBITS) & heightmask)]]; dest += SCREENWIDTH; frac += fracstep; screen[dest] = colormap[0x00FF & source[((frac >> FRACBITS) & heightmask)]]; dest += SCREENWIDTH; frac += fracstep; screen[dest] = colormap[0x00FF & source[((frac >> FRACBITS) & heightmask)]]; dest += SCREENWIDTH; frac += fracstep; count -= 4; } while (count > 0) { screen[dest] = colormap[0x00FF & source[((frac >> FRACBITS) & heightmask)]]; dest += SCREENWIDTH; frac += fracstep; count--; } } } } } } package rr.drawfuns; import i.IDoomSystem; import static m.fixed_t.FRACBITS; /** * Adapted from Killough's Boom code. Low-detail variation, with DC SOURCE * optimization. * * @author admin * */ public abstract class R_DrawColumnBoomOptLow<T,V> extends DoomColumnFunction<T,V> { public R_DrawColumnBoomOptLow(int SCREENWIDTH, int SCREENHEIGHT, int[] ylookup, int[] columnofs, ColVars<T,V> dcvars, V screen, IDoomSystem I) { super(SCREENWIDTH, SCREENHEIGHT, ylookup, columnofs, dcvars, screen, I); this.flags=DcFlags.LOW_DETAIL; } public static final class HiColor extends R_DrawColumnBoomOptLow<byte[],short[]>{ public HiColor(int SCREENWIDTH, int SCREENHEIGHT, int[] ylookup, int[] columnofs, ColVars<byte[], short[]> dcvars, short[] screen, IDoomSystem I) { super(SCREENWIDTH, SCREENHEIGHT, ylookup, columnofs, dcvars, screen, I); } public void invoke() { int count; int dest,dest2; // killough int frac; // killough final int fracstep; count = dcvars.dc_yh - dcvars.dc_yl + 1; // Assumed to be always zero for optimized draws. //dc_source_ofs=dcvars.dc_source_ofs; if (count <= 0) // Zero length, column does not exceed a pixel. return; if (RANGECHECK) { performRangeCheck(); } // Framebuffer destination address. // Use ylookup LUT to avoid multiply with ScreenWidth. // Use columnofs LUT for subwindows? dest = blockyDest1(); dest2 = blockyDest2(); // Determine scaling, which is the only mapping to be done. fracstep = dcvars.dc_iscale; frac = dcvars.dc_texturemid + (dcvars.dc_yl - dcvars.centery) * fracstep; // Inner loop that does the actual texture mapping, // e.g. a DDA-lile scaling. // This is as fast as it gets. (Yeah, right!!! -- killough) // // killough 2/1/98: more performance tuning { final byte[] source = dcvars.dc_source; final short[] colormap = dcvars.dc_colormap; int heightmask = dcvars.dc_texheight - 1; if ((dcvars.dc_texheight & heightmask) != 0) // not a power of 2 -- // killough { heightmask++; heightmask <<= FRACBITS; if (frac < 0) while ((frac += heightmask) < 0) ; else while (frac >= heightmask) frac -= heightmask; do { // Re-map color indices from wall texture column // using a lighting/special effects LUT. // heightmask is the Tutti-Frutti fix -- killough screen[dest] = screen[dest2]=colormap[0x00FF & source[((frac >> FRACBITS))]]; dest += SCREENWIDTH; dest2 += SCREENWIDTH; if ((frac += fracstep) >= heightmask) frac -= heightmask; } while (--count > 0); } else { while (count >= 4) // texture height is a power of 2 -- // killough { screen[dest] = screen[dest2]= colormap[0x00FF & source[((frac >> FRACBITS) & heightmask)]]; dest += SCREENWIDTH; dest2 += SCREENWIDTH; frac += fracstep; screen[dest] = screen[dest2]= colormap[0x00FF & source[((frac >> FRACBITS) & heightmask)]]; dest += SCREENWIDTH; dest2 += SCREENWIDTH; frac += fracstep; screen[dest] = screen[dest2]= colormap[0x00FF & source[((frac >> FRACBITS) & heightmask)]]; dest += SCREENWIDTH; dest2 += SCREENWIDTH; frac += fracstep; screen[dest] = screen[dest2]= colormap[0x00FF & source[((frac >> FRACBITS) & heightmask)]]; dest += SCREENWIDTH; dest2 += SCREENWIDTH; frac += fracstep; count -= 4; } while (count > 0) { screen[dest] = screen[dest2]= colormap[0x00FF & source[((frac >> FRACBITS) & heightmask)]]; dest += SCREENWIDTH; dest2 += SCREENWIDTH; frac += fracstep; count--; } } } } } public static final class Indexed extends R_DrawColumnBoomOptLow<byte[],byte[]>{ public Indexed(int SCREENWIDTH, int SCREENHEIGHT, int[] ylookup, int[] columnofs, ColVars<byte[], byte[]> dcvars, byte[] screen, IDoomSystem I) { super(SCREENWIDTH, SCREENHEIGHT, ylookup, columnofs, dcvars, screen, I); } public void invoke() { int count; int dest,dest2; // killough int frac; // killough final int fracstep; count = dcvars.dc_yh - dcvars.dc_yl + 1; // Assumed to be always zero for optimized draws. //dc_source_ofs=dcvars.dc_source_ofs; if (count <= 0) // Zero length, column does not exceed a pixel. return; if (RANGECHECK) { performRangeCheck(); } // Framebuffer destination address. // Use ylookup LUT to avoid multiply with ScreenWidth. // Use columnofs LUT for subwindows? dest = blockyDest1(); dest2 = blockyDest2(); // Determine scaling, which is the only mapping to be done. fracstep = dcvars.dc_iscale; frac = dcvars.dc_texturemid + (dcvars.dc_yl - dcvars.centery) * fracstep; // Inner loop that does the actual texture mapping, // e.g. a DDA-lile scaling. // This is as fast as it gets. (Yeah, right!!! -- killough) // // killough 2/1/98: more performance tuning { final byte[] source = dcvars.dc_source; final byte[] colormap = dcvars.dc_colormap; int heightmask = dcvars.dc_texheight - 1; if ((dcvars.dc_texheight & heightmask) != 0) // not a power of 2 -- // killough { heightmask++; heightmask <<= FRACBITS; if (frac < 0) while ((frac += heightmask) < 0) ; else while (frac >= heightmask) frac -= heightmask; do { // Re-map color indices from wall texture column // using a lighting/special effects LUT. // heightmask is the Tutti-Frutti fix -- killough screen[dest] = screen[dest2]=colormap[0x00FF & source[((frac >> FRACBITS))]]; dest += SCREENWIDTH; dest2 += SCREENWIDTH; if ((frac += fracstep) >= heightmask) frac -= heightmask; } while (--count > 0); } else { while (count >= 4) // texture height is a power of 2 -- // killough { screen[dest] = screen[dest2]= colormap[0x00FF & source[((frac >> FRACBITS) & heightmask)]]; dest += SCREENWIDTH; dest2 += SCREENWIDTH; frac += fracstep; screen[dest] = screen[dest2]= colormap[0x00FF & source[((frac >> FRACBITS) & heightmask)]]; dest += SCREENWIDTH; dest2 += SCREENWIDTH; frac += fracstep; screen[dest] = screen[dest2]= colormap[0x00FF & source[((frac >> FRACBITS) & heightmask)]]; dest += SCREENWIDTH; dest2 += SCREENWIDTH; frac += fracstep; screen[dest] = screen[dest2]= colormap[0x00FF & source[((frac >> FRACBITS) & heightmask)]]; dest += SCREENWIDTH; dest2 += SCREENWIDTH; frac += fracstep; count -= 4; } while (count > 0) { screen[dest] = screen[dest2]= colormap[0x00FF & source[((frac >> FRACBITS) & heightmask)]]; dest += SCREENWIDTH; dest2 += SCREENWIDTH; frac += fracstep; count--; } } } } } public static final class TrueColor extends R_DrawColumnBoomOptLow<byte[],int[]>{ public TrueColor(int SCREENWIDTH, int SCREENHEIGHT, int[] ylookup, int[] columnofs, ColVars<byte[], int[]> dcvars, int[] screen, IDoomSystem I) { super(SCREENWIDTH, SCREENHEIGHT, ylookup, columnofs, dcvars, screen, I); } public void invoke() { int count; int dest,dest2; // killough int frac; // killough final int fracstep; count = dcvars.dc_yh - dcvars.dc_yl + 1; // Assumed to be always zero for optimized draws. //dc_source_ofs=dcvars.dc_source_ofs; if (count <= 0) // Zero length, column does not exceed a pixel. return; if (RANGECHECK) { performRangeCheck(); } // Framebuffer destination address. // Use ylookup LUT to avoid multiply with ScreenWidth. // Use columnofs LUT for subwindows? dest = blockyDest1(); dest2 = blockyDest2(); // Determine scaling, which is the only mapping to be done. fracstep = dcvars.dc_iscale; frac = dcvars.dc_texturemid + (dcvars.dc_yl - dcvars.centery) * fracstep; // Inner loop that does the actual texture mapping, // e.g. a DDA-lile scaling. // This is as fast as it gets. (Yeah, right!!! -- killough) // // killough 2/1/98: more performance tuning { final byte[] source = dcvars.dc_source; final int[] colormap = dcvars.dc_colormap; int heightmask = dcvars.dc_texheight - 1; if ((dcvars.dc_texheight & heightmask) != 0) // not a power of 2 -- // killough { heightmask++; heightmask <<= FRACBITS; if (frac < 0) while ((frac += heightmask) < 0) ; else while (frac >= heightmask) frac -= heightmask; do { // Re-map color indices from wall texture column // using a lighting/special effects LUT. // heightmask is the Tutti-Frutti fix -- killough screen[dest] = screen[dest2]=colormap[0x00FF & source[((frac >> FRACBITS))]]; dest += SCREENWIDTH; dest2 += SCREENWIDTH; if ((frac += fracstep) >= heightmask) frac -= heightmask; } while (--count > 0); } else { while (count >= 4) // texture height is a power of 2 -- // killough { screen[dest] = screen[dest2]= colormap[0x00FF & source[((frac >> FRACBITS) & heightmask)]]; dest += SCREENWIDTH; dest2 += SCREENWIDTH; frac += fracstep; screen[dest] = screen[dest2]= colormap[0x00FF & source[((frac >> FRACBITS) & heightmask)]]; dest += SCREENWIDTH; dest2 += SCREENWIDTH; frac += fracstep; screen[dest] = screen[dest2]= colormap[0x00FF & source[((frac >> FRACBITS) & heightmask)]]; dest += SCREENWIDTH; dest2 += SCREENWIDTH; frac += fracstep; screen[dest] = screen[dest2]= colormap[0x00FF & source[((frac >> FRACBITS) & heightmask)]]; dest += SCREENWIDTH; dest2 += SCREENWIDTH; frac += fracstep; count -= 4; } while (count > 0) { screen[dest] = screen[dest2]= colormap[0x00FF & source[((frac >> FRACBITS) & heightmask)]]; dest += SCREENWIDTH; dest2 += SCREENWIDTH; frac += fracstep; count--; } } } } } } package rr.drawfuns; import i.IDoomSystem; import static m.fixed_t.FRACBITS; public abstract class R_DrawTranslatedColumn<T, V> extends DoomColumnFunction<T, V> { public R_DrawTranslatedColumn(int SCREENWIDTH, int SCREENHEIGHT, int[] ylookup, int[] columnofs, ColVars<T, V> dcvars, V screen, IDoomSystem I) { super(SCREENWIDTH, SCREENHEIGHT, ylookup, columnofs, dcvars, screen, I); this.flags = DcFlags.TRANSLATED; } public static final class HiColor extends R_DrawTranslatedColumn<byte[], short[]> { public HiColor(int SCREENWIDTH, int SCREENHEIGHT, int[] ylookup, int[] columnofs, ColVars<byte[], short[]> dcvars, short[] screen, IDoomSystem I) { super(SCREENWIDTH, SCREENHEIGHT, ylookup, columnofs, dcvars, screen, I); } public void invoke() { int count; // MAES: you know the deal by now... int dest; int frac; final int fracstep; final int dc_source_ofs = dcvars.dc_source_ofs; final byte[] dc_source = dcvars.dc_source; final short[] dc_colormap = dcvars.dc_colormap; final byte[] dc_translation = dcvars.dc_translation; count = dcvars.dc_yh - dcvars.dc_yl; if (count < 0) return; if (RANGECHECK) { super.performRangeCheck(); } // WATCOM VGA specific. /* * Keep for fixing. if (detailshift) { if (dc_x & 1) outp * (SC_INDEX+1,12); else outp (SC_INDEX+1,3); dest = destview + * dc_yl*80 + (dc_x>>1); } else { outp (SC_INDEX+1,1<<(dc_x&3)); * dest = destview + dc_yl*80 + (dc_x>>2); } */ // FIXME. As above. dest = computeScreenDest(); // Looks familiar. fracstep = dcvars.dc_iscale; frac = dcvars.dc_texturemid + (dcvars.dc_yl - dcvars.centery) * fracstep; // Here we do an additional index re-mapping. // Maes: Unroll by 4 if (count >= 4) do { // Translation tables are used // to map certain colorramps to other ones, // used with PLAY sprites. // Thus the "green" ramp of the player 0 sprite // is mapped to gray, red, black/indigo. screen[dest] = dc_colormap[0x00FF & dc_translation[0xFF & dc_source[dc_source_ofs + (frac >> FRACBITS)]]]; dest += SCREENWIDTH; frac += fracstep; screen[dest] = dc_colormap[0x00FF & dc_translation[0xFF & dc_source[dc_source_ofs + (frac >> FRACBITS)]]]; dest += SCREENWIDTH; frac += fracstep; screen[dest] = dc_colormap[0x00FF & dc_translation[0xFF & dc_source[dc_source_ofs + (frac >> FRACBITS)]]]; dest += SCREENWIDTH; frac += fracstep; screen[dest] = dc_colormap[0x00FF & dc_translation[0xFF & dc_source[dc_source_ofs + (frac >> FRACBITS)]]]; dest += SCREENWIDTH; frac += fracstep; } while ((count -= 4) > 4); if (count > 0) do { // Translation tables are used // to map certain colorramps to other ones, // used with PLAY sprites. // Thus the "green" ramp of the player 0 sprite // is mapped to gray, red, black/indigo. screen[dest] = dc_colormap[0x00FF & dc_translation[0xFF & dc_source[dc_source_ofs + (frac >> FRACBITS)]]]; dest += SCREENWIDTH; frac += fracstep; } while (count-- != 0); } } public static final class Indexed extends R_DrawTranslatedColumn<byte[], byte[]> { public Indexed(int SCREENWIDTH, int SCREENHEIGHT, int[] ylookup, int[] columnofs, ColVars<byte[], byte[]> dcvars, byte[] screen, IDoomSystem I) { super(SCREENWIDTH, SCREENHEIGHT, ylookup, columnofs, dcvars, screen, I); } public void invoke() { int count; // MAES: you know the deal by now... int dest; int frac; final int fracstep; final int dc_source_ofs = dcvars.dc_source_ofs; final byte[] dc_source = dcvars.dc_source; final byte[] dc_colormap = dcvars.dc_colormap; final byte[] dc_translation = dcvars.dc_translation; count = dcvars.dc_yh - dcvars.dc_yl; if (count < 0) return; if (RANGECHECK) { super.performRangeCheck(); } // WATCOM VGA specific. /* * Keep for fixing. if (detailshift) { if (dc_x & 1) outp * (SC_INDEX+1,12); else outp (SC_INDEX+1,3); dest = destview + * dc_yl*80 + (dc_x>>1); } else { outp (SC_INDEX+1,1<<(dc_x&3)); * dest = destview + dc_yl*80 + (dc_x>>2); } */ // FIXME. As above. dest = computeScreenDest(); // Looks familiar. fracstep = dcvars.dc_iscale; frac = dcvars.dc_texturemid + (dcvars.dc_yl - dcvars.centery) * fracstep; // Here we do an additional index re-mapping. // Maes: Unroll by 4 if (count >= 4) do { // Translation tables are used // to map certain colorramps to other ones, // used with PLAY sprites. // Thus the "green" ramp of the player 0 sprite // is mapped to gray, red, black/indigo. screen[dest] = dc_colormap[0x00FF & dc_translation[0xFF & dc_source[dc_source_ofs + (frac >> FRACBITS)]]]; dest += SCREENWIDTH; frac += fracstep; screen[dest] = dc_colormap[0x00FF & dc_translation[0xFF & dc_source[dc_source_ofs + (frac >> FRACBITS)]]]; dest += SCREENWIDTH; frac += fracstep; screen[dest] = dc_colormap[0x00FF & dc_translation[0xFF & dc_source[dc_source_ofs + (frac >> FRACBITS)]]]; dest += SCREENWIDTH; frac += fracstep; screen[dest] = dc_colormap[0x00FF & dc_translation[0xFF & dc_source[dc_source_ofs + (frac >> FRACBITS)]]]; dest += SCREENWIDTH; frac += fracstep; } while ((count -= 4) > 4); if (count > 0) do { // Translation tables are used // to map certain colorramps to other ones, // used with PLAY sprites. // Thus the "green" ramp of the player 0 sprite // is mapped to gray, red, black/indigo. screen[dest] = dc_colormap[0x00FF & dc_translation[0xFF & dc_source[dc_source_ofs + (frac >> FRACBITS)]]]; dest += SCREENWIDTH; frac += fracstep; } while (count-- != 0); } } public static final class TrueColor extends R_DrawTranslatedColumn<byte[], int[]> { public TrueColor(int SCREENWIDTH, int SCREENHEIGHT, int[] ylookup, int[] columnofs, ColVars<byte[], int[]> dcvars, int[] screen, IDoomSystem I) { super(SCREENWIDTH, SCREENHEIGHT, ylookup, columnofs, dcvars, screen, I); } public void invoke() { int count; // MAES: you know the deal by now... int dest; int frac; final int fracstep; final int dc_source_ofs = dcvars.dc_source_ofs; final byte[] dc_source = dcvars.dc_source; final int[] dc_colormap = dcvars.dc_colormap; final byte[] dc_translation = dcvars.dc_translation; count = dcvars.dc_yh - dcvars.dc_yl; if (count < 0) return; if (RANGECHECK) { super.performRangeCheck(); } // WATCOM VGA specific. /* * Keep for fixing. if (detailshift) { if (dc_x & 1) outp * (SC_INDEX+1,12); else outp (SC_INDEX+1,3); dest = destview + * dc_yl*80 + (dc_x>>1); } else { outp (SC_INDEX+1,1<<(dc_x&3)); * dest = destview + dc_yl*80 + (dc_x>>2); } */ // FIXME. As above. dest = computeScreenDest(); // Looks familiar. fracstep = dcvars.dc_iscale; frac = dcvars.dc_texturemid + (dcvars.dc_yl - dcvars.centery) * fracstep; // Here we do an additional index re-mapping. // Maes: Unroll by 4 if (count >= 4) do { // Translation tables are used // to map certain colorramps to other ones, // used with PLAY sprites. // Thus the "green" ramp of the player 0 sprite // is mapped to gray, red, black/indigo. screen[dest] = dc_colormap[0x00FF & dc_translation[0xFF & dc_source[dc_source_ofs + (frac >> FRACBITS)]]]; dest += SCREENWIDTH; frac += fracstep; screen[dest] = dc_colormap[0x00FF & dc_translation[0xFF & dc_source[dc_source_ofs + (frac >> FRACBITS)]]]; dest += SCREENWIDTH; frac += fracstep; screen[dest] = dc_colormap[0x00FF & dc_translation[0xFF & dc_source[dc_source_ofs + (frac >> FRACBITS)]]]; dest += SCREENWIDTH; frac += fracstep; screen[dest] = dc_colormap[0x00FF & dc_translation[0xFF & dc_source[dc_source_ofs + (frac >> FRACBITS)]]]; dest += SCREENWIDTH; frac += fracstep; } while ((count -= 4) > 4); if (count > 0) do { // Translation tables are used // to map certain colorramps to other ones, // used with PLAY sprites. // Thus the "green" ramp of the player 0 sprite // is mapped to gray, red, black/indigo. screen[dest] = dc_colormap[0x00FF & dc_translation[0xFF & dc_source[dc_source_ofs + (frac >> FRACBITS)]]]; dest += SCREENWIDTH; frac += fracstep; } while (count-- != 0); } } } package rr.drawfuns; import i.IDoomSystem; import static m.fixed_t.FRACBITS; public abstract class R_DrawTranslatedColumnLow<T, V> extends DoomColumnFunction<T, V> { public R_DrawTranslatedColumnLow(int SCREENWIDTH, int SCREENHEIGHT, int[] ylookup, int[] columnofs, ColVars<T, V> dcvars, V screen, IDoomSystem I) { super(SCREENWIDTH, SCREENHEIGHT, ylookup, columnofs, dcvars, screen, I); this.flags = DcFlags.TRANSLATED | DcFlags.LOW_DETAIL; } public static final class HiColor extends R_DrawTranslatedColumnLow<byte[], short[]> { public HiColor(int SCREENWIDTH, int SCREENHEIGHT, int[] ylookup, int[] columnofs, ColVars<byte[], short[]> dcvars, short[] screen, IDoomSystem I) { super(SCREENWIDTH, SCREENHEIGHT, ylookup, columnofs, dcvars, screen, I); } public void invoke() { int count; // MAES: you know the deal by now... int dest, dest2; int frac; final int fracstep; final int dc_source_ofs = dcvars.dc_source_ofs; final byte[] dc_source = dcvars.dc_source; final short[] dc_colormap = dcvars.dc_colormap; final byte[] dc_translation = dcvars.dc_translation; count = dcvars.dc_yh - dcvars.dc_yl; if (count < 0) return; if (RANGECHECK) { super.performRangeCheck(); } // The idea is to draw more than one pixel at a time. dest = blockyDest1(); dest2 = blockyDest2(); // Looks familiar. fracstep = dcvars.dc_iscale; frac = dcvars.dc_texturemid + (dcvars.dc_yl - dcvars.centery) * fracstep; // Here we do an additional index re-mapping. // Maes: Unroll by 4 if (count >= 4) do { // Translation tables are used // to map certain colorramps to other ones, // used with PLAY sprites. // Thus the "green" ramp of the player 0 sprite // is mapped to gray, red, black/indigo. screen[dest] = screen[dest2] = dc_colormap[0x00FF & dc_translation[0xFF & dc_source[dc_source_ofs + (frac >> FRACBITS)]]]; dest += SCREENWIDTH; dest2 += SCREENWIDTH; frac += fracstep; screen[dest] = screen[dest2] = dc_colormap[0x00FF & dc_translation[0xFF & dc_source[dc_source_ofs + (frac >> FRACBITS)]]]; dest += SCREENWIDTH; dest2 += SCREENWIDTH; frac += fracstep; screen[dest] = screen[dest2] = dc_colormap[0x00FF & dc_translation[0xFF & dc_source[dc_source_ofs + (frac >> FRACBITS)]]]; dest += SCREENWIDTH; dest2 += SCREENWIDTH; frac += fracstep; screen[dest] = screen[dest2] = dc_colormap[0x00FF & dc_translation[0xFF & dc_source[dc_source_ofs + (frac >> FRACBITS)]]]; dest += SCREENWIDTH; dest2 += SCREENWIDTH; frac += fracstep; } while ((count -= 4) > 4); if (count > 0) do { // Translation tables are used // to map certain colorramps to other ones, // used with PLAY sprites. // Thus the "green" ramp of the player 0 sprite // is mapped to gray, red, black/indigo. screen[dest] = screen[dest2] = dc_colormap[0x00FF & dc_translation[0xFF & dc_source[dc_source_ofs + (frac >> FRACBITS)]]]; dest += SCREENWIDTH; dest2 += SCREENWIDTH; frac += fracstep; } while (count-- != 0); } } public static final class Indexed extends R_DrawTranslatedColumnLow<byte[], byte[]> { public Indexed(int SCREENWIDTH, int SCREENHEIGHT, int[] ylookup, int[] columnofs, ColVars<byte[], byte[]> dcvars, byte[] screen, IDoomSystem I) { super(SCREENWIDTH, SCREENHEIGHT, ylookup, columnofs, dcvars, screen, I); } public void invoke() { int count; // MAES: you know the deal by now... int dest, dest2; int frac; final int fracstep; final int dc_source_ofs = dcvars.dc_source_ofs; final byte[] dc_source = dcvars.dc_source; final byte[] dc_colormap = dcvars.dc_colormap; final byte[] dc_translation = dcvars.dc_translation; count = dcvars.dc_yh - dcvars.dc_yl; if (count < 0) return; if (RANGECHECK) { super.performRangeCheck(); } // The idea is to draw more than one pixel at a time. dest = blockyDest1(); dest2 = blockyDest2(); // Looks familiar. fracstep = dcvars.dc_iscale; frac = dcvars.dc_texturemid + (dcvars.dc_yl - dcvars.centery) * fracstep; // Here we do an additional index re-mapping. // Maes: Unroll by 4 if (count >= 4) do { // Translation tables are used // to map certain colorramps to other ones, // used with PLAY sprites. // Thus the "green" ramp of the player 0 sprite // is mapped to gray, red, black/indigo. screen[dest] = screen[dest2] = dc_colormap[0x00FF & dc_translation[0xFF & dc_source[dc_source_ofs + (frac >> FRACBITS)]]]; dest += SCREENWIDTH; dest2 += SCREENWIDTH; frac += fracstep; screen[dest] = screen[dest2] = dc_colormap[0x00FF & dc_translation[0xFF & dc_source[dc_source_ofs + (frac >> FRACBITS)]]]; dest += SCREENWIDTH; dest2 += SCREENWIDTH; frac += fracstep; screen[dest] = screen[dest2] = dc_colormap[0x00FF & dc_translation[0xFF & dc_source[dc_source_ofs + (frac >> FRACBITS)]]]; dest += SCREENWIDTH; dest2 += SCREENWIDTH; frac += fracstep; screen[dest] = screen[dest2] = dc_colormap[0x00FF & dc_translation[0xFF & dc_source[dc_source_ofs + (frac >> FRACBITS)]]]; dest += SCREENWIDTH; dest2 += SCREENWIDTH; frac += fracstep; } while ((count -= 4) > 4); if (count > 0) do { // Translation tables are used // to map certain colorramps to other ones, // used with PLAY sprites. // Thus the "green" ramp of the player 0 sprite // is mapped to gray, red, black/indigo. screen[dest] = screen[dest2] = dc_colormap[0x00FF & dc_translation[0xFF & dc_source[dc_source_ofs + (frac >> FRACBITS)]]]; dest += SCREENWIDTH; dest2 += SCREENWIDTH; frac += fracstep; } while (count-- != 0); } } public static final class TrueColor extends R_DrawTranslatedColumnLow<byte[], int[]> { public TrueColor(int SCREENWIDTH, int SCREENHEIGHT, int[] ylookup, int[] columnofs, ColVars<byte[], int[]> dcvars, int[] screen, IDoomSystem I) { super(SCREENWIDTH, SCREENHEIGHT, ylookup, columnofs, dcvars, screen, I); } public void invoke() { int count; // MAES: you know the deal by now... int dest, dest2; int frac; final int fracstep; final int dc_source_ofs = dcvars.dc_source_ofs; final byte[] dc_source = dcvars.dc_source; final int[] dc_colormap = dcvars.dc_colormap; final byte[] dc_translation = dcvars.dc_translation; count = dcvars.dc_yh - dcvars.dc_yl; if (count < 0) return; if (RANGECHECK) { super.performRangeCheck(); } // The idea is to draw more than one pixel at a time. dest = blockyDest1(); dest2 = blockyDest2(); // Looks familiar. fracstep = dcvars.dc_iscale; frac = dcvars.dc_texturemid + (dcvars.dc_yl - dcvars.centery) * fracstep; // Here we do an additional index re-mapping. // Maes: Unroll by 4 if (count >= 4) do { // Translation tables are used // to map certain colorramps to other ones, // used with PLAY sprites. // Thus the "green" ramp of the player 0 sprite // is mapped to gray, red, black/indigo. screen[dest] = screen[dest2] = dc_colormap[0x00FF & dc_translation[0xFF & dc_source[dc_source_ofs + (frac >> FRACBITS)]]]; dest += SCREENWIDTH; dest2 += SCREENWIDTH; frac += fracstep; screen[dest] = screen[dest2] = dc_colormap[0x00FF & dc_translation[0xFF & dc_source[dc_source_ofs + (frac >> FRACBITS)]]]; dest += SCREENWIDTH; dest2 += SCREENWIDTH; frac += fracstep; screen[dest] = screen[dest2] = dc_colormap[0x00FF & dc_translation[0xFF & dc_source[dc_source_ofs + (frac >> FRACBITS)]]]; dest += SCREENWIDTH; dest2 += SCREENWIDTH; frac += fracstep; screen[dest] = screen[dest2] = dc_colormap[0x00FF & dc_translation[0xFF & dc_source[dc_source_ofs + (frac >> FRACBITS)]]]; dest += SCREENWIDTH; dest2 += SCREENWIDTH; frac += fracstep; } while ((count -= 4) > 4); if (count > 0) do { // Translation tables are used // to map certain colorramps to other ones, // used with PLAY sprites. // Thus the "green" ramp of the player 0 sprite // is mapped to gray, red, black/indigo. screen[dest] = screen[dest2] = dc_colormap[0x00FF & dc_translation[0xFF & dc_source[dc_source_ofs + (frac >> FRACBITS)]]]; dest += SCREENWIDTH; dest2 += SCREENWIDTH; frac += fracstep; } while (count-- != 0); } } } package rr.drawfuns; /** Flags used to mark column functions according to type, * for quick type identification. * * @author velktron * */ public final class DcFlags { public static final int FUZZY=0x1; public static final int TRANSLATED=0x2; public static final int TRANSPARENT=0x4; public static final int LOW_DETAIL=0x8; } package rr.drawfuns; import i.IDoomSystem; public abstract class DoomSpanFunction<T,V> implements SpanFunction<T,V> { protected final boolean RANGECHECK=false; protected final int SCREENWIDTH; protected final int SCREENHEIGHT; protected SpanVars<T,V> dsvars; protected final int[] ylookup; protected final int[] columnofs; protected final V screen; protected final IDoomSystem I; public DoomSpanFunction(int sCREENWIDTH, int sCREENHEIGHT, int[] ylookup, int[] columnofs,SpanVars<T,V> dsvars, V screen,IDoomSystem I) { SCREENWIDTH = sCREENWIDTH; SCREENHEIGHT = sCREENHEIGHT; this.ylookup=ylookup; this.columnofs=columnofs; this.dsvars = dsvars; this.screen = screen; this.I=I; } protected final void doRangeCheck(){ if (dsvars.ds_x2 < dsvars.ds_x1 || dsvars.ds_x1 < 0 || dsvars.ds_x2 >= SCREENWIDTH || dsvars.ds_y > SCREENHEIGHT) { I.Error("R_DrawSpan: %d to %d at %d", dsvars.ds_x1, dsvars.ds_x2, dsvars.ds_y); } } @Override public final void invoke(SpanVars<T,V> dsvars) { this.dsvars=dsvars; invoke(); } } package rr.drawfuns; import static m.fixed_t.FRACBITS; import i.IDoomSystem; /** * Adapted from Killough's Boom code. There are optimized as well as low-detail * versions of it. * * @author admin */ public abstract class R_DrawColumnBoom<T, V> extends DoomColumnFunction<T, V> { public R_DrawColumnBoom(int SCREENWIDTH, int SCREENHEIGHT, int[] ylookup, int[] columnofs, ColVars<T, V> dcvars, V screen, IDoomSystem I) { super(SCREENWIDTH, SCREENHEIGHT, ylookup, columnofs, dcvars, screen, I); } public static final class HiColor extends R_DrawColumnBoom<byte[], short[]> { public HiColor(int SCREENWIDTH, int SCREENHEIGHT, int[] ylookup, int[] columnofs, ColVars<byte[], short[]> dcvars, short[] screen, IDoomSystem I) { super(SCREENWIDTH, SCREENHEIGHT, ylookup, columnofs, dcvars, screen, I); } public void invoke() { int count; int dest; // killough int frac; // killough int fracstep; final int dc_source_ofs = dcvars.dc_source_ofs; count = dcvars.dc_yh - dcvars.dc_yl + 1; if (count <= 0) // Zero length, column does not exceed a pixel. return; if (RANGECHECK) { performRangeCheck(); } dest = computeScreenDest(); // Determine scaling, which is the only mapping to be done. fracstep = dcvars.dc_iscale; frac = dcvars.dc_texturemid + (dcvars.dc_yl - dcvars.centery) * fracstep; // Inner loop that does the actual texture mapping, // e.g. a DDA-lile scaling. // This is as fast as it gets. (Yeah, right!!! -- killough) // // killough 2/1/98: more performance tuning { final byte[] source = dcvars.dc_source; final short[] colormap = dcvars.dc_colormap; int heightmask = dcvars.dc_texheight - 1; if ((dcvars.dc_texheight & heightmask) != 0) // not a power of 2 // -- // killough { heightmask++; heightmask <<= FRACBITS; if (frac < 0) while ((frac += heightmask) < 0) ; else while (frac >= heightmask) frac -= heightmask; do { // Re-map color indices from wall texture column // using a lighting/special effects LUT. // heightmask is the Tutti-Frutti fix -- killoughdcvars screen[dest] = colormap[0x00FF & source[((frac >> FRACBITS))]]; dest += SCREENWIDTH; if ((frac += fracstep) >= heightmask) frac -= heightmask; } while (--count > 0); } else { while (count >= 4) // texture height is a power of 2 -- // killough { // System.err.println(dest); screen[dest] = colormap[0x00FF & source[dc_source_ofs + ((frac >> FRACBITS) & heightmask)]]; dest += SCREENWIDTH; frac += fracstep; screen[dest] = colormap[0x00FF & source[dc_source_ofs + ((frac >> FRACBITS) & heightmask)]]; dest += SCREENWIDTH; frac += fracstep; screen[dest] = colormap[0x00FF & source[dc_source_ofs + ((frac >> FRACBITS) & heightmask)]]; dest += SCREENWIDTH; frac += fracstep; screen[dest] = colormap[0x00FF & source[dc_source_ofs + ((frac >> FRACBITS) & heightmask)]]; dest += SCREENWIDTH; frac += fracstep; count -= 4; } while (count > 0) { try { screen[dest] = colormap[0x00FF & source[dc_source_ofs + ((frac >> FRACBITS) & heightmask)]]; } catch (Exception e) { System.err.printf("%s %s %x %x %x\n", colormap, source, dc_source_ofs, frac, heightmask); } dest += SCREENWIDTH; frac += fracstep; count--; } } } } } public static final class Indexed extends R_DrawColumnBoom<byte[], byte[]> { public Indexed(int SCREENWIDTH, int SCREENHEIGHT, int[] ylookup, int[] columnofs, ColVars<byte[], byte[]> dcvars, byte[] screen, IDoomSystem I) { super(SCREENWIDTH, SCREENHEIGHT, ylookup, columnofs, dcvars, screen, I); } public void invoke() { int count; int dest; // killough int frac; // killough int fracstep; final int dc_source_ofs = dcvars.dc_source_ofs; count = dcvars.dc_yh - dcvars.dc_yl + 1; if (count <= 0) // Zero length, column does not exceed a pixel. return; if (RANGECHECK) { performRangeCheck(); } dest = computeScreenDest(); // Determine scaling, which is the only mapping to be done. fracstep = dcvars.dc_iscale; frac = dcvars.dc_texturemid + (dcvars.dc_yl - dcvars.centery) * fracstep; // Inner loop that does the actual texture mapping, // e.g. a DDA-lile scaling. // This is as fast as it gets. (Yeah, right!!! -- killough) // // killough 2/1/98: more performance tuning { final byte[] source = dcvars.dc_source; final byte[] colormap = dcvars.dc_colormap; int heightmask = dcvars.dc_texheight - 1; if ((dcvars.dc_texheight & heightmask) != 0) // not a power of 2 // -- // killough { heightmask++; heightmask <<= FRACBITS; if (frac < 0) while ((frac += heightmask) < 0) ; else while (frac >= heightmask) frac -= heightmask; do { // Re-map color indices from wall texture column // using a lighting/special effects LUT. // heightmask is the Tutti-Frutti fix -- killoughdcvars screen[dest] = colormap[0x00FF & source[((frac >> FRACBITS))]]; dest += SCREENWIDTH; if ((frac += fracstep) >= heightmask) frac -= heightmask; } while (--count > 0); } else { while (count >= 4) // texture height is a power of 2 -- // killough { // System.err.println(dest); screen[dest] = colormap[0x00FF & source[dc_source_ofs + ((frac >> FRACBITS) & heightmask)]]; dest += SCREENWIDTH; frac += fracstep; screen[dest] = colormap[0x00FF & source[dc_source_ofs + ((frac >> FRACBITS) & heightmask)]]; dest += SCREENWIDTH; frac += fracstep; screen[dest] = colormap[0x00FF & source[dc_source_ofs + ((frac >> FRACBITS) & heightmask)]]; dest += SCREENWIDTH; frac += fracstep; screen[dest] = colormap[0x00FF & source[dc_source_ofs + ((frac >> FRACBITS) & heightmask)]]; dest += SCREENWIDTH; frac += fracstep; count -= 4; } while (count > 0) { try { screen[dest] = colormap[0x00FF & source[dc_source_ofs + ((frac >> FRACBITS) & heightmask)]]; } catch (Exception e) { System.err.printf("%s %s %x %x %x\n", colormap, source, dc_source_ofs, frac, heightmask); } dest += SCREENWIDTH; frac += fracstep; count--; } } } } } public static final class TrueColor extends R_DrawColumnBoom<byte[], int[]> { public TrueColor(int SCREENWIDTH, int SCREENHEIGHT, int[] ylookup, int[] columnofs, ColVars<byte[], int[]> dcvars, int[] screen, IDoomSystem I) { super(SCREENWIDTH, SCREENHEIGHT, ylookup, columnofs, dcvars, screen, I); } public void invoke() { int count; int dest; // killough int frac; // killough int fracstep; final int dc_source_ofs = dcvars.dc_source_ofs; count = dcvars.dc_yh - dcvars.dc_yl + 1; if (count <= 0) // Zero length, column does not exceed a pixel. return; if (RANGECHECK) { performRangeCheck(); } dest = computeScreenDest(); // Determine scaling, which is the only mapping to be done. fracstep = dcvars.dc_iscale; frac = dcvars.dc_texturemid + (dcvars.dc_yl - dcvars.centery) * fracstep; // Inner loop that does the actual texture mapping, // e.g. a DDA-lile scaling. // This is as fast as it gets. (Yeah, right!!! -- killough) // // killough 2/1/98: more performance tuning { final byte[] source = dcvars.dc_source; final int[] colormap = dcvars.dc_colormap; int heightmask = dcvars.dc_texheight - 1; if ((dcvars.dc_texheight & heightmask) != 0) // not a power of 2 // -- // killough { heightmask++; heightmask <<= FRACBITS; if (frac < 0) while ((frac += heightmask) < 0) ; else while (frac >= heightmask) frac -= heightmask; do { // Re-map color indices from wall texture column // using a lighting/special effects LUT. // heightmask is the Tutti-Frutti fix -- killoughdcvars screen[dest] = colormap[0x00FF & source[((frac >> FRACBITS))]]; dest += SCREENWIDTH; if ((frac += fracstep) >= heightmask) frac -= heightmask; } while (--count > 0); } else { while (count >= 4) // texture height is a power of 2 -- // killough { // System.err.println(dest); screen[dest] = colormap[0x00FF & source[dc_source_ofs + ((frac >> FRACBITS) & heightmask)]]; dest += SCREENWIDTH; frac += fracstep; screen[dest] = colormap[0x00FF & source[dc_source_ofs + ((frac >> FRACBITS) & heightmask)]]; dest += SCREENWIDTH; frac += fracstep; screen[dest] = colormap[0x00FF & source[dc_source_ofs + ((frac >> FRACBITS) & heightmask)]]; dest += SCREENWIDTH; frac += fracstep; screen[dest] = colormap[0x00FF & source[dc_source_ofs + ((frac >> FRACBITS) & heightmask)]]; dest += SCREENWIDTH; frac += fracstep; count -= 4; } while (count > 0) { try { screen[dest] = colormap[0x00FF & source[dc_source_ofs + ((frac >> FRACBITS) & heightmask)]]; } catch (Exception e) { System.err.printf("%s %s %x %x %x\n", colormap, source, dc_source_ofs, frac, heightmask); } dest += SCREENWIDTH; frac += fracstep; count--; } } } } } } package rr.drawfuns; import i.IDoomSystem; public abstract class R_DrawSpanLow<T, V> extends DoomSpanFunction<T, V> { public R_DrawSpanLow(int SCREENWIDTH, int SCREENHEIGHT, int[] ylookup, int[] columnofs, SpanVars<T, V> dsvars, V screen, IDoomSystem I) { super(SCREENWIDTH, SCREENHEIGHT, ylookup, columnofs, dsvars, screen, I); } public static final class HiColor extends R_DrawSpanLow<byte[], short[]> { public HiColor(int SCREENWIDTH, int SCREENHEIGHT, int[] ylookup, int[] columnofs, SpanVars<byte[], short[]> dsvars, short[] screen, IDoomSystem I) { super(SCREENWIDTH, SCREENHEIGHT, ylookup, columnofs, dsvars, screen, I); } @Override public void invoke() { final byte[] ds_source = dsvars.ds_source; final short[] ds_colormap = dsvars.ds_colormap; final int ds_xstep = dsvars.ds_xstep; final int ds_ystep = dsvars.ds_ystep; int f_xfrac = dsvars.ds_xfrac; int f_yfrac = dsvars.ds_yfrac; int dest; int count; int spot; if (RANGECHECK) { doRangeCheck(); // dscount++; } // MAES: count must be performed before shifting. count = dsvars.ds_x2 - dsvars.ds_x1; // Blocky mode, need to multiply by 2. dsvars.ds_x1 <<= 1; dsvars.ds_x2 <<= 1; dest = ylookup[dsvars.ds_y] + columnofs[dsvars.ds_x1]; do { spot = ((f_yfrac >> (16 - 6)) & (63 * 64)) + ((f_xfrac >> 16) & 63); // Lowres/blocky mode does it twice, // while scale is adjusted appropriately. screen[dest++] = ds_colormap[0x00FF & ds_source[spot]]; screen[dest++] = ds_colormap[0x00FF & ds_source[spot]]; f_xfrac += ds_xstep; f_yfrac += ds_ystep; } while (count-- > 0); } } public static final class Indexed extends R_DrawSpanLow<byte[], byte[]> { public Indexed(int SCREENWIDTH, int SCREENHEIGHT, int[] ylookup, int[] columnofs, SpanVars<byte[], byte[]> dsvars, byte[] screen, IDoomSystem I) { super(SCREENWIDTH, SCREENHEIGHT, ylookup, columnofs, dsvars, screen, I); } public void invoke() { final byte[] ds_source = dsvars.ds_source; final byte[] ds_colormap = dsvars.ds_colormap; final int ds_xstep = dsvars.ds_xstep; final int ds_ystep = dsvars.ds_ystep; int f_xfrac = dsvars.ds_xfrac; int f_yfrac = dsvars.ds_yfrac; int dest; int count; int spot; if (RANGECHECK) { doRangeCheck(); // dscount++; } // MAES: count must be performed before shifting. count = dsvars.ds_x2 - dsvars.ds_x1; // Blocky mode, need to multiply by 2. dsvars.ds_x1 <<= 1; dsvars.ds_x2 <<= 1; dest = ylookup[dsvars.ds_y] + columnofs[dsvars.ds_x1]; do { spot = ((f_yfrac >> (16 - 6)) & (63 * 64)) + ((f_xfrac >> 16) & 63); // Lowres/blocky mode does it twice, // while scale is adjusted appropriately. screen[dest++] = ds_colormap[0x00FF & ds_source[spot]]; screen[dest++] = ds_colormap[0x00FF & ds_source[spot]]; f_xfrac += ds_xstep; f_yfrac += ds_ystep; } while (count-- > 0); } } public static final class TrueColor extends R_DrawSpanLow<byte[], int[]> { public TrueColor(int SCREENWIDTH, int SCREENHEIGHT, int[] ylookup, int[] columnofs, SpanVars<byte[], int[]> dsvars, int[] screen, IDoomSystem I) { super(SCREENWIDTH, SCREENHEIGHT, ylookup, columnofs, dsvars, screen, I); } @Override public void invoke() { final byte[] ds_source = dsvars.ds_source; final int[] ds_colormap = dsvars.ds_colormap; final int ds_xstep = dsvars.ds_xstep; final int ds_ystep = dsvars.ds_ystep; int f_xfrac = dsvars.ds_xfrac; int f_yfrac = dsvars.ds_yfrac; int dest; int count; int spot; if (RANGECHECK) { doRangeCheck(); // dscount++; } // MAES: count must be performed before shifting. count = dsvars.ds_x2 - dsvars.ds_x1; // Blocky mode, need to multiply by 2. dsvars.ds_x1 <<= 1; dsvars.ds_x2 <<= 1; dest = ylookup[dsvars.ds_y] + columnofs[dsvars.ds_x1]; do { spot = ((f_yfrac >> (16 - 6)) & (63 * 64)) + ((f_xfrac >> 16) & 63); // Lowres/blocky mode does it twice, // while scale is adjusted appropriately. screen[dest++] = ds_colormap[0x00FF & ds_source[spot]]; screen[dest++] = ds_colormap[0x00FF & ds_source[spot]]; f_xfrac += ds_xstep; f_yfrac += ds_ystep; } while (count-- > 0); } } } package rr.drawfuns; import i.IDoomSystem; import static m.fixed_t.FRACBITS; public final class R_DrawColumnLow extends DoomColumnFunction<byte[],short[]> { public R_DrawColumnLow(int SCREENWIDTH, int SCREENHEIGHT, int[] ylookup, int[] columnofs, ColVars<byte[],short[]> dcvars, short[] screen, IDoomSystem I) { super(SCREENWIDTH, SCREENHEIGHT, ylookup, columnofs, dcvars, screen, I); this.flags=DcFlags.LOW_DETAIL; } public void invoke() { int count; // MAES: were pointers. Of course... int dest, dest2; final byte[] dc_source=dcvars.dc_source; final short[] dc_colormap=dcvars.dc_colormap; final int dc_source_ofs=dcvars.dc_source_ofs; // Maes: fixed_t never used as such. int frac; final int fracstep; count = dcvars.dc_yh - dcvars.dc_yl; // Zero length. if (count < 0) return; if (RANGECHECK) { performRangeCheck(); } // The idea is to draw more than one pixel at a time. dest = blockyDest1(); dest2 = blockyDest2(); fracstep = dcvars.dc_iscale; frac = dcvars.dc_texturemid + (dcvars.dc_yl - dcvars.centery) * fracstep; // int spot=(frac >>> FRACBITS) & 127; do { // Hack. Does not work correctly. // MAES: that's good to know. screen[dest] = screen[dest2] = dc_colormap[0x00FF & dc_source[dc_source_ofs + ((frac >>> FRACBITS) & 127)]]; dest += SCREENWIDTH; dest2 += SCREENWIDTH; frac += fracstep; } while (count-- != 0); } } package rr.drawfuns; public class SpanVars<T,V> { public int ds_xfrac; public int ds_yfrac; public int ds_xstep; public T ds_source; /** DrawSpan colormap. */ public V ds_colormap; public int ds_y; public int ds_x2; public int ds_x1; public int ds_ystep; public DoomSpanFunction<T,V> spanfunc; } package rr.drawfuns; /** This is all the information needed to draw a particular column. Really. * So if we store all of this crap somewhere instead of drawing, we can do the * drawing when it's more convenient, and since they are non-overlapping we can * parallelize them. Any questions? * */ public class ColVars<T,V> { /** when passing dc_source around, also set this */ public T dc_source; public int dc_source_ofs; public T dc_translation; public int viewheight; /** Used by functions that accept transparency or other special * remapping tables. * */ public T tranmap; public int centery; public int dc_iscale; public int dc_texturemid; public int dc_texheight; // Boom enhancement public int dc_x; public int dc_yh; public int dc_yl; public int dc_flags; /** * MAES: this was a typedef for unsigned bytes, called "lighttable_t". It * makes more sense to make it generic and parametrize it to an array of * primitives since it's performance-critical in the renderer. * Now, whether this should be made bytes or shorts or chars or even ints * is debatable. */ public V dc_colormap; /** Copies all BUT flags */ public final void copyFrom(ColVars<T,V> dcvars) { this.dc_source=dcvars.dc_source; this.dc_colormap=dcvars.dc_colormap; this.dc_source_ofs=dcvars.dc_source_ofs; this.viewheight=dcvars.viewheight; this.centery=dcvars.centery; this.dc_x=dcvars.dc_x; this.dc_yh=dcvars.dc_yh; this.dc_yl=dcvars.dc_yl; this.dc_texturemid=dcvars.dc_texturemid; this.dc_iscale=dcvars.dc_iscale; this.dc_texheight=dcvars.dc_texheight; } /** Assigns specific flags */ public void copyFrom(ColVars<T,V> dcvars,int flags) { this.copyFrom(dcvars); this.dc_flags=flags; } } package rr.drawfuns; /** Either draws a column or a span * * @author velktron * */ public interface SpanFunction<T,V> { public void invoke(); public void invoke(SpanVars<T,V> dsvars); } package rr; import java.io.IOException; import java.nio.ByteBuffer; import utils.C2JUtils; import w.CacheableDoomObject; /** column_t is a list of 0 or more post_t, (byte)-1 terminated * typedef post_t column_t; * For the sake of efficiency, "column_t" will store raw data, however I added * some stuff to make my life easier. * */ public class column_t implements CacheableDoomObject{ /** Static buffers used during I/O. * There's ABSO-FUCKING-LUTELY no reason to manipulate them externally!!! * I'M NOT KIDDING!!!11!! */ private static final int[] guesspostofs=new int[256]; private static final short[] guesspostlens=new short[256]; private static final short[] guesspostdeltas=new short[256]; // MAES: there are useless, since the renderer is using raw byte data anyway, and the per-post // data is available in the special arrays. // public short topdelta; // -1 is the last post in a column (actually 0xFF, since this was unsigned???) // public short length; // length data bytes follows (actually add +2) //public column_t[] posts; // This is quite tricky to read. /** The RAW data (includes initial header and padding, because no post gets preferential treatment). */ public byte[] data; /** Actual number of posts inside this column. All guesswork is done while loading */ public int posts; /** Positions of posts inside the raw data (point at headers) */ public int[] postofs; /** Posts lengths, intended as actual drawable pixels. Add +4 to get the whole post length */ public short[] postlen; /** Vertical offset of each post. In theory it should be possible to quickly * clip to the next visible post when drawing a column */ public short[] postdeltas; @Override public void unpack(ByteBuffer buf) throws IOException { // Mark current position. buf.mark(); int skipped=0; short postlen=0; int colheight=0; int len=0; // How long is the WHOLE column, until the final FF? int postno=0; // Actual number of posts. int topdelta=0; int prevdelta=-1; // HACK for DeepSea tall patches. // Scan every byte until we encounter an 0xFF which definitively marks the end of a column. while((topdelta=C2JUtils.toUnsignedByte(buf.get()))!=0xFF){ // From the wiki: // A column's topdelta is compared to the previous column's topdelta // (or to -1 if there is no previous column in this row). If the new // topdelta is lesser than the previous, it is interpreted as a tall // patch and the two values are added together, the sum serving as the // current column's actual offset. int tmp=topdelta; if (topdelta<prevdelta) { topdelta+=prevdelta; } prevdelta=tmp; // First byte of a post should be its "topdelta" guesspostdeltas[postno]=(short)topdelta; guesspostofs[postno]=skipped+3; // 0 for first post // Read one more byte...this should be the post length. postlen=(short)C2JUtils.toUnsignedByte(buf.get()); guesspostlens[postno++]=(short) (postlen); // So, we already read 2 bytes (topdelta + length) // Two further bytes are padding so we can safely skip 2+2+postlen bytes until the next post skipped+=4+postlen; buf.position(buf.position()+2+postlen); // Obviously, this adds to the height of the column, which might not be equal to the patch that // contains it. colheight+=postlen; } // Skip final padding byte ? skipped++; len = finalizeStatus(skipped, colheight, postno); // Go back...and read the raw data. That's what will actually be used in the renderer. buf.reset(); buf.get(data, 0, len); } /** This -almost- completes reading, by filling in the header information * before the raw column data is read in. * * @param skipped * @param colheight * @param postno * @return */ private int finalizeStatus(int skipped, int colheight, int postno) { int len; // That's the TOTAL length including all padding. // This means we redundantly read some data len=(int) (skipped); this.data=new byte[len]; this.postofs=new int[postno]; this.postlen=new short[postno]; // this.length=(short) colheight; this.postdeltas=new short[postno]; System.arraycopy(guesspostofs, 0,this.postofs, 0, postno); System.arraycopy(guesspostlens, 0,this.postlen, 0, postno); System.arraycopy(guesspostdeltas, 0,this.postdeltas, 0, postno); this.posts=postno; return len; } /** based on raw data */ public int getTopDelta(){ return C2JUtils.toUnsignedByte(data[0]); } /** based on raw data */ public int getLength(){ return C2JUtils.toUnsignedByte(data[1]); } } // $Log: column_t.java,v $ // Revision 1.18 2011/10/04 14:34:08 velktron // Removed length and topdelta members (unused). // // Revision 1.17 2011/07/25 14:37:20 velktron // Added support for DeePSea tall patches. // // Revision 1.16 2011/07/12 16:32:05 velktron // 4 extra padding bytes uneeded. // // Revision 1.15 2011/06/23 17:01:24 velktron // column_t no longer needs to support IReadableDoomObject, since patch_t doesn't. If you really need it back, it can be done through buffering and using unpack(), without duplicate code. Also added setData() method for synthesized columns. // // Revision 1.14 2011/06/08 16:11:13 velktron // IMPORTANT: postofs now skip delta,height and padding ENTIRELY (added +3). This eliminates the need to add +3 before accessing the data, saving some CPU cycles for each column. Of course, anything using column_t must take this into account. // package rr; /** An interface used to ease the use of the GetCachedColumn by part * of parallelized renderers. * * @author Maes * */ public interface IGetColumn<T> { T GetColumn(int tex, int col); } package rr; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import w.CacheableDoomObject; /** * Texture definition. * Each texture is composed of one or more patches, * with patches being lumps stored in the WAD. * The lumps are referenced by number, and patched * into the rectangular texture space using origin * and possibly other attributes. */ public class mappatch_t implements CacheableDoomObject { public short originx; public short originy; public short patch; public short stepdir; public short colormap; @Override public void unpack(ByteBuffer buf) throws IOException { buf.order(ByteOrder.LITTLE_ENDIAN); originx=buf.getShort(); originy=buf.getShort(); patch=buf.getShort(); stepdir=buf.getShort(); colormap=buf.getShort(); } public static final int size() { return 10; } }; package rr; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; public class z_vertex_t extends vertex_t { public z_vertex_t(){ super(); } /** Notice how we auto-expand to fixed_t */ @Override public void unpack(ByteBuffer buf) throws IOException { buf.order(ByteOrder.LITTLE_ENDIAN); this.x=buf.getInt(); this.y=buf.getInt(); } public final static int sizeOf() { return 8; } } package rr; import static data.Defines.FF_FRAMEMASK; import static data.Defines.FF_FULLBRIGHT; import static data.Defines.SIL_BOTTOM; import static data.Defines.SIL_TOP; import static data.Defines.pw_invisibility; import static doom.player_t.NUMPSPRITES; import static m.fixed_t.FRACBITS; import static m.fixed_t.FRACUNIT; import static m.fixed_t.FixedMul; import static p.mobj_t.MF_TRANSLATION; import static p.mobj_t.MF_TRANSSHIFT; import static rr.LightsAndColors.LIGHTLEVELS; import static rr.LightsAndColors.LIGHTSCALESHIFT; import static rr.LightsAndColors.LIGHTSEGSHIFT; import static rr.LightsAndColors.MAXLIGHTSCALE; import static rr.line_t.ML_DONTPEGBOTTOM; import i.IDoomSystem; import p.pspdef_t; import rr.drawfuns.ColFuncs; import rr.drawfuns.ColVars; import rr.drawfuns.ColumnFunction; import v.IVideoScale; import w.IWadLoader; /** * Refresh of things, i.e. objects represented by sprites. This abstract * class is the base for all implementations, and contains the gory clipping * and priority stuff. It can terminate by drawing directly, or by buffering * into a pipeline for parallelized drawing. * * It need to be aware of almost everything in the renderer, which means that * it's a PITA to keep "disembodied". Then again, this probably means it's more * extensible... * * * */ public abstract class AbstractThings<T,V> implements IMaskedDrawer<T,V> { private final static boolean RANGECHECK=false; protected short[] maskedtexturecol; protected int pmaskedtexturecol = 0; // Cache those you get from the sprite manager protected int[] spritewidth, spriteoffset, spritetopoffset; /** fixed_t */ protected int pspritescale, pspriteiscale, pspritexscale, pspriteyscale, skyscale; // Used for masked segs protected int rw_scalestep; protected int spryscale; protected int sprtopscreen; protected short[] mfloorclip; protected int p_mfloorclip; protected short[] mceilingclip; protected int p_mceilingclip; protected sector_t frontsector; protected sector_t backsector; // This must be "pegged" to the one used by the default renderer. protected ColVars<T, V> maskedcvars; protected ColumnFunction<T,V> colfunc; protected ColFuncs<T,V> colfuncs; protected ColFuncs<T,V> colfuncshi; protected ColFuncs<T,V> colfuncslow; protected final LightsAndColors<V> colormaps; protected final ViewVars view; protected final SegVars seg_vars; protected final TextureManager<T> TexMan; protected final IDoomSystem I; protected final ISpriteManager SM; protected final BSPVars MyBSP; protected final IVisSpriteManagement<V> VIS; protected final IWadLoader W; protected final vissprite_t<V> avis; public AbstractThings(Renderer<T,V> R) { this.colfuncshi=R.getColFuncsHi(); this.colfuncslow=R.getColFuncsLow(); this.colormaps=R.getColorMap(); this.view=R.getView(); this.seg_vars=R.getSegVars(); this.TexMan=R.getTextureManager(); this.I=R.getDoomSystem(); this.SM=R.getSpriteManager(); this.MyBSP=R.getBSPVars(); this.VIS=R.getVisSpriteManager(); this.W=R.getWadLoader(); this.avis=new vissprite_t<V>(); this.maskedcvars=R.getMaskedDCVars(); } @Override public void cacheSpriteManager(ISpriteManager SM) { this.spritewidth = SM.getSpriteWidth(); this.spriteoffset = SM.getSpriteOffset(); this.spritetopoffset = SM.getSpriteTopOffset(); } // ///////////// VIDEO SCALE STUFF ///////////////////// protected int SCREENWIDTH; protected int SCREENHEIGHT; protected IVideoScale vs; @Override public void setVideoScale(IVideoScale vs) { this.vs = vs; } @Override public void initScaling() { this.SCREENHEIGHT = vs.getScreenHeight(); this.SCREENWIDTH = vs.getScreenWidth(); // Pre-scale stuff. clipbot = new short[SCREENWIDTH]; cliptop = new short[SCREENWIDTH]; } /** * R_DrawVisSprite mfloorclip and mceilingclip should also be set. * Sprites are actually drawn here. MAES: Optimized. No longer needed to * pass x1 and x2 parameters (useless) +2 fps on nuts.wad timedemo. */ protected void DrawVisSprite(vissprite_t<V> vis) { column_t column; int texturecolumn; int frac; // fixed_t patch_t patch; // At this point, the view angle (and patch) has already been // chosen. Go back. patch = W.CachePatchNum(vis.patch + SM.getFirstSpriteLump()); maskedcvars.dc_colormap = vis.colormap; // colfunc=glasscolfunc; if (maskedcvars.dc_colormap == null) { // NULL colormap = shadow draw colfunc = colfuncs.fuzz; } else if ((vis.mobjflags & MF_TRANSLATION) != 0) { colfunc = colfuncs.trans; maskedcvars.dc_translation = (T) colormaps.getTranslationTable(vis.mobjflags); } maskedcvars.dc_iscale = Math.abs(vis.xiscale) >> view.detailshift; maskedcvars.dc_texturemid = vis.texturemid; frac = vis.startfrac; spryscale = vis.scale; sprtopscreen = view.centeryfrac - FixedMul(maskedcvars.dc_texturemid, spryscale); // A texture height of 0 means "not tiling" and holds for // all sprite/masked renders. maskedcvars.dc_texheight = 0; for (maskedcvars.dc_x = vis.x1; maskedcvars.dc_x <= vis.x2; maskedcvars.dc_x++, frac += vis.xiscale) { texturecolumn = frac >> FRACBITS; if (RANGECHECK) { if (texturecolumn < 0 || texturecolumn >= patch.width) I.Error("R_DrawSpriteRange: bad texturecolumn"); } column = patch.columns[texturecolumn]; DrawMaskedColumn(column); } colfunc = colfuncs.masked; } /** * R_RenderMaskedSegRange * * @param ds * @param x1 * @param x2 */ protected void RenderMaskedSegRange(drawseg_t ds, int x1, int x2) { int index; int lightnum; int texnum; // System.out.printf("RenderMaskedSegRange from %d to %d\n",x1,x2); // Calculate light table. // Use different light tables // for horizontal / vertical / diagonal. Diagonal? // OPTIMIZE: get rid of LIGHTSEGSHIFT globally MyBSP.curline = ds.curline; frontsector = MyBSP.curline.frontsector; backsector = MyBSP.curline.backsector; texnum = TexMan.getTextureTranslation(MyBSP.curline.sidedef.midtexture); // System.out.print(" for texture "+textures[texnum].name+"\n:"); lightnum = (frontsector.lightlevel >> LIGHTSEGSHIFT) + colormaps.extralight; if (MyBSP.curline.v1y == MyBSP.curline.v2y) lightnum--; else if (MyBSP.curline.v1x == MyBSP.curline.v2x) lightnum++; // Killough code. colormaps.walllights = lightnum >= LIGHTLEVELS ? colormaps.scalelight[LIGHTLEVELS - 1] : lightnum < 0 ? colormaps.scalelight[0] : colormaps.scalelight[lightnum]; // Get the list maskedtexturecol = ds.getMaskedTextureColList(); // And this is the pointer. pmaskedtexturecol = ds.getMaskedTextureColPointer(); rw_scalestep = ds.scalestep; spryscale = ds.scale1 + (x1 - ds.x1) * rw_scalestep; // HACK to get "pointers" inside clipping lists mfloorclip = ds.getSprBottomClipList(); p_mfloorclip = ds.getSprBottomClipPointer(); mceilingclip = ds.getSprTopClipList(); p_mceilingclip = ds.getSprTopClipPointer(); // find positioning if ((MyBSP.curline.linedef.flags & ML_DONTPEGBOTTOM) != 0) { maskedcvars.dc_texturemid = frontsector.floorheight > backsector.floorheight ? frontsector.floorheight : backsector.floorheight; maskedcvars.dc_texturemid = maskedcvars.dc_texturemid + TexMan.getTextureheight(texnum) - view.z; } else { maskedcvars.dc_texturemid = frontsector.ceilingheight < backsector.ceilingheight ? frontsector.ceilingheight : backsector.ceilingheight; maskedcvars.dc_texturemid = maskedcvars.dc_texturemid - view.z; } maskedcvars.dc_texturemid += MyBSP.curline.sidedef.rowoffset; if (colormaps.fixedcolormap != null) maskedcvars.dc_colormap = colormaps.fixedcolormap; // Texture height must be set at this point. This will trigger // tiling. For sprites, it should be set to 0. maskedcvars.dc_texheight = TexMan.getTextureheight(texnum) >> FRACBITS; // draw the columns for (maskedcvars.dc_x = x1; maskedcvars.dc_x <= x2; maskedcvars.dc_x++) { // calculate lighting if (maskedtexturecol[pmaskedtexturecol + maskedcvars.dc_x] != Short.MAX_VALUE) { if (colormaps.fixedcolormap == null) { index = spryscale >>> LIGHTSCALESHIFT; if (index >= MAXLIGHTSCALE) index = MAXLIGHTSCALE - 1; maskedcvars.dc_colormap = colormaps.walllights[index]; } sprtopscreen = view.centeryfrac - FixedMul(maskedcvars.dc_texturemid, spryscale); maskedcvars.dc_iscale = (int) (0xffffffffL / spryscale); // draw the texture column_t data = TexMan.GetColumnStruct(texnum, (int) maskedtexturecol[pmaskedtexturecol + maskedcvars.dc_x]);// -3); DrawMaskedColumn(data); maskedtexturecol[pmaskedtexturecol + maskedcvars.dc_x] = Short.MAX_VALUE; } spryscale += rw_scalestep; } } /** * R_DrawPSprite Draws a "player sprite" with slighly different rules * than normal sprites. This is actually a PITA, at best :-/ */ protected void DrawPSprite(pspdef_t psp) { int tx; int x1; int x2; spritedef_t sprdef; spriteframe_t sprframe; vissprite_t<V> vis; int lump; boolean flip; // decide which patch to use (in terms of angle?) if (RANGECHECK) { if (psp.state.sprite.ordinal() >= SM.getNumSprites()) I.Error("R_ProjectSprite: invalid sprite number %d ", psp.state.sprite); } sprdef = SM.getSprite(psp.state.sprite.ordinal()); if (RANGECHECK) { if ((psp.state.frame & FF_FRAMEMASK) >= sprdef.numframes) I.Error("R_ProjectSprite: invalid sprite frame %d : %d ", psp.state.sprite, psp.state.frame); } sprframe = sprdef.spriteframes[psp.state.frame & FF_FRAMEMASK]; // Base frame for "angle 0" aka viewed from dead-front. lump = sprframe.lump[0]; // Q: where can this be set? A: at sprite loadtime. flip = (boolean) (sprframe.flip[0] != 0); // calculate edges of the shape. tx is expressed in "view units". tx = (int) (FixedMul(psp.sx, view.BOBADJUST) - view.WEAPONADJUST); tx -= spriteoffset[lump]; // So...centerxfrac is the center of the screen (pixel coords in // fixed point). x1 = (view.centerxfrac + FixedMul(tx, pspritescale)) >> FRACBITS; // off the right side if (x1 > view.width) return; tx += spritewidth[lump]; x2 = ((view.centerxfrac + FixedMul(tx, pspritescale)) >> FRACBITS) - 1; // off the left side if (x2 < 0) return; // store information in a vissprite ? vis = avis; vis.mobjflags = 0; vis.texturemid = ((BASEYCENTER + view.lookdir) << FRACBITS) + FRACUNIT / 2 - (psp.sy - spritetopoffset[lump]); vis.x1 = x1 < 0 ? 0 : x1; vis.x2 = x2 >= view.width ? view.width - 1 : x2; vis.scale = (pspritescale) << view.detailshift; if (flip) { vis.xiscale = -pspriteiscale; vis.startfrac = spritewidth[lump] - 1; } else { vis.xiscale = pspriteiscale; vis.startfrac = 0; } if (vis.x1 > x1) vis.startfrac += vis.xiscale * (vis.x1 - x1); vis.patch = lump; if ((view.player.powers[pw_invisibility] > 4 * 32) || (view.player.powers[pw_invisibility] & 8) != 0) { // shadow draw vis.colormap = null; } else if (colormaps.fixedcolormap != null) { // fixed color vis.colormap = colormaps.fixedcolormap; // vis.pcolormap=0; } else if ((psp.state.frame & FF_FULLBRIGHT) != 0) { // full bright vis.colormap = colormaps.colormaps[0]; // vis.pcolormap=0; } else { // local light vis.colormap = colormaps.spritelights[MAXLIGHTSCALE - 1]; } // System.out.println("Weapon draw "+vis); DrawVisSprite(vis); } protected int PSpriteSY[] = { 0, // staff 5 * FRACUNIT, // goldwand 15 * FRACUNIT, // crossbow 15 * FRACUNIT, // blaster 15 * FRACUNIT, // skullrod 15 * FRACUNIT, // phoenix rod 15 * FRACUNIT, // mace 15 * FRACUNIT, // gauntlets 15 * FRACUNIT // beak }; /** * R_DrawPlayerSprites This is where stuff like guns is drawn...right? */ protected final void DrawPlayerSprites() { int i; int lightnum; pspdef_t psp; // get light level lightnum = (view.player.mo.subsector.sector.lightlevel >> LIGHTSEGSHIFT) + colormaps.extralight; if (lightnum < 0) colormaps.spritelights = colormaps.scalelight[0]; else if (lightnum >= LIGHTLEVELS) colormaps.spritelights = colormaps.scalelight[LIGHTLEVELS - 1]; else colormaps.spritelights = colormaps.scalelight[lightnum]; // clip to screen bounds mfloorclip = view.screenheightarray; p_mfloorclip = 0; mceilingclip = view.negonearray; p_mceilingclip = 0; // add all active psprites // MAES 25/5/2011 Fixed another stupid bug that prevented // PSP from actually being updated. This in turn uncovered // other bugs in the way psp and state were treated, and the way // flash states were set. It should be OK now. for (i = 0; i < NUMPSPRITES; i++) { psp = view.player.psprites[i]; if (psp.state != null && psp.state.id != 0) { DrawPSprite(psp); } } } // MAES: Scale to SCREENWIDTH protected short[] clipbot; protected short[] cliptop; /** * R_DrawSprite */ protected final void DrawSprite(vissprite_t<V> spr) { int ds; drawseg_t dss; int x; int r1; int r2; int scale; // fixed int lowscale; // fixed int silhouette; for (x = spr.x1; x <= spr.x2; x++) clipbot[x] = cliptop[x] = -2; // Scan drawsegs from end to start for obscuring segs. // The first drawseg that has a greater scale // is the clip seg. for (ds = seg_vars.ds_p - 1; ds >= 0; ds--) { // determine if the drawseg obscures the sprite // System.out.println("Drawseg "+ds+"of "+(ds_p-1)); dss = seg_vars.drawsegs[ds]; if (dss.x1 > spr.x2 || dss.x2 < spr.x1 || ((dss.silhouette == 0) && (dss .nullMaskedTextureCol()))) { // does not cover sprite continue; } r1 = dss.x1 < spr.x1 ? spr.x1 : dss.x1; r2 = dss.x2 > spr.x2 ? spr.x2 : dss.x2; if (dss.scale1 > dss.scale2) { lowscale = dss.scale2; scale = dss.scale1; } else { lowscale = dss.scale1; scale = dss.scale2; } if (scale < spr.scale || (lowscale < spr.scale && (dss.curline .PointOnSegSide(spr.gx, spr.gy) == 0))) { // masked mid texture? if (!dss.nullMaskedTextureCol()) RenderMaskedSegRange(dss, r1, r2); // seg is behind sprite continue; } // clip this piece of the sprite silhouette = dss.silhouette; if (spr.gz >= dss.bsilheight) silhouette &= ~SIL_BOTTOM; if (spr.gzt <= dss.tsilheight) silhouette &= ~SIL_TOP; // BOTTOM clipping if (silhouette == 1) { // bottom sil for (x = r1; x <= r2; x++) if (clipbot[x] == -2) clipbot[x] = dss.getSprBottomClip(x); } else if (silhouette == 2) { // top sil for (x = r1; x <= r2; x++) if (cliptop[x] == -2) cliptop[x] = dss.getSprTopClip(x); } else if (silhouette == 3) { // both for (x = r1; x <= r2; x++) { if (clipbot[x] == -2) clipbot[x] = dss.getSprBottomClip(x); if (cliptop[x] == -2) cliptop[x] = dss.getSprTopClip(x); } } } // all clipping has been performed, so draw the sprite // check for unclipped columns for (x = spr.x1; x <= spr.x2; x++) { if (clipbot[x] == -2) clipbot[x] = (short) view.height; // ?? What's this bullshit? if (cliptop[x] == -2) cliptop[x] = -1; } mfloorclip = clipbot; p_mfloorclip = 0; mceilingclip = cliptop; p_mceilingclip = 0; DrawVisSprite(spr); } /** * R_DrawMasked Sorts and draws vissprites (room for optimization in * sorting func.) Draws masked textures. Draws player weapons and * overlays (psprites). Sorting function can be swapped for almost * anything, and it will work better, in-place and be simpler to draw, * too. */ @Override public void DrawMasked() { // vissprite_t spr; int ds; drawseg_t dss; // Well, it sorts visspite objects. // It actually IS faster to sort with comparators, but you need to // go into NUTS.WAD-like wads. // numbers. The built-in sort if about as good as it gets. In fact, // it's hardly slower // to draw sprites without sorting them when using the built-in // modified mergesort, while // the original algorithm is so dreadful it actually does slow // things down. VIS.SortVisSprites(); // If you are feeling adventurous, try these ones. They *might* // perform // better in very extreme situations where all sprites are always on // one side // of your view, but I hardly see any benefits in that. They are // both // much better than the original anyway. // combSort(vissprites,vissprite_p); // shellsort(vissprites,vissprite_p); // pQuickSprite.sort(vissprites); // The original sort. It's incredibly bad on so many levels (uses a // separate // linked list for the sorted sequence, which is pointless since the // vissprite_t // array is gonna be changed all over in the next frame anyway, it's // not like // it helps preseving or anything. It does work in Java too, but I'd // say to Keep Away. No srsly. /* * SortVisSprites (); // Sprite "0" not visible? /*if (vissprite_p > * 0) { // draw all vissprites back to front for (spr = * vsprsortedhead.next ; spr != vsprsortedhead ; spr=spr.next) { * DrawSprite (spr); } } */ // After using in-place sorts, sprites can be drawn as simply as // that. colfunc = colfuncs.masked; // Sprites use fully-masked capable // function. final vissprite_t<V>[] vissprites = VIS.getVisSprites(); final int numvissprites = VIS.getNumVisSprites(); for (int i = 0; i < numvissprites; i++) { DrawSprite(vissprites[i]); } // render any remaining masked mid textures for (ds = seg_vars.ds_p - 1; ds >= 0; ds--) { dss = seg_vars.drawsegs[ds]; if (!dss.nullMaskedTextureCol()) RenderMaskedSegRange(dss, dss.x1, dss.x2); } // draw the psprites on top of everything // but does not draw on side views // if (viewangleoffset==0) colfunc = colfuncs.player; DrawPlayerSprites(); colfunc = colfuncs.masked; } /** * R_DrawMaskedColumn Used for sprites and masked mid textures. Masked * means: partly transparent, i.e. stored in posts/runs of opaque * pixels. NOTE: this version accepts raw bytes, in case you know what * you're doing. */ /* protected final void DrawMaskedColumn(T column) { int topscreen; int bottomscreen; int basetexturemid; // fixed_t int topdelta; int length; basetexturemid = maskedcvars.dc_texturemid; // That's true for the whole column. maskedcvars.dc_source = (T) column; int pointer = 0; // for each post... while ((topdelta = 0xFF & column[pointer]) != 0xFF) { // calculate unclipped screen coordinates // for post topscreen = sprtopscreen + spryscale * topdelta; length = 0xff & column[pointer + 1]; bottomscreen = topscreen + spryscale * length; maskedcvars.dc_yl = (topscreen + FRACUNIT - 1) >> FRACBITS; maskedcvars.dc_yh = (bottomscreen - 1) >> FRACBITS; if (maskedcvars.dc_yh >= mfloorclip[p_mfloorclip + maskedcvars.dc_x]) maskedcvars.dc_yh = mfloorclip[p_mfloorclip + maskedcvars.dc_x] - 1; if (maskedcvars.dc_yl <= mceilingclip[p_mceilingclip + maskedcvars.dc_x]) maskedcvars.dc_yl = mceilingclip[p_mceilingclip + maskedcvars.dc_x] + 1; // killough 3/2/98, 3/27/98: Failsafe against overflow/crash: if (maskedcvars.dc_yl <= maskedcvars.dc_yh && maskedcvars.dc_yh < view.height) { // Set pointer inside column to current post's data // Rremember, it goes {postlen}{postdelta}{pad}[data]{pad} maskedcvars.dc_source_ofs = pointer + 3; maskedcvars.dc_texturemid = basetexturemid - (topdelta << FRACBITS); // Drawn by either R_DrawColumn // or (SHADOW) R_DrawFuzzColumn. maskedcvars.dc_texheight = 0; // Killough completeColumn(); } pointer += length + 4; } maskedcvars.dc_texturemid = basetexturemid; } */ /** * R_DrawMaskedColumn Used for sprites and masked mid textures. Masked * means: partly transparent, i.e. stored in posts/runs of opaque * pixels. FIXME: while it does work with "raw columns", if the initial * post is drawn outside of the screen the rest appear screwed up. * SOLUTION: use the version taking raw byte[] arguments. */ protected final void DrawMaskedColumn(column_t column) { int topscreen; int bottomscreen; int basetexturemid; // fixed_t basetexturemid = maskedcvars.dc_texturemid; // That's true for the whole column. maskedcvars.dc_source = (T) column.data; // dc_source_ofs=0; // for each post... for (int i = 0; i < column.posts; i++) { maskedcvars.dc_source_ofs = column.postofs[i]; // calculate unclipped screen coordinates // for post topscreen = sprtopscreen + spryscale * column.postdeltas[i]; bottomscreen = topscreen + spryscale * column.postlen[i]; maskedcvars.dc_yl = (topscreen + FRACUNIT - 1) >> FRACBITS; maskedcvars.dc_yh = (bottomscreen - 1) >> FRACBITS; if (maskedcvars.dc_yh >= mfloorclip[p_mfloorclip + maskedcvars.dc_x]) maskedcvars.dc_yh = mfloorclip[p_mfloorclip + maskedcvars.dc_x] - 1; if (maskedcvars.dc_yl <= mceilingclip[p_mceilingclip + maskedcvars.dc_x]) maskedcvars.dc_yl = mceilingclip[p_mceilingclip + maskedcvars.dc_x] + 1; // killough 3/2/98, 3/27/98: Failsafe against overflow/crash: if (maskedcvars.dc_yl <= maskedcvars.dc_yh && maskedcvars.dc_yh < maskedcvars.viewheight) { // Set pointer inside column to current post's data // Remember, it goes {postlen}{postdelta}{pad}[data]{pad} maskedcvars.dc_texturemid = basetexturemid - (column.postdeltas[i] << FRACBITS); // Drawn by either R_DrawColumn or (SHADOW) // R_DrawFuzzColumn. // MAES: when something goes bad here, it means that the // following: // // fracstep = dc_iscale; // frac = dc_texturemid + (dc_yl - centery) * fracstep; // // results in a negative initial frac number. // Drawn by either R_DrawColumn // or (SHADOW) R_DrawFuzzColumn. // FUN FACT: this was missing and fucked my shit up. maskedcvars.dc_texheight=0; // Killough completeColumn(); } } maskedcvars.dc_texturemid = basetexturemid; } /* * R_DrawMaskedColumn * Used for sprites and masked mid textures. * Masked means: partly transparent, i.e. stored * in posts/runs of opaque pixels. * * NOTE: this version accepts raw bytes, in case you know what you're doing. * NOTE: this is a legacy function. Do not reactivate unless * REALLY needed. * */ /* protected final void DrawMaskedColumn (byte[] column) { int topscreen; int bottomscreen; int basetexturemid; // fixed_t int topdelta; int length; basetexturemid = dc_texturemid; // That's true for the whole column. dc_source = column; int pointer=0; // for each post... while((topdelta=0xFF&column[pointer])!=0xFF) { // calculate unclipped screen coordinates // for post topscreen = sprtopscreen + spryscale*topdelta; length=0xff&column[pointer+1]; bottomscreen = topscreen + spryscale*length; dc_yl = (topscreen+FRACUNIT-1)>>FRACBITS; dc_yh = (bottomscreen-1)>>FRACBITS; if (dc_yh >= mfloorclip[p_mfloorclip+dc_x]) dc_yh = mfloorclip[p_mfloorclip+dc_x]-1; if (dc_yl <= mceilingclip[p_mceilingclip+dc_x]) dc_yl = mceilingclip[p_mceilingclip+dc_x]+1; // killough 3/2/98, 3/27/98: Failsafe against overflow/crash: if (dc_yl <= dc_yh && dc_yh < viewheight) { // Set pointer inside column to current post's data // Rremember, it goes {postlen}{postdelta}{pad}[data]{pad} dc_source_ofs = pointer+3; dc_texturemid = basetexturemid - (topdelta<<FRACBITS); // Drawn by either R_DrawColumn // or (SHADOW) R_DrawFuzzColumn. dc_texheight=0; // Killough maskedcolfunc.invoke(); } pointer+=length + 4; } dc_texturemid = basetexturemid; } */ @Override public void setPspriteIscale(int i) { pspriteiscale = i; } @Override public void setPspriteScale(int i) { pspritescale = i; } @Override public void setDetail(int detailshift){ switch (detailshift){ case HIGH_DETAIL: colfuncs=colfuncshi; break; case LOW_DETAIL: colfuncs=colfuncslow; break; } } } package rr; // // ? // public class drawseg_t { public drawseg_t(){ } /** MAES: was pointer. Not array? */ public seg_t curline; public int x1, x2; /** fixed_t */ public int scale1, scale2, scalestep; /** 0=none, 1=bottom, 2=top, 3=both */ public int silhouette; /** do not clip sprites above this (fixed_t) */ public int bsilheight; /** do not clip sprites below this (fixed_t) */ public int tsilheight; /** Indexes to lists for sprite clipping, all three adjusted so [x1] is first value. */ private int psprtopclip, psprbottomclip, pmaskedtexturecol; /** Pointers to the actual lists */ private short[] sprtopclip, sprbottomclip, maskedtexturecol; ///////////////// Accessor methods to simulate mid-array pointers /////////// public void setSprTopClip(short[] array, int index){ this.sprtopclip=array; this.psprtopclip=index; } public void setSprBottomClip(short[] array, int index){ this.sprbottomclip=array; this.psprbottomclip=index; } public void setMaskedTextureCol(short[] array, int index){ this.maskedtexturecol=array; this.pmaskedtexturecol=index; } public short getSprTopClip(int index){ return this.sprtopclip[this.psprtopclip+index]; } public short getSprBottomClip( int index){ return this.sprbottomclip[this.psprbottomclip+index]; } public short getMaskedTextureCol(int index){ return this.maskedtexturecol[this.pmaskedtexturecol+index]; } public short[] getSprTopClipList(){ return this.sprtopclip; } public short[] getSprBottomClipList(){ return this.sprbottomclip; } public short[] getMaskedTextureColList(){ return this.maskedtexturecol; } public int getSprTopClipPointer(){ return this.psprtopclip; } public int getSprBottomClipPointer(){ return this.psprbottomclip; } public int getMaskedTextureColPointer(){ return this.pmaskedtexturecol; } public void setSprTopClipPointer(int index){ this.psprtopclip=index; } public void setSprBottomClipPointer(int index){ this.psprbottomclip=index; } public void setMaskedTextureColPointer(int index){ this.pmaskedtexturecol=index; } public boolean nullSprTopClip(){ return this.sprtopclip==null; } public boolean nullSprBottomClip(){ return this.sprbottomclip==null; } public boolean nullMaskedTextureCol(){ return this.maskedtexturecol==null; } } package rr; import java.io.DataInputStream; import java.io.IOException; import java.nio.ByteBuffer; import java.util.Arrays; import p.Interceptable; import p.Resettable; import s.degenmobj_t; import w.DoomIO; import w.IPackableDoomObject; import w.IReadableDoomObject; import static m.BBox.BOXBOTTOM; import static m.BBox.BOXLEFT; import static m.BBox.BOXRIGHT; import static m.BBox.BOXTOP; import static m.fixed_t.*; import static utils.C2JUtils.eval; import defines.slopetype_t; import doom.thinker_t; /** This is the actual linedef */ public class line_t implements Interceptable, IReadableDoomObject, IPackableDoomObject, Resettable { public static final char NO_INDEX=0xFFFF; public line_t() { sidenum = new char[2]; bbox = new int[4]; slopetype = slopetype_t.ST_HORIZONTAL; } /** * Vertices, from v1 to v2. NOTE: these are almost never passed as-such, nor * linked to Maybe we can get rid of them and only use the value semantics? */ public vertex_t v1, v2; /** remapped vertex coords, for quick lookup with value semantics */ public int v1x, v1y, v2x, v2y; /** (fixed_t) Precalculated v2 - v1 for side checking. */ public int dx, dy; /** Animation related. */ public short flags, special, tag; /** * Visual appearance: SideDefs. sidenum[1] will be 0xFFFF if one sided */ public char[] sidenum; /** * Neat. Another bounding box, for the extent of the LineDef. MAES: make * this a proper bbox? fixed_t bbox[4]; */ public int[] bbox; /** To aid move clipping. */ public slopetype_t slopetype; /** * Front and back sector. Note: redundant? Can be retrieved from SideDefs. * MAES: pointers */ public sector_t frontsector, backsector; public int frontsectorid, backsectorid; /** if == validcount, already checked */ public int validcount; /** thinker_t for reversable actions MAES: (void*) */ public thinker_t specialdata; public int specialdataid; public degenmobj_t soundorg; // From Boom public int tranlump; public int id; /** killough 4/17/98: improves searches for tags. */ public int firsttag,nexttag; /** For Boom stuff, interprets sidenum specially */ public int getSpecialSidenum() { return (sidenum[0] << 16) & (0x0000ffff & sidenum[1]); } public void assignVertexValues() { this.v1x = v1.x; this.v1y = v1.y; this.v2x = v2.x; this.v2y = v2.y; } /** * P_PointOnLineSide * * @param x * fixed_t * @param y * fixed_t * @return 0 or 1 (false, true) - (front, back) */ public boolean PointOnLineSide(int x, int y) { return (dx==0) ? x <= this.v1x ? this.dy > 0 : this.dy < 0 : (dy==0) ? y <= this.v1y ? this.dx < 0 : this.dx > 0 : FixedMul(y-this.v1y, this.dx>>FRACBITS) >= FixedMul(this.dy>>FRACBITS, x-this.v1x); /* int dx, dy, left, right; if (this.dx == 0) { if (x <= this.v1x) return this.dy > 0; return this.dy < 0; } if (this.dy == 0) { if (y <= this.v1y) return this.dx < 0; return this.dx > 0; } dx = (x - this.v1x); dy = (y - this.v1y); left = FixedMul(this.dy >> FRACBITS, dx); right = FixedMul(dy, this.dx >> FRACBITS); if (right < left) return false; // front side return true; // back side*/ } /** * P_BoxOnLineSide Considers the line to be infinite Returns side 0 or 1, -1 * if box crosses the line. Doubles as a convenient check for whether a * bounding box crosses a line at all * * @param tmbox * fixed_t[] */ public int BoxOnLineSide(int[] tmbox) { boolean p1 = false; boolean p2 = false; switch (this.slopetype) { // Line perfectly horizontal, box floating "north" of line case ST_HORIZONTAL: p1 = tmbox[BOXTOP] > v1y; p2 = tmbox[BOXBOTTOM] > v1y; if (dx < 0) { p1 ^= true; p2 ^= true; } break; // Line perfectly vertical, box floating "west" of line case ST_VERTICAL: p1 = tmbox[BOXRIGHT] < v1x; p2 = tmbox[BOXLEFT] < v1x; if (dy < 0) { p1 ^= true; p2 ^= true; } break; case ST_POSITIVE: // Positive slope, both points on one side. p1 = PointOnLineSide(tmbox[BOXLEFT], tmbox[BOXTOP]); p2 = PointOnLineSide(tmbox[BOXRIGHT], tmbox[BOXBOTTOM]); break; case ST_NEGATIVE: // Negative slope, both points (mirrored horizontally) on one side. p1 = PointOnLineSide(tmbox[BOXRIGHT], tmbox[BOXTOP]); p2 = PointOnLineSide(tmbox[BOXLEFT], tmbox[BOXBOTTOM]); break; } if (p1 == p2) return p1 ? 1 : 0; // Any other result means non-inclusive crossing. return -1; } /** * Variant of P_BoxOnLineSide. Uses inclusive checks, so that even lines on * the border of a box will be considered crossing. This is more useful for * building blockmaps. * * @param tmbox * fixed_t[] */ public int BoxOnLineSideInclusive(int[] tmbox) { boolean p1 = false; boolean p2 = false; switch (this.slopetype) { // Line perfectly horizontal, box floating "north" of line case ST_HORIZONTAL: p1 = tmbox[BOXTOP] >= v1y; p2 = tmbox[BOXBOTTOM] >= v1y; if (dx < 0) { p1 ^= true; p2 ^= true; } break; // Line perfectly vertical, box floating "west" of line case ST_VERTICAL: p1 = tmbox[BOXRIGHT] <= v1x; p2 = tmbox[BOXLEFT] <= v1x; if (dy < 0) { p1 ^= true; p2 ^= true; } break; case ST_POSITIVE: // Positive slope, both points on one side. p1 = PointOnLineSide(tmbox[BOXLEFT], tmbox[BOXTOP]); p2 = PointOnLineSide(tmbox[BOXRIGHT], tmbox[BOXBOTTOM]); break; case ST_NEGATIVE: // Negative slope, both points (mirrored horizontally) on one side. p1 = PointOnLineSide(tmbox[BOXRIGHT], tmbox[BOXTOP]); p2 = PointOnLineSide(tmbox[BOXLEFT], tmbox[BOXBOTTOM]); break; } if (p1 == p2) return p1 ? 1 : 0; // Any other result means non-inclusive crossing. return -1; } /** * getNextSector() Return sector_t * of sector next to current. NULL if not * two-sided line */ public sector_t getNextSector(sector_t sec) { if (!eval(flags& ML_TWOSIDED)) return null; if (frontsector == sec) return backsector; return frontsector; } public String toString() { return (String.format("Line %d Flags: %x Special %d Tag: %d ", this.id,this.flags, this.special, this.tag)); } @Override public void read(DataInputStream f) throws IOException { // For histerical reasons, these are the only parts of line_t that // are archived in vanilla savegames. Go figure. this.flags = DoomIO.readLEShort(f); this.special = DoomIO.readLEShort(f); this.tag = DoomIO.readLEShort(f); } @Override public void pack(ByteBuffer buffer) { buffer.putShort(flags); buffer.putShort(special); buffer.putShort(tag); // buffer.putShort((short) 0XDEAD); // buffer.putShort((short) 0XBABE); // buffer.putShort((short) 0XBEEF); } @Override public void reset() { v1 = v2 = null; v1x = v1y = v2x = v2y = 0; dx = dy = 0; flags = special = tag = 0; Arrays.fill(sidenum, (char) 0); Arrays.fill(bbox, 0); slopetype = slopetype_t.ST_HORIZONTAL; frontsector = backsector = null; frontsectorid = backsectorid = 0; validcount = 0; specialdata = null; specialdataid = 0; soundorg = null; tranlump = 0; } /** * LUT, motion clipping, walls/grid element // // LineDef attributes. // /** * Solid, is an obstacle. */ public static final int ML_BLOCKING = 1; /** Blocks monsters only. */ public static final int ML_BLOCKMONSTERS = 2; /** Backside will not be present at all if not two sided. */ public static final int ML_TWOSIDED = 4; // If a texture is pegged, the texture will have // the end exposed to air held constant at the // top or bottom of the texture (stairs or pulled // down things) and will move with a height change // of one of the neighbor sectors. // Unpegged textures allways have the first row of // the texture at the top pixel of the line for both // top and bottom textures (use next to windows). /** upper texture unpegged */ public static final int ML_DONTPEGTOP = 8; /** lower texture unpegged */ public static final int ML_DONTPEGBOTTOM = 16; /** In AutoMap: don't map as two sided: IT'S A SECRET! */ public static final int ML_SECRET = 32; /** Sound rendering: don't let sound cross two of these. */ public static final int ML_SOUNDBLOCK = 64; /** Don't draw on the automap at all. */ public static final int ML_DONTDRAW = 128; /** Set if already seen, thus drawn in automap. */ public static final int ML_MAPPED = 256; } package rr; import data.Limits; import utils.C2JUtils; public class SegVars { // /// FROM BSP ///////// public int MAXDRAWSEGS = Limits.MAXDRAWSEGS; /** pointer to drawsegs */ public int ds_p; public drawseg_t[] drawsegs; public short[] maskedtexturecol; public int pmaskedtexturecol = 0; /** * R_ClearDrawSegs * * The drawseg list is reset by pointing back at 0. * */ public void ClearDrawSegs() { ds_p = 0; } public final void ResizeDrawsegs() { drawsegs = C2JUtils.resize(drawsegs[0], drawsegs, drawsegs.length*2); } } package rr; public class pQuickSprite{ public static final void sort(vissprite_t[] c){ int i,j,left = 0,right = c.length - 1,stack_pointer = -1; int[] stack = new int[128]; vissprite_t swap,temp; while(true){ if(right - left <= 7){ for(j=left+1;j<=right;j++){ swap = c[j]; i = j-1; while(i>=left && c[i].scale>swap.scale) c[i+1] = c[i--]; c[i+1] = swap; } if(stack_pointer == -1) break; right = stack[stack_pointer--]; left = stack[stack_pointer--]; }else{ int median = (left + right) >> 1; i = left + 1; j = right; swap = c[median]; c[median] = c[i]; c[i] = swap; /* make sure: c[left] <= c[left+1] <= c[right] */ if(c[left].scale > c[right].scale){ swap = c[left]; c[left] = c[right]; c[right] = swap; }if(c[i].scale>c[right].scale ){ swap = c[i]; c[i] = c[right]; c[right] = swap; }if(c[left].scale>c[i].scale){ swap = c[left]; c[left] = c[i]; c[i] = swap; } temp = c[i]; while(true){ do i++; while(c[i].scale<temp.scale); do j--; while(c[j].scale>temp.scale); if(j < i) break; swap = c[i]; c[i] = c[j]; c[j] = swap; } c[left + 1] = c[j]; c[j] = temp; if(right-i+1 >= j-left){ stack[++stack_pointer] = i; stack[++stack_pointer] = right; right = j-1; }else{ stack[++stack_pointer] = left; stack[++stack_pointer] = j-1; left = i; } } } } } package rr; /** Sprites are patches with a special naming convention * so they can be recognized by R_InitSprites. * The base name is NNNNFx or NNNNFxFx, with * x indicating the rotation, x = 0, 1-7. * The sprite and frame specified by a thing_t * is range checked at run time. * A sprite is a patch_t that is assumed to represent * a three dimensional object and may have multiple * rotations pre drawn. * Horizontal flipping is used to save space, * thus NNNNF2F5 defines a mirrored patch. * Some sprites will only have one picture used * for all views: NNNNF0 */ public class spriteframe_t implements Cloneable{ public spriteframe_t(){ lump=new int[8]; flip=new byte[8]; } /** If false use 0 for any position. * Note: as eight entries are available, * we might as well insert the same name eight times. * * FIXME: this is used as a tri-state. * 0= false * 1= true * -1= cleared/indeterminate, which should not evaluate to either true or false. * */ public int rotate; /** Lump to use for view angles 0-7. */ public int[] lump; /** Flip bit (1 = flip) to use for view angles 0-7. */ public byte[] flip; public spriteframe_t clone(){ spriteframe_t response=new spriteframe_t(); response.rotate=rotate; System.arraycopy(this.lump, 0, response.lump, 0, lump.length); System.arraycopy(this.flip, 0, response.flip, 0, flip.length); return response; } } package rr; import static p.MobjFlags.MF_TRANSLATION; import static p.MobjFlags.MF_TRANSSHIFT; import i.Main; /** * Combined colormap and light LUTs. * Used for z-depth cuing per column/row, * and other lighting effects (sector ambient, flash). * * @author velktron * * @param <V> The data type of the SCREEN */ public class LightsAndColors<V> { /** For HiColor, these are, effectively, a bunch of 555 RGB palettes, * for TrueColor they are a bunch of 32-bit ARGB palettes etc. * Only for indexed they represent index remappings. */ /** "peg" this to the one from RendererData */ public V[] colormaps; /** lighttable_t** */ public V[] walllights; /** Use in conjunction with pfixedcolormap */ public V fixedcolormap; /** Use in conjunction with fixedcolormap[] */ public int pfixedcolormap; /** * Color tables for different players, translate a limited part to another * (color ramps used for suit colors). */ public byte[][] translationtables; /** Bits representing color levels. 5 for 32. */ public static final int LBITS; /** * These two are tied by an inverse relationship. E.g. 256 levels, 0 shift * 128 levels, 1 shift ...etc... 16 levels, 4 shift (default). Or even less, * if you want. * * By setting it to the max however you get smoother light and get rid of * lightsegshift globally, too. Of course, by increasing the number of light * levels, you also put more memory pressure, and due to their being only * 256 colors to begin with, visually, there won't be many differences. */ public static final int LIGHTLEVELS; public static final int LIGHTSEGSHIFT; /** Number of diminishing brightness levels. There a 0-31, i.e. 32 LUT in the COLORMAP lump. TODO: how can those be distinct from the light levels??? */ public static final int NUMCOLORMAPS; // These are a bit more tricky to figure out though. /** Maximum index used for light levels of sprites. In practice, * it's capped by the number of light levels??? * * Normally set to 48 (32 +16???) */ public static final int MAXLIGHTSCALE; /** Used to scale brightness of walls and sprites. Their "scale" is shifted by * this amount, and this results in an index, which is capped by MAXLIGHTSCALE. * Normally it's 12 for 32 levels, so 11 for 64, 10 for 128, ans 9 for 256. * */ public static final int LIGHTSCALESHIFT; /** This one seems arbitrary. Will auto-fit to 128 possible levels? */ public static final int MAXLIGHTZ; public static final int LIGHTBRIGHT; /** Normally 20 for 32 colormaps, applied to distance. * Formula: 25-LBITS * */ public static final int LIGHTZSHIFT; public V[][] scalelight; public V[] scalelightfixed; public V[][] zlight; public V[] spritelights; // bumped light from gun blasts public int extralight; static { // Horrible hack. switch (Main.bpp){ case Indexed: case TrueColor32: case HiColor: LBITS=5; break; case TrueColor: LBITS=8; break; default: LBITS=5; break; } LIGHTLEVELS = 1<<LBITS; MAXLIGHTZ=Math.max(LIGHTLEVELS*4,256); LIGHTBRIGHT= 2; LIGHTSEGSHIFT = 8-LBITS; NUMCOLORMAPS= LIGHTLEVELS; MAXLIGHTSCALE = 3*LIGHTLEVELS/2; LIGHTSCALESHIFT = 17 -LBITS; LIGHTZSHIFT=25-LBITS; } public final byte[] getTranslationTable(long mobjflags) { return translationtables[(int) ((mobjflags & MF_TRANSLATION)>>MF_TRANSSHIFT)]; } } package rr; /** A maptexturedef_t describes a rectangular texture, * which is composed of one or more mappatch_t structures * that arrange graphic patches. * * This is the in-memory format, which is similar to maptexture_t (which is on-disk). * * @author Maes * */ public class texture_t { /** Keep name for switch changing, etc. */ public String name; public short width; public short height; // All the patches[patchcount] // are drawn back to front into the cached texture. public short patchcount; public texpatch_t[] patches; /** Unmarshalling at its best! */ public void copyFromMapTexture(maptexture_t mt){ this.name=new String(mt.name); this.width=mt.width; this.height=mt.height; this.patchcount=mt.patchcount; this.patches=new texpatch_t[patchcount]; for (int i=0;i<patchcount;i++){ patches[i]=new texpatch_t(); patches[i].copyFromMapPatch(mt.patches[i]); } } @Override public String toString(){ StringBuilder sb=new StringBuilder(); sb.append(name); sb.append(" Height "); sb.append(height); sb.append(" Width "); sb.append(width); sb.append(" Patchcount "); sb.append(patchcount); return sb.toString(); } } package rr; /** Resets limit-removing stuff back to their initial values, * either for initialization reasons or to regain memory * e.g. playing MAP02 after nuts.wad should free up some vissprite buffers. * * @author admin * */ public interface ILimitResettable { public void resetLimits(); } package rr; import doom.thinker_t; /** Used for special sector-based function for doors, ceilings * etc. that are treated as a thinker by the engine. The sector * is part of the spec, so extending classes don't need to override * it. Also, it extends thinker so futher extensions are thinkers too. * */ public abstract class SectorAction extends thinker_t { public sector_t sector; /** Special, only used when (un)archiving in order to re-link stuff * to their proper sector. */ public int sectorid; } package rr; import static m.fixed_t.FRACBITS; import static m.fixed_t.FixedMul; import static utils.C2JUtils.eval; import java.util.Arrays; import p.Resettable; import utils.C2JUtils; import m.BBox; import m.ISyncLogger; /** BSP node. * * @author Maes * */ public class node_t implements Resettable{ public node_t(){ bbox=new BBox[2]; children= new int[2]; C2JUtils.initArrayOfObjects(bbox, BBox.class); } public node_t(int x, int y, int dx, int dy, BBox[] bbox, int[] children) { this.x = x; this.y = y; this.dx = dx; this.dy = dy; this.bbox = bbox; this.children = children; } /** (fixed_t) Partition line. */ public int x, y, dx, dy; /** Bounding box for each child. */ //public fixed_t bbox[2][4]; // Maes: make this into two proper bboxes? public BBox[] bbox; /** If NF_SUBSECTOR its a subsector. * * e6y: support for extented nodes */ public int[] children; /** * R_PointOnSide * Traverse BSP (sub) tree, * check point against partition plane. * Returns side 0 (front) or 1 (back). * @param x fixed * @param y fixed * */ public static int PointOnSide ( int x, int y, node_t node ) { // MAES: These are used mainly as ints, no need to use fixed_t internally. // fixed_t will only be used as a "pass type", but calculations will be done with ints, preferably. int dx; int dy; int left; int right; if (node.dx==0) { if (x <= node.x) return (node.dy > 0)?1:0; return (node.dy < 0)?1:0; } if (node.dy==0) { if (y <= node.y) return (node.dx < 0)?1:0; return (node.dx > 0)?1:0; } dx = (x - node.x); dy = (y - node.y); // Try to quickly decide by looking at sign bits. if ( ((node.dy ^ node.dx ^ dx ^ dy)&0x80000000 )!=0) { if ( ((node.dy ^ dx) & 0x80000000 )!=0) { // (left is negative) return 1; } return 0; } left = FixedMul ( node.dy>>FRACBITS , dx ); right = FixedMul ( dy , node.dx>>FRACBITS ); if (right < left) { // front side return 0; } // back side return 1; } /** Since no context is needed, this is perfect for an instance method * * @param x fixed * @param y fixed * @return */ public int PointOnSide ( int x, int y ) { // MAES: These are used mainly as ints, no need to use fixed_t internally. // fixed_t will only be used as a "pass type", but calculations will be done with ints, preferably. int dx; int dy; int left; int right; if (this.dx==0) { if (x <= this.x) return (this.dy > 0)?1:0; return (this.dy < 0)?1:0; } if (this.dy==0) { if (y <= this.y) return (this.dx < 0)?1:0; return (this.dx > 0)?1:0; } dx = (x - this.x); dy = (y - this.y); // Try to quickly decide by looking at sign bits. if ( ((this.dy ^ this.dx ^ dx ^ dy)&0x80000000 )!=0) { if ( ((this.dy ^ dx) & 0x80000000 )!=0) { // (left is negative) return 1; } return 0; } left = FixedMul ( this.dy>>FRACBITS , dx ); right = FixedMul ( dy , this.dx>>FRACBITS ); if (right < left) { // front side return 0; } // back side return 1; } /** * Clone of divline_t's method. Same contract, but working on node_t's to avoid casts. * P_DivlineSide * Returns side 0 (front), 1 (back), or 2 (on). */ public int DivlineSide ( int x, int y) { int left,right; // Boom-style code. Da fack. // [Maes]: using it leads to very different DEMO4 UD behavior. return (this.dx==0) ? x == this.x ? 2 : x <= this.x ? eval(this.dy > 0) : eval(this.dy < 0) : (this.dy==0) ? (olddemo ? x : y) == this.y ? 2 : y <= this.y ? eval(this.dx < 0) : eval(this.dx > 0) : (this.dy==0) ? y == this.y ? 2 : y <= this.y ? eval(this.dx < 0) : eval(this.dx > 0) : (right = ((y - this.y) >> FRACBITS) * (this.dx >> FRACBITS)) < (left = ((x - this.x) >> FRACBITS) * (this.dy >> FRACBITS)) ? 0 : right == left ? 2 : 1; /* int dx; // fixed_t int dy; if (this.dx==0) { if (x==this.x) return 2; if (x <= this.x) return (this.dy > 0)?1:0; return (this.dy < 0)?1:0; } if (this.dy==0) { if (x==this.y) return 2; if (y <= this.y) return (this.dx < 0)?1:0; return (this.dx > 0)?1:0; } dx = (x - this.x); dy = (y - this.y); left = (this.dy>>FRACBITS) * (dx>>FRACBITS); right = (dy>>FRACBITS) * (this.dx>>FRACBITS); if (right < left) return 0; // front side if (left == right) return 2; return 1; // back side */ } private static final boolean olddemo = true; public int DivlineSide ( int x, int y, ISyncLogger SL, boolean sync) { int result=DivlineSide(x,y); if (sync){ SL.sync("DLS %d\n", result); } return result; } @Override public void reset() { x=y=dx = dy = 0; for (int i=0;i<2;i++){ bbox[i].ClearBox(); } Arrays.fill(children, 0); } } package rr; /** * A very "simple" things class which just does serial rendering and uses all * the base methods from AbstractThings. * * @author velktron * @param <T> * @param <V> */ public final class SimpleThings<T,V> extends AbstractThings<T,V> { public SimpleThings(Renderer<T, V> R) { super(R); } @Override public void completeColumn() { colfunc.invoke(); } } package rr; /** An interface used to ease the use of the GetCachedColumn by part * of parallelized renderers. * * @author Maes * */ public interface IGetCachedColumn<T> { T GetCachedColumn(int tex, int col); } package rr.parallel; import java.util.concurrent.BrokenBarrierException; import java.util.concurrent.CyclicBarrier; import java.util.concurrent.Executor; import rr.AbstractThings; import rr.IMaskedDrawer; import rr.ISpriteManager; import rr.IVisSpriteManagement; import rr.Renderer; import v.IVideoScale; /** Alternate parallel sprite renderer using a split-screen strategy. * For N threads, each thread gets to render only the sprites that are entirely * in its own 1/Nth portion of the screen. * * Sprites that span more than one section, are drawn partially. Each thread * only has to worry with the priority of its own sprites. Similar to the * split-seg parallel drawer. * * Uses the "masked workers" subsystem, there is no column pipeline: workers * "tap" directly in the sprite sorted table and act accordingly (draw entirely, * draw nothing, draw partially). * * It uses masked workers to perform the actual work, each of which is a complete * Thing Drawer. * * @author velktron * */ public final class ParallelThings2<T,V> implements IMaskedDrawer<T,V> { MaskedWorker<T,V>[] maskedworkers; CyclicBarrier maskedbarrier; Executor tp; protected final IVisSpriteManagement<V> VIS; public ParallelThings2(Renderer<T,V> R) { this.VIS=R.getVisSpriteManager(); } @Override public void DrawMasked() { VIS.SortVisSprites(); for (int i = 0; i < maskedworkers.length; i++) { tp.execute(maskedworkers[i]); } try { maskedbarrier.await(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (BrokenBarrierException e) { // TODO Auto-generated catch block e.printStackTrace(); } } @Override public void completeColumn() { // Does nothing. Dummy. } @Override public void setPspriteScale(int scale) { for (int i = 0; i < maskedworkers.length; i++) maskedworkers[i].setPspriteScale(scale); } @Override public void setPspriteIscale(int scale) { for (int i = 0; i < maskedworkers.length; i++) maskedworkers[i].setPspriteIscale(scale); } @Override public void setVideoScale(IVideoScale vs) { for (int i = 0; i < maskedworkers.length; i++) maskedworkers[i].setVideoScale(vs); } @Override public void initScaling() { for (int i = 0; i < maskedworkers.length; i++) maskedworkers[i].initScaling(); } @Override public void setDetail(int detailshift) { for (int i = 0; i < maskedworkers.length; i++) maskedworkers[i].setDetail(detailshift); } @Override public void cacheSpriteManager(ISpriteManager SM) { for (int i = 0; i < maskedworkers.length; i++) maskedworkers[i].cacheSpriteManager(SM); } } package rr.parallel; /** This is all the information needed to draw a particular SEG. * It's quite a lot, actually, but much better than in testing * versions. * */ public class RenderSegInstruction<V> { public int rw_x,rw_stopx; public int toptexture,midtexture,bottomtexture; public int pixhigh,pixlow,pixhighstep,pixlowstep, topfrac, topstep,bottomfrac, bottomstep; public boolean segtextured,markfloor,markceiling; public long rw_centerangle; // angle_t /** fixed_t */ public int rw_offset,rw_distance,rw_scale, rw_scalestep,rw_midtexturemid,rw_toptexturemid,rw_bottomtexturemid; public int viewheight; V[] walllights; public int centery; } package rr.parallel; import i.IDoomSystem; import java.util.concurrent.BrokenBarrierException; import java.util.concurrent.CyclicBarrier; import rr.IDetailAware; import rr.drawfuns.ColVars; import rr.drawfuns.DcFlags; import rr.drawfuns.DoomColumnFunction; import rr.drawfuns.R_DrawColumnBoom; import rr.drawfuns.R_DrawColumnBoomLow; import rr.drawfuns.R_DrawFuzzColumn; import rr.drawfuns.R_DrawFuzzColumnLow; import rr.drawfuns.R_DrawTranslatedColumn; import rr.drawfuns.R_DrawTranslatedColumnLow; /** * This is what actual executes the RenderWallInstruction. Essentially it's a * self-contained column rendering function. * * @author velktron */ public abstract class RenderMaskedExecutor<T,V> implements Runnable,IDetailAware { protected CyclicBarrier barrier; protected ColVars<T,V>[] RMI; protected int rmiend; protected boolean lowdetail=false; protected int start, end; protected DoomColumnFunction<T,V> colfunchi, colfunclow; protected DoomColumnFunction<T,V> fuzzfunchi, fuzzfunclow; protected DoomColumnFunction<T,V> transfunchi, transfunclow; protected DoomColumnFunction<T,V> colfunc; public RenderMaskedExecutor(int SCREENWIDTH, int SCREENHEIGHT, ColVars<T,V>[] RMI, CyclicBarrier barrier ) { this.RMI = RMI; this.barrier = barrier; this.SCREENWIDTH = SCREENWIDTH; this.SCREENHEIGHT = SCREENHEIGHT; } public void setRange(int start, int end) { this.end = end; this.start = start; } public void setDetail(int detailshift) { if (detailshift == 0) lowdetail=false; else lowdetail=true; } public void run() { // System.out.println("Wall executor from "+start +" to "+ end); int dc_flags=0; // Check out ALL valid RMIs, but only draw those on YOUR side of the screen. for (int i = 0; i < rmiend; i++) { if (RMI[i].dc_x>=start && RMI[i].dc_x<=end){ // Change function type according to flags. // No flag change means reusing the last used type dc_flags=RMI[i].dc_flags; //System.err.printf("Flags transition %d\n",dc_flags); if (lowdetail){ if ((dc_flags&DcFlags.FUZZY)!=0) colfunc=fuzzfunclow; else if ((dc_flags&DcFlags.TRANSLATED)!=0) colfunc=transfunclow; else colfunc=colfunclow; } else { if ((dc_flags&DcFlags.FUZZY)!=0) colfunc=fuzzfunchi; else if ((dc_flags&DcFlags.TRANSLATED)!=0) colfunc=transfunchi; else colfunc=colfunchi; } // No need to set shared DCvars, because it's passed with the arg. colfunc.invoke(RMI[i]); } } try { barrier.await(); } catch (InterruptedException e) { e.printStackTrace(); } catch (BrokenBarrierException e) { e.printStackTrace(); } } public void setRMIEnd(int rmiend){ this.rmiend=rmiend; } public void updateRMI(ColVars<T,V>[] RMI) { this.RMI = RMI; } /////////////// VIDEO SCALE STUFF////////////////////// protected final int SCREENWIDTH; protected final int SCREENHEIGHT; /* * protected IVideoScale vs; * @Override public void setVideoScale(IVideoScale vs) { this.vs=vs; } * @Override public void initScaling() { * this.SCREENHEIGHT=vs.getScreenHeight(); * this.SCREENWIDTH=vs.getScreenWidth(); } */ public static final class HiColor extends RenderMaskedExecutor<byte[],short[]>{ public HiColor(int SCREENWIDTH, int SCREENHEIGHT, int[] columnofs, int[] ylookup, short[] screen, ColVars<byte[], short[]>[] RMI, CyclicBarrier barrier,IDoomSystem I) { super(SCREENWIDTH, SCREENHEIGHT,RMI, barrier); // Regular masked columns this.colfunc = new R_DrawColumnBoom.HiColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,null,screen,I); this.colfunclow = new R_DrawColumnBoomLow.HiColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,null,screen,I); // Fuzzy columns this.fuzzfunchi= new R_DrawFuzzColumn.HiColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,null,screen,I); this.fuzzfunclow =new R_DrawFuzzColumnLow.HiColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,null,screen,I); // Translated columns this.transfunchi=new R_DrawTranslatedColumn.HiColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,null,screen,I); this.transfunclow= new R_DrawTranslatedColumnLow.HiColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,null,screen,I); } } public static final class Indexed extends RenderMaskedExecutor<byte[],byte[]>{ public Indexed(int SCREENWIDTH, int SCREENHEIGHT, int[] columnofs, int[] ylookup, byte[] screen, ColVars<byte[], byte[]>[] RMI, CyclicBarrier barrier,IDoomSystem I,byte[] BLURRY_MAP) { super(SCREENWIDTH, SCREENHEIGHT,RMI, barrier); // Regular masked columns this.colfunc = new R_DrawColumnBoom.Indexed(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,null,screen,I); this.colfunclow = new R_DrawColumnBoomLow.Indexed(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,null,screen,I); // Fuzzy columns this.fuzzfunchi= new R_DrawFuzzColumn.Indexed(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,null,screen,I,BLURRY_MAP); this.fuzzfunclow =new R_DrawFuzzColumnLow.Indexed(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,null,screen,I,BLURRY_MAP); // Translated columns this.transfunchi=new R_DrawTranslatedColumn.Indexed(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,null,screen,I); this.transfunclow= new R_DrawTranslatedColumnLow.Indexed(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,null,screen,I); } } public static final class TrueColor extends RenderMaskedExecutor<byte[],int[]>{ public TrueColor(int SCREENWIDTH, int SCREENHEIGHT, int[] columnofs, int[] ylookup, int[] screen, ColVars<byte[], int[]>[] RMI, CyclicBarrier barrier,IDoomSystem I) { super(SCREENWIDTH, SCREENHEIGHT,RMI, barrier); // Regular masked columns this.colfunc = new R_DrawColumnBoom.TrueColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,null,screen,I); this.colfunclow = new R_DrawColumnBoomLow.TrueColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,null,screen,I); // Fuzzy columns this.fuzzfunchi= new R_DrawFuzzColumn.TrueColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,null,screen,I); this.fuzzfunclow =new R_DrawFuzzColumnLow.TrueColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,null,screen,I); // Translated columns this.transfunchi=new R_DrawTranslatedColumn.TrueColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,null,screen,I); this.transfunclow= new R_DrawTranslatedColumnLow.TrueColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,null,screen,I); } } } package rr.parallel; import java.util.concurrent.BrokenBarrierException; import java.util.concurrent.CyclicBarrier; import p.pspdef_t; import static rr.LightsAndColors.*; import rr.AbstractThings; import rr.IDetailAware; import rr.Renderer; import rr.column_t; import rr.drawseg_t; import rr.patch_t; import rr.spritedef_t; import rr.spriteframe_t; import rr.vissprite_t; import rr.drawfuns.ColFuncs; import rr.drawfuns.ColVars; import rr.drawfuns.R_DrawColumnBoom; import rr.drawfuns.R_DrawColumnBoomLow; import rr.drawfuns.R_DrawFuzzColumn; import rr.drawfuns.R_DrawFuzzColumnLow; import rr.drawfuns.R_DrawTranslatedColumn; import rr.drawfuns.R_DrawTranslatedColumnLow; import static p.mobj_t.MF_TRANSLATION; import static p.mobj_t.MF_TRANSSHIFT; import static m.fixed_t.*; import static rr.line_t.*; import static data.Defines.FF_FRAMEMASK; import static data.Defines.FF_FULLBRIGHT; import static data.Defines.pw_invisibility; /** A "Masked Worker" draws sprites in a split-screen strategy. Used by * ParallelRenderer2. Each Masked Worker is essentially a complete Things * drawer, and reuses much of the serial methods. * * @author velktron * * @param <T> * @param <V> */ public abstract class MaskedWorker<T,V> extends AbstractThings<T,V> implements Runnable, IDetailAware{ private final static boolean DEBUG=false; private final static boolean RANGECHECK=false; protected final CyclicBarrier barrier; protected final int id; protected final int numthreads; protected ColVars<T,V> maskedcvars; public MaskedWorker(Renderer<T,V> R,int id,int SCREENWIDTH, int SCREENHEIGHT,int numthreads,CyclicBarrier barrier){ super(R); // Workers have their own set, not a "pegged" one. this.colfuncshi=new ColFuncs<T,V>(); this.colfuncslow=new ColFuncs<T,V>(); this.maskedcvars=new ColVars<T,V>(); this.id=id; this.numthreads=numthreads; this.barrier=barrier; } public final void completeColumn(){ // Does nothing. Shuts up inheritance } public static final class HiColor extends MaskedWorker<byte[],short[]>{ public HiColor(Renderer<byte[],short[]> R,int id, int SCREENWIDTH, int SCREENHEIGHT, int[] ylookup, int[] columnofs, int numthreads, short[] screen, CyclicBarrier barrier) { super(R,id, SCREENWIDTH, SCREENHEIGHT,numthreads, barrier); // Non-optimized stuff for masked. colfuncshi.base=colfuncshi.main=colfuncshi.masked=new R_DrawColumnBoom.HiColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I); colfuncslow.masked=new R_DrawColumnBoomLow.HiColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I); // Fuzzy columns. These are also masked. colfuncshi.fuzz=new R_DrawFuzzColumn.HiColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I); colfuncslow.fuzz=new R_DrawFuzzColumnLow.HiColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I); // Translated columns are usually sprites-only. colfuncshi.trans=new R_DrawTranslatedColumn.HiColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I); colfuncslow.trans=new R_DrawTranslatedColumnLow.HiColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I); colfuncs=colfuncshi; } } public static final class Indexed extends MaskedWorker<byte[],byte[]>{ public Indexed(Renderer<byte[],byte[]> R,int id, int SCREENWIDTH, int SCREENHEIGHT, int[] ylookup, int[] columnofs, int numthreads, byte[] screen, CyclicBarrier barrier,byte[] BLURRY_MAP) { super(R,id, SCREENWIDTH, SCREENHEIGHT,numthreads, barrier); colfuncshi.base=colfuncshi.main=colfuncshi.masked=new R_DrawColumnBoom.Indexed(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I); colfuncslow.masked=new R_DrawColumnBoomLow.Indexed(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I); // Fuzzy columns. These are also masked. colfuncshi.fuzz=new R_DrawFuzzColumn.Indexed(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I,BLURRY_MAP); colfuncslow.fuzz=new R_DrawFuzzColumnLow.Indexed(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I,BLURRY_MAP); // Translated columns are usually sprites-only. colfuncshi.trans=new R_DrawTranslatedColumn.Indexed(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I); colfuncslow.trans=new R_DrawTranslatedColumnLow.Indexed(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I); colfuncs=colfuncshi; } } public static final class TrueColor extends MaskedWorker<byte[],int[]>{ public TrueColor(Renderer<byte[],int[]> R,int id, int SCREENWIDTH, int SCREENHEIGHT, int[] ylookup, int[] columnofs, int numthreads, int[] screen, CyclicBarrier barrier) { super(R,id, SCREENWIDTH, SCREENHEIGHT,numthreads, barrier); // Non-optimized stuff for masked. colfuncshi.base=colfuncshi.main=colfuncshi.masked=new R_DrawColumnBoom.TrueColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I); colfuncslow.masked=new R_DrawColumnBoomLow.TrueColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I); // Fuzzy columns. These are also masked. colfuncshi.fuzz=new R_DrawFuzzColumn.TrueColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I); colfuncslow.fuzz=new R_DrawFuzzColumnLow.TrueColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I); // Translated columns are usually sprites-only. colfuncshi.trans=new R_DrawTranslatedColumn.TrueColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I); colfuncslow.trans=new R_DrawTranslatedColumnLow.TrueColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I); colfuncs=colfuncshi; } } protected int startx, endx; /** * R_DrawVisSprite mfloorclip and mceilingclip should also be set. * * Sprites are actually drawn here. Obviously overrides the serial * method, and only draws a portion of the sprite. * * */ protected final void DrawVisSprite(vissprite_t<V> vis) { column_t column; int texturecolumn; int frac; // fixed_t patch_t patch; // The sprite may have been partially drawn on another portion of the // screen. int bias=startx-vis.x1; if (bias<0) bias=0; // nope, it ain't. // Trim bounds to zone NOW int x1=Math.max(startx, vis.x1); int x2=Math.min(endx,vis.x2); // At this point, the view angle (and patch) has already been // chosen. Go back. patch = W.CachePatchNum(vis.patch + SM.getFirstSpriteLump()); maskedcvars.dc_colormap = vis.colormap; // colfunc=glasscolfunc; if (maskedcvars.dc_colormap == null) { // NULL colormap = shadow draw colfunc = colfuncs.fuzz; } else if ((vis.mobjflags & MF_TRANSLATION) != 0) { colfunc = colfuncs.trans; maskedcvars.dc_translation = (T) colormaps.getTranslationTable(vis.mobjflags); } maskedcvars.dc_iscale = Math.abs(vis.xiscale) >> view.detailshift; maskedcvars.dc_texturemid = vis.texturemid; // Add bias to compensate for partially drawn sprite which has not been rejected. frac = vis.startfrac+vis.xiscale*bias; spryscale = vis.scale; sprtopscreen = view.centeryfrac - FixedMul(maskedcvars.dc_texturemid, spryscale); // A texture height of 0 means "not tiling" and holds for // all sprite/masked renders. maskedcvars.dc_texheight=0; for (maskedcvars.dc_x = x1; maskedcvars.dc_x <= x2; maskedcvars.dc_x++, frac += vis.xiscale) { texturecolumn = frac >> FRACBITS; if (true) { if (texturecolumn < 0 || texturecolumn >= patch.width) I.Error("R_DrawSpriteRange: bad texturecolumn %d vs %d %d %d",texturecolumn,patch.width,x1,x2); } column = patch.columns[texturecolumn]; if (column==null) System.err.printf("Null column for texturecolumn %d\n",texturecolumn,x1,x2); else DrawMaskedColumn(column); } colfunc = colfuncs.masked; } /** * R_RenderMaskedSegRange * * @param ds * @param x1 * @param x2 */ protected final void RenderMaskedSegRange(drawseg_t ds, int x1, int x2) { // Trivial rejection if (ds.x1>endx || ds.x2<startx) return; // Trim bounds to zone NOW x1=Math.max(startx, x1); x2=Math.min(endx,x2); int index; int lightnum; int texnum; int bias=startx-ds.x1; // Correct for starting outside if (bias<0) bias=0; // nope, it ain't. // System.out.printf("RenderMaskedSegRange from %d to %d\n",x1,x2); // Calculate light table. // Use different light tables // for horizontal / vertical / diagonal. Diagonal? // OPTIMIZE: get rid of LIGHTSEGSHIFT globally MyBSP.curline = ds.curline; frontsector = MyBSP.curline.frontsector; backsector = MyBSP.curline.backsector; texnum = TexMan.getTextureTranslation(MyBSP.curline.sidedef.midtexture); // System.out.print(" for texture "+textures[texnum].name+"\n:"); lightnum = (frontsector.lightlevel >> LIGHTSEGSHIFT) + colormaps.extralight; if (MyBSP.curline.v1y == MyBSP.curline.v2y) lightnum--; else if (MyBSP.curline.v1x == MyBSP.curline.v2x) lightnum++; // Killough code. colormaps.walllights = lightnum >= LIGHTLEVELS ? colormaps.scalelight[LIGHTLEVELS - 1] : lightnum < 0 ? colormaps.scalelight[0] : colormaps.scalelight[lightnum]; // Get the list maskedtexturecol = ds.getMaskedTextureColList(); // And this is the pointer. pmaskedtexturecol = ds.getMaskedTextureColPointer(); rw_scalestep = ds.scalestep; spryscale = ds.scale1 + (x1 - ds.x1) * rw_scalestep; // HACK to get "pointers" inside clipping lists mfloorclip = ds.getSprBottomClipList(); p_mfloorclip = ds.getSprBottomClipPointer(); mceilingclip = ds.getSprTopClipList(); p_mceilingclip = ds.getSprTopClipPointer(); // find positioning if ((MyBSP.curline.linedef.flags & ML_DONTPEGBOTTOM) != 0) { maskedcvars.dc_texturemid = frontsector.floorheight > backsector.floorheight ? frontsector.floorheight : backsector.floorheight; maskedcvars.dc_texturemid = maskedcvars.dc_texturemid + TexMan.getTextureheight(texnum) - view.z; } else { maskedcvars.dc_texturemid = frontsector.ceilingheight < backsector.ceilingheight ? frontsector.ceilingheight : backsector.ceilingheight; maskedcvars.dc_texturemid = maskedcvars.dc_texturemid - view.z; } maskedcvars.dc_texturemid += MyBSP.curline.sidedef.rowoffset; if (colormaps.fixedcolormap != null) maskedcvars.dc_colormap = colormaps.fixedcolormap; // Texture height must be set at this point. This will trigger // tiling. For sprites, it should be set to 0. maskedcvars.dc_texheight = TexMan.getTextureheight(texnum) >> FRACBITS; // draw the columns for (maskedcvars.dc_x = x1; maskedcvars.dc_x <= x2; maskedcvars.dc_x++) { // calculate lighting if (maskedtexturecol[pmaskedtexturecol + maskedcvars.dc_x] != Short.MAX_VALUE) { if (colormaps.fixedcolormap == null) { index = spryscale >>> LIGHTSCALESHIFT; if (index >= MAXLIGHTSCALE) index = MAXLIGHTSCALE - 1; maskedcvars.dc_colormap = colormaps.walllights[index]; } sprtopscreen = view.centeryfrac - FixedMul(maskedcvars.dc_texturemid, spryscale); maskedcvars.dc_iscale = (int) (0xffffffffL / spryscale); // draw the texture column_t data = TexMan.GetSmpColumn(texnum, maskedtexturecol[pmaskedtexturecol + maskedcvars.dc_x],id); DrawMaskedColumn(data); maskedtexturecol[pmaskedtexturecol + maskedcvars.dc_x] = Short.MAX_VALUE; } spryscale += rw_scalestep; } } /** * R_DrawPSprite * * Draws a "player sprite" with slighly different rules than normal * sprites. This is actually a PITA, at best :-/ * * Also different than normal implementation. * */ protected final void DrawPSprite(pspdef_t psp) { int tx; int x1; int x2; spritedef_t sprdef; spriteframe_t sprframe; vissprite_t<V> vis; int lump; boolean flip; // // decide which patch to use (in terms of angle?) if (RANGECHECK) { if (psp.state.sprite.ordinal() >= SM.getNumSprites()) I.Error("R_ProjectSprite: invalid sprite number %d ", psp.state.sprite); } sprdef = SM.getSprite(psp.state.sprite.ordinal()); if (RANGECHECK) { if ((psp.state.frame & FF_FRAMEMASK) >= sprdef.numframes) I.Error("R_ProjectSprite: invalid sprite frame %d : %d ", psp.state.sprite, psp.state.frame); } sprframe = sprdef.spriteframes[psp.state.frame & FF_FRAMEMASK]; // Base frame for "angle 0" aka viewed from dead-front. lump = sprframe.lump[0]; // Q: where can this be set? A: at sprite loadtime. flip = (boolean) (sprframe.flip[0] != 0); // calculate edges of the shape. tx is expressed in "view units". tx = (int) (FixedMul(psp.sx, view.BOBADJUST) - view.WEAPONADJUST); tx -= spriteoffset[lump]; // So...centerxfrac is the center of the screen (pixel coords in // fixed point). x1 = (view.centerxfrac + FixedMul(tx, pspritescale)) >> FRACBITS; // off the right side if (x1 > endx) return; tx += spritewidth[lump]; x2 = ((view.centerxfrac + FixedMul(tx, pspritescale)) >> FRACBITS) - 1; // off the left side if (x2 < startx) return; // store information in a vissprite ? vis = avis; vis.mobjflags = 0; vis.texturemid = ((BASEYCENTER+view.lookdir) << FRACBITS) + FRACUNIT / 2 - (psp.sy - spritetopoffset[lump]); vis.x1 = x1 < startx ? startx : x1; vis.x2 = x2 >= endx ? endx - 1 : x2; vis.scale = (pspritescale) << view.detailshift; if (flip) { vis.xiscale = -pspriteiscale; vis.startfrac = spritewidth[lump] - 1; } else { vis.xiscale = pspriteiscale; vis.startfrac = 0; } if (vis.x1 > x1) vis.startfrac += vis.xiscale * (vis.x1 - x1); vis.patch = lump; if ((view.player.powers[pw_invisibility] > 4 * 32) || (view.player.powers[pw_invisibility] & 8) != 0) { // shadow draw vis.colormap = null; } else if (colormaps.fixedcolormap != null) { // fixed color vis.colormap = colormaps.fixedcolormap; // vis.pcolormap=0; } else if ((psp.state.frame & FF_FULLBRIGHT) != 0) { // full bright vis.colormap = colormaps.colormaps[0]; // vis.pcolormap=0; } else { // local light vis.colormap = colormaps.spritelights[MAXLIGHTSCALE - 1]; } //System.out.printf("Weapon draw from %d to %d\n",vis.x1,vis.x2); DrawVisSprite(vis); } /** * R_DrawMasked * * Sorts and draws vissprites (room for optimization in sorting func.) * Draws masked textures. Draws player weapons and overlays (psprites). * * Sorting function can be swapped for almost anything, and it will work * better, in-place and be simpler to draw, too. * * */ @Override public void run() { // vissprite_t spr; int ds; drawseg_t dss; // Sprites should already be sorted for distance colfunc = colfuncs.masked; // Sprites use fully-masked capable // function. // Update view height this.maskedcvars.viewheight=view.height; this.maskedcvars.centery=view.centery; this.startx=((id*view.width)/numthreads); this.endx=(((id+1)*view.width)/numthreads); // Update thread's own vissprites final vissprite_t<V>[] vissprites=VIS.getVisSprites(); final int numvissprites=VIS.getNumVisSprites(); //System.out.printf("Sprites to render: %d\n",numvissprites); // Try drawing all sprites that are on your side of // the screen. Limit by x1 and x2, if you have to. for (int i = 0; i < numvissprites; i++) { DrawSprite(vissprites[i]); } //System.out.printf("Segs to render: %d\n",ds_p); // render any remaining masked mid textures for (ds = seg_vars.ds_p - 1; ds >= 0; ds--) { dss = seg_vars.drawsegs[ds]; if (!(dss.x1>endx || dss.x2<startx)&&!dss.nullMaskedTextureCol()) RenderMaskedSegRange(dss, dss.x1,dss.x2); } // draw the psprites on top of everything // but does not draw on side views // if (viewangleoffset==0) colfunc = colfuncs.player; DrawPlayerSprites(); colfunc = colfuncs.masked; try { barrier.await(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (BrokenBarrierException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } package rr.parallel; import static rr.LightsAndColors.LIGHTLEVELS; import static rr.LightsAndColors.MAXLIGHTSCALE; import static rr.LightsAndColors.MAXLIGHTZ; import java.io.IOException; import java.util.concurrent.BrokenBarrierException; import java.util.concurrent.CyclicBarrier; import java.util.concurrent.Executor; import java.util.concurrent.Executors; import rr.IDetailAware; import rr.PlaneDrawer; import rr.SimpleThings; import rr.patch_t; import rr.drawfuns.ColVars; import rr.drawfuns.DcFlags; import rr.drawfuns.R_DrawColumnBoom; import rr.drawfuns.R_DrawColumnBoomLow; import rr.drawfuns.R_DrawColumnBoomOpt; import rr.drawfuns.R_DrawColumnBoomOptLow; import rr.drawfuns.R_DrawFuzzColumn; import rr.drawfuns.R_DrawFuzzColumnLow; import rr.drawfuns.R_DrawSpanLow; import rr.drawfuns.R_DrawSpanUnrolled; import rr.drawfuns.R_DrawTLColumn; import rr.drawfuns.R_DrawTranslatedColumn; import rr.drawfuns.R_DrawTranslatedColumnLow; import utils.C2JUtils; import v.DoomVideoRenderer; import doom.DoomMain; import doom.player_t; /** * This is Mocha Doom's famous parallel software renderer. It builds on the * basic software renderer, but adds specialized handling for drawing segs * (walls) and spans (floors) in parallel. There's inherent parallelism between * walls and floor, and internal parallelism between walls and between floors. * However, visplane limits and openings need to be pre-computed before any * actual drawing starts, that's why rendering of walls is stored in "RWI"s or * "Render Wall Instructions", and then rendered once they are all in place and * the can be parallelized between rendering threads. Rendering of sprites is * NOT parallelized yet (and probably not worth it, at this point). * * @author admin */ public abstract class ParallelRenderer<T, V> extends AbstractParallelRenderer<T, V> { public ParallelRenderer(DoomMain<T, V> DM, int wallthread, int floorthreads, int nummaskedthreads) { super(DM, wallthread, floorthreads, nummaskedthreads); // Register parallel seg drawer with list of RWI subsystems. ParallelSegs tmp= new ParallelSegs(this); this.MySegs = tmp; RWIs= tmp; this.MyThings = new SimpleThings<T,V>(this); //this.MyPlanes = new Planes(this);// new ParallelPlanes<T, V>(DM.R); } /** * Default constructor, 1 seg, 1 span and two masked threads. * * @param DM */ public ParallelRenderer(DoomMain<T, V> DM) { this(DM, 1, 1, 2); } /** * R_RenderView As you can guess, this renders the player view of a * particular player object. In practice, it could render the view of any * mobj too, provided you adapt the SetupFrame method (where the viewing * variables are set). * * @throws IOException */ public void RenderPlayerView(player_t player) { // Viewing variables are set according to the player's mobj. Interesting // hacks like // free cameras or monster views can be done. SetupFrame(player); /* * Uncommenting this will result in a very existential experience if * (Math.random()>0.999){ thinker_t shit=P.getRandomThinker(); try { * mobj_t crap=(mobj_t)shit; player.mo=crap; } catch (ClassCastException * e){ } } */ // Clear buffers. MyBSP.ClearClipSegs(); seg_vars.ClearDrawSegs(); vp_vars.ClearPlanes(); MySegs.ClearClips(); VIS.ClearSprites(); // Check for new console commands. DGN.NetUpdate(); // The head node is the last node output. MyBSP.RenderBSPNode(LL.numnodes - 1); // System.out.printf("Submitted %d RWIs\n",RWIcount); MySegs.CompleteRendering(); // Check for new console commands. DGN.NetUpdate(); // "Warped floor" fixed, same-height visplane merging fixed. MyPlanes.DrawPlanes(); // Check for new console commands. DGN.NetUpdate(); MySegs.sync(); MyPlanes.sync(); // drawsegsbarrier.await(); // visplanebarrier.await(); MyThings.DrawMasked(); // RenderRMIPipeline(); /* * try { maskedbarrier.await(); } catch (Exception e) { * e.printStackTrace(); } */ // Check for new console commands. DGN.NetUpdate(); } public static final class Indexed extends ParallelRenderer<byte[], byte[]> { public Indexed(DoomMain<byte[], byte[]> DM, int wallthread, int floorthreads, int nummaskedthreads) { super(DM, wallthread, floorthreads, nummaskedthreads); // Init light levels colormaps.scalelight = new byte[LIGHTLEVELS][MAXLIGHTSCALE][]; colormaps.scalelightfixed = new byte[MAXLIGHTSCALE][]; colormaps.zlight = new byte[LIGHTLEVELS][MAXLIGHTZ][]; completeInit(); } /** * R_InitColormaps * * @throws IOException */ protected void InitColormaps() throws IOException { int lump, length; // Load in the light tables, // 256 byte align tables. lump = W.GetNumForName("COLORMAP"); length = W.LumpLength(lump) + 256; colormaps.colormaps = new byte[(length / 256)][256]; System.out.println("Colormaps: " + colormaps.colormaps.length); byte[] tmp = new byte[length]; W.ReadLump(lump,tmp); for (int i = 0; i < colormaps.colormaps.length; i++) { System.arraycopy(tmp, i * 256, colormaps.colormaps[i], 0, 256); } // MAES: blurry effect is hardcoded to this colormap. BLURRY_MAP=colormaps.colormaps[6]; // colormaps = (byte *)( ((int)colormaps + 255)&~0xff); } protected void R_InitDrawingFunctions(){ // Span functions. Common to all renderers unless overriden // or unused e.g. parallel renderers ignore them. DrawSpan=new R_DrawSpanUnrolled.Indexed(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,dsvars,screen,I); DrawSpanLow=new R_DrawSpanLow.Indexed(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,dsvars,screen,I); // Translated columns are usually sprites-only. DrawTranslatedColumn=new R_DrawTranslatedColumn.Indexed(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I); DrawTranslatedColumnLow=new R_DrawTranslatedColumnLow.Indexed(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I); // DrawTLColumn=new R_DrawTLColumn(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I); // Fuzzy columns. These are also masked. DrawFuzzColumn=new R_DrawFuzzColumn.Indexed(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I,BLURRY_MAP); DrawFuzzColumnLow=new R_DrawFuzzColumnLow.Indexed(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I,BLURRY_MAP); // Regular draw for solid columns/walls. Full optimizations. DrawColumn=new R_DrawColumnBoomOpt.Indexed(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,dcvars,screen,I); DrawColumnLow=new R_DrawColumnBoomOptLow.Indexed(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,dcvars,screen,I); // Non-optimized stuff for masked. DrawColumnMasked=new R_DrawColumnBoom.Indexed(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I); DrawColumnMaskedLow=new R_DrawColumnBoomLow.Indexed(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I); // Player uses masked DrawColumnPlayer=DrawColumnMasked; // Player normally uses masked. // Skies use their own. This is done in order not to stomp parallel threads. DrawColumnSkies=new R_DrawColumnBoomOpt.Indexed(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,skydcvars,screen,I); DrawColumnSkiesLow=new R_DrawColumnBoomOptLow.Indexed(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,skydcvars,screen,I); super.R_InitDrawingFunctions(); } protected void InitMaskedWorkers() { for (int i = 0; i < NUMMASKEDTHREADS; i++) { maskedworkers[i] = new MaskedWorker.Indexed(this, i, SCREENWIDTH, SCREENHEIGHT, ylookup, columnofs, NUMMASKEDTHREADS, screen, maskedbarrier, BLURRY_MAP); detailaware.add(maskedworkers[i]); // "Peg" to sprite manager. maskedworkers[i].cacheSpriteManager(SM); } } @Override public RenderWallExecutor<byte[], byte[]>[] InitRWIExecutors( int num,ColVars<byte[], byte[]>[] RWI) { RenderWallExecutor<byte[],byte[]>[] tmp= new RenderWallExecutor.Indexed[num]; for (int i=0;i<num;i++) tmp[i]=new RenderWallExecutor.Indexed(SCREENWIDTH, SCREENHEIGHT, columnofs, ylookup, screen, RWI, drawsegsbarrier); return tmp; } } @Override protected void InitParallelStuff() { // ...yeah, it works. if (!(RWIs==null)){ ColVars<T,V>[] RWI=RWIs.getRWI(); RenderWallExecutor<T,V>[] RWIExec=InitRWIExecutors(NUMWALLTHREADS,RWI); RWIs.setExecutors(RWIExec); for (int i = 0; i < NUMWALLTHREADS; i++) { detailaware.add(RWIExec[i]); } } // CATCH: this must be executed AFTER screen is set, and // AFTER we initialize the RWI themselves, // before V is set (right?) // This actually only creates the necessary arrays and // barriers. Things aren't "wired" yet. // Using "render wall instruction" subsystem // Using masked sprites // RMIExec = new RenderMaskedExecutor[NUMMASKEDTHREADS]; // Using //vpw = new Runnable[NUMFLOORTHREADS]; //maskedworkers = new MaskedWorker.Indexed[NUMMASKEDTHREADS]; // RWIcount = 0; // InitRWISubsystem(); // InitRMISubsystem(); // InitPlaneWorkers(); // InitMaskedWorkers(); // If using masked threads, set these too. TexMan.setSMPVars(NUMMASKEDTHREADS); } /* * private void InitPlaneWorkers(){ for (int i = 0; i < NUMFLOORTHREADS; * i++) { vpw[i] = new VisplaneWorker2(i,SCREENWIDTH, SCREENHEIGHT, * columnofs, ylookup, screen, visplanebarrier, NUMFLOORTHREADS); * //vpw[i].id = i; detailaware.add((IDetailAware) vpw[i]); } } */ @Override public void initScaling() { super.initScaling(); /* * TODO: relay to dependent objects. super.initScaling(); * ColVars<byte[],byte[]> fake = new ColVars<byte[],byte[]>(); RWI = * C2JUtils.createArrayOfObjects(fake, SCREENWIDTH * 3); // Be MUCH more * generous with this one. RMI = C2JUtils.createArrayOfObjects(fake, * SCREENWIDTH * 6); */ } protected abstract void InitMaskedWorkers(); public static final class HiColor extends ParallelRenderer<byte[], short[]> { public HiColor(DoomMain<byte[], short[]> DM, int wallthread, int floorthreads, int nummaskedthreads) { super(DM, wallthread, floorthreads, nummaskedthreads); // Init light levels colormaps.scalelight = new short[LIGHTLEVELS][MAXLIGHTSCALE][]; colormaps.scalelightfixed = new short[MAXLIGHTSCALE][]; colormaps.zlight = new short[LIGHTLEVELS][MAXLIGHTZ][]; } protected void InitMaskedWorkers() { for (int i = 0; i < NUMMASKEDTHREADS; i++) { maskedworkers[i] = new MaskedWorker.HiColor(this, i, SCREENWIDTH, SCREENHEIGHT, ylookup, columnofs, NUMMASKEDTHREADS, screen, maskedbarrier); detailaware.add(maskedworkers[i]); // "Peg" to sprite manager. maskedworkers[i].cacheSpriteManager(SM); } } /** * R_InitColormaps This is VERY different for hicolor. * * @throws IOException */ protected void InitColormaps() throws IOException { colormaps.colormaps = V.getColorMaps(); System.out.println("COLORS15 Colormaps: " + colormaps.colormaps.length); // MAES: blurry effect is hardcoded to this colormap. // Pointless, since we don't use indexes. Instead, a half-brite // processing works just fine. BLURRY_MAP = null;// colormaps[0]; } protected void R_InitDrawingFunctions(){ // Span functions. Common to all renderers unless overriden // or unused e.g. parallel renderers ignore them. DrawSpan=new R_DrawSpanUnrolled.HiColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,dsvars,screen,I); DrawSpanLow=new R_DrawSpanLow.HiColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,dsvars,screen,I); // Translated columns are usually sprites-only. DrawTranslatedColumn=new R_DrawTranslatedColumn.HiColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I); DrawTranslatedColumnLow=new R_DrawTranslatedColumnLow.HiColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I); DrawTLColumn=new R_DrawTLColumn(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I); // Fuzzy columns. These are also masked. DrawFuzzColumn=new R_DrawFuzzColumn.HiColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I); DrawFuzzColumnLow=new R_DrawFuzzColumnLow.HiColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I); // Regular draw for solid columns/walls. Full optimizations. DrawColumn=new R_DrawColumnBoomOpt.HiColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,dcvars,screen,I); DrawColumnLow=new R_DrawColumnBoomOptLow.HiColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,dcvars,screen,I); // Non-optimized stuff for masked. DrawColumnMasked=new R_DrawColumnBoom.HiColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I); DrawColumnMaskedLow=new R_DrawColumnBoomLow.HiColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I); // Player uses masked DrawColumnPlayer=DrawColumnMasked; // Player normally uses masked. // Skies use their own. This is done in order not to stomp parallel threads. DrawColumnSkies=new R_DrawColumnBoomOpt.HiColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,skydcvars,screen,I); DrawColumnSkiesLow=new R_DrawColumnBoomOptLow.HiColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,skydcvars,screen,I); super.R_InitDrawingFunctions(); } @Override public RenderWallExecutor<byte[], short[]>[] InitRWIExecutors( int num,ColVars<byte[], short[]>[] RWI) { RenderWallExecutor<byte[],short[]>[] tmp= new RenderWallExecutor.HiColor[num]; for (int i=0;i<num;i++) tmp[i]=new RenderWallExecutor.HiColor(SCREENWIDTH, SCREENHEIGHT, columnofs, ylookup, screen, RWI, drawsegsbarrier); return tmp; } } public static final class TrueColor extends ParallelRenderer<byte[], int[]> { public TrueColor(DoomMain<byte[], int[]> DM, int wallthread, int floorthreads, int nummaskedthreads) { super(DM, wallthread, floorthreads, nummaskedthreads); // Init light levels colormaps.scalelight = new int[LIGHTLEVELS][MAXLIGHTSCALE][]; colormaps.scalelightfixed = new int[MAXLIGHTSCALE][]; colormaps.zlight = new int[LIGHTLEVELS][MAXLIGHTZ][]; } protected void R_InitDrawingFunctions(){ // Span functions. Common to all renderers unless overriden // or unused e.g. parallel renderers ignore them. DrawSpan=new R_DrawSpanUnrolled.TrueColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,dsvars,screen,I); DrawSpanLow=new R_DrawSpanLow.TrueColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,dsvars,screen,I); // Translated columns are usually sprites-only. DrawTranslatedColumn=new R_DrawTranslatedColumn.TrueColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I); DrawTranslatedColumnLow=new R_DrawTranslatedColumnLow.TrueColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I); //DrawTLColumn=new R_DrawTLColumn(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I); // Fuzzy columns. These are also masked. DrawFuzzColumn=new R_DrawFuzzColumn.TrueColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I); DrawFuzzColumnLow=new R_DrawFuzzColumnLow.TrueColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I); // Regular draw for solid columns/walls. Full optimizations. DrawColumn=new R_DrawColumnBoomOpt.TrueColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,dcvars,screen,I); DrawColumnLow=new R_DrawColumnBoomOptLow.TrueColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,dcvars,screen,I); // Non-optimized stuff for masked. DrawColumnMasked=new R_DrawColumnBoom.TrueColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I); DrawColumnMaskedLow=new R_DrawColumnBoomLow.TrueColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I); // Player uses masked DrawColumnPlayer=DrawColumnMasked; // Player normally uses masked. // Skies use their own. This is done in order not to stomp parallel threads. DrawColumnSkies=new R_DrawColumnBoomOpt.TrueColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,skydcvars,screen,I); DrawColumnSkiesLow=new R_DrawColumnBoomOptLow.TrueColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,skydcvars,screen,I); super.R_InitDrawingFunctions(); } /** * R_InitColormaps This is VERY different for hicolor. * * @throws IOException */ protected void InitColormaps() throws IOException { colormaps.colormaps = V.getColorMaps(); System.out.println("COLORS15 Colormaps: " + colormaps.colormaps.length); // MAES: blurry effect is hardcoded to this colormap. // Pointless, since we don't use indexes. Instead, a half-brite // processing works just fine. BLURRY_MAP = null;// colormaps[0]; } protected void InitMaskedWorkers() { for (int i = 0; i < NUMMASKEDTHREADS; i++) { maskedworkers[i] = new MaskedWorker.TrueColor(this,i, SCREENWIDTH, SCREENHEIGHT, ylookup, columnofs, NUMMASKEDTHREADS, screen, maskedbarrier); detailaware.add(maskedworkers[i]); // "Peg" to sprite manager. maskedworkers[i].cacheSpriteManager(SM); } } @Override public RenderWallExecutor<byte[], int[]>[] InitRWIExecutors( int num,ColVars<byte[], int[]>[] RWI) { RenderWallExecutor<byte[],int[]>[] tmp= new RenderWallExecutor.TrueColor[num]; for (int i=0;i<num;i++) tmp[i]=new RenderWallExecutor.TrueColor(SCREENWIDTH, SCREENHEIGHT, columnofs, ylookup, screen, RWI, drawsegsbarrier); return tmp; } } } package rr.parallel; import static data.Limits.*; import java.util.concurrent.CyclicBarrier; import java.util.concurrent.Executors; import rr.patch_t; import utils.C2JUtils; import v.DoomVideoRenderer; import doom.DoomMain; import doom.DoomStatus; import doom.player_t; /** This is a second attempt at building a seg-focused parallel renderer, instead of * column-based. It does function, but is broken and has unsolved data dependencies. * It's therefore not used in official releases, and I chose to deprecate it. * If you still want to develop it, be my guest. * * @author velktron * */ public class ParallelRenderer2 extends AbstractParallelRenderer { public ParallelRenderer2(DoomStatus DS, int wallthread, int floorthreads,int nummaskedthreads) { super(DS,wallthread,floorthreads,nummaskedthreads); System.out.println("Parallel Renderer 2 (Seg-based)"); this.MySegs=new ParallelSegs2(); this.MyPlanes=new ParallelPlanes(); this.MyThings=new ParallelThings2(); } public ParallelRenderer2(DoomStatus DS, int wallthread, int floorthreads) { super(DS,wallthread,floorthreads); System.out.println("Parallel Renderer 2 (Seg-based)"); this.MySegs=new ParallelSegs2(); this.MyPlanes=new ParallelPlanes(); this.MyThings=new ParallelThings2(); } /** Default constructor, 2 wall threads and one floor thread. * * @param DM */ public ParallelRenderer2(DoomMain DM) { this(DM,2,1); } @Override protected void InitParallelStuff() { // Prepare parallel stuff RSIExec=new RenderSegExecutor[NUMWALLTHREADS]; tp= Executors.newFixedThreadPool(NUMWALLTHREADS+NUMFLOORTHREADS); // Prepare the barrier for MAXTHREADS + main thread. //wallbarrier=new CyclicBarrier(NUMWALLTHREADS+1); visplanebarrier=new CyclicBarrier(NUMFLOORTHREADS+NUMWALLTHREADS+1); vpw=new VisplaneWorker[NUMFLOORTHREADS]; // Uses "seg" parallel drawer, so RSI. InitRSISubsystem(); // Masked workers. maskedworkers=new MaskedWorker[NUMMASKEDTHREADS]; maskedbarrier = new CyclicBarrier(NUMMASKEDTHREADS + 1); InitMaskedWorkers(); // If using masked threads, set these too. smp_composite=new boolean[NUMMASKEDTHREADS];// = false; smp_lasttex=new int[NUMMASKEDTHREADS];// = -1; smp_lastlump=new int[NUMMASKEDTHREADS];// = -1; smp_lastpatch=new patch_t[NUMMASKEDTHREADS];// = null; } ///////////////////////// The actual rendering calls /////////////////////// /** * R_RenderView * * As you can guess, this renders the player view of a particular player object. * In practice, it could render the view of any mobj too, provided you adapt the * SetupFrame method (where the viewing variables are set). * */ public void RenderPlayerView (player_t player) { // Viewing variables are set according to the player's mobj. Interesting hacks like // free cameras or monster views can be done. SetupFrame (player); /* Uncommenting this will result in a very existential experience if (Math.random()>0.999){ thinker_t shit=P.getRandomThinker(); try { mobj_t crap=(mobj_t)shit; player.mo=crap; } catch (ClassCastException e){ } }*/ // Clear buffers. MyBSP.ClearClipSegs (); MyBSP.ClearDrawSegs (); MyPlanes.ClearPlanes (); MySegs.ClearClips(); VIS.ClearSprites (); // Check for new console commands. DGN.NetUpdate (); // The head node is the last node output. MyBSP.RenderBSPNode (LL.numnodes-1); RenderRSIPipeline(); // Check for new console commands. DGN.NetUpdate (); // "Warped floor" fixed, same-height visplane merging fixed. MyPlanes.DrawPlanes (); try { visplanebarrier.await(); } catch (Exception e){ e.printStackTrace(); } // Check for new console commands. DGN.NetUpdate (); MyThings.DrawMasked (); colfunc=basecolfunc; // Check for new console commands. DGN.NetUpdate (); } /** * R_Init */ //public int detailLevel; //public int screenblocks=9; // has defa7ult public void Init () { // Any good reason for this to be here? //drawsegs=new drawseg_t[MAXDRAWSEGS]; //C2JUtils.initArrayOfObjects(drawsegs); // DON'T FORGET ABOUT MEEEEEE!!!11!!! this.screen=V.getScreen(DoomVideoRenderer.SCREEN_FG); System.out.print("\nR_InitData"); InitData (); //InitPointToAngle (); System.out.print("\nR_InitPointToAngle"); // ds.DM.viewwidth / ds.viewheight / detailLevel are set by the defaults System.out.print ("\nR_InitTables"); InitTables (); SetViewSize (DM.M.getScreenBlocks(), DM.M.getDetailLevel()); System.out.print ("\nR_InitPlanes"); MyPlanes.InitPlanes (); System.out.print("\nR_InitLightTables"); InitLightTables (); System.out.print("\nR_InitSkyMap: "+TexMan.InitSkyMap ()); System.out.print("\nR_InitTranslationsTables"); InitTranslationTables (); System.out.print("\nR_InitParallelStuff: "); InitParallelStuff(); System.out.print("\nR_InitTranMap: "); R_InitTranMap(0); System.out.print("\nR_InitDrawingFunctions: "); R_InitDrawingFunctions(); framecount = 0; } @Override public void initScaling(){ super.initScaling(); this.RSI=new RenderSegInstruction[MAXSEGS*3]; C2JUtils.initArrayOfObjects(RSI); } } package rr.parallel; import static data.Tables.finetangent; import static m.fixed_t.FRACBITS; import static m.fixed_t.FixedMul; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.concurrent.BrokenBarrierException; import java.util.concurrent.CyclicBarrier; import java.util.concurrent.Executor; import java.util.concurrent.Executors; import data.Tables; import doom.DoomStatus; import rr.PlaneDrawer; import rr.Renderer; import rr.RendererState; import rr.drawfuns.ColVars; import utils.C2JUtils; /** * Features and functionality which is common among parallel renderers * * @author velktron */ public abstract class AbstractParallelRenderer<T, V> extends RendererState<T, V> implements RWI.Init<T, V>{ public AbstractParallelRenderer(DoomStatus<T, V> DS, int wallthread, int floorthreads, int nummaskedthreads) { super(DS); this.NUMWALLTHREADS = wallthread; this.NUMFLOORTHREADS = floorthreads; this.NUMMASKEDTHREADS = nummaskedthreads; // Prepare the barriers for MAXTHREADS + main thread. drawsegsbarrier = new CyclicBarrier(NUMWALLTHREADS + 1); visplanebarrier = new CyclicBarrier(NUMFLOORTHREADS + 1); maskedbarrier = new CyclicBarrier(NUMMASKEDTHREADS + 1); tp = Executors.newCachedThreadPool(); } public AbstractParallelRenderer(DoomStatus<T, V> DS, int wallthread, int floorthreads) { super(DS); this.NUMWALLTHREADS = wallthread; this.NUMFLOORTHREADS = floorthreads; this.NUMMASKEDTHREADS = 1; // Prepare the barriers for MAXTHREADS + main thread. drawsegsbarrier = new CyclicBarrier(NUMWALLTHREADS + 1); visplanebarrier = new CyclicBarrier(NUMFLOORTHREADS + 1); maskedbarrier = new CyclicBarrier(NUMMASKEDTHREADS + 1); tp = Executors.newCachedThreadPool(); } // //////// PARALLEL OBJECTS ///////////// protected final int NUMWALLTHREADS; protected final int NUMMASKEDTHREADS; protected final int NUMFLOORTHREADS; protected Executor tp; protected Runnable[] vpw; protected MaskedWorker<T, V>[] maskedworkers; protected CyclicBarrier drawsegsbarrier; protected CyclicBarrier visplanebarrier; protected CyclicBarrier maskedbarrier; protected static final boolean DEBUG = false; protected final class ParallelSegs extends SegDrawer implements RWI.Get<T,V>{ public ParallelSegs(Renderer<?, ?> R) { super(R); } /** * Parallel version. Since there's so much crap to take into account * when rendering, the number of walls to render is unknown a-priori and * the BSP trasversal itself is not worth parallelizing, it makes more * sense to store "rendering instructions" as quickly as the BSP can be * transversed, and then execute those in parallel. Also saves on having * to duplicate way too much status. */ @Override protected final void CompleteColumn() { // Don't wait to go over if (RWIcount >= RWI.length) { ResizeRWIBuffer(); } // A deep copy is still necessary, as dc RWI[RWIcount].copyFrom(dcvars); // We only need to point to the next one in the list. RWIcount++; } /** * Starts the RenderWallExecutors. Sync is EXTERNAL, however. */ @Override public void CompleteRendering() { for (int i = 0; i < NUMWALLTHREADS; i++) { RWIExec[i].setRange((i * RWIcount) / NUMWALLTHREADS, ((i + 1) * RWIcount) / NUMWALLTHREADS); // RWIExec[i].setRange(i%NUMWALLTHREADS,RWIcount,NUMWALLTHREADS); tp.execute(RWIExec[i]); } // System.out.println("RWI count"+RWIcount); RWIcount = 0; } /* * Just what are "RWIs"? Stored wall rendering instructions. They can be * at most 3*SCREENWIDTH (if there are low, mid and high textures on * every column of the screen) Remember to init them and set screen and * ylookup for all of them. Their max number is static and work * partitioning can be done in any way, as long as you keep track of how * many there are in any given frame. This value is stored inside * RWIcount. TODO: there are cases when more than 3*SCREENWIDTH * instructions need to be stored. therefore we really need a resizeable * array here, but ArrayList is way too slow for our needs. Storing a * whole wall is not an option, as, once again, a wall may have a * variable number of columns and an irregular height profile -> we'd * need to look into visplanes ugh... */ protected RenderWallExecutor<T, V>[] RWIExec; /** Array of "wall" (actually, column) instructions */ protected ColVars<T, V>[] RWI; /** * Increment this as you submit RWIs to the "queue". Remember to reset * to 0 when you have drawn everything! */ protected int RWIcount = 0; /** * Resizes RWI buffer, updates executors. Sorry for the hackish * implementation but ArrayList and pretty much everything in * Collections is way too slow for what we're trying to accomplish. */ protected final void ResizeRWIBuffer() { ColVars<T, V> fake = new ColVars<T, V>(); // Bye bye, old RWI. RWI = C2JUtils.resize(fake, RWI, RWI.length * 2); for (int i = 0; i < NUMWALLTHREADS; i++) { RWIExec[i].updateRWI(RWI); } // System.err.println("RWI Buffer resized. Actual capacity " + // RWI.length); } /** * R_InitRWISubsystem Initialize RWIs and RWI Executors. Pegs them to * the RWI, ylookup and screen[0]. */ public void initScaling() { super.initScaling(); ColVars<T,V> fake=new ColVars<T,V>(); RWI=C2JUtils.createArrayOfObjects(fake, 3*SCREENWIDTH); } @Override public ColVars<T, V>[] getRWI() { return RWI; } @Override public void setExecutors(RenderWallExecutor<T, V>[] RWIExec) { this.RWIExec=RWIExec; } public void sync(){ try { drawsegsbarrier.await(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (BrokenBarrierException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } protected final class ParallelPlanes<T, V> extends PlaneDrawer<T, V> { protected ParallelPlanes(Renderer<T, V> R) { super(R); } /** * R_DrawPlanes At the end of each frame. This also means that visplanes * must have been set BEFORE we called this function. Therefore, look * for errors behind. * * @throws IOException */ public void DrawPlanes() { if (RANGECHECK) { rangeCheckErrors(); } // vpw[0].setRange(0,lastvisplane/2); // vpw[1].setRange(lastvisplane/2,lastvisplane); for (int i = 0; i < NUMFLOORTHREADS; i++) tp.execute(vpw[i]); } } // End Plane class protected final class ParallelSegs2 extends SegDrawer { /** * RenderSeg subsystem. Similar concept to RWI, but stores * "Render Seg Instructions" instead. More complex to build, but * potentially faster in some situations, as it allows distributing load * per-wall, rather than per-screen portion. Requires careful * concurrency considerations. */ protected RenderSegInstruction<V>[] RSI; /** * Increment this as you submit RSIs to the "queue". Remember to reset * to 0 when you have drawn everything! */ protected int RSIcount = 0; protected RenderSegExecutor<byte[], V>[] RSIExec; public ParallelSegs2(Renderer<?, ?> R) { super(R); } @Override protected void RenderSegLoop() { int angle; int yl, top, bottom, yh, mid, texturecolumn = 0; // Generate Seg rendering instruction BEFORE the looping start // and anything is modified. The loop will be repeated in the // threads, but without marking ceilings/floors etc. GenerateRSI(); for (; rw_x < rw_stopx; rw_x++) { // mark floor / ceiling areas yl = (topfrac + HEIGHTUNIT - 1) >> HEIGHTBITS; // no space above wall? if (yl < ceilingclip[rw_x] + 1) yl = ceilingclip[rw_x] + 1; if (markceiling) { top = ceilingclip[rw_x] + 1; bottom = yl - 1; if (bottom >= floorclip[rw_x]) bottom = floorclip[rw_x] - 1; if (top <= bottom) { vp_vars.visplanes[vp_vars.ceilingplane].setTop(rw_x, (char) top); vp_vars.visplanes[vp_vars.ceilingplane].setBottom(rw_x, (char) bottom); } } yh = bottomfrac >> HEIGHTBITS; if (yh >= floorclip[rw_x]) yh = floorclip[rw_x] - 1; // System.out.printf("Precompute: rw %d yl %d yh %d\n",rw_x,yl,yh); // A particular seg has been identified as a floor marker. if (markfloor) { top = yh + 1; bottom = floorclip[rw_x] - 1; if (top <= ceilingclip[rw_x]) top = ceilingclip[rw_x] + 1; if (top <= bottom) { vp_vars.visplanes[vp_vars.floorplane].setTop(rw_x, (char) top); vp_vars.visplanes[vp_vars.floorplane].setBottom(rw_x, (char) bottom); } } // texturecolumn and lighting are independent of wall tiers if (segtextured) { // calculate texture offset. Still important to do because // of masked angle = Tables.toBAMIndex(rw_centerangle + (int) xtoviewangle[rw_x]); texturecolumn = rw_offset - FixedMul(finetangent[angle], rw_distance); texturecolumn >>= FRACBITS; } // Don't to any drawing, only compute bounds. if (midtexture != 0) { dcvars.dc_source = TexMan.GetCachedColumn(midtexture, texturecolumn); // dc_m=dcvars.dc_source_ofs; // single sided line ceilingclip[rw_x] = (short) view.height; floorclip[rw_x] = -1; } else { // two sided line if (toptexture != 0) { // top wall mid = pixhigh >> HEIGHTBITS; pixhigh += pixhighstep; if (mid >= floorclip[rw_x]) mid = floorclip[rw_x] - 1; if (mid >= yl) { dcvars.dc_source = TexMan.GetCachedColumn(toptexture, texturecolumn); ceilingclip[rw_x] = (short) mid; } else ceilingclip[rw_x] = (short) (yl - 1); } else { // no top wall if (markceiling) ceilingclip[rw_x] = (short) (yl - 1); } if (bottomtexture != 0) { // bottom wall mid = (pixlow + HEIGHTUNIT - 1) >> HEIGHTBITS; pixlow += pixlowstep; // no space above wall? if (mid <= ceilingclip[rw_x]) mid = ceilingclip[rw_x] + 1; if (mid <= yh) { dcvars.dc_source = TexMan.GetCachedColumn(bottomtexture, texturecolumn); floorclip[rw_x] = (short) mid; } else floorclip[rw_x] = (short) (yh + 1); } else { // no bottom wall if (markfloor) floorclip[rw_x] = (short) (yh + 1); } if (maskedtexture) { // save texturecol // for backdrawing of masked mid texture seg_vars.maskedtexturecol[seg_vars.pmaskedtexturecol + rw_x] = (short) texturecolumn; } } rw_scale += rw_scalestep; topfrac += topstep; bottomfrac += bottomstep; } } protected final void GenerateRSI() { if (RSIcount >= RSI.length) { ResizeRSIBuffer(); } RenderSegInstruction<V> rsi = RSI[RSIcount]; rsi.centery = view.centery; rsi.bottomfrac = bottomfrac; rsi.bottomstep = bottomstep; rsi.bottomtexture = bottomtexture; rsi.markceiling = markceiling; rsi.markfloor = markfloor; rsi.midtexture = midtexture; rsi.pixhigh = pixhigh; rsi.pixhighstep = pixhighstep; rsi.pixlow = pixlow; rsi.pixlowstep = pixlowstep; rsi.rw_bottomtexturemid = rw_bottomtexturemid; rsi.rw_centerangle = rw_centerangle; rsi.rw_distance = rw_distance; rsi.rw_midtexturemid = rw_midtexturemid; rsi.rw_offset = rw_offset; rsi.rw_scale = rw_scale; rsi.rw_scalestep = rw_scalestep; rsi.rw_stopx = rw_stopx; rsi.rw_toptexturemid = rw_toptexturemid; rsi.rw_x = rw_x; rsi.segtextured = segtextured; rsi.topfrac = topfrac; rsi.topstep = topstep; rsi.toptexture = toptexture; rsi.walllights = colormaps.walllights; rsi.viewheight = view.height; // rsi.floorplane=floorplane; // rsi.ceilingplane=ceilingplane; RSIcount++; } @Override protected void CompleteColumn() { // TODO Auto-generated method stub } protected void RenderRSIPipeline() { for (int i = 0; i < NUMWALLTHREADS; i++) { RSIExec[i].setRSIEnd(RSIcount); // RWIExec[i].setRange(i%NUMWALLTHREADS,RWIcount,NUMWALLTHREADS); tp.execute(RSIExec[i]); } // System.out.println("RWI count"+RWIcount); RSIcount = 0; } /** * Resizes RWI buffer, updates executors. Sorry for the hackish * implementation but ArrayList and pretty much everything in * Collections is way too slow for what we're trying to accomplish. */ protected void ResizeRSIBuffer() { RenderSegInstruction<V> fake = new RenderSegInstruction<V>(); // Bye bye, old RSI. RSI = C2JUtils.resize(fake, RSI, RSI.length * 2); for (int i = 0; i < NUMWALLTHREADS; i++) { RSIExec[i].updateRSI(RSI); } System.out.println("RWI Buffer resized. Actual capacity " + RSI.length); } } protected final class ParallelPlanes2<T, V> extends PlaneDrawer<T, V> { protected ParallelPlanes2(Renderer<T, V> R) { super(R); } /** * R_DrawPlanes At the end of each frame. This also means that visplanes * must have been set BEFORE we called this function. Therefore, look * for errors behind. * * @throws IOException */ public void DrawPlanes() { if (RANGECHECK) { rangeCheckErrors(); } // vpw[0].setRange(0,lastvisplane/2); // vpw[1].setRange(lastvisplane/2,lastvisplane); for (int i = 0; i < NUMFLOORTHREADS; i++) tp.execute(vpw[i]); } } // End Plane class // / RWI SUBSYSTEM // / AKA "Render Wall Instruction": instructions that store only a single // column // from a wall /** * R_InitRSISubsystem Initialize RSIs and RSI Executors. Pegs them to the * RSI, ylookup and screen[0]. */ // protected abstract void InitRSISubsystem(); /* * { // CATCH: this must be executed AFTER screen is set, and // AFTER we * initialize the RWI themselves, // before V is set (right?) //offsets=new * int[NUMWALLTHREADS]; for (int i=0;i<NUMWALLTHREADS;i++){ RSIExec[i]=new * RenderSegExecutor.HiColor( SCREENWIDTH, SCREENHEIGHT, i, screen, this, * TexMan, RSI, MySegs.getBLANKCEILINGCLIP(), MySegs.getBLANKFLOORCLIP(), * MySegs.getCeilingClip(), MySegs.getFloorClip(), columnofs, xtoviewangle, * ylookup, this.visplanes, this.visplanebarrier); * RSIExec[i].setVideoScale(this.vs); RSIExec[i].initScaling(); // Each * SegExecutor sticks to its own half (or 1/nth) of the screen. * RSIExec[i].setScreenRange * (i*(SCREENWIDTH/NUMWALLTHREADS),(i+1)*(SCREENWIDTH/NUMWALLTHREADS)); * detailaware.add(RSIExec[i]); } for (int i=0;i<NUMFLOORTHREADS;i++){ * vpw[i]=new VisplaneWorker(i,SCREENWIDTH,SCREENHEIGHT,columnofs,ylookup, * screen,visplanebarrier,NUMFLOORTHREADS); detailaware.add((IDetailAware) * vpw[i]); } } */ /** Creates RMI Executors */ // protected abstract void InitRMISubsystem(); /* * { for (int i = 0; i < NUMMASKEDTHREADS; i++) { // Each masked executor * gets its own set of column functions. RMIExec[i] = new * RenderMaskedExecutor(SCREENWIDTH, SCREENHEIGHT, columnofs, ylookup, * screen, RMI, maskedbarrier, // Regular masked columns new * R_DrawColumnBoom * (SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I), new * R_DrawColumnBoomLow * (SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I), // * Fuzzy columns new * R_DrawFuzzColumn.HiColor(SCREENWIDTH,SCREENHEIGHT,ylookup * ,columnofs,maskedcvars,screen,I), new * R_DrawFuzzColumnLow.HiColor(SCREENWIDTH * ,SCREENHEIGHT,ylookup,columnofs,maskedcvars,screen,I), // Translated * columns new * R_DrawTranslatedColumn(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs * ,maskedcvars,screen,I), new * R_DrawTranslatedColumnLow(SCREENWIDTH,SCREENHEIGHT * ,ylookup,columnofs,maskedcvars,screen,I) ); detailaware.add(RMIExec[i]); * } } */ public void Init(){ super.Init(); InitParallelStuff(); } /** * Any scaling and implementation-specific stuff to do for parallel stuff * should go here. This method is called internally by the public Init(). * The idea is that the renderer should be up & running after you finally * called this. */ protected abstract void InitParallelStuff(); /** Override this in one of the final implementors, if you want it to work */ public RenderWallExecutor<T,V>[] InitRWIExecutors(int num,ColVars<T,V>[] RWI){ return null; } RWI.Get<T,V> RWIs; } package rr.parallel; import java.util.concurrent.BrokenBarrierException; import java.util.concurrent.CyclicBarrier; import rr.IDetailAware; import rr.drawfuns.ColVars; import rr.drawfuns.DoomColumnFunction; import rr.drawfuns.R_DrawColumnBoomOpt; import rr.drawfuns.R_DrawColumnBoomOptLow; /** * This is what actual executes the RenderWallInstruction. Essentially it's a * self-contained column rendering function. * * @author admin */ public class RenderWallExecutor<T,V> implements Runnable,IDetailAware { protected CyclicBarrier barrier; protected ColVars<T,V>[] RWI; protected int start, end; protected DoomColumnFunction<T,V> colfunchi, colfunclow; protected DoomColumnFunction<T,V> colfunc; public RenderWallExecutor(int SCREENWIDTH, int SCREENHEIGHT, int[] columnofs, int[] ylookup, V screen, ColVars<T,V>[] RWI, CyclicBarrier barrier) { this.RWI = RWI; this.barrier = barrier; this.SCREENWIDTH = SCREENWIDTH; this.SCREENHEIGHT = SCREENHEIGHT; } public void setRange(int start, int end) { this.end = end; this.start = start; } public void setDetail(int detailshift) { if (detailshift == 0) colfunc = colfunchi; else colfunc = colfunclow; } public void run() { // System.out.println("Wall executor from "+start +" to "+ end); for (int i = start; i < end; i++) { colfunc.invoke(RWI[i]); } try { barrier.await(); } catch (InterruptedException e) { e.printStackTrace(); } catch (BrokenBarrierException e) { e.printStackTrace(); } } public void updateRWI(ColVars<T,V>[] RWI) { this.RWI = RWI; } /////////////// VIDEO SCALE STUFF////////////////////// protected final int SCREENWIDTH; protected final int SCREENHEIGHT; /* * protected IVideoScale vs; * @Override public void setVideoScale(IVideoScale vs) { this.vs=vs; } * @Override public void initScaling() { * this.SCREENHEIGHT=vs.getScreenHeight(); * this.SCREENWIDTH=vs.getScreenWidth(); } */ public static final class HiColor extends RenderWallExecutor<byte[],short[]> { public HiColor(int SCREENWIDTH, int SCREENHEIGHT, int[] columnofs, int[] ylookup, short[] screen, ColVars<byte[], short[]>[] RWI, CyclicBarrier barrier) { super(SCREENWIDTH, SCREENHEIGHT, columnofs, ylookup, screen, RWI, barrier); colfunc = colfunchi = new R_DrawColumnBoomOpt.HiColor(SCREENWIDTH, SCREENHEIGHT, ylookup, columnofs, null, screen, null); colfunclow = new R_DrawColumnBoomOptLow.HiColor(SCREENWIDTH, SCREENHEIGHT, ylookup, columnofs, null, screen, null); } } public static final class Indexed extends RenderWallExecutor<byte[],byte[]> { public Indexed(int SCREENWIDTH, int SCREENHEIGHT, int[] columnofs, int[] ylookup, byte[] screen, ColVars<byte[], byte[]>[] RWI, CyclicBarrier barrier) { super(SCREENWIDTH, SCREENHEIGHT, columnofs, ylookup, screen, RWI, barrier); colfunc = colfunchi = new R_DrawColumnBoomOpt.Indexed(SCREENWIDTH, SCREENHEIGHT, ylookup, columnofs, null, screen, null); colfunclow = new R_DrawColumnBoomOptLow.Indexed(SCREENWIDTH, SCREENHEIGHT, ylookup, columnofs, null, screen, null); } } public static final class TrueColor extends RenderWallExecutor<byte[],int[]> { public TrueColor(int SCREENWIDTH, int SCREENHEIGHT, int[] columnofs, int[] ylookup, int[] screen, ColVars<byte[], int[]>[] RWI, CyclicBarrier barrier) { super(SCREENWIDTH, SCREENHEIGHT, columnofs, ylookup, screen, RWI, barrier); colfunc = colfunchi = new R_DrawColumnBoomOpt.TrueColor(SCREENWIDTH, SCREENHEIGHT, ylookup, columnofs, null, screen, null); colfunclow = new R_DrawColumnBoomOptLow.TrueColor(SCREENWIDTH, SCREENHEIGHT, ylookup, columnofs, null, screen, null); } } } package rr.parallel; import rr.column_t; /** An interface used to ease the use of the GetCachedColumn by part * of parallelized renderers. * * @author Maes * */ /** * Special version of GetColumn meant to be called concurrently by different * seg rendering threads, identfiex by index. This serves to avoid stomping * on mutual cached textures and causing crashes. * */ public interface IGetSmpColumn { column_t GetSmpColumn(int tex, int col,int id); } package rr.parallel; import java.util.List; import java.util.concurrent.BrokenBarrierException; import java.util.concurrent.CyclicBarrier; import java.util.concurrent.Executor; import rr.AbstractThings; import rr.IDetailAware; import rr.Renderer; import rr.drawfuns.ColVars; import rr.drawfuns.DcFlags; import utils.C2JUtils; /** * Parallel Things drawing class, column based, using RMI pipeline. * For N threads, each thread only draws those columns of sprites that * are in its own 1/N portion of the screen. * * Overrides only the terminal drawing methods from things, using a * mechanism very similar to column-based wall threading. It's not very * efficient, since some of the really heavy parts (such as visibility * priority) are still done serially, and actually do take up a lot of the * actual rendering time, and the number of columns generated is REALLY * enormous (100K+ for something like nuts.wad), and the thing chokes on * synchronization, more than anything. The only appropriate thing to do * would be to have a per-vissprite renderer, which would actually move much * of the brunt work away from the main thread. Some interesting benchmarks * on nuts.wad timedemo: Normal things serial renderer: 60-62 fps "Dummy" * completeColumns: 72 fps "Dummy" things renderer without final drawing: 80 * fps "Dummy" things renderer without ANY calculations: 90 fps. This means * that even a complete parallelization will likely have a quite limited * impact. * * @author velktron */ public abstract class ParallelThings<T,V> extends AbstractThings<T,V> { // stuff to get from container /** Render Masked Instuction subsystem. Essentially, a way to split sprite work * between threads on a column-basis. */ protected ColVars<T, V>[] RMI; /** * Increment this as you submit RMIs to the "queue". Remember to reset to 0 * when you have drawn everything! */ protected int RMIcount = 0; protected RenderMaskedExecutor<T,V>[] RMIExec; protected final int NUMMASKEDTHREADS; protected final CyclicBarrier maskedbarrier; protected final Executor tp; public ParallelThings(Renderer<T,V> R,Executor tp, int numthreads){ super(R); this.tp=tp; this.NUMMASKEDTHREADS=numthreads; this.maskedbarrier=new CyclicBarrier(NUMMASKEDTHREADS+1); } @Override public void DrawMasked() { // This just generates the RMI instructions. super.DrawMasked(); // This splits the work among threads and fires them up RenderRMIPipeline(); try { // Wait for them to be done. maskedbarrier.await(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (BrokenBarrierException e) { // TODO Auto-generated catch block e.printStackTrace(); } } @Override public void completeColumn() { if (view.detailshift == 1) flags = DcFlags.LOW_DETAIL; // Don't wait to go over if (RMIcount >= RMI.length) { ResizeRMIBuffer(); } // A deep copy is still necessary, as well as setting dc_flags RMI[RMIcount].copyFrom(maskedcvars, colfunc.getFlags()); // We only need to point to the next one in the list. RMIcount++; } int flags; protected void RenderRMIPipeline() { for (int i = 0; i < NUMMASKEDTHREADS; i++) { RMIExec[i].setRange((i * this.SCREENWIDTH) / NUMMASKEDTHREADS, ((i + 1) * this.SCREENWIDTH) / NUMMASKEDTHREADS); RMIExec[i].setRMIEnd(RMIcount); // RWIExec[i].setRange(i%NUMWALLTHREADS,RWIcount,NUMWALLTHREADS); tp.execute(RMIExec[i]); } // System.out.println("RWI count"+RWIcount); RMIcount = 0; } protected void ResizeRMIBuffer() { ColVars<T, V> fake = new ColVars<T, V>(); ColVars<T, V>[] tmp = // TODO Auto-generated constructor stub C2JUtils.createArrayOfObjects(fake, RMI.length * 2); System.arraycopy(RMI, 0, tmp, 0, RMI.length); // Bye bye, old RMI. RMI = tmp; for (int i = 0; i < NUMMASKEDTHREADS; i++) { RMIExec[i].updateRMI(RMI); } System.err.println("RMI Buffer resized. Actual capacity " + RMI.length); } protected abstract void InitRMISubsystem(int[] columnofs, int[] ylookup,V screen, CyclicBarrier maskedbarrier,V BLURRY_MAP, List<IDetailAware> detailaware); public static class Indexed extends ParallelThings<byte[],byte[]>{ public Indexed(Renderer<byte[], byte[]> R, Executor tp, int numthreads) { super(R, tp, numthreads); } protected void InitRMISubsystem(int[] columnofs, int[] ylookup,byte[] screen, CyclicBarrier maskedbarrier,byte[] BLURRY_MAP, List<IDetailAware> detailaware) { for (int i = 0; i < NUMMASKEDTHREADS; i++) { RMIExec[i] = new RenderMaskedExecutor.Indexed(SCREENWIDTH, SCREENHEIGHT, columnofs, ylookup, screen, RMI, maskedbarrier,I,BLURRY_MAP); detailaware.add(RMIExec[i]); } } } public static class HiColor extends ParallelThings<byte[],short[]>{ public HiColor(Renderer<byte[], short[]> R, Executor tp, int numthreads) { super(R, tp, numthreads); } protected void InitRMISubsystem(int[] columnofs, int[] ylookup,short[] screen, CyclicBarrier maskedbarrier,short[] BLURRY_MAP, List<IDetailAware> detailaware) { for (int i = 0; i < NUMMASKEDTHREADS; i++) { RMIExec[i] = new RenderMaskedExecutor.HiColor(SCREENWIDTH, SCREENHEIGHT, columnofs, ylookup, screen, RMI, maskedbarrier,I); detailaware.add(RMIExec[i]); } } } public static class TrueColor extends ParallelThings<byte[],int[]>{ public TrueColor(Renderer<byte[], int[]> R, Executor tp, int numthreads) { super(R, tp, numthreads); } protected void InitRMISubsystem(int[] columnofs, int[] ylookup,int[] screen, CyclicBarrier maskedbarrier,int[] BLURRY_MAP, List<IDetailAware> detailaware) { for (int i = 0; i < NUMMASKEDTHREADS; i++) { RMIExec[i] = new RenderMaskedExecutor.TrueColor(SCREENWIDTH, SCREENHEIGHT, columnofs, ylookup, screen, RMI, maskedbarrier,I); detailaware.add(RMIExec[i]); } } } } package rr.parallel; import rr.drawfuns.ColVars; public interface RWI<T,V> { public interface Init<T,V>{ RenderWallExecutor<T,V>[] InitRWIExecutors(int num,ColVars<T,V>[] RWI); } public interface Get<T,V>{ ColVars<T,V>[] getRWI(); void setExecutors(RenderWallExecutor<T,V>[] RWIExec); } } package rr.parallel; import static data.Defines.ANGLETOSKYSHIFT; import static data.Tables.addAngles; import static m.fixed_t.FRACBITS; import static rr.LightsAndColors.LIGHTLEVELS; import static rr.LightsAndColors.LIGHTSEGSHIFT; import java.util.concurrent.BrokenBarrierException; import java.util.concurrent.CyclicBarrier; import rr.IDetailAware; import rr.PlaneDrawer; import rr.Renderer; import rr.visplane_t; import rr.drawfuns.ColVars; import rr.drawfuns.DoomColumnFunction; import rr.drawfuns.DoomSpanFunction; import rr.drawfuns.R_DrawColumnBoomOpt; import rr.drawfuns.R_DrawColumnBoomOptLow; import rr.drawfuns.R_DrawSpanLow; import rr.drawfuns.R_DrawSpanUnrolled; import rr.drawfuns.SpanVars; /** Visplane worker which shares work in an equal-visplane number strategy * with other workers. Might be unbalanced if one worker gets too large * visplanes and others get smaller ones. Balancing strategy is applied in * run(), otherwise it's practically similar to a PlaneDrwer. * * * @author velktron * */ public abstract class VisplaneWorker<T,V> extends PlaneDrawer<T,V> implements Runnable,IDetailAware{ // Private to each thread. protected final int id; protected final int NUMFLOORTHREADS; protected final CyclicBarrier barrier; protected int vpw_planeheight; protected V[] vpw_planezlight; protected int vpw_basexscale,vpw_baseyscale; protected SpanVars<T,V> vpw_dsvars; protected ColVars<T,V> vpw_dcvars; // OBVIOUSLY each thread must have its own span functions. protected DoomSpanFunction<T,V> vpw_spanfunc; protected DoomColumnFunction<T,V> vpw_skyfunc; protected DoomSpanFunction<T,V> vpw_spanfunchi; protected DoomSpanFunction<T,V> vpw_spanfunclow; protected DoomColumnFunction<T,V> vpw_skyfunchi; protected DoomColumnFunction<T,V> vpw_skyfunclow; public VisplaneWorker(int id,int SCREENWIDTH, int SCREENHEIGHT, Renderer<T,V> R,CyclicBarrier visplanebarrier,int NUMFLOORTHREADS) { super(R); this.barrier=visplanebarrier; this.id=id; this.NUMFLOORTHREADS=NUMFLOORTHREADS; } public static final class HiColor extends VisplaneWorker<byte[],short[]>{ public HiColor(int id, int SCREENWIDTH, int SCREENHEIGHT,Renderer<byte[],short[]> R, int[] columnofs, int[] ylookup, short[] screen, CyclicBarrier visplanebarrier, int NUMFLOORTHREADS) { super(id, SCREENWIDTH, SCREENHEIGHT, R, visplanebarrier, NUMFLOORTHREADS); // Alias to those of Planes. vpw_dsvars=new SpanVars<byte[],short[]>(); vpw_dcvars=new ColVars<byte[],short[]>(); vpw_spanfunc=vpw_spanfunchi=new R_DrawSpanUnrolled.HiColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,vpw_dsvars,screen,I); vpw_spanfunclow=new R_DrawSpanLow.HiColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,vpw_dsvars,screen,I); vpw_skyfunc=vpw_skyfunchi=new R_DrawColumnBoomOpt.HiColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,vpw_dcvars,screen,I); vpw_skyfunclow=new R_DrawColumnBoomOptLow.HiColor(SCREENWIDTH,SCREENHEIGHT,ylookup,columnofs,vpw_dcvars,screen,I); } } public void setDetail(int detailshift) { if (detailshift == 0){ vpw_spanfunc = vpw_spanfunchi; vpw_skyfunc= vpw_skyfunchi; } else{ vpw_spanfunc = vpw_spanfunclow; vpw_skyfunc =vpw_skyfunclow; } } @Override public void run() { visplane_t pln=null; //visplane_t // These must override the global ones int light; int x; int stop; int angle; // Now it's a good moment to set them. vpw_basexscale=vpvars.getBaseXScale(); vpw_baseyscale=vpvars.getBaseYScale(); // TODO: find a better way to split work. As it is, it's very uneven // and merged visplanes in particular are utterly dire. for (int pl= this.id; pl <vpvars.lastvisplane; pl+=NUMFLOORTHREADS) { pln=vpvars.visplanes[pl]; // System.out.println(id +" : "+ pl); if (pln.minx > pln.maxx) continue; // sky flat if (pln.picnum == TexMan.getSkyFlatNum() ) { // Cache skytexture stuff here. They aren't going to change while // being drawn, after all, are they? int skytexture=TexMan.getSkyTexture(); // MAES: these must be updated to keep up with screen size changes. vpw_dcvars.viewheight=view.height; vpw_dcvars.centery=view.centery; vpw_dcvars.dc_texheight=TexMan.getTextureheight(skytexture)>>FRACBITS; vpw_dcvars.dc_iscale = vpvars.getSkyScale()>>view.detailshift; vpw_dcvars.dc_colormap = colormap.colormaps[0]; vpw_dcvars.dc_texturemid = TexMan.getSkyTextureMid(); for (x=pln.minx ; x <= pln.maxx ; x++) { vpw_dcvars.dc_yl = pln.getTop(x); vpw_dcvars.dc_yh = pln.getBottom(x); if (vpw_dcvars.dc_yl <= vpw_dcvars.dc_yh) { angle = (int) (addAngles(view.angle, view.xtoviewangle[x])>>>ANGLETOSKYSHIFT); vpw_dcvars.dc_x = x; // Optimized: texheight is going to be the same during normal skies drawing...right? vpw_dcvars.dc_source = TexMan.GetCachedColumn(TexMan.getSkyTexture(), angle); vpw_skyfunc.invoke(); } } continue; } // regular flat vpw_dsvars.ds_source = TexMan.getSafeFlat(pln.picnum); vpw_planeheight = Math.abs(pln.height-view.z); light = (pln.lightlevel >>> LIGHTSEGSHIFT)+colormap.extralight; if (light >= LIGHTLEVELS) light = LIGHTLEVELS-1; if (light < 0) light = 0; vpw_planezlight = colormap.zlight[light]; // We set those values at the border of a plane's top to a "sentinel" value...ok. pln.setTop(pln.maxx+1,(char) 0xffff); pln.setTop(pln.minx-1, (char) 0xffff); stop = pln.maxx + 1; for (x=pln.minx ; x<= stop ; x++) { MakeSpans(x,pln.getTop(x-1), pln.getBottom(x-1), pln.getTop(x), pln.getBottom(x)); } } // We're done, wait. try { barrier.await(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (BrokenBarrierException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } package rr.parallel; import static data.Defines.ANGLETOSKYSHIFT; import static data.Defines.PU_STATIC; import static data.Tables.ANGLETOFINESHIFT; import static data.Tables.BITS32; import static data.Tables.addAngles; import static data.Tables.finecosine; import static data.Tables.finesine; import static m.fixed_t.FRACBITS; import static m.fixed_t.FixedMul; import static rr.LightsAndColors.*; import java.util.concurrent.BrokenBarrierException; import java.util.concurrent.CyclicBarrier; import rr.PlaneDrawer; import rr.Renderer; import rr.flat_t; import rr.visplane_t; import rr.drawfuns.ColVars; import rr.drawfuns.DoomColumnFunction; import rr.drawfuns.DoomSpanFunction; import rr.drawfuns.R_DrawColumnBoomOpt; import rr.drawfuns.R_DrawColumnBoomOptLow; import rr.drawfuns.R_DrawSpanLow; import rr.drawfuns.R_DrawSpanUnrolled; import rr.drawfuns.SpanVars; /** Visplane worker which shares work in an equal screen-portions strategy. * * More balanced, but requires careful synchronization to avoid overdrawing and * stomping. * * * @author vepitrop. * */ public abstract class VisplaneWorker2<T,V> extends PlaneDrawer<T,V> implements Runnable{ protected final int id; protected final int NUMFLOORTHREADS; protected int startvp; protected int endvp; protected int vpw_planeheight; protected V[] vpw_planezlight; protected int vpw_basexscale,vpw_baseyscale; protected final SpanVars<T,V> vpw_dsvars; protected final ColVars<T,V> vpw_dcvars; protected DoomSpanFunction<T,V> vpw_spanfunc; protected DoomColumnFunction<T,V> vpw_skyfunc; protected DoomSpanFunction<T,V> vpw_spanfunchi; protected DoomSpanFunction<T,V> vpw_spanfunclow; protected DoomColumnFunction<T,V> vpw_skyfunchi; protected DoomColumnFunction<T,V> vpw_skyfunclow; protected visplane_t pln; public VisplaneWorker2(Renderer<T,V> R,int id,int sCREENWIDTH, int sCREENHEIGHT, CyclicBarrier visplanebarrier,int NUMFLOORTHREADS) { super(R); this.barrier=visplanebarrier; this.id=id; // Alias to those of Planes. vpw_dsvars=new SpanVars<T,V>(); vpw_dcvars=new ColVars<T,V>(); this.NUMFLOORTHREADS=NUMFLOORTHREADS; } public static class HiColor extends VisplaneWorker2<byte[],short[]>{ public HiColor(Renderer<byte[],short[]> R,int id, int sCREENWIDTH, int sCREENHEIGHT, int[] columnofs, int[] ylookup, short[] screen, CyclicBarrier visplanebarrier, int NUMFLOORTHREADS) { super(R,id, sCREENWIDTH, sCREENHEIGHT, visplanebarrier, NUMFLOORTHREADS); vpw_spanfunc=vpw_spanfunchi=new R_DrawSpanUnrolled.HiColor(sCREENWIDTH,sCREENHEIGHT,ylookup,columnofs,vpw_dsvars,screen,I); vpw_spanfunclow=new R_DrawSpanLow.HiColor(sCREENWIDTH,sCREENHEIGHT,ylookup,columnofs,vpw_dsvars,screen,I); vpw_skyfunc=vpw_skyfunchi=new R_DrawColumnBoomOpt.HiColor(sCREENWIDTH,sCREENHEIGHT,ylookup,columnofs,vpw_dcvars,screen,I); vpw_skyfunclow=new R_DrawColumnBoomOptLow.HiColor(sCREENWIDTH,sCREENHEIGHT,ylookup,columnofs,vpw_dcvars,screen,I); } } public static class Indexed extends VisplaneWorker2<byte[],byte[]>{ public Indexed(Renderer<byte[],byte[]> R,int id, int sCREENWIDTH, int sCREENHEIGHT, int[] columnofs, int[] ylookup, byte[] screen, CyclicBarrier visplanebarrier, int NUMFLOORTHREADS) { super(R,id, sCREENWIDTH, sCREENHEIGHT, visplanebarrier, NUMFLOORTHREADS); vpw_spanfunc=vpw_spanfunchi=new R_DrawSpanUnrolled.Indexed(sCREENWIDTH,sCREENHEIGHT,ylookup,columnofs,vpw_dsvars,screen,I); vpw_spanfunclow=new R_DrawSpanLow.Indexed(sCREENWIDTH,sCREENHEIGHT,ylookup,columnofs,vpw_dsvars,screen,I); vpw_skyfunc=vpw_skyfunchi=new R_DrawColumnBoomOpt.Indexed(sCREENWIDTH,sCREENHEIGHT,ylookup,columnofs,vpw_dcvars,screen,I); vpw_skyfunclow=new R_DrawColumnBoomOptLow.Indexed(sCREENWIDTH,sCREENHEIGHT,ylookup,columnofs,vpw_dcvars,screen,I); } } public static class TrueColor extends VisplaneWorker2<byte[],int[]>{ public TrueColor(Renderer<byte[],int[]> R,int id, int sCREENWIDTH, int sCREENHEIGHT, int[] columnofs, int[] ylookup, int[] screen, CyclicBarrier visplanebarrier, int NUMFLOORTHREADS) { super(R,id, sCREENWIDTH, sCREENHEIGHT, visplanebarrier, NUMFLOORTHREADS); vpw_spanfunc=vpw_spanfunchi=new R_DrawSpanUnrolled.TrueColor(sCREENWIDTH,sCREENHEIGHT,ylookup,columnofs,vpw_dsvars,screen,I); vpw_spanfunclow=new R_DrawSpanLow.TrueColor(sCREENWIDTH,sCREENHEIGHT,ylookup,columnofs,vpw_dsvars,screen,I); vpw_skyfunc=vpw_skyfunchi=new R_DrawColumnBoomOpt.TrueColor(sCREENWIDTH,sCREENHEIGHT,ylookup,columnofs,vpw_dcvars,screen,I); vpw_skyfunclow=new R_DrawColumnBoomOptLow.TrueColor(sCREENWIDTH,sCREENHEIGHT,ylookup,columnofs,vpw_dcvars,screen,I); } } @Override public void run() { pln=null; //visplane_t // These must override the global ones int light; int x; int stop; int angle; int minx,maxx; // Now it's a good moment to set them. vpw_basexscale=vpvars.getBaseXScale(); vpw_baseyscale=vpvars.getBaseYScale(); startvp=((id*view.width)/NUMFLOORTHREADS); endvp=(((id+1)*view.width)/NUMFLOORTHREADS); // TODO: find a better way to split work. As it is, it's very uneven // and merged visplanes in particular are utterly dire. for (int pl= 0; pl <vpvars.lastvisplane; pl++) { pln=vpvars.visplanes[pl]; // System.out.println(id +" : "+ pl); // Trivial rejection. if ((pln.minx > endvp) || (pln.maxx <startvp)) continue; // Reject non-visible if (pln.minx > pln.maxx) continue; // Trim to zone minx=Math.max(pln.minx,startvp); maxx=Math.min(pln.maxx,endvp); // sky flat if (pln.picnum == TexMan.getSkyFlatNum() ) { // Cache skytexture stuff here. They aren't going to change while // being drawn, after all, are they? int skytexture=TexMan.getSkyTexture(); // MAES: these must be updated to keep up with screen size changes. vpw_dcvars.viewheight=view.height; vpw_dcvars.centery=view.centery; vpw_dcvars.dc_texheight=TexMan.getTextureheight(skytexture)>>FRACBITS; vpw_dcvars.dc_iscale = vpvars.getSkyScale()>>view.detailshift; vpw_dcvars.dc_colormap = colormap.colormaps[0]; vpw_dcvars.dc_texturemid = TexMan.getSkyTextureMid(); for (x=minx ; x <= maxx ; x++) { vpw_dcvars.dc_yl = pln.getTop(x); vpw_dcvars.dc_yh = pln.getBottom(x); if (vpw_dcvars.dc_yl <= vpw_dcvars.dc_yh) { angle = (int) (addAngles(view.angle, view.xtoviewangle[x])>>>ANGLETOSKYSHIFT); vpw_dcvars.dc_x = x; vpw_dcvars.dc_texheight=TexMan.getTextureheight(TexMan.getSkyTexture())>>FRACBITS; vpw_dcvars.dc_source = TexMan.GetCachedColumn(TexMan.getSkyTexture(), angle); vpw_skyfunc.invoke(); } } continue; } // regular flat vpw_dsvars.ds_source = TexMan.getSafeFlat(pln.picnum); vpw_planeheight = Math.abs(pln.height-view.z); light = (pln.lightlevel >>> LIGHTSEGSHIFT)+colormap.extralight; if (light >= LIGHTLEVELS) light = LIGHTLEVELS-1; if (light < 0) light = 0; vpw_planezlight = colormap.zlight[light]; // Some tinkering required to make sure visplanes // don't end prematurely on each other's stop markers char value=pln.getTop(maxx+1); if (!isMarker(value)) { // is it a marker? value|=visplane_t.SENTINEL; // Mark it so. value&=visplane_t.THREADIDCLEAR; //clear id bits value|=(id<<visplane_t.THREADIDSHIFT); // set our own id. } // Otherwise, it was set by another thread. // Leave it be. pln.setTop(maxx+1,value); value=pln.getTop(minx-1); if (!isMarker(value)) { // is it a marker? value|=visplane_t.SENTINEL; // Mark it so. value&=visplane_t.THREADIDCLEAR; //clear id bits value|=(id<<visplane_t.THREADIDSHIFT); // set our own id. } // Otherwise, it was set by another thread. // Leave it be. pln.setTop(minx-1, value); stop = maxx+1; for (x=minx ; x<= stop ; x++) { MakeSpans(x,pln.getTop(x-1), pln.getBottom(x-1), pln.getTop(x), pln.getBottom(x)); } } // We're done, wait. try { barrier.await(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (BrokenBarrierException e) { // TODO Auto-generated catch block e.printStackTrace(); } } private final boolean isMarker(int t1){ return ((t1&visplane_t.SENTINEL)!=0); } private final int decodeID(int t1){ return (t1&visplane_t.THREADIDBITS)>>visplane_t.THREADIDSHIFT; } private final int decodeValue(int t1){ return t1&visplane_t.THREADVALUEBITS; } public void setDetail(int detailshift) { if (detailshift == 0){ vpw_spanfunc = vpw_spanfunchi; vpw_skyfunc= vpw_skyfunchi; } else{ vpw_spanfunc = vpw_spanfunclow; vpw_skyfunc =vpw_skyfunclow; } } /** * R_MakeSpans * * Called only by DrawPlanes. * If you wondered where the actual boundaries for the visplane * flood-fill are laid out, this is it. * * The system of coords seems to be defining a sort of cone. * * * @param x Horizontal position * @param t1 Top-left y coord? * @param b1 Bottom-left y coord? * @param t2 Top-right y coord ? * @param b2 Bottom-right y coord ? * */ protected final void MakeSpans(int x, int t1, int b1, int t2, int b2) { // Top 1 sentinel encountered. if (isMarker(t1)) if (decodeID(t1)!=id) // We didn't put it here. t1=decodeValue(t1); // Top 2 sentinel encountered. if (isMarker(t2)) if (decodeID(t2)!=id) t2=decodeValue(t2); // If t1 = [sentinel value] then this part won't be executed. while (t1 < t2 && t1 <= b1) { this.MapPlane(t1, spanstart[t1], x - 1); t1++; } while (b1 > b2 && b1 >= t1) { this.MapPlane(b1, spanstart[b1], x - 1); b1--; } // So...if t1 for some reason is < t2, we increase t2 AND store the current x // at spanstart [t2] :-S while (t2 < t1 && t2 <= b2) { spanstart[t2] = x; t2++; } // So...if t1 for some reason b2 > b1, we decrease b2 AND store the current x // at spanstart [t2] :-S while (b2 > b1 && b2 >= t2) { spanstart[b2] = x; b2--; } } /** * R_MapPlane * * Called only by R_MakeSpans. * * This is where the actual span drawing function is called. * * Uses global vars: * planeheight * ds_source -> flat data has already been set. * basexscale -> actual drawing angle and position is computed from these * baseyscale * viewx * viewy * * BASIC PRIMITIVE */ /* TODO: entirely similar to serial version? private void MapPlane ( int y, int x1, int x2 ) { // MAES: angle_t int angle; // fixed_t int distance; int length; int index; if (RANGECHECK){ if (x2 < x1 || x1<0 || x2>=view.width || y>view.height) { I.Error ("R_MapPlane: %d, %d at %d",x1,x2,y); } } if (vpw_planeheight != cachedheight[y]) { cachedheight[y] = vpw_planeheight; distance = cacheddistance[y] = FixedMul (vpw_planeheight , yslope[y]); vpw_dsvars.ds_xstep = cachedxstep[y] = FixedMul (distance,vpw_basexscale); vpw_dsvars.ds_ystep = cachedystep[y] = FixedMul (distance,vpw_baseyscale); } else { distance = cacheddistance[y]; vpw_dsvars.ds_xstep = cachedxstep[y]; vpw_dsvars.ds_ystep = cachedystep[y]; } length = FixedMul (distance,distscale[x1]); angle = (int)(((view.angle +xtoviewangle[x1])&BITS32)>>>ANGLETOFINESHIFT); vpw_dsvars.ds_xfrac = view.x + FixedMul(finecosine[angle], length); vpw_dsvars.ds_yfrac = -view.y - FixedMul(finesine[angle], length); if (colormap.fixedcolormap!=null) vpw_dsvars.ds_colormap = colormap.fixedcolormap; else { index = distance >>> LIGHTZSHIFT; if (index >= MAXLIGHTZ ) index = MAXLIGHTZ-1; vpw_dsvars.ds_colormap = vpw_planezlight[index]; } vpw_dsvars.ds_y = y; vpw_dsvars.ds_x1 = x1; vpw_dsvars.ds_x2 = x2; // high or low detail if (view.detailshift==0) vpw_spanfunc.invoke(); else vpw_spanfunclow.invoke(); } */ // Private to each thread. int[] spanstart; int[] spanstop; CyclicBarrier barrier; } package rr.parallel; import static data.Tables.finetangent; import static m.fixed_t.*; import static rr.LightsAndColors.*; import java.util.concurrent.BrokenBarrierException; import java.util.concurrent.CyclicBarrier; import rr.IDetailAware; import rr.IGetColumn; import rr.TextureManager; import rr.visplane_t; import rr.drawfuns.ColVars; import rr.drawfuns.DoomColumnFunction; import rr.drawfuns.R_DrawColumnBoomOpt; import rr.drawfuns.R_DrawColumnBoomOptLow; import data.Tables; import v.IVideoScale; import v.IVideoScaleAware; /** This is what actual executes the RenderSegInstructions. * * * Each thread actually operates on a FIXED PORTION OF THE SCREEN * (e.g. half-width, third-width etc.) and only renders the portions * of the RenderSegInstructions that are completely contained * within its own screen area. For this reason, all threads * check out all RenderSegInstructions of the list, and render any * and all portions that are within their responsability domain, so * to speak. * * FIXME there's a complex data dependency with ceilingclip/floorclip * I was not quite able to fix yet. Practically, in the serial renderer, * calls to RenderSegLoop are done in a correct, non-overlapping order, * and certain parts are drawn before others in order to set current * floor/ceiling markers and visibility e.g. think of a wall visible * through windows. * * FIXME 7/6/2011 Data dependencies and per-thread clipping are now * fixed, however there is still visible "jitter" or "noise" on some * of the walls, probably related to column offsets. * * @author velktron * */ public abstract class RenderSegExecutor<T,V> implements Runnable, IVideoScaleAware,IDetailAware { // This needs to be set by the partitioner. protected int rw_start, rw_end,rsiend; // These need to be set on creation, and are unchangeable. protected final TextureManager<T> TexMan; protected final CyclicBarrier barrier; protected RenderSegInstruction<V>[] RSI; protected final long[] xtoviewangle; protected final short[] ceilingclip, floorclip; // Each thread should do its own ceiling/floor blanking protected final short[] BLANKFLOORCLIP; protected final short[] BLANKCEILINGCLIP; protected static final int HEIGHTBITS = 12; protected static final int HEIGHTUNIT = (1<<HEIGHTBITS); protected final int id; protected DoomColumnFunction<T,V> colfunchi,colfunclow; protected DoomColumnFunction<T,V> colfunc; protected ColVars<T,V> dcvars; public RenderSegExecutor(int SCREENWIDTH, int SCREENHEIGHT,int id,V screen, TextureManager<T> texman, RenderSegInstruction<V>[] RSI, short[] BLANKCEILINGCLIP, short[] BLANKFLOORCLIP, short[] ceilingclip, short[] floorclip, int[] columnofs, long[] xtoviewangle, int[] ylookup, visplane_t[] visplanes, CyclicBarrier barrier){ this.id=id; this.TexMan=texman; this.RSI=RSI; this.barrier=barrier; this.ceilingclip=ceilingclip; this.floorclip=floorclip; this.xtoviewangle=xtoviewangle; this.BLANKCEILINGCLIP=BLANKCEILINGCLIP; this.BLANKFLOORCLIP=BLANKFLOORCLIP; } protected final void ProcessRSI(RenderSegInstruction<V> rsi, int startx,int endx,boolean contained){ int angle; // angle_t int index; int yl; // low int yh; // hight int mid; int pixlow,pixhigh,pixhighstep,pixlowstep; int rw_scale,topfrac,bottomfrac,bottomstep; // These are going to be modified A LOT, so we cache them here. pixhighstep=rsi.pixhighstep; pixlowstep=rsi.pixlowstep; bottomstep=rsi.bottomstep; // We must re-scale it. int rw_scalestep= rsi.rw_scalestep; int topstep=rsi.topstep; int texturecolumn=0; // fixed_t final int bias; // Well is entirely contained in our screen zone // (or the very least it starts in it). if (contained) bias=0; // We are continuing a wall that started in another // screen zone. else bias=(startx-rsi.rw_x); // PROBLEM: these must be pre-biased when multithreading. rw_scale=rsi.rw_scale+bias*rw_scalestep; topfrac = rsi.topfrac+bias*topstep; bottomfrac = rsi.bottomfrac+ bias*bottomstep; pixlow=rsi.pixlow+bias*pixlowstep; pixhigh=rsi.pixhigh+bias*pixhighstep; { for ( int rw_x=startx; rw_x < endx ; rw_x++) { // mark floor / ceiling areas yl = (topfrac+HEIGHTUNIT-1)>>HEIGHTBITS; // no space above wall? if (yl < ceilingclip[rw_x]+1) yl = ceilingclip[rw_x]+1; yh = bottomfrac>>HEIGHTBITS; if (yh >= floorclip[rw_x]) yh = floorclip[rw_x]-1; // System.out.printf("Thread: rw %d yl %d yh %d\n",rw_x,yl,yh); // A particular seg has been identified as a floor marker. // texturecolumn and lighting are independent of wall tiers if (rsi.segtextured) { // calculate texture offset // CAREFUL: a VERY anomalous point in the code. Their sum is supposed // to give an angle not exceeding 45 degrees (or 0x0FFF after shifting). // If added with pure unsigned rules, this doesn't hold anymore, // not even if accounting for overflow. angle = Tables.toBAMIndex(rsi.rw_centerangle + (int)xtoviewangle[rw_x]); //angle = (int) (((rw_centerangle + xtoviewangle[rw_x])&BITS31)>>>ANGLETOFINESHIFT); //angle&=0x1FFF; // FIXME: We are accessing finetangent here, the code seems pretty confident // in that angle won't exceed 4K no matter what. But xtoviewangle // alone can yield 8K when shifted. // This usually only overflows if we idclip and look at certain directions // (probably angles get fucked up), however it seems rare enough to just // "swallow" the exception. You can eliminate it by anding with 0x1FFF // if you're so inclined. texturecolumn = rsi.rw_offset-FixedMul(finetangent[angle],rsi.rw_distance); texturecolumn >>= FRACBITS; // calculate lighting index = rw_scale>>LIGHTSCALESHIFT; if (index >= MAXLIGHTSCALE ) index = MAXLIGHTSCALE-1; dcvars.dc_colormap = rsi.walllights[index]; dcvars.dc_x = rw_x; dcvars.dc_iscale = (int) (0xffffffffL / rw_scale); } // draw the wall tiers if (rsi.midtexture!=0) { // single sided line dcvars.dc_yl = yl; dcvars.dc_yh = yh; dcvars.dc_texheight = TexMan.getTextureheight(rsi.midtexture)>>FRACBITS; // killough dcvars.dc_texturemid = rsi.rw_midtexturemid; dcvars.dc_source = TexMan.GetCachedColumn(rsi.midtexture,texturecolumn); dcvars.dc_source_ofs=0; colfunc.invoke(); ceilingclip[rw_x] = (short) rsi.viewheight; floorclip[rw_x] = -1; } else { // two sided line if (rsi.toptexture!=0) { // top wall mid = pixhigh>>HEIGHTBITS; pixhigh += pixhighstep; if (mid >= floorclip[rw_x]) mid = floorclip[rw_x]-1; if (mid >= yl) { dcvars.dc_yl = yl; dcvars.dc_yh = mid; dcvars.dc_texturemid = rsi.rw_toptexturemid; dcvars.dc_texheight=TexMan.getTextureheight(rsi.toptexture)>>FRACBITS; dcvars.dc_source = TexMan.GetCachedColumn(rsi.toptexture,texturecolumn); //dc_source_ofs=0; colfunc.invoke(); ceilingclip[rw_x] = (short) mid; } else ceilingclip[rw_x] = (short) (yl-1); } // if toptexture else { // no top wall if (rsi.markceiling) ceilingclip[rw_x] = (short) (yl-1); } if (rsi.bottomtexture!=0) { // bottom wall mid = (pixlow+HEIGHTUNIT-1)>>HEIGHTBITS; pixlow += pixlowstep; // no space above wall? if (mid <= ceilingclip[rw_x]) mid = ceilingclip[rw_x]+1; if (mid <= yh) { dcvars.dc_yl = mid; dcvars.dc_yh = yh; dcvars.dc_texturemid = rsi.rw_bottomtexturemid; dcvars.dc_texheight=TexMan.getTextureheight(rsi.bottomtexture)>>FRACBITS; dcvars.dc_source = TexMan.GetCachedColumn(rsi.bottomtexture,texturecolumn); // dc_source_ofs=0; colfunc.invoke(); floorclip[rw_x] = (short) mid; } else floorclip[rw_x] = (short) (yh+1); } // end-bottomtexture else { // no bottom wall if (rsi.markfloor) floorclip[rw_x] = (short) (yh+1); } } // end-else (two-sided line) rw_scale += rw_scalestep; topfrac += topstep; bottomfrac += bottomstep; } // end-rw } // end-block } @Override public void setDetail(int detailshift) { if (detailshift == 0) colfunc = colfunchi; else colfunc = colfunclow; } /** Only called once per screen width change */ public void setScreenRange(int rwstart, int rwend){ this.rw_end=rwend; this.rw_start=rwstart; } /** How many instructions TOTAL are there to wade through. * Not all will be executed on one thread, except in some rare * circumstances. * * @param rsiend */ public void setRSIEnd(int rsiend){ this.rsiend=rsiend; } public void run() { RenderSegInstruction<V> rsi; // Each worker blanks its own portion of the floor/ceiling clippers. System.arraycopy(BLANKFLOORCLIP,rw_start,floorclip, rw_start,rw_end-rw_start); System.arraycopy(BLANKCEILINGCLIP,rw_start,ceilingclip, rw_start,rw_end-rw_start); // For each "SegDraw" instruction... for (int i=0;i<rsiend;i++){ rsi=RSI[i]; dcvars.centery=RSI[i].centery; int startx,endx; // Does a wall actually start in our screen zone? // If yes, we need no bias, since it was meant for it. // If the wall started BEFORE our zone, then we // will need to add a bias to it (see ProcessRSI). // If its entirely non-contained, ProcessRSI won't be // called anyway, so we don't need to check for the end. boolean contained=(rsi.rw_x>=rw_start); // Keep to your part of the screen. It's possible that several // threads will process the same RSI, but different parts of it. // Trim stuff that starts before our rw_start position. startx=Math.max(rsi.rw_x,rw_start); // Similarly, trim stuff after our rw_end position. endx=Math.min(rsi.rw_stopx,rw_end); // Is there anything to actually draw? if ((endx-startx)>0) { ProcessRSI(rsi,startx,endx,contained); } } // end-instruction try { barrier.await(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (BrokenBarrierException e) { // TODO Auto-generated catch block e.printStackTrace(); } } //protected abstract void ProcessRSI(RenderSegInstruction<V> rsi, int startx,int endx,boolean contained); ////////////////////////////VIDEO SCALE STUFF //////////////////////////////// protected int SCREENWIDTH; protected int SCREENHEIGHT; protected IVideoScale vs; @Override public void setVideoScale(IVideoScale vs) { this.vs=vs; } @Override public void initScaling() { this.SCREENHEIGHT=vs.getScreenHeight(); this.SCREENWIDTH=vs.getScreenWidth(); } public void updateRSI(RenderSegInstruction<V>[] rsi) { this.RSI=rsi; } public static final class HiColor extends RenderSegExecutor<byte[],short[]>{ public HiColor(int SCREENWIDTH, int SCREENHEIGHT, int id, short[] screen, TextureManager<byte[]> texman, RenderSegInstruction<short[]>[] RSI, short[] BLANKCEILINGCLIP, short[] BLANKFLOORCLIP, short[] ceilingclip, short[] floorclip, int[] columnofs, long[] xtoviewangle, int[] ylookup, visplane_t[] visplanes, CyclicBarrier barrier) { super(SCREENWIDTH, SCREENHEIGHT, id, screen, texman, RSI, BLANKCEILINGCLIP, BLANKFLOORCLIP, ceilingclip, floorclip, columnofs, xtoviewangle, ylookup, visplanes, barrier); dcvars=new ColVars<byte[],short[]>(); colfunc=colfunchi=new R_DrawColumnBoomOpt.HiColor(SCREENWIDTH, SCREENHEIGHT,ylookup,columnofs,dcvars,screen,null ); colfunclow=new R_DrawColumnBoomOptLow.HiColor(SCREENWIDTH, SCREENHEIGHT,ylookup,columnofs,dcvars,screen,null ); } } public static final class Indexed extends RenderSegExecutor<byte[],byte[]>{ public Indexed(int SCREENWIDTH, int SCREENHEIGHT, int id, byte[] screen, IGetColumn<byte[]> gc, TextureManager<byte[]> texman, RenderSegInstruction<byte[]>[] RSI, short[] BLANKCEILINGCLIP, short[] BLANKFLOORCLIP, short[] ceilingclip, short[] floorclip, int[] columnofs, long[] xtoviewangle, int[] ylookup, visplane_t[] visplanes, CyclicBarrier barrier) { super(SCREENWIDTH, SCREENHEIGHT, id, screen, texman, RSI, BLANKCEILINGCLIP, BLANKFLOORCLIP, ceilingclip, floorclip, columnofs, xtoviewangle, ylookup, visplanes, barrier); dcvars=new ColVars<byte[],byte[]>(); colfunc=colfunchi=new R_DrawColumnBoomOpt.Indexed(SCREENWIDTH, SCREENHEIGHT,ylookup,columnofs,dcvars,screen,null ); colfunclow=new R_DrawColumnBoomOptLow.Indexed(SCREENWIDTH, SCREENHEIGHT,ylookup,columnofs,dcvars,screen,null ); } } } // $Log: RenderSegExecutor.java,v $ // Revision 1.2 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.1.2.2 2012/09/21 16:18:29 velktron // More progress...but no cigar, yet. // // Revision 1.1.2.1 2012/09/20 14:20:10 velktron // Parallel stuff in their own package (STILL BROKEN) // // Revision 1.12.2.4 2012/09/19 21:45:41 velktron // More extensions... // // Revision 1.12.2.3 2012/09/18 16:11:50 velktron // Started new "all in one" approach for unifying Indexed, HiColor and (future) TrueColor branches. // // Revision 1.12.2.2 2012/06/15 14:44:09 velktron // Doesn't need walllights? // // Revision 1.12.2.1 2011/11/14 00:27:11 velktron // A barely functional HiColor branch. Most stuff broken. DO NOT USE // // Revision 1.12 2011/11/01 19:04:06 velktron // Cleaned up, using IDetailAware for subsystems. // // Revision 1.11 2011/10/31 18:33:56 velktron // Much cleaner implementation using the new rendering model. Now it's quite faster, too. // // Revision 1.10 2011/07/25 11:39:10 velktron // Optimized to work without dc_source_ofs (uses only cached, solid textures) // // Revision 1.9 2011/07/12 16:29:35 velktron // Now using GetCachedColumn // // Revision 1.8 2011/07/12 16:25:02 velktron // Removed dependency on per-thread column pointers. // // Revision 1.7 2011/06/07 21:21:15 velktron // Definitively fixed jitter bug, which was due to dc_offset_contention. Now the alternative parallel renderer is just as good as the original one. // // Revision 1.6 2011/06/07 13:35:38 velktron // Definitively fixed drawing priority/zones. Now to solve the jitter :-/ // // Revision 1.5 2011/06/07 01:32:32 velktron // Parallel Renderer 2 still buggy :-( // // Revision 1.4 2011/06/07 00:50:47 velktron // Alternate Parallel Renderer fixed. // // Revision 1.3 2011/06/07 00:11:11 velktron // Fixed alternate parallel renderer (seg based). No longer deprecated. // package rr; /** A vissprite_t is a thing * that will be drawn during a refresh. * I.e. a sprite object that is partly visible. */ public class vissprite_t<V> implements Comparable<vissprite_t<V>>{ // Doubly linked list. public vissprite_t<V> prev; public vissprite_t<V> next; public int x1; public int x2; // for line side calculation public int gx; public int gy; // global bottom / top for silhouette clipping public int gz; public int gzt; // horizontal position of x1 public int startfrac; public int scale; // negative if flipped public int xiscale; public int texturemid; public int patch; /** for color translation and shadow draw, * maxbright frames as well. * * Use paired with pcolormap; */ public V colormap; /* pointer into colormap public int pcolormap; */ public long mobjflags; /** visspites are sorted by scale */ @Override public final int compareTo(vissprite_t<V> o) { // We only really care if it's drawn before. if (this.scale> o.scale) return 1; if (this.scale< o.scale) return -1; return 0; } public String toString(){ return ("Effective drawing position x1: "+x1 + " x2: "+ x2 +" scale "+(scale/65535.0) +" iscale "+(xiscale/65535.0)); } } package rr; import java.util.Arrays; import v.IVideoScale; /** Now what is a visplane, anyway? * Basically, it's a bunch of arrays buffer representing * a top and a bottom boundary of a region to be filled with a * specific kind of flat. They are as wide as the screen, * and actually store height bounding values or sentinel valuesThink of it as an arbitrary boundary. * * These are refreshed continuously during rendering, and mark * the limits between flat regions. Special values mean "do not * render this column at all", while clipping out of the map bounds * results in well-known bleeding effects. * * @author admin * */ public class visplane_t{ public static final int TOPOFFSET=1; public static final int MIDDLEPADDING=2; public static int BOTTOMOFFSET; // Multithreading trickery (for strictly x-bounded drawers) // The thread if is encoded in the upper 3 bits (puts an upper limit // of 8 floor threads), and the stomped value is encoded in the next 12 // bits (this puts an upper height limit of 4096 pixels). // Not the cleanest system possible, but it's backwards compatible // TODO: expand visplane buffers to full-fledged ints? public static final char SENTINEL=0x8000; public static final char THREADIDSHIFT=12; public static final char THREADIDCLEAR=0x8FFF; public static final char THREADIDBITS=0XFFFF-THREADIDCLEAR; public static final char THREADVALUEBITS=THREADIDCLEAR-SENTINEL; public visplane_t(){ this.data=new char[4+2*SCREENWIDTH]; this.updateHashCode(); } public visplane_t(int height, int picnum, int lightlevel){ this.height=height; this.picnum=picnum; this.lightlevel=lightlevel; this.updateHashCode(); this.data=new char[4+2*SCREENWIDTH]; } /** (fixed_t) */ public int height; public int picnum; public int lightlevel; public int minx; public int maxx; // leave pads for [minx-1]/[maxx+1] /* public byte pad1; // Here lies the rub for all // dynamic resize/change of resolution. public byte[] top=new byte[SCREENWIDTH]; public byte pad2; public byte pad3; // See above. public byte[] bottom=new byte [SCREENWIDTH]; public byte pad4;*/ char data[]; // Hack to allow quick clearing of visplanes. protected static char[] clearvisplane; /** "Clear" the top with FF's. */ public void clearTop(){ System.arraycopy(clearvisplane, 0, this.data, TOPOFFSET, SCREENWIDTH); } /** "Clear" the bottom with FF's. */ public void clearBottom(){ System.arraycopy(clearvisplane, 0, this.data, BOTTOMOFFSET, SCREENWIDTH); } public void setTop(int index, char value){ this.data[TOPOFFSET+index]=value; } public char getTop(int index){ return this.data[TOPOFFSET+index]; } public void setBottom(int index, char value){ this.data[BOTTOMOFFSET+index]=value; } public int getBottom(int index){ return this.data[BOTTOMOFFSET+index]; } public String toString(){ sb.setLength(0); sb.append("Visplane\n"); sb.append('\t'); sb.append("Height: "); sb.append(this.height); sb.append('\t'); sb.append("Min-Max: "); sb.append(this.minx); sb.append('-'); sb.append(this.maxx); sb.append('\t'); sb.append("Picnum: "); sb.append(this.picnum); sb.append('\t'); sb.append("Lightlevel: "); sb.append(this.lightlevel); return sb.toString(); } protected int hash; /** Call this upon any changed in height, picnum or lightlevel */ public void updateHashCode(){ this.hash=height^picnum^lightlevel; } public int hashCode(){ return this.hash; } public static int visplaneHash(int height, int picnum, int lightlevel){ return height^picnum^lightlevel; } protected static StringBuilder sb=new StringBuilder(); // HACK: the resolution awareness is shared between all visplanes. // Change this if you ever plan on running multiple renderers with // different resolution or something. protected static int SCREENWIDTH; protected static int SCREENHEIGHT; protected static IVideoScale vs; public static void setVideoScale(IVideoScale vs) { visplane_t.vs=vs; } public static void initScaling() { SCREENHEIGHT=vs.getScreenHeight(); SCREENWIDTH=vs.getScreenWidth(); // Pre-scale stuff. BOTTOMOFFSET=SCREENWIDTH+TOPOFFSET+MIDDLEPADDING; if (clearvisplane==null || clearvisplane.length<SCREENWIDTH) { clearvisplane=new char[SCREENWIDTH]; Arrays.fill(clearvisplane,Character.MAX_VALUE); } } }; package rr; import static data.Tables.ANG90; import static data.Tables.finecosine; import static data.Tables.finesine; import static m.fixed_t.FixedDiv; import utils.C2JUtils; import v.IVideoScale; import v.IVideoScaleAware; import data.Limits; import data.Tables; /** Actual visplane data and methods are isolate here. * This allows more encapsulation and some neat hacks like sharing * visplane data among parallel renderers, without duplicating them. */ public class Visplanes implements IVideoScaleAware{ private static final boolean DEBUG2=false; protected final ViewVars view; protected final TextureManager<?> TexMan; public Visplanes(ViewVars view,TextureManager<?> TexMan){ this.view=view; this.TexMan=TexMan; } protected int SCREENHEIGHT; protected int SCREENWIDTH; // HACK: An all zeroes array used for fast clearing of certain visplanes. public int[] cachedheight,BLANKCACHEDHEIGHT; /** To treat as fixed_t */ public int basexscale, baseyscale; /** To treat at fixed_t */ protected int[] yslope; // initially. public int MAXVISPLANES = Limits.MAXVISPLANES; public int MAXOPENINGS; /** visplane_t*, treat as indexes into visplanes */ public int lastvisplane, floorplane, ceilingplane; public visplane_t[] visplanes = new visplane_t[MAXVISPLANES]; /** * openings is supposed to show where "openings" in visplanes start and end * e.g. due to sprites, windows etc. */ public short[] openings; /** Maes: this is supposed to be a pointer inside openings */ public int lastopening; protected int skyscale; /** * Call only after visplanes have been properly resized for resolution. * In case of dynamic resolution changes, the old ones should just be * discarded, as they would be nonsensical. */ public void initVisplanes() { cachedheight = new int[SCREENHEIGHT]; C2JUtils.initArrayOfObjects(visplanes); } public int getBaseXScale(){ return basexscale; } public int getBaseYScale(){ return baseyscale; } public int getSkyScale(){ return skyscale; } public void setSkyScale(int i) { skyscale=i; } public int getLength(){ return visplanes.length; } /** Return the last of visplanes, allocating a new one if needed */ public visplane_t allocate(){ if (lastvisplane == visplanes.length) { // visplane overflows could occur at this point. resizeVisplanes(); } return visplanes[lastvisplane++]; } public final void resizeVisplanes() { // Bye bye, old visplanes. visplanes = C2JUtils.resize(visplanes[0], visplanes, visplanes.length*2); } /** * R_FindPlane * * Checks whether a visplane with the specified height, picnum and light * level exists among those already created. This looks like a half-assed * attempt at reusing already existing visplanes, rather than creating new * ones. The tricky part is understanding what happens if one DOESN'T exist. * Called only from within R_Subsector (so while we're still trasversing * stuff). * * @param height * (fixed_t) * @param picnum * @param lightlevel * @return was visplane_t*, returns index into visplanes[] */ public final int FindPlane(int height, int picnum, int lightlevel) { // System.out.println("\tChecking for visplane merging..."); int check = 0; // visplane_t* visplane_t chk = null; if (picnum == TexMan.getSkyFlatNum()) { height = 0; // all skys map together lightlevel = 0; } chk = visplanes[0]; // Find visplane with the desired attributes for (check = 0; check < lastvisplane; check++) { chk = visplanes[check]; if (height == chk.height && picnum == chk.picnum && lightlevel == chk.lightlevel) { // Found a visplane with the desired specs. break; } } if (check < lastvisplane) { return check; } // This should return the next available visplane and resize if needed, // no need to hack with lastvisplane++ chk = allocate(); // Add a visplane chk.height = height; chk.picnum = picnum; chk.lightlevel = lightlevel; chk.minx = SCREENWIDTH; chk.maxx = -1; // memset (chk.top,0xff,sizeof(chk.top)); chk.clearTop(); return check; } /** * R_ClearPlanes At begining of frame. * */ public void ClearPlanes() { int angle; /* * View planes are cleared at the beginning of every plane, by * setting them "just outside" the borders of the screen (-1 and * viewheight). */ // Point to #1 in visplane list? OK... ?! lastvisplane = 0; // We point back to the first opening of the list openings[0], // again. lastopening = 0; // texture calculation System.arraycopy(BLANKCACHEDHEIGHT, 0, cachedheight, 0, BLANKCACHEDHEIGHT.length); // left to right mapping // FIXME: If viewangle is ever < ANG90, you're fucked. How can this // be prevented? // Answer: 32-bit unsigned are supposed to roll over. You can & with // 0xFFFFFFFFL. angle = (int) Tables.toBAMIndex(view.angle - ANG90); // scale will be unit scale at SCREENWIDTH/2 distance basexscale = FixedDiv(finecosine[angle], view.centerxfrac); baseyscale = -FixedDiv(finesine[angle], view.centerxfrac); } /** * R_CheckPlane * * Called from within StoreWallRange * * Presumably decides if a visplane should be split or not? * */ public int CheckPlane(int index, int start, int stop) { if (DEBUG2) System.out.println("Checkplane " + index + " between " + start + " and " + stop); // Interval ? int intrl; int intrh; // Union? int unionl; int unionh; // OK, so we check out ONE particular visplane. visplane_t pl = visplanes[index]; if (DEBUG2) System.out.println("Checking out plane " + pl); int x; // If start is smaller than the plane's min... // // start minx maxx stop // | | | | // --------PPPPPPPPPPPPPP----------- // // if (start < pl.minx) { intrl = pl.minx; unionl = start; // Then we will have this: // // unionl intrl maxx stop // | | | | // --------PPPPPPPPPPPPPP----------- // } else { unionl = pl.minx; intrl = start; // else we will have this: // // union1 intrl maxx stop // | | | | // --------PPPPPPPPPPPPPP----------- // // unionl comes before intrl in any case. // // } // Same as before, for for stop and maxx. // This time, intrh comes before unionh. // if (stop > pl.maxx) { intrh = pl.maxx; unionh = stop; } else { unionh = pl.maxx; intrh = stop; } // An interval is now defined, which is entirely contained in the // visplane. // // If the value FF is NOT stored ANYWWHERE inside it, we bail out // early for (x = intrl; x <= intrh; x++) if (pl.getTop(x) != Character.MAX_VALUE) break; // This can only occur if the loop above completes, // else the visplane we were checking has non-visible/clipped // portions within that range: we must split. if (x > intrh) { // Merge the visplane pl.minx = unionl; pl.maxx = unionh; // System.out.println("Plane modified as follows "+pl); // use the same one return index; } // SPLIT: make a new visplane at "last" position, copying materials // and light. visplane_t last=allocate(); last.height = pl.height; last.picnum = pl.picnum; last.lightlevel = pl.lightlevel; pl = last; pl.minx = start; pl.maxx = stop; // memset (pl.top,0xff,sizeof(pl.top)); pl.clearTop(); // return pl; // System.out.println("New plane created: "+pl); return lastvisplane - 1; } public void initScaling() { MAXOPENINGS = SCREENWIDTH * 64; openings = new short[MAXOPENINGS]; BLANKCACHEDHEIGHT = new int[SCREENHEIGHT]; yslope = new int[SCREENHEIGHT]; } @Override public void setVideoScale(IVideoScale vs) { this.SCREENHEIGHT=vs.getScreenHeight(); this.SCREENWIDTH=vs.getScreenWidth(); } /* /** * A hashtable used to retrieve planes with particular attributes faster * -hopefully-. The planes are still stored in the visplane array for * convenience, but we can search them in the hashtable too -as a bonus, we * can reuse previously created planes that match newer ones-. */ /* Hashtable<visplane_t, Integer> planehash = new Hashtable<visplane_t, Integer>( 128); visplane_t check = new visplane_t(); */ /* protected final int FindPlane2(int height, int picnum, int lightlevel) { // System.out.println("\tChecking for visplane merging..."); // int check=0; // visplane_t* visplane_t chk = null; Integer checknum; if (picnum == TexMan.getSkyFlatNum()) { height = 0; // all skys map together lightlevel = 0; } // Try and find this. check.lightlevel = lightlevel; check.picnum = picnum; check.height = height; check.updateHashCode(); checknum = planehash.get(check); // Something found, get it. if (!(checknum == null)) { // Visplane exists and is within those allocated in the current tic. if (checknum < lastvisplane) { return checknum; } // Found a visplane, but we can't add anymore. // Resize right away. This shouldn't take too long. if (lastvisplane == MAXVISPLANES) { // I.Error ("R_FindPlane: no more visplanes"); ResizeVisplanes(); } } // We found a visplane (possibly one allocated on a previous tic) // but we can't link directly to it, we need to copy its data // around. checknum = new Integer(Math.max(0, lastvisplane)); chk = visplanes[checknum]; // Add a visplane lastvisplane++; chk.height = height; chk.picnum = picnum; chk.lightlevel = lightlevel; chk.minx = SCREENWIDTH; chk.maxx = -1; chk.updateHashCode(); planehash.put(chk, checknum); // memset (chk.top,0xff,sizeof(chk.top)); chk.clearTop(); return checknum; } */ } package rr; public class base_ratio_t { public base_ratio_t(int base_width, int base_height, int psprite_offset, int multiplier, float gl_ratio) { this.base_width = base_width; this.base_height = base_height; this.psprite_offset = psprite_offset; this.multiplier = multiplier; this.gl_ratio = (float) (RMUL*gl_ratio); } public int base_width; // Base width (unused) public int base_height; // Base height (used for wall visibility multiplier) public int psprite_offset; // Psprite offset (needed for "tallscreen" modes) public int multiplier; // Width or height multiplier public float gl_ratio; public static final double RMUL =1.6d/1.333333d; } package awt; import static g.Keys.*; import i.DoomEventInterface; import java.awt.AWTEvent; import java.awt.Component; import java.awt.Cursor; import java.awt.Dimension; import java.awt.Event; import java.awt.KeyEventDispatcher; import java.awt.MouseInfo; import java.awt.Point; import java.awt.Robot; import java.awt.Toolkit; import java.awt.event.InputEvent; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.event.MouseMotionListener; import java.awt.event.WindowEvent; import java.awt.event.WindowListener; import java.awt.image.BufferedImage; import java.util.ArrayList; import java.util.LinkedList; import doom.DoomMain; import doom.event_t; import doom.evtype_t; public class AWTEvents2 implements WindowListener,KeyEventDispatcher,KeyListener,MouseListener,MouseMotionListener,DoomEventInterface { // modifications of eventQueue must be thread safe! public LinkedList<DoomEvent> eventQueue = new LinkedList<DoomEvent>(); public DoomMain DM; public Component canvas; Robot robby; public AWTEvents2(DoomMain DM, Component canvas) { this.DM = DM; this.canvas = canvas; // AWT: create cursors. this.normal=canvas.getCursor(); this.hidden=this.createInvisibleCursor(); // Create AWT Robot for forcing mouse try { robby=new Robot(); } catch (Exception e){ System.out.println("AWT Robot could not be created, mouse input focus will be loose!"); } } //////////// LISTENERS ////////// @Override public void mouseClicked(MouseEvent mouseevent) { addEvent(mouseevent); } @Override public void mouseEntered(MouseEvent mouseevent) { addEvent(mouseevent); } @Override public void mouseExited(MouseEvent mouseevent) { addEvent(mouseevent); relockMouse(mouseevent); } @Override public void mousePressed(MouseEvent mouseevent) { addEvent(mouseevent); } @Override public void mouseReleased(MouseEvent mouseevent) { addEvent(mouseevent); } @Override public void mouseDragged(MouseEvent mouseevent) { //addEvent(mouseevent); mouseMoved(mouseevent); } ArrayList<Point[]> cancelMoves = new ArrayList<Point[]>(); Point lastMousePos = new Point(0,0); @Override public void mouseMoved(MouseEvent mouseevent) { DoomEvent dEv = new DoomEvent(mouseevent); Point srcPos = lastMousePos; Point destPos = mouseevent.getLocationOnScreen(); Point deltaPoint = new Point(destPos.x-srcPos.x, destPos.y-srcPos.y); dEv.deltaPoint = deltaPoint; addEvent(dEv); relockMouse(mouseevent); } public void relockMouse(MouseEvent mouseevent) { if (canvas.hasFocus()) { Point destPoint = new Point(canvas.getX()+canvas.getWidth()/2, canvas.getY()+canvas.getHeight()/2); canvas.removeMouseListener(this); robby.mouseMove(destPoint.x, destPoint.y); canvas.addMouseListener(this); } lastMousePos = MouseInfo.getPointerInfo().getLocation(); } @Override public boolean dispatchKeyEvent(KeyEvent e) { return false; } @Override public void windowActivated(WindowEvent windowevent) { canvas.getInputContext().selectInputMethod(java.util.Locale.US); } @Override public void windowClosed(WindowEvent windowevent) { // TODO Auto-generated method stub } @Override public void windowClosing(WindowEvent windowevent) { // TODO Auto-generated method stub } @Override public void windowDeactivated(WindowEvent windowevent) { // TODO Auto-generated method stub } @Override public void windowDeiconified(WindowEvent windowevent) { canvas.getInputContext().selectInputMethod(java.util.Locale.US); } @Override public void windowIconified(WindowEvent windowevent) { // TODO Auto-generated method stub } @Override public void windowOpened(WindowEvent windowevent) { canvas.getInputContext().selectInputMethod(java.util.Locale.US); } public void keyPressed(KeyEvent e) { //if ((e.getModifiersEx() & UNACCEPTABLE_MODIFIERS) ==0) { if (isAcceptableKey(new DoomEvent(e))) { addEvent(e); } e.consume(); } public void keyReleased(KeyEvent e) { //if ((e.getModifiersEx() & UNACCEPTABLE_MODIFIERS) ==0) { if (isAcceptableKey(new DoomEvent(e))) { addEvent(e); } e.consume(); } public void keyTyped(KeyEvent e) { // if ((e.getModifiersEx() & UNACCEPTABLE_MODIFIERS) ==0){ if (isAcceptableKey(new DoomEvent(e))) { addEvent(e); } } int AWTMouseToKey(DoomEvent ev) { int buttonIdx = ev.getButton() - MouseEvent.BUTTON1; return KEY_MOUSE1+buttonIdx; } // number of total 'button' inputs, include keyboard keys, plus virtual // keys (mousebuttons and joybuttons becomes keys) public static final int NUMKEYS = 256; public static final int MOUSEBUTTONS = 8; public static final int JOYBUTTONS = 14; // 10 bases + 4 hat // // mouse and joystick buttons are handled as 'virtual' keys // public static final int KEY_MOUSE1 = NUMKEYS; public static final int KEY_JOY1 = KEY_MOUSE1+MOUSEBUTTONS; public static final int KEY_DBLMOUSE1 = KEY_JOY1+JOYBUTTONS; // double clicks public static final int KEY_DBLJOY1 = KEY_DBLMOUSE1+MOUSEBUTTONS; public static final int KEY_2MOUSE1 = KEY_DBLJOY1+JOYBUTTONS; public static final int KEY_DBL2MOUSE1 = KEY_2MOUSE1+MOUSEBUTTONS; public static final int KEY_MOUSEWHEELUP = KEY_DBL2MOUSE1+MOUSEBUTTONS; public static final int KEY_MOUSEWHEELDOWN= KEY_MOUSEWHEELUP+1; public static final int KEY_2MOUSEWHEELUP = KEY_MOUSEWHEELUP+2; public static final int KEY_2MOUSEWHEELDOWN= KEY_MOUSEWHEELUP+3; public static final int NUMINPUTS = KEY_MOUSEWHEELUP+4; int currentMouseButtons = 0; @Override public void GetEvent() { DoomEvent X_event; event_t event=new event_t();; // put event-grabbing stuff in here if (eventQueue.isEmpty()) return; X_event=nextEvent(); //System.out.println("Event type:"+X_event.getID()); // Check for Event.??? shit if (!ignorebutton){ switch (X_event.getID()) { case Event.KEY_PRESS: event=new event_t(evtype_t.ev_keydown, xlatekey(/*(KeyEvent)*/X_event)); DM.PostEvent(event); //System.err.println("k"); break; case Event.KEY_RELEASE: event=new event_t(evtype_t.ev_keyup, xlatekey(/*(KeyEvent)*/X_event)); DM.PostEvent(event); //System.err.println( "ku"); break; } } switch (X_event.getID()){ // ButtonPress case Event.MOUSE_DOWN: /* currentMouseButtons = (X_event.getButton() == MouseEvent.BUTTON1 ? 1 : 0) | ((X_event.getButton() == MouseEvent.BUTTON3) ? 2 : 0) | ((X_event.getButton() == MouseEvent.BUTTON2) ? 4 : 0);*/ event.data1 = AWTMouseToKey(X_event); event.data2 = event.data3 = 0; event.type=evtype_t.ev_keydown;//ev_mouse; DM.PostEvent(event); break; // ButtonRelease case Event.MOUSE_UP: event.type = evtype_t.ev_keyup;// ev_mouse; /* currentMouseButtons = currentMouseButtons & ~(X_event.getButton() == MouseEvent.BUTTON1 ? 1 : 0) & ~(X_event.getButton() == MouseEvent.BUTTON3 ? 2 : 0) & ~(X_event.getButton() == MouseEvent.BUTTON2 ? 4 : 0);*/ event.data1 = AWTMouseToKey(X_event); event.data2 = event.data3 = 0; DM.PostEvent(event); break; // MotionNotify: case Event.MOUSE_MOVE: case Event.MOUSE_DRAG: event.type = evtype_t.ev_mouse; // Get buttons, as usual. event.data1 = currentMouseButtons; event.data2 = X_event.deltaPoint.x << 2; event.data3 = -X_event.deltaPoint.y << 2; if ((event.data2 | event.data3)!=0) { DM.PostEvent(event); } break; case Event.MOUSE_ENTER: System.err.println("ACCEPTING keyboard input"); canvas.requestFocus(); canvas.setCursor(hidden); ignorebutton=false; break; case Event.MOUSE_EXIT: System.err.println("IGNORING keyboard input"); canvas.setCursor(normal); ignorebutton=true; break; case Event.WINDOW_EXPOSE: // No real single equivalent for "ConfigureNotify" case Event.WINDOW_MOVED: case Event.WINDOW_DESTROY: break; default: // NOT NEEDED in AWT if (doShm && X_event.type == X_shmeventtype) shmFinished = true; break; } } boolean isAcceptableKey(DoomEvent e) { return e.getKeyCode()<=KeyEvent.VK_F12 || e.getKeyChar()=='#'; } void addEvent(AWTEvent ev) { addEvent(new DoomEvent(ev)); if (ev instanceof InputEvent) { ((InputEvent)ev).consume(); } } void addEvent(DoomEvent ev) { synchronized (eventQueue) { eventQueue.addLast(ev); } } public DoomEvent nextEvent() { DoomEvent ev; synchronized (eventQueue) { ev = (!eventQueue.isEmpty())?(DoomEvent)eventQueue.removeFirst():null; } return ev; } public static class DoomEvent { int keyCode; char keyChar; int ID; int button; int x; int y; Point deltaPoint; public DoomEvent(AWTEvent evt) { this.ID = evt.getID(); if (evt instanceof KeyEvent) { this.keyChar = ((KeyEvent)evt).getKeyChar(); this.keyCode = ((KeyEvent)evt).getKeyCode(); } if (evt instanceof MouseEvent) { this.button = ((MouseEvent)evt).getButton(); this.x = ((MouseEvent)evt).getX(); this.y = ((MouseEvent)evt).getY(); } } public int getKeyCode() { return keyCode; } public char getKeyChar() { return keyChar; } public int getID() { return ID; } public int getButton() { return button; } public int getX() { return x; } public int getY() { return y; } } /** FIXME: input must be made scancode dependent rather than VK_Dependent, * else a lot of things just don't work. * * @param e * @return */ public int xlatekey(DoomEvent e) { int rc; switch(rc = e.getKeyCode()) //Event.XKeycodeToKeysym(X_display, X_event.xkey.keycode, 0)) { case KeyEvent.VK_LEFT: rc = KEY_LEFTARROW; break; case KeyEvent.VK_RIGHT: rc = KEY_RIGHTARROW; break; case KeyEvent.VK_DOWN: rc = KEY_DOWNARROW; break; case KeyEvent.VK_UP: rc = KEY_UPARROW; break; case KeyEvent.VK_ESCAPE: rc = KEY_ESCAPE; break; case KeyEvent.VK_ENTER: rc = KEY_ENTER; break; case KeyEvent.VK_CONTROL: rc= KEY_CTRL; break; case KeyEvent.VK_ALT: rc=KEY_ALT; break; case KeyEvent.VK_SHIFT: rc=KEY_SHIFT; break; // Added handling of pgup/pgdown/home etc. case KeyEvent.VK_PAGE_DOWN: rc= KEY_PGDN; break; case KeyEvent.VK_PAGE_UP: rc= KEY_PGUP; break; case KeyEvent.VK_HOME: rc= KEY_HOME; break; case KeyEvent.VK_F1: rc = KEY_F1; break; case KeyEvent.VK_F2: rc = KEY_F2; break; case KeyEvent.VK_F3: rc = KEY_F3; break; case KeyEvent.VK_F4: rc = KEY_F4; break; case KeyEvent.VK_F5: rc = KEY_F5; break; case KeyEvent.VK_F6: rc = KEY_F6; break; case KeyEvent.VK_F7: rc = KEY_F7; break; case KeyEvent.VK_F8: rc = KEY_F8; break; case KeyEvent.VK_F9: rc = KEY_F9; break; case KeyEvent.VK_F10: rc = KEY_F10; break; case KeyEvent.VK_F11: rc = KEY_F11; break; case KeyEvent.VK_F12: rc = KEY_F12; break; case KeyEvent.VK_BACK_SPACE: case KeyEvent.VK_DELETE: rc = KEY_BACKSPACE; break; case KeyEvent.VK_PAUSE: rc = KEY_PAUSE; break; case KeyEvent.KEY_RELEASED: case KeyEvent.VK_TAB: rc = KEY_TAB; break; case KeyEvent.KEY_PRESSED: switch(e.getKeyCode()){ case KeyEvent.VK_PLUS: case KeyEvent.VK_EQUALS: rc = KEY_EQUALS; break; case (13): case KeyEvent.VK_SUBTRACT: case KeyEvent.VK_MINUS: rc = KEY_MINUS; break; case KeyEvent.VK_SHIFT: rc = KEY_SHIFT; break; case KeyEvent.VK_CONTROL: rc = KEY_CTRL; break; case KeyEvent.VK_ALT: rc = KEY_ALT; break; case KeyEvent.VK_SPACE: rc = ' '; break; } default: /*if (rc >= KeyEvent.VK_SPACE && rc <= KeyEvent.VK_DEAD_TILDE) { rc = (int) (rc - KeyEvent.FOCUS_EVENT_MASK + ' '); break; } */ if (rc >= KeyEvent.VK_A && rc <= KeyEvent.VK_Z){ rc = rc-KeyEvent.VK_A +'a'; break; } // Unknown. Probably fucking up with the keyboard locale. Switch to be sure. // Sorry for this horrible hack, but Java can't read locale-free keycodes -_- // this.getInputContext().selectInputMethod(java.util.Locale.US); //if (rc>KEY_F12) rc=KEY_RSHIFT; break; } //System.out.println("Typed "+e.getKeyCode()+" char "+e.getKeyChar()+" mapped to "+Integer.toHexString(rc)); // Sanitize. Nothing beyond F12 must pass through, else you will // get the "all cheats" bug. return rc;//Math.min(rc,KEY_F12); } // This stuff should NOT get through in keyboard events. protected final int UNACCEPTABLE_MODIFIERS=(int) (InputEvent.ALT_GRAPH_DOWN_MASK+ InputEvent.META_DOWN_MASK+ InputEvent.META_MASK+ InputEvent.WINDOW_EVENT_MASK+ InputEvent.WINDOW_FOCUS_EVENT_MASK); protected boolean ignorebutton; /** 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); } Cursor hidden; Cursor normal; /** * NASTY hack to hide the cursor. * * Create a 'hidden' cursor by using a transparent image * ...return the invisible cursor */ protected Cursor createInvisibleCursor() { Dimension bestCursorDim = Toolkit.getDefaultToolkit().getBestCursorSize(2, 2); BufferedImage transparentImage = new BufferedImage(bestCursorDim.width, bestCursorDim.height, BufferedImage.TYPE_INT_ARGB); Cursor hiddenCursor = Toolkit.getDefaultToolkit( ).createCustomCursor(transparentImage, new Point(1, 1), "HiddenCursor"); return hiddenCursor; } } package awt; import java.awt.*; import java.awt.event.*; import javax.swing.JLabel; /** A convenient message box class to pop up here and there. * * @author zyklon * */ public class MsgBox extends Dialog implements ActionListener { /** * */ private static final long serialVersionUID = -872019680203708495L; private Button ok, can; private boolean isOk = false; /* * * @param frame parent frame * * @param msg message to be displayed * * @param okcan true : ok cancel buttons, false : ok button only */ public boolean isOk() { return isOk; } public MsgBox(Frame frame, String title, String msg, boolean okcan) { super(frame, title, true); setLayout(new BorderLayout()); add("Center", new JLabel(msg)); addOKCancelPanel(okcan); createFrame(); pack(); setVisible(true); this.can.requestFocus(); } public MsgBox(Frame frame, String msg) { this(frame, "Message", msg, false); } private void addOKCancelPanel(boolean okcan) { Panel p = new Panel(); p.setLayout(new FlowLayout()); createOKButton(p); if (okcan == true) createCancelButton(p); add("South", p); } private void createOKButton(Panel p) { p.add(ok = new Button("OK")); ok.addActionListener(this); } private void createCancelButton(Panel p) { p.add(can = new Button("Cancel")); can.addActionListener(this); } private void createFrame() { Dimension d = getToolkit().getScreenSize(); setLocation(d.width / 3, d.height / 3); } public void actionPerformed(ActionEvent ae) { if (ae.getSource() == ok) { isOk = true; setVisible(false); } else if (ae.getSource() == can) { setVisible(false); } } /* * public static void main(String args[]) { //Frame f = new Frame(); * //f.setSize(200,200); //f.setVisible(true); MsgBox message = new MsgBox * (null , "Hey you user, are you sure ?", true); if (message.isOk) * System.out.println("Ok pressed"); if (!message.isOk) * System.out.println("Cancel pressed"); message.dispose(); } */ } package awt; import java.awt.Canvas; import java.awt.Graphics; import java.awt.Graphics2D; import v.DoomVideoRenderer; import doom.DoomMain; /** A simple Doom display & keyboard driver for AWT. * Uses a Canvas for painting and implements some * of the IVideo methods. * * Uses 8-bit buffered images and switchable IndexColorModels. * * It's really basic, but allows testing and user interaction. * Heavily based on the original LinuxDoom X11 implementation, and * is similar in goals: just a functional, reference implementation * to build upon whatever fancy extensions you like. * * The only "hitch" is that this implementation expects to be * initialized upon a BufferedRenderer with multiple images per * screen buffer in order to perform the palette switching trick. * * The images themselves don't have to be "BufferedImage", * and in theory it could be possible to use e.g. MemoryImageSource * for the same purpose . Oh well. * * * * @author Velktron * */ public abstract class AWTDoom<V> extends DoomFrame<V> { /** * */ private static final long serialVersionUID = 1L; /** Gimme some raw palette RGB data. * I will do the rest * * (hint: read this from the PLAYPAL * lump in the IWAD!!!). * */ public AWTDoom(DoomMain<?,V> DM, DoomVideoRenderer<?,V> V) { super(DM, V); drawhere=new Canvas(); gelatine=new Canvas(); // Don't do anything yet until InitGraphics is called. } public void SetGamma(int level){ if (D) System.err.println("Setting gamma "+level); V.setUsegamma(level); screen=V.getCurrentScreen(); // Refresh screen after change. RAWSCREEN=V.getScreen(DoomVideoRenderer.SCREEN_FG); } public static final class HiColor extends AWTDoom<short[]>{ /** * */ private static final long serialVersionUID = 1L; public HiColor(DoomMain<?, short[]> DM, DoomVideoRenderer<?,short[]> V) { super(DM, V); } @Override public void ReadScreen(short[] scr) { System.arraycopy(this.RAWSCREEN, 0, scr, 0, RAWSCREEN.length); } @Override public void FinishUpdate() { int tics; int i; // draws little dots on the bottom of the screen /*if (true) { i = I.GetTime(); tics = i - lasttic; lasttic = i; if (tics > 20) tics = 20; if (tics < 1) tics = 1; for (i=0 ; i<tics*2 ; i+=2) RAWSCREEN[ (SCREENHEIGHT-1)*SCREENWIDTH + i] = (byte) 0xff; for ( ; i<20*2 ; i+=2) RAWSCREEN[ (SCREENHEIGHT-1)*SCREENWIDTH + i] = 0x0; } */ if (true) { i = TICK.GetTime(); tics = i - lasttic; lasttic = i; if (tics<1) frames++; else { //frames*=35; for (i=0 ; i<frames*2 ; i+=2) RAWSCREEN[ (height-1)*width + i] = (short) 0xffff; for ( ; i<20*2 ; i+=2) RAWSCREEN[ (height-1)*width + i] = 0x0; frames=0; } } this.update(null); //this.getInputContext().selectInputMethod(java.util.Locale.US); } } public static final class Indexed extends AWTDoom<byte[]>{ /** * */ private static final long serialVersionUID = 1L; public Indexed(DoomMain<?,byte[]> DM, DoomVideoRenderer<?,byte[]> V) { super(DM, V); } @Override public void ReadScreen(byte[] scr) { System.arraycopy(this.RAWSCREEN, 0, scr, 0, RAWSCREEN.length); } @Override public void FinishUpdate() { int tics; int i; // draws little dots on the bottom of the screen /*if (true) { i = I.GetTime(); tics = i - lasttic; lasttic = i; if (tics > 20) tics = 20; if (tics < 1) tics = 1; for (i=0 ; i<tics*2 ; i+=2) RAWSCREEN[ (SCREENHEIGHT-1)*SCREENWIDTH + i] = (byte) 0xff; for ( ; i<20*2 ; i+=2) RAWSCREEN[ (SCREENHEIGHT-1)*SCREENWIDTH + i] = 0x0; } */ if (true) { i = TICK.GetTime(); tics = i - lasttic; lasttic = i; if (tics<1) frames++; else { //frames*=35; for (i=0 ; i<frames*2 ; i+=2) RAWSCREEN[ (height-1)*width + i] = (short) 0xffff; for ( ; i<20*2 ; i+=2) RAWSCREEN[ (height-1)*width + i] = 0x0; frames=0; } } this.update(null); //this.getInputContext().selectInputMethod(java.util.Locale.US); } } public static final class TrueColor extends AWTDoom<int[]>{ /** * */ private static final long serialVersionUID = 1L; public TrueColor(DoomMain<?, int[]> DM, DoomVideoRenderer<?,int[]> V) { super(DM, V); } @Override public void ReadScreen(int[] scr) { System.arraycopy(this.RAWSCREEN, 0, scr, 0, RAWSCREEN.length); } @Override public void FinishUpdate() { int tics; int i; // draws little dots on the bottom of the screen /*if (true) { i = I.GetTime(); tics = i - lasttic; lasttic = i; if (tics > 20) tics = 20; if (tics < 1) tics = 1; for (i=0 ; i<tics*2 ; i+=2) RAWSCREEN[ (SCREENHEIGHT-1)*SCREENWIDTH + i] = (byte) 0xff; for ( ; i<20*2 ; i+=2) RAWSCREEN[ (SCREENHEIGHT-1)*SCREENWIDTH + i] = 0x0; } */ if (true) { i = TICK.GetTime(); tics = i - lasttic; lasttic = i; if (tics<1) frames++; else { //frames*=35; for (i=0 ; i<frames*2 ; i+=2) RAWSCREEN[ (height-1)*width + i] = (short) 0xffff; for ( ; i<20*2 ; i+=2) RAWSCREEN[ (height-1)*width + i] = 0x0; frames=0; } } this.update(null); //this.getInputContext().selectInputMethod(java.util.Locale.US); } } } //$Log: AWTDoom.java,v $ //Revision 1.16 2012/11/06 16:04:34 velktron //Spiffy new fullscreen switching system. // //Revision 1.15 2012/09/24 17:16:23 velktron //Massive merge between HiColor and HEAD. There's no difference from now on, and development continues on HEAD. // //Revision 1.14.2.5 2012/09/24 16:58:06 velktron //TrueColor, Generics. // //Revision 1.14.2.4 2012/09/20 14:06:58 velktron //Generic AWTDoom // //Revision 1.14.2.3 2012/09/17 15:57:07 velktron //Moved common code to DoomFrame // //Revision 1.14.2.2 2011/11/18 21:38:30 velktron //Uses 16-bit stuff. // //Revision 1.14.2.1 2011/11/14 00:27:11 velktron //A barely functional HiColor branch. Most stuff broken. DO NOT USE // //Revision 1.14 2011/11/01 19:03:10 velktron //Using screen number constants // //Revision 1.13 2011/10/23 18:11:07 velktron //Split functionality into DoomFrame, gerenic compliance. // //Revision 1.12 2011/10/11 13:24:51 velktron //Major overhaul to work with new renderer interface. Now only available windowing system. // //Revision 1.11 2011/08/01 00:59:57 velktron //Shut up debug messages. // //Revision 1.10 2011/07/15 13:57:54 velktron //Implement VI.ReadScreen as a future good practice. // //Revision 1.9 2011/06/23 15:42:38 velktron //Added modular palette rotation to handle sub-14 cases. // //Revision 1.8 2011/06/14 09:54:20 velktron //Separated palette generation/fixed OldAWTDoom // //Revision 1.7 2011/06/08 17:24:59 velktron //Added support for gamma changes. // //Revision 1.6 2011/06/02 14:54:18 velktron //Old AWTEvents deprecated. MochaEvents now default. // //Revision 1.3 2011/06/01 17:42:49 velktron //Removed stupid nagging. // //Revision 1.2 2011/06/01 17:17:24 velktron //New event system. // //Revision 1.1 2011/06/01 17:04:23 velktron //New event system. // //Revision 1.4 2011/05/30 02:25:50 velktron //Centering and offsetting on expose, proper exiting. // //Revision 1.3 2011/05/29 22:15:32 velktron //Introduced IRandom interface. // //Revision 1.2 2011/05/29 20:58:58 velktron //Added better mouse grabbing method, more reliable, more cross-OS. // //Revision 1.1 2011/05/27 13:26:56 velktron //A slightly better, though not perfect, way to handle input, partially based on_D_'s work. // package awt; import static g.Keys.*; import i.DoomEventInterface; import java.awt.AWTEvent; import java.awt.Component; import java.awt.Cursor; import java.awt.Dimension; import java.awt.Event; import java.awt.KeyEventDispatcher; import java.awt.KeyboardFocusManager; import java.awt.MouseInfo; import java.awt.Point; import java.awt.Robot; import java.awt.Toolkit; import java.awt.event.InputEvent; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.event.MouseMotionListener; import java.awt.event.WindowEvent; import java.awt.event.WindowListener; import java.awt.image.BufferedImage; import java.util.ArrayList; import java.util.LinkedList; import doom.DoomMain; import doom.event_t; import doom.evtype_t; /** A very basic event handling class, directly based on linuxdoom's. * Sadly, it doesn't work so well cross-platform. Use MochaEvents instead. * * @author velktron * */ @Deprecated public class AWTEvents implements WindowListener,KeyEventDispatcher,KeyListener,MouseListener,MouseMotionListener,DoomEventInterface { // modifications of eventQueue must be thread safe! static LinkedList<AWTEvent> eventQueue = new LinkedList<AWTEvent>(); //// STATUS STUFF /////////// public final DoomMain DM; public final Component canvas; Robot robby; Cursor hidden; Cursor normal; //////// CURRENT MOVEMENT AND INPUT STATUS ////////////// protected static final int POINTER_WARP_COUNTDOWN = 1; protected int lastmousex; protected int lastmousey; protected Point lastmouse; protected int mousedx; protected int mousedy; protected boolean mousemoved; protected boolean ignorebutton; protected boolean grabMouse=true; protected int doPointerWarp=POINTER_WARP_COUNTDOWN; public AWTEvents(DoomMain DM, Component canvas) { this.DM = DM; this.canvas = canvas; // AWT: create cursors. this.normal=canvas.getCursor(); this.hidden=this.createInvisibleCursor(); // Create AWT Robot for forcing mouse try { robby=new Robot(); } catch (Exception e){ System.out.println("AWT Robot could not be created, mouse input focus will be loose!"); } } //////////// LISTENERS ////////// @Override public boolean dispatchKeyEvent(KeyEvent e) { return false; } public void keyPressed(KeyEvent e) { if (e.getKeyCode()<=KeyEvent.VK_F12) { addEvent(e); } e.consume(); } public void keyReleased(KeyEvent e) { //if ((e.getModifiersEx() & UNACCEPTABLE_MODIFIERS) ==0) { if (e.getKeyCode()<=KeyEvent.VK_F12) { addEvent(e); } e.consume(); } public void keyTyped(KeyEvent e) { if (e.getKeyCode()<=KeyEvent.VK_F12) { addEvent(e); } e.consume(); } ///////////////////// MOUSE EVENTS /////////////////////// @Override public void mouseClicked(MouseEvent mouseevent) { addEvent(mouseevent); } @Override public void mouseEntered(MouseEvent mouseevent) { addEvent(mouseevent); } @Override public void mouseExited(MouseEvent mouseevent) { addEvent(mouseevent); } @Override public void mousePressed(MouseEvent mouseevent) { addEvent(mouseevent); } @Override public void mouseReleased(MouseEvent mouseevent) { addEvent(mouseevent); } @Override public void mouseDragged(MouseEvent mouseevent) { addEvent(mouseevent); } @Override public void mouseMoved(MouseEvent mouseevent) { addEvent(mouseevent); } ///////////////////// QUEUE HANDLING /////////////////////// static void addEvent(AWTEvent ev) { synchronized (eventQueue) { eventQueue.addLast(ev); } } public static AWTEvent nextEvent() { AWTEvent ev; synchronized (eventQueue) { ev = (!eventQueue.isEmpty())?(AWTEvent)eventQueue.removeFirst():null; } return ev; } // This event here is used as a static scratch copy. When sending out // messages, its contents are to be actually copied (struct-like). // This avoids the continuous object creation/destruction overhead, // And it also allows creating "sticky" status. final event_t event=new event_t(); // Special FORCED and PAINFUL key and mouse cancel event. final event_t cancelkey=new event_t(evtype_t.ev_clear,0xFF,0,0); final event_t cancelmouse=new event_t(evtype_t.ev_mouse,0,0,0); int prevmousebuttons; @Override public void GetEvent() { AWTEvent X_event; MouseEvent MEV; Point tmp; // put event-grabbing stuff in here if (eventQueue.isEmpty()) return; X_event=nextEvent(); //System.out.println("Event type:"+X_event.getID()); // Keyboard events get priority vs mouse events. // In the case of combined input, however, we need if (!ignorebutton){ switch (X_event.getID()) { case Event.KEY_PRESS: { event.type=evtype_t.ev_keydown; event.data1=xlatekey((KeyEvent)X_event); DM.PostEvent(event); if (prevmousebuttons!=0){ // Allow combined mouse/keyboard events. event.data1=prevmousebuttons; event.type=evtype_t.ev_mouse; DM.PostEvent(event); } //System.err.println("k"); break; } case Event.KEY_RELEASE: event.type=evtype_t.ev_keyup; event.data1=xlatekey((KeyEvent)X_event); DM.PostEvent(event); if (prevmousebuttons!=0){ // Allow combined mouse/keyboard events. event.data1=prevmousebuttons; event.type=evtype_t.ev_mouse; DM.PostEvent(event); } //System.err.println( "ku"); break; } } // Mouse events are also handled, but with secondary priority. switch (X_event.getID()){ // ButtonPress case Event.MOUSE_DOWN: MEV=(MouseEvent)X_event; event.type=evtype_t.ev_mouse; event.data1 = prevmousebuttons= (MEV.getButton() == MouseEvent.BUTTON1 ? 1: 0) | (MEV.getButton() == MouseEvent.BUTTON2 ? 2: 0)| (MEV.getButton() == MouseEvent.BUTTON3 ? 4: 0); event.data2 = event.data3 = 0; DM.PostEvent(event); //System.err.println( "b"); break; // ButtonRelease // This must send out an amended event. case Event.MOUSE_UP: MEV=(MouseEvent)X_event; event.type = evtype_t.ev_mouse; event.data1 =prevmousebuttons^= (MEV.getButton() == MouseEvent.BUTTON1 ? 1: 0) | (MEV.getButton() == MouseEvent.BUTTON2 ? 2: 0)| (MEV.getButton() == MouseEvent.BUTTON3 ? 4: 0); // A PURE mouse up event has no movement. event.data2 = event.data3 = 0; DM.PostEvent(event); //System.err.println("bu"); break; // MotionNotify: case Event.WINDOW_MOVED: // Moving the window does change the absolute reference point, while events only // give a RELATIVE one. offset.x=(int) (canvas.getLocationOnScreen().x); offset.y=(int) (canvas.getLocationOnScreen().y); //System.out.printf("Center MOVED to %d %d\n", center.x, center.y); case Event.MOUSE_MOVE: MEV=(MouseEvent)X_event; tmp=MEV.getPoint(); //this.AddPoint(tmp,center); event.type = evtype_t.ev_mouse; this.mousedx=tmp.x-this.lastmousex; this.mousedy=this.lastmousey-tmp.y; this.lastmousex=tmp.x; this.lastmousey=tmp.y; MEV=(MouseEvent)X_event; // A pure move has no buttons. event.data1=prevmousebuttons=0; event.data2 = (mousedx) << 3; event.data3 = (mousedy) << 3; // System.out.printf("Mouse MOVED to %d %d\n", lastmousex, lastmousey); //System.out.println("Mouse moved without buttons: "+event.data1); if ((event.data2 | event.data3)!=0) { DM.PostEvent(event); //System.err.println( "m"); mousemoved = true; } else { mousemoved = false; } break; case Event.MOUSE_DRAG: MEV=(MouseEvent)X_event; tmp=MEV.getPoint(); //this.AddPoint(tmp,center); this.mousedx=tmp.x-this.lastmousex; this.mousedy=this.lastmousey-tmp.y; this.lastmousex=tmp.x; this.lastmousey=tmp.y; // System.out.printf("Mouse MOVED to %d %d\n", lastmousex, lastmousey); event.type = evtype_t.ev_mouse; // Get buttons, as usual. // Well, NOT as usual: in case of a drag, the usual // mousebutton flags don't work. A "drag" means that at // least the 1st mouse button is held down, while the other // two might or might not be so. event.data1 = prevmousebuttons; //(MEV.getModifiers() == MouseEvent.BUTTON1_DOWN_MASK ? 1: 0)| //(MEV.getModifiers() == MouseEvent.BUTTON2_DOWN_MASK ? 2: 0)| //(MEV.getModifiers() == MouseEvent.BUTTON3_DOWN_MASK ? 4: 0); event.data2 = (mousedx) << 3; event.data3 = (mousedy) << 3; //System.out.printf("Mouse DRAGGED to %d %d\n", mousedx, mousedy); //System.out.println("Mouse moved with buttons pressed: "+event.data1); if ((event.data2 | event.data3)!=0) { DM.PostEvent(event); //System.err.println( "m"); mousemoved = false; } else { mousemoved = true; } break; case Event.MOUSE_ENTER: //System.err.println("ACCEPTING keyboard input"); canvas.requestFocus(); canvas.setCursor(hidden); offset.x=(int) (canvas.getLocationOnScreen().x); offset.y=(int) (canvas.getLocationOnScreen().y); System.out.printf("Offset MOVED to %d %d\n", offset.x, offset.y); this.grabMouse(); ignorebutton=false; break; case Event.MOUSE_EXIT: // Forcibly clear events DM.PostEvent(cancelmouse); DM.PostEvent(cancelkey); reposition(); System.out.printf("FORCED and PAINFUL event clearing!\n"); canvas.setCursor(normal); ignorebutton=true; break; case Event.WINDOW_EXPOSE: offset.x=(int) (canvas.getLocationOnScreen().x); offset.y=(int) (canvas.getLocationOnScreen().y); System.out.printf("Center MOVED to %d %d\n", offset.x, offset.y); // No real single equivalent for "ConfigureNotify" case Event.WINDOW_DESTROY: break; default: // NOT NEEDED in AWT if (doShm && X_event.type == X_shmeventtype) shmFinished = true; break; } } /** Update relative position offset, and force mouse there. * */ void reposition() { lastmousex=canvas.getWidth()/2; lastmousey=canvas.getHeight()/2; offset.x=(int) (canvas.getLocationOnScreen().x); offset.y=(int) (canvas.getLocationOnScreen().y); } Point offset=new Point(); /** 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); } ///////////////////// WINDOW STUFF ////////////////////// @Override public void windowActivated(WindowEvent windowevent) { System.out.println("Window activated"); canvas.getInputContext().selectInputMethod(java.util.Locale.US); } @Override public void windowClosed(WindowEvent windowevent) { // TODO Auto-generated method stub } @Override public void windowClosing(WindowEvent windowevent) { // TODO Auto-generated method stub } @Override public void windowDeactivated(WindowEvent windowevent) { // Clear the queue if focus is lost. System.out.println("Eventqueue flushed!"); eventQueue.clear(); } @Override public void windowDeiconified(WindowEvent windowevent) { canvas.getInputContext().selectInputMethod(java.util.Locale.US); } @Override public void windowIconified(WindowEvent windowevent) { // TODO Auto-generated method stub } @Override public void windowOpened(WindowEvent windowevent) { canvas.getInputContext().selectInputMethod(java.util.Locale.US); } /** * NASTY hack to hide the cursor. * * Create a 'hidden' cursor by using a transparent image * ...return the invisible cursor */ protected Cursor createInvisibleCursor() { Dimension bestCursorDim = Toolkit.getDefaultToolkit().getBestCursorSize(2, 2); BufferedImage transparentImage = new BufferedImage(bestCursorDim.width, bestCursorDim.height, BufferedImage.TYPE_INT_ARGB); Cursor hiddenCursor = Toolkit.getDefaultToolkit( ).createCustomCursor(transparentImage, new Point(1, 1), "HiddenCursor"); return hiddenCursor; } public void grabMouse() { // Warp the pointer back to the middle of the window // or it will wander off - that is, the game will // loose input focus within X11. if (grabMouse) { if (doPointerWarp--<=0) { // Don't warp back if we deliberately alt-tabbed away. Point p=canvas.getMousePosition(); if (p!=null){ robby.mouseMove( offset.x+canvas.getWidth()/2, offset.y+canvas.getHeight()/2); lastmousex=/*center.x+*/canvas.getWidth()/2; lastmousey=/*center.y+*/canvas.getHeight()/2; //System.out.printf("Mouse FORCED back to %d %d\n", lastmousex, lastmousey); } doPointerWarp = POINTER_WARP_COUNTDOWN; } } mousemoved = false; } /** FIXME: input must be made scancode dependent rather than VK_Dependent, * else a lot of things just don't work. * * @param e * @return */ public int xlatekey(KeyEvent e) { int rc; switch(rc = e.getKeyCode()) //Event.XKeycodeToKeysym(X_display, X_event.xkey.keycode, 0)) { case KeyEvent.VK_LEFT: rc = KEY_LEFTARROW; break; case KeyEvent.VK_RIGHT: rc = KEY_RIGHTARROW; break; case KeyEvent.VK_DOWN: rc = KEY_DOWNARROW; break; case KeyEvent.VK_UP: rc = KEY_UPARROW; break; case KeyEvent.VK_ESCAPE: rc = KEY_ESCAPE; break; case KeyEvent.VK_ENTER: rc = KEY_ENTER; break; case KeyEvent.VK_CONTROL: rc= KEY_CTRL; break; case KeyEvent.VK_ALT: rc=KEY_ALT; break; case KeyEvent.VK_SHIFT: rc=KEY_SHIFT; break; // Added handling of pgup/pgdown/home etc. case KeyEvent.VK_PAGE_DOWN: rc= KEY_PGDN; break; case KeyEvent.VK_PAGE_UP: rc= KEY_PGUP; break; case KeyEvent.VK_HOME: rc= KEY_HOME; break; case KeyEvent.VK_F1: rc = KEY_F1; break; case KeyEvent.VK_F2: rc = KEY_F2; break; case KeyEvent.VK_F3: rc = KEY_F3; break; case KeyEvent.VK_F4: rc = KEY_F4; break; case KeyEvent.VK_F5: rc = KEY_F5; break; case KeyEvent.VK_F6: rc = KEY_F6; break; case KeyEvent.VK_F7: rc = KEY_F7; break; case KeyEvent.VK_F8: rc = KEY_F8; break; case KeyEvent.VK_F9: rc = KEY_F9; break; case KeyEvent.VK_F10: rc = KEY_F10; break; case KeyEvent.VK_F11: rc = KEY_F11; break; case KeyEvent.VK_F12: rc = KEY_F12; break; case KeyEvent.VK_BACK_SPACE: case KeyEvent.VK_DELETE: rc = KEY_BACKSPACE; break; case KeyEvent.VK_PAUSE: rc = KEY_PAUSE; break; case KeyEvent.KEY_RELEASED: case KeyEvent.VK_TAB: rc = KEY_TAB; break; case KeyEvent.KEY_PRESSED: switch(e.getKeyCode()){ case KeyEvent.VK_PLUS: case KeyEvent.VK_EQUALS: rc = KEY_EQUALS; break; case (13): case KeyEvent.VK_SUBTRACT: case KeyEvent.VK_MINUS: rc = KEY_MINUS; break; case KeyEvent.VK_SHIFT: rc = KEY_SHIFT; break; case KeyEvent.VK_CONTROL: rc = KEY_CTRL; break; case KeyEvent.VK_ALT: rc = KEY_ALT; break; case KeyEvent.VK_SPACE: rc = ' '; break; } default: /*if (rc >= KeyEvent.VK_SPACE && rc <= KeyEvent.VK_DEAD_TILDE) { rc = (int) (rc - KeyEvent.FOCUS_EVENT_MASK + ' '); break; } */ if (rc >= KeyEvent.VK_A && rc <= KeyEvent.VK_Z){ rc = rc-KeyEvent.VK_A +'a'; break; } // Unknown. Probably fucking up with the keyboard locale. Switch to be sure. // Sorry for this horrible hack, but Java can't read locale-free keycodes -_- // this.getInputContext().selectInputMethod(java.util.Locale.US); //if (rc>KEY_F12) rc=KEY_RSHIFT; break; } //System.out.println("Typed "+e.getKeyCode()+" char "+e.getKeyChar()+" mapped to "+Integer.toHexString(rc)); // Sanitize. Nothing beyond F12 must pass through, else you will // get the "all cheats" bug. return rc;//Math.min(rc,KEY_F12); } protected void AddPoint(Point A, Point B){ A.x+=B.x; A.y+=B.y; } } package awt; import java.awt.DisplayMode; import java.awt.GraphicsDevice; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; public class DisplayModePicker { protected GraphicsDevice device; protected DisplayMode default_mode; public DisplayModePicker(GraphicsDevice device){ this.device=device; this.default_mode=device.getDisplayMode(); } public DisplayMode pickClosest(int width, int height){ DisplayMode[] modes=device.getDisplayModes(); List<DisplayMode> picks=new ArrayList<DisplayMode>(); WidthComparator wc=new WidthComparator(); HeightComparator hc=new HeightComparator(); // Filter out those with too small dimensions. for (DisplayMode dm:modes){ if (dm.getWidth()>=width && dm.getHeight()>=height) picks.add(dm); } if (picks.size()>0) { Collections.sort(picks, wc); Collections.sort(picks, hc); } // First one is the minimum that satisfies the desired criteria. return picks.get(0); } /** Return offsets to center rasters too oddly shaped to fit entirely into * a standard display mode (unfortunately, this means most stuff > 640 x 400), * with doom's standard 8:5 ratio. * * @param width * @param height * @param dm * @return array, x-offset and y-offset. */ public int[] getCentering(int width, int height,DisplayMode dm){ int xy[]=new int[2]; xy[0]=(dm.getWidth()-width)/2; xy[1]=(dm.getHeight()-height)/2; return xy; } class WidthComparator implements Comparator<DisplayMode>{ @Override public int compare(DisplayMode arg0, DisplayMode arg1) { if (arg0.getWidth()> arg1.getWidth()) return 1; if (arg0.getWidth()< arg1.getWidth()) return -1; return 0; } } class HeightComparator implements Comparator<DisplayMode>{ @Override public int compare(DisplayMode arg0, DisplayMode arg1) { if (arg0.getHeight()> arg1.getHeight()) return 1; if (arg0.getHeight()< arg1.getHeight()) return -1; return 0; } } } package awt; import java.awt.Canvas; import java.awt.Color; import java.awt.Graphics; public class DoomCanvas extends Canvas { /** * */ private static final long serialVersionUID = 1L; public void paint(Graphics g){ g.setColor(new Color(.3f, .4f, .5f, .6f)); g.fillRect(0, 0, this.getWidth(), this.getHeight()); } } package awt; import i.InputListener; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Point; import java.awt.Rectangle; import java.awt.event.InputEvent; import javax.swing.JPanel; import v.DoomVideoRenderer; import doom.DoomMain; import doom.event_t; /** A simple Doom display & keyboard driver for AWT. * Uses a Canvas for painting and implements some * of the IVideo methods. * * Uses 8-bit buffered images and switchable IndexColorModels. * * It's really basic, but allows testing and user interaction. * Heavily based on the original LinuxDoom X11 implementation, and * is similar in goals: just a functional, reference implementation * to build upon whatever fancy extensions you like. * * The only "hitch" is that this implementation expects to be * initialized upon a BufferedRenderer with multiple images per * screen buffer in order to perform the palette switching trick. * * The images themselves don't have to be "BufferedImage", * and in theory it could be possible to use e.g. MemoryImageSource * for the same purpose . Oh well. * * * * @author Velktron * */ public abstract class SwingDoom<V> extends DoomFrame<V> { private static final long serialVersionUID = 3118508722502652276L; JPanel drawhere; /** Gimme some raw palette RGB data. * I will do the rest * * (hint: read this from the PLAYPAL * lump in the IWAD!!!). * */ @SuppressWarnings("unchecked") public SwingDoom(DoomMain<?,V> DM, DoomVideoRenderer<?,V> V) { super(DM, V); drawhere=new JPanel(); } Point center; Rectangle rect; public String processEvents(){ StringBuffer tmp=new StringBuffer(); event_t event; while ( (event=InputListener.nextEvent()) != null ) { tmp.append(event.type.ordinal()+"\n"); } return tmp.toString(); } public void SetGamma(int level){ if (D) System.err.println("Setting gamma "+level); V.setUsegamma(level); screen=V.getCurrentScreen(); // Refresh screen after change. RAWSCREEN=V.getScreen(DoomVideoRenderer.SCREEN_FG); } // This stuff should NOT get through in keyboard events. protected final int UNACCEPTABLE_MODIFIERS=(int) (InputEvent.ALT_GRAPH_DOWN_MASK+ InputEvent.META_DOWN_MASK+ InputEvent.META_MASK+ InputEvent.WINDOW_EVENT_MASK+ InputEvent.WINDOW_FOCUS_EVENT_MASK); public static final class HiColor extends SwingDoom<short[]>{ /** * */ private static final long serialVersionUID = 1L; public HiColor(DoomMain<?, short[]> DM, DoomVideoRenderer<?,short[]> V) { super(DM, V); } @Override public void ReadScreen(short[] scr) { System.arraycopy(this.RAWSCREEN, 0, scr, 0, RAWSCREEN.length); } @Override public void FinishUpdate() { int tics; int i; // draws little dots on the bottom of the screen /*if (true) { i = I.GetTime(); tics = i - lasttic; lasttic = i; if (tics > 20) tics = 20; if (tics < 1) tics = 1; for (i=0 ; i<tics*2 ; i+=2) RAWSCREEN[ (SCREENHEIGHT-1)*SCREENWIDTH + i] = (byte) 0xff; for ( ; i<20*2 ; i+=2) RAWSCREEN[ (SCREENHEIGHT-1)*SCREENWIDTH + i] = 0x0; } */ if (true) { i = TICK.GetTime(); tics = i - lasttic; lasttic = i; if (tics<1) frames++; else { //frames*=35; for (i=0 ; i<frames*2 ; i+=2) RAWSCREEN[ (height-1)*width + i] = (short) 0xffff; for ( ; i<20*2 ; i+=2) RAWSCREEN[ (height-1)*width + i] = 0x0; frames=0; } } this.update(null); //this.getInputContext().selectInputMethod(java.util.Locale.US); } } public static final class Indexed extends SwingDoom<byte[]>{ /** * */ private static final long serialVersionUID = 1L; public Indexed(DoomMain<?,byte[]> DM, DoomVideoRenderer<?,byte[]> V) { super(DM, V); } @Override public void ReadScreen(byte[] scr) { System.arraycopy(this.RAWSCREEN, 0, scr, 0, RAWSCREEN.length); } @Override public void FinishUpdate() { int tics; int i; // draws little dots on the bottom of the screen /*if (true) { i = I.GetTime(); tics = i - lasttic; lasttic = i; if (tics > 20) tics = 20; if (tics < 1) tics = 1; for (i=0 ; i<tics*2 ; i+=2) RAWSCREEN[ (SCREENHEIGHT-1)*SCREENWIDTH + i] = (byte) 0xff; for ( ; i<20*2 ; i+=2) RAWSCREEN[ (SCREENHEIGHT-1)*SCREENWIDTH + i] = 0x0; } */ if (true) { i = TICK.GetTime(); tics = i - lasttic; lasttic = i; if (tics<1) frames++; else { //frames*=35; for (i=0 ; i<frames*2 ; i+=2) RAWSCREEN[ (height-1)*width + i] = (short) 0xffff; for ( ; i<20*2 ; i+=2) RAWSCREEN[ (height-1)*width + i] = 0x0; frames=0; } } this.update(null); //this.getInputContext().selectInputMethod(java.util.Locale.US); } } public static final class TrueColor extends SwingDoom<int[]>{ /** * */ private static final long serialVersionUID = 1L; public TrueColor(DoomMain<?, int[]> DM, DoomVideoRenderer<?,int[]> V) { super(DM, V); } @Override public void ReadScreen(int[] scr) { System.arraycopy(this.RAWSCREEN, 0, scr, 0, RAWSCREEN.length); } @Override public void FinishUpdate() { int tics; int i; // draws little dots on the bottom of the screen /*if (true) { i = I.GetTime(); tics = i - lasttic; lasttic = i; if (tics > 20) tics = 20; if (tics < 1) tics = 1; for (i=0 ; i<tics*2 ; i+=2) RAWSCREEN[ (SCREENHEIGHT-1)*SCREENWIDTH + i] = (byte) 0xff; for ( ; i<20*2 ; i+=2) RAWSCREEN[ (SCREENHEIGHT-1)*SCREENWIDTH + i] = 0x0; } */ if (true) { i = TICK.GetTime(); tics = i - lasttic; lasttic = i; if (tics<1) frames++; else { //frames*=35; for (i=0 ; i<frames*2 ; i+=2) RAWSCREEN[ (height-1)*width + i] = (short) 0xffff; for ( ; i<20*2 ; i+=2) RAWSCREEN[ (height-1)*width + i] = 0x0; frames=0; } } this.update(null); //this.getInputContext().selectInputMethod(java.util.Locale.US); } } } package awt; import static g.Keys.*; import i.DoomEventInterface; import java.awt.Component; import java.awt.Cursor; import java.awt.Dimension; import java.awt.KeyEventDispatcher; import java.awt.Point; import java.awt.Robot; import java.awt.Toolkit; import java.awt.event.ComponentEvent; import java.awt.event.ComponentListener; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.event.MouseMotionListener; import java.awt.event.WindowEvent; import java.awt.event.WindowFocusListener; import java.awt.event.WindowListener; import java.awt.image.BufferedImage; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; import java.util.Map; import doom.DoomMain; import doom.event_t; import doom.evtype_t; /** An alternate events class, more suited for handling the complex situations * that might arise during daily use, window moving, etc. * Use instead of the old AWTEvents, which is present but deprecated. * * @author vekltron * */ public class MochaEvents implements WindowListener,ComponentListener,KeyEventDispatcher,KeyListener,MouseListener,MouseMotionListener,WindowFocusListener,DoomEventInterface { // modifications of eventQueue must be thread safe! private LinkedList<MochaDoomInputEvent> eventQueue = new LinkedList<MochaDoomInputEvent>(); private HashMap<Integer, Boolean> lockingKeyStates = new HashMap<Integer, Boolean>(); private static final boolean D=false; //// STATUS STUFF /////////// public final DoomMain DM; public final Component canvas; Robot robby; Cursor hidden; Cursor normal; //////// CURRENT MOVEMENT AND INPUT STATUS ////////////// protected static final int POINTER_WARP_COUNTDOWN = 1; protected int lastmousex; protected int lastmousey; protected Point lastmouse; protected int mousedx; protected int mousedy; protected boolean mousemoved; protected boolean ignorebutton; protected boolean grabMouse=true; protected int doPointerWarp=POINTER_WARP_COUNTDOWN; public MochaEvents(DoomMain DM, Component canvas) { this.DM = DM; this.canvas = canvas; // AWT: create cursors. this.normal=canvas.getCursor(); this.hidden=this.createInvisibleCursor(); // Create AWT Robot for forcing mouse try { robby=new Robot(); } catch (Exception e){ System.err.println("AWT Robot could not be created, mouse input focus will be loose!"); } lockingKeyStates.put(KeyEvent.VK_CAPS_LOCK, null); lockingKeyStates.put(KeyEvent.VK_NUM_LOCK, null); lockingKeyStates.put(KeyEvent.VK_SCROLL_LOCK, null); } ///////////////////// QUEUE HANDLING /////////////////////// public synchronized void addEvent(MochaDoomInputEvent ev) { synchronized (eventQueue) { eventQueue.addLast(ev); } } public MochaDoomInputEvent nextEvent() { MochaDoomInputEvent ev; synchronized (eventQueue) { ev = (!eventQueue.isEmpty())?(MochaDoomInputEvent)eventQueue.removeFirst():null; } return ev; } // This event here is used as a static scratch copy. When sending out // messages, its contents are to be actually copied (struct-like). // This avoids the continuous object creation/destruction overhead, // And it also allows creating "sticky" status. final event_t event=new event_t(); // Special FORCED and PAINFUL key and mouse cancel event. final event_t cancelkey=new event_t(evtype_t.ev_clear,0xFF,0,0); final event_t cancelmouse=new event_t(evtype_t.ev_mouse,0,0,0); int prevmousebuttons; // Nasty hack for CAPS LOCK. Apparently, there's no RELIABLE way // to get the caps lock state programmatically, so we have to make // do with simply toggling boolean capstoggle=false; @Override public void GetEvent() { MochaDoomInputEvent X_event; MouseEvent MEV; Point tmp; // Unlike most keys, caps lock etc. can be polled, so no need to worry // about them getting stuck. So they are re-polled after all other // key states have beeen cleared. /* if (DM.shouldPollLockingKeys()) { for (Map.Entry<Integer, Boolean> e: lockingKeyStates.entrySet()) { e.setValue(null); } updateLockingKeys(); } */ // put event-grabbing stuff in here if (eventQueue.isEmpty()) return; X_event=nextEvent(); //System.out.println("Event type:"+X_event.getID()); // Keyboard events get priority vs mouse events. // In the case of combined input, however, we need if (!ignorebutton){ switch (X_event.type) { case MochaDoomInputEvent.KeyPress: { event.type=evtype_t.ev_keydown; event.data1=xlatekey((KeyEvent)X_event.ev, -1); // Toggle, but don't it go through. if (event.data1==KEY_CAPSLOCK) capstoggle=true; if (event.data1!=KEY_CAPSLOCK) DM.PostEvent(event); if (prevmousebuttons!=0){ // Allow combined mouse/keyboard events. event.data1=prevmousebuttons; event.type=evtype_t.ev_mouse; DM.PostEvent(event); } //System.err.println("k"); break; } case MochaDoomInputEvent.KeyRelease: event.type=evtype_t.ev_keyup; event.data1=xlatekey((KeyEvent)X_event.ev, -1); if ((event.data1!=KEY_CAPSLOCK) || ((event.data1==KEY_CAPSLOCK)&&capstoggle)) { DM.PostEvent(event); } capstoggle=false; if (prevmousebuttons!=0){ // Allow combined mouse/keyboard events. event.data1=prevmousebuttons; event.type=evtype_t.ev_mouse; DM.PostEvent(event); } //System.err.println( "ku"); break; /* UNUSED, see caps lock problems case MochaDoomInputEvent.LockOn: { event.type=evtype_t.ev_keydown; event.data1=xlatekey(null, X_event.value); DM.PostEvent(event); if (prevmousebuttons!=0){ // Allow combined mouse/keyboard events. event.data1=prevmousebuttons; event.type=evtype_t.ev_mouse; DM.PostEvent(event); } //System.err.println("l1"); break; } case MochaDoomInputEvent.LockOff: { event.type=evtype_t.ev_keyup; event.data1=xlatekey(null, X_event.value); DM.PostEvent(event); if (prevmousebuttons!=0){ // Allow combined mouse/keyboard events. event.data1=prevmousebuttons; event.type=evtype_t.ev_mouse; DM.PostEvent(event); } //System.err.println( "l0"); break; } */ case MochaDoomInputEvent.KeyType: event.type=evtype_t.ev_keyup; event.data1=xlatekey((KeyEvent)X_event.ev, -1); DM.PostEvent(event); if (prevmousebuttons!=0){ // Allow combined mouse/keyboard events. event.data1=prevmousebuttons; event.type=evtype_t.ev_mouse; DM.PostEvent(event); } //System.err.println( "ku"); break; } } // Ignore ALL mouse events if we are moving the window. // Mouse events are also handled, but with secondary priority. switch (X_event.type){ // ButtonPress case MochaDoomInputEvent.ButtonPress: MEV=(MouseEvent)X_event.ev; event.type=evtype_t.ev_mouse; event.data1 = prevmousebuttons= (MEV.getButton() == MouseEvent.BUTTON1 ? 1: 0) | (MEV.getButton() == MouseEvent.BUTTON2 ? 2: 0)| (MEV.getButton() == MouseEvent.BUTTON3 ? 4: 0); event.data2 = event.data3 = 0; DM.PostEvent(event); //System.err.println( "b"); break; // ButtonRelease // This must send out an amended event. case MochaDoomInputEvent.ButtonRelease: MEV=(MouseEvent)X_event.ev; event.type = evtype_t.ev_mouse; event.data1 =prevmousebuttons^= (MEV.getButton() == MouseEvent.BUTTON1 ? 1: 0) | (MEV.getButton() == MouseEvent.BUTTON2 ? 2: 0)| (MEV.getButton() == MouseEvent.BUTTON3 ? 4: 0); // A PURE mouse up event has no movement. event.data2 = event.data3 = 0; DM.PostEvent(event); //System.err.println("bu"); break; case MochaDoomInputEvent.MotionNotify: MEV=(MouseEvent)X_event.ev; tmp=MEV.getPoint(); //this.AddPoint(tmp,center); event.type = evtype_t.ev_mouse; this.mousedx=(tmp.x-win_w2); this.mousedy=(win_h2-tmp.y); // A pure move has no buttons. event.data1=prevmousebuttons=0; event.data2 = (mousedx) << 2; event.data3 = (mousedy) << 2; // System.out.printf("Mouse MOVED to %d %d\n", lastmousex, lastmousey); //System.out.println("Mouse moved without buttons: "+event.data1); if ((event.data2 | event.data3)!=0) { DM.PostEvent(event); //System.err.println( "m"); mousemoved = true; } else { mousemoved = false; } break; case MochaDoomInputEvent.DragNotify: MEV=(MouseEvent)X_event.ev; tmp=MEV.getPoint(); this.mousedx=(tmp.x-win_w2); this.mousedy=(win_h2-tmp.y); event.type = evtype_t.ev_mouse; // A drag means no change in button state. event.data1 = prevmousebuttons; event.data2 = (mousedx) << 2; event.data3 = (mousedy) << 2; if ((event.data2 | event.data3)!=0) { DM.PostEvent(event); //System.err.println( "m"); mousemoved = true; } else { mousemoved = false; } break; } // Now for window events. This includes the mouse breaking the border. switch (X_event.type){ case MochaDoomInputEvent.FocusLost: case MochaDoomInputEvent.MouseExited: // Forcibly clear events DM.PostEvent(cancelmouse); DM.PostEvent(cancelkey); canvas.setCursor(normal); ignorebutton=true; break; case MochaDoomInputEvent.WindowMoving: // Don't try to reposition immediately during a move // event, wait for a mouse click. we_are_moving=true; ignorebutton=true; // Forcibly clear events DM.PostEvent(cancelmouse); DM.PostEvent(cancelkey); move++; break; case MochaDoomInputEvent.MouseEntered: case MochaDoomInputEvent.FocusGained: we_are_moving=false; //reposition(); case MochaDoomInputEvent.ConfigureNotify: case MochaDoomInputEvent.CreateNotify: // All events that have to do with the window being changed, // moved etc. should go here. The most often result // in focus being lost and position being changed, so we // need to take charge. DM.justfocused=true; canvas.requestFocus(); reposition(); ignorebutton=false; break; default: // NOT NEEDED in AWT if (doShm && X_event.type == X_shmeventtype) shmFinished = true; break; } // If the mouse moved, don't wait until it managed to get out of the // window to bring it back. if (!we_are_moving && (mousedx != 0 || mousedy != 0)) { // move the mouse to the window center again robby.mouseMove(offset.x + win_w2, offset.y + win_h2); } mousedx=mousedy=0; // don't spaz. } private int win_w2, win_h2; private int move=0; /** Update relative position offset, and force mouse there. * */ void reposition() { offset.x=(int) (canvas.getLocationOnScreen().x); offset.y=(int) (canvas.getLocationOnScreen().y); // Shamelessly ripped from Jake 2. Maybe it works better? Component c = this.canvas; //offset.x = 0; //offset.y = 0; win_w2 = c.getWidth() / 2; win_h2 = c.getHeight() / 2; robby.mouseMove(offset.x + win_w2, offset.y + win_h2); canvas.getInputContext().selectInputMethod(java.util.Locale.US); canvas.setCursor(hidden); if (D) System.err.printf("Jake 2 method: offset MOVED to %d %d\n", offset.x, offset.y); } Point offset=new Point(); /** 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); } ///////////////////// WINDOW STUFF ////////////////////// @Override public void windowActivated(WindowEvent windowevent) { if (D) System.err.println("Window activated"); eventQueue.add(new MochaDoomInputEvent(MochaDoomInputEvent.ConfigureNotify, null)); } @Override public void windowClosed(WindowEvent windowevent) { // TODO Auto-generated method stub } @Override public void windowClosing(WindowEvent windowevent) { // TODO Auto-generated method stub } @Override public void windowDeactivated(WindowEvent windowevent) { // Clear the queue if focus is lost. eventQueue.clear(); } @Override public void windowDeiconified(WindowEvent windowevent) { eventQueue.add(new MochaDoomInputEvent(MochaDoomInputEvent.ConfigureNotify, null)); } @Override public void windowIconified(WindowEvent windowevent) { // TODO Auto-generated method stub eventQueue.clear(); } @Override public void windowOpened(WindowEvent windowevent) { eventQueue.add(new MochaDoomInputEvent(MochaDoomInputEvent.CreateNotify, null)); } /** * NASTY hack to hide the cursor. * * Create a 'hidden' cursor by using a transparent image * ...return the invisible cursor */ protected Cursor createInvisibleCursor() { Dimension bestCursorDim = Toolkit.getDefaultToolkit().getBestCursorSize(2, 2); BufferedImage transparentImage = new BufferedImage(bestCursorDim.width, bestCursorDim.height, BufferedImage.TYPE_INT_ARGB); Cursor hiddenCursor = Toolkit.getDefaultToolkit( ).createCustomCursor(transparentImage, new Point(1, 1), "HiddenCursor"); return hiddenCursor; } /** FIXME: input must be made scancode dependent rather than VK_Dependent, * else a lot of things just don't work. * * @param e * @param rc * @return */ public int xlatekey(KeyEvent e, int rc) { if (e != null) rc = e.getKeyCode(); switch(rc) //Event.XKeycodeToKeysym(X_display, X_event.xkey.keycode, 0)) { case KeyEvent.VK_LEFT: rc = KEY_LEFTARROW; break; case KeyEvent.VK_RIGHT: rc = KEY_RIGHTARROW; break; case KeyEvent.VK_DOWN: rc = KEY_DOWNARROW; break; case KeyEvent.VK_UP: rc = KEY_UPARROW; break; case KeyEvent.VK_ESCAPE: rc = KEY_ESCAPE; break; case KeyEvent.VK_ENTER: rc = KEY_ENTER; break; case KeyEvent.VK_CONTROL: rc= KEY_CTRL; break; case KeyEvent.VK_ALT: rc=KEY_ALT; break; case KeyEvent.VK_SHIFT: rc=KEY_SHIFT; break; // Added handling of pgup/pgdown/home etc. case KeyEvent.VK_PAGE_DOWN: rc= KEY_PGDN; break; case KeyEvent.VK_PAGE_UP: rc= KEY_PGUP; break; case KeyEvent.VK_HOME: rc= KEY_HOME; break; case KeyEvent.VK_END: rc= KEY_END; break; case KeyEvent.VK_F1: rc = KEY_F1; break; case KeyEvent.VK_F2: rc = KEY_F2; break; case KeyEvent.VK_F3: rc = KEY_F3; break; case KeyEvent.VK_F4: rc = KEY_F4; break; case KeyEvent.VK_F5: rc = KEY_F5; break; case KeyEvent.VK_F6: rc = KEY_F6; break; case KeyEvent.VK_F7: rc = KEY_F7; break; case KeyEvent.VK_F8: rc = KEY_F8; break; case KeyEvent.VK_F9: rc = KEY_F9; break; case KeyEvent.VK_F10: rc = KEY_F10; break; case KeyEvent.VK_F11: rc = KEY_F11; break; case KeyEvent.VK_F12: rc = KEY_F12; break; case KeyEvent.VK_BACK_SPACE: case KeyEvent.VK_DELETE: rc = KEY_BACKSPACE; break; case KeyEvent.VK_PAUSE: rc = KEY_PAUSE; break; case KeyEvent.VK_TAB: rc = KEY_TAB; break; case KeyEvent.VK_CAPS_LOCK: rc = KEY_CAPSLOCK; break; case KeyEvent.VK_NUM_LOCK: rc = KEY_NUMLOCK; break; case KeyEvent.VK_SCROLL_LOCK: rc = KEY_SCROLLLOCK; break; /* case KeyEvent.KEY_RELEASED: case KeyEvent.KEY_PRESSED: switch(e.getKeyCode()){ case KeyEvent.VK_PLUS: case KeyEvent.VK_EQUALS: rc = KEY_EQUALS; break; case (13): case KeyEvent.VK_SUBTRACT: case KeyEvent.VK_MINUS: rc = KEY_MINUS; break; case KeyEvent.VK_SHIFT: rc = KEY_RSHIFT; break; case KeyEvent.VK_CONTROL: rc = KEY_RCTRL; break; case KeyEvent.VK_ALT: rc = KEY_RALT; break; case KeyEvent.VK_SPACE: rc = ' '; break; } */ default: /*if (rc >= KeyEvent.VK_SPACE && rc <= KeyEvent.VK_DEAD_TILDE) { rc = (int) (rc - KeyEvent.FOCUS_EVENT_MASK + ' '); break; } */ if (rc >= KeyEvent.VK_A && rc <= KeyEvent.VK_Z){ rc = rc-KeyEvent.VK_A +'a'; break; } break; } //System.out.println("Typed "+e.getKeyCode()+" char "+e.getKeyChar()+" mapped to "+Integer.toHexString(rc)); return rc;//Math.min(rc,KEY_F12); } protected void AddPoint(Point A, Point B){ A.x+=B.x; A.y+=B.y; } ////////////LISTENERS ////////// ///////////////////////// KEYBOARD EVENTS /////////////////////////////////// // UNUSED used commented out because it doesn't appear to work // I think that there is no bulletproof method to poll the state of // the CAPS LOCK key, and forcing a call to getLockingKeyState() // results in an UnsupportedOperationException // For now, the best course of action seems to be intercepting toggling only, // rather than state. It also looks like a compicated thing to // run at every key update. private void updateLockingKeys() { Toolkit toolkit = canvas.getToolkit(); for (Iterator<Map.Entry<Integer, Boolean>> it = lockingKeyStates.entrySet().iterator(); it.hasNext(); ) { Map.Entry<Integer, Boolean> entry = it.next(); Integer keyCode = entry.getKey(); Boolean oldState = entry.getValue(); try { if (D) System.err.println("Trying"); boolean newState = toolkit.getLockingKeyState(keyCode); if (! Boolean.valueOf(newState).equals(oldState)) { if (D) System.out.println("New event"); int eventType = newState ? MochaDoomInputEvent.LockOn : MochaDoomInputEvent.LockOff; addEvent(new MochaDoomInputEvent(eventType,keyCode)); entry.setValue(newState); } } catch (UnsupportedOperationException ex) { // Key not present it.remove(); } } } public void keyPressed(KeyEvent e) { // // updateLockingKeys(); // if (! lockingKeyStates.containsKey(e.getKeyCode())) { if (e.getKeyCode()<=KeyEvent.VK_F12) { addEvent(new MochaDoomInputEvent(MochaDoomInputEvent.KeyPress,e)); } e.consume(); // } } public void keyReleased(KeyEvent e) { // updateLockingKeys(); // if (! lockingKeyStates.containsKey(e.getKeyCode())) { //if ((e.getModifiersEx() & UNACCEPTABLE_MODIFIERS) ==0) { if (e.getKeyCode()<=KeyEvent.VK_F12) { addEvent(new MochaDoomInputEvent(MochaDoomInputEvent.KeyRelease,e)); } e.consume(); // } } public void keyTyped(KeyEvent e) { //updateLockingKeys(); //if (! lockingKeyStates.containsKey(e.getKeyCode())) { if (e.getKeyCode()<=KeyEvent.VK_F12) { addEvent(new MochaDoomInputEvent(MochaDoomInputEvent.KeyType,e)); } e.consume(); //} } //////////////////////////// MOUSE EVENTS //////////////////////////// @Override public void mouseClicked(MouseEvent mouseevent) { // Marks the end of a move. A press + release during a move will // trigger a "click" event, which is handled specially. if (we_are_moving) { we_are_moving=false; reposition(); ignorebutton=false; } } @Override public void mouseEntered(MouseEvent mouseevent) { //System.out.println("Mouse entered"); addEvent(new MochaDoomInputEvent(MochaDoomInputEvent.MouseEntered,null)); } @Override public void mouseExited(MouseEvent mouseevent) { //System.out.println("Mouse exited"); addEvent(new MochaDoomInputEvent(MochaDoomInputEvent.MouseExited,null)); } @Override public void mousePressed(MouseEvent mouseevent) { if (!we_are_moving) // Don't let presses go through when moving. addEvent(new MochaDoomInputEvent(MochaDoomInputEvent.ButtonPress,mouseevent)); } @Override public void mouseReleased(MouseEvent mouseevent) { if (!we_are_moving) // Don't let presses go through when moving. addEvent(new MochaDoomInputEvent(MochaDoomInputEvent.ButtonRelease,mouseevent)); } @Override public void mouseDragged(MouseEvent mouseevent) { addEvent(new MochaDoomInputEvent(MochaDoomInputEvent.DragNotify,mouseevent)); } @Override public void mouseMoved(MouseEvent mouseevent) { addEvent(new MochaDoomInputEvent(MochaDoomInputEvent.MotionNotify,mouseevent)); } //////////////// COMPONENT EVENTS ////////////////////////// boolean we_are_moving=false; @Override public void componentHidden(ComponentEvent e) { // Do what, here? Pausing would be a good idea. } @Override public void componentMoved(ComponentEvent e) { eventQueue.add(new MochaDoomInputEvent(MochaDoomInputEvent.WindowMoving, null)); } @Override public void componentResized(ComponentEvent e) { eventQueue.add(new MochaDoomInputEvent(MochaDoomInputEvent.ConfigureNotify, null)); } @Override public void componentShown(ComponentEvent e) { eventQueue.add(new MochaDoomInputEvent(MochaDoomInputEvent.CreateNotify, null)); } @Override public void windowGainedFocus(WindowEvent arg0) { eventQueue.add(new MochaDoomInputEvent(MochaDoomInputEvent.FocusGained, null)); } @Override public void windowLostFocus(WindowEvent arg0) { eventQueue.add(new MochaDoomInputEvent(MochaDoomInputEvent.FocusLost, null)); } public boolean hasMoreEvents() { return !this.eventQueue.isEmpty(); } @Override public boolean dispatchKeyEvent(KeyEvent e) { //if (e.getKeyCode() == KeyEvent.VK_TAB) // eventQueue.add(new MochaDoomInputEvent(MochaDoomInputEvent.KeyRelease, e)); return false; } } package awt; import java.awt.Canvas; import java.awt.Color; import java.awt.Component; import java.awt.Dimension; import java.awt.DisplayMode; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.GraphicsDevice; import java.awt.GraphicsEnvironment; import java.awt.Image; import java.awt.KeyEventDispatcher; import java.awt.KeyboardFocusManager; import java.awt.Point; import java.awt.Rectangle; import java.awt.Robot; import java.awt.event.InputEvent; import java.awt.event.KeyEvent; import java.util.StringTokenizer; import javax.swing.JFrame; import i.DoomVideoInterface; import i.IDoomSystem; import i.InputListener; import i.Strings; import timing.ITicker; import v.DoomVideoRenderer; import doom.DoomMain; import doom.ICommandLineManager; import doom.event_t; /** Common code for Doom's video frames */ public abstract class DoomFrame<V> extends JFrame implements DoomVideoInterface<V> { protected V RAWSCREEN; // Normally only used in fullscreen mode /** This might differ from the raster's width & height attribute for number of reasons */ protected Dimension size; protected DisplayMode oldDisplayMode; protected DisplayMode currentDisplayMode; protected GraphicsDevice device; protected int X_OFF; protected int Y_OFF; public DoomFrame(DoomMain<?,V> DM,DoomVideoRenderer<?,V> V) { GraphicsEnvironment env = GraphicsEnvironment. getLocalGraphicsEnvironment(); GraphicsDevice[] devices = env.getScreenDevices(); // Get device 0, because we're lazy. if (devices!=null) if (devices.length>0) device=devices[0]; this.DM=DM; this.CM=DM.CM; this.TICK=DM.TICK; this.I=DM.I; this.V= V; this.width=V.getWidth(); this.height=V.getHeight(); // Set those here. If fullscreen isn't used, then they won't change. // They are necessary for normal initialization, though. setDefaultDimension(width,height); } /** Default window size and center spot. These might change * upon entering full screen, so don't consider them absolute. * Due to letterboxing and screen doubling, stretching etc. * they might be different that the screen buffer (typically, * larger). * * @param width * @param height */ private void setDefaultDimension(int width, int height){ this.size=new Dimension(width*multiply,height*multiply); this.center=new Point (X_OFF+size.width/2, Y_OFF+size.height/2); } /** * */ private static final long serialVersionUID = -4130528877723831825L; protected static final boolean D=false; // Must be aware of "Doom" so we can pass it event messages inside a crude queue. public DoomMain<?,V> DM; // Must be aware of general status. public ICommandLineManager CM; // Must be aware of command line interface. protected IDoomSystem I; // Must be aware of some other shit like event handler protected DoomVideoRenderer<?,V> V; // Must have a video renderer.... protected ITicker TICK; // Must be aware of the ticker/ protected MochaEvents eventhandler; // Separate event handler a la _D_. // However I won't make it fully "eternity like" yet // also because it works quite flakey on Linux. protected Robot robby; protected Canvas drawhere; protected Canvas gelatine; /** This is the actual screen */ protected Image screen; protected int palette=0; //InputListener in; protected Graphics2D g2d; protected Graphics2D gel2d; protected Point center; /** Dimensions of the screen buffers. The display however, might differ due * to e.g. letterboxing */ protected int width,height; protected int multiply=1; // This stuff should NOT get through in keyboard events. protected final int UNACCEPTABLE_MODIFIERS=(int) (InputEvent.ALT_GRAPH_DOWN_MASK+ InputEvent.META_DOWN_MASK+ InputEvent.META_MASK+ InputEvent.WINDOW_EVENT_MASK+ InputEvent.WINDOW_FOCUS_EVENT_MASK); public String processEvents(){ StringBuffer tmp=new StringBuffer(); event_t event; while ( (event=InputListener.nextEvent()) != null ) { tmp.append(event.type.ordinal()+"\n"); } return tmp.toString(); } /** * I_SetPalette * * Any bit-depth specific palette manipulation is performed by * the VideoRenderer. It can range from simple (paintjob) to * complex (multiple BufferedImages with locked data bits...ugh! * *@param palette index (normally between 0-14). */ @Override public void SetPalette (int palette) { V.setPalette(palette); this.screen=V.getCurrentScreen(); } /** Call this before attempting to draw anything. * This will create the window, the canvas and everything. * Unlike a simple JFrame, this is not automatic because of the order * Doom does things. * */ @Override public void InitGraphics() { String displayname; String d; int n; int pnum; int x=0; int y=0; // warning: char format, different type arg int xsign=' '; int ysign=' '; boolean oktodraw; long attribmask; // Try setting the locale the US, otherwise there will be problems // with non-US keyboards. if (this.getInputContext()==null || !this.getInputContext().selectInputMethod(java.util.Locale.US)){ System.err.println("Could not set the input context to US! Keyboard input will be glitchy!"); } else { System.err.println("Input context successfully set to US."); } // check for command-line display name if ( (pnum=CM.CheckParm("-disp"))!=0 ) // suggest parentheses around assignment displayname = CM.getArgv(pnum+1); else displayname = null; // check for command-line geometry if ( (pnum=CM.CheckParm("-geom"))!=0 ) // suggest parentheses around assignment { try{ String eval=CM.getArgv(pnum+1).trim(); // warning: char format, different type arg 3,5 //n = sscanf(myargv[pnum+1], "%c%d%c%d", &xsign, &x, &ysign, &y); // OK, so we have to read a string that may contain // ' '/'+'/'-' and a number. Twice. StringTokenizer tk=new StringTokenizer(eval,"-+ "); // Signs. Consider positive. xsign=1;ysign=1; for (int i=0;i<eval.length();i++){ if (eval.charAt(i)=='-'){ // First '-' on trimmed string: negagive if (i==0) xsign=-1; else ysign=-1; } } //this should parse two numbers. if (tk.countTokens()==2){ x=xsign*Integer.parseInt(tk.nextToken()); y=ysign*Integer.parseInt(tk.nextToken()); } } catch (NumberFormatException e){ I.Error("bad -geom parameter"); } } // open the display // AWT: create the canvas. try{ //drawhere=new Canvas(); // MAES: this method works even on "stubborn" Linux distros that // fuck up the window size. setCanvasSize(size); this.eventhandler=new MochaEvents(DM,drawhere); // AWT: Add listeners to CANVAS element. // Maybe it should go to the gelatine component? drawhere.addKeyListener(eventhandler); drawhere.addMouseListener(eventhandler); drawhere.addMouseMotionListener(eventhandler); addComponentListener(eventhandler); addWindowFocusListener(eventhandler); addWindowListener(eventhandler); if (DM.VM.getSetting("fullscreen").getBoolean()) switchToFullScreen(); } catch (Exception e){ I.Error("Error creating AWTDoom frame. Exiting. Reason: %s",e.getMessage()); } // AWT: tab is a special case :-/ // We need to "peg" it to the JFrame, rather than the canvas, // and the handler itself cannot auto-assign it. final Component me=drawhere; KeyboardFocusManager. getCurrentKeyboardFocusManager(). addKeyEventDispatcher(new KeyEventDispatcher() { boolean press=false; public boolean dispatchKeyEvent(KeyEvent e) { if (e.getKeyCode() == KeyEvent.VK_TAB) { // MAES: simulating a key type. if (press) eventhandler.keyPressed( new KeyEvent(me, e.getID(), System.nanoTime(),0 , KeyEvent.VK_TAB, KeyEvent.CHAR_UNDEFINED)); else eventhandler.keyReleased( new KeyEvent(me, e.getID(), System.nanoTime(),0 , KeyEvent.VK_TAB, KeyEvent.CHAR_UNDEFINED)); press=!press; } return false; } }); this.add(drawhere); // this.add(gelatine); this.getContentPane().setPreferredSize(drawhere.getPreferredSize()); // JFrame's size is auto-set here. this.pack(); this.setVisible(true); this.setResizable(false); this.setTitle(Strings.MOCHA_DOOM_TITLE); this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // Gently tell the eventhandler to wake up and set itself. this.requestFocus(); this.eventhandler.addEvent(MochaDoomInputEvent.GET_YOUR_ASS_OFF); SetGamma(0); } private void setCanvasSize(Dimension size) { drawhere.setPreferredSize(size); drawhere.setBounds(0, 0, drawhere.getWidth()-1,drawhere.getHeight()-1); drawhere.setBackground(Color.black); gelatine.setPreferredSize(size); gelatine.setBounds(0, 0, drawhere.getWidth()-1,drawhere.getHeight()-1); gelatine.setBackground(Color.black); } /** FULLSCREEN SWITCH CODE * TODO: it's not enough to do this without also switching the screen's resolution. * Unfortunately, Java only has a handful of options which depend on the OS, driver, * display, JVM etc. and it's not possible to switch to arbitrary resolutions. * * Therefore, a "best fit" strategy with centering is used. * */ private void switchToFullScreen() { boolean isFullScreen = device.isFullScreenSupported(); setUndecorated(isFullScreen); setResizable(!isFullScreen); // In case we need to revert. oldDisplayMode = device.getDisplayMode(); DisplayModePicker dmp=new DisplayModePicker(device); // TODO: what if bit depths are too small? DisplayMode dm=dmp.pickClosest(width,height); int[] xy=dmp.getCentering(width,height, dm); this.X_OFF=xy[0]; this.Y_OFF=xy[1]; device.getDisplayModes(); if (isFullScreen) { // Full-screen mode device.setFullScreenWindow(this); if (device.isDisplayChangeSupported()) device.setDisplayMode(dm); validate(); Dimension newsize=new Dimension(dm.getWidth(),dm.getHeight()); this.setDefaultDimension(dm.getWidth(),dm.getHeight()); setCanvasSize(newsize); } else { // Windowed mode pack(); setVisible(true); } } @Override public void StartFrame() { // Dummy. Nothing special to do...yet. } @Override public void StartTic() { if (!this.isActive()) return; // System.out.println("Getting events..."); while (eventhandler.hasMoreEvents()) eventhandler.GetEvent(); //eventhandler.grabMouse(); } protected int lasttic; protected int frames; @Override public void UpdateNoBlit() { // Quite pointless, no? } // Convenience, for testers. public void GetEvent() { this.eventhandler.GetEvent(); } @Override public void ShutdownGraphics() { this.dispose(); ((JFrame)this).dispose(); } public void SetGamma(int level){ if (D) System.err.println("Setting gamma "+level); V.setUsegamma(level); screen=V.getCurrentScreen(); // Refresh screen after change. RAWSCREEN=V.getScreen(DoomVideoRenderer.SCREEN_FG); } /** Modified update method: no context needs to passed. * Will render only internal screens. Common between AWT * and Swing * */ public void paint(Graphics g) { // Techdemo v1.3: Mac OSX fix, compatible with Windows and Linux. // Should probably run just once. Overhead is minimal // compared to actually DRAWING the stuff. if (g2d==null) g2d = (Graphics2D)drawhere.getGraphics(); //if (gel2d==null) gel2d= (Graphics2D)gelatine.getGraphics(); V.update(); //voli.getGraphics().drawImage(bi,0,0,null); g2d.drawImage(screen,X_OFF,Y_OFF,this); //gel2d.setColor(new Color(.3f, .4f, .5f, .1f)); //gel2d.fillRect(0, 0, this.getWidth(), this.getHeight()); } } package awt; import java.awt.AWTEvent; /** The older system was just a miserable pile of fuck. * This system clearly codifies everything we need to be aware * of, namely keypresses, mousebuttonpresses, window state * changes, etc. * * Based on Jake2's JakeInputEvent. * * @author velktron * */ public class MochaDoomInputEvent { static final int KeyPress = 0; static final int KeyRelease = 1; static final int KeyType = 2; static final int MotionNotify = 3; static final int DragNotify = 4; static final int ButtonPress = 5; static final int ButtonRelease = 6; static final int CreateNotify = 7; static final int ConfigureNotify = 8; static final int WheelMoved = 9; static final int MouseExited =10; static final int MouseEntered=11; static final int WindowMoving=12; static final int FocusGained=13; static final int FocusLost=14; static final int LockOn=15; static final int LockOff=16; static final int KEY_MASK=0X100; // Extract info from lower bits for this int type; int value; AWTEvent ev; MochaDoomInputEvent(int type, AWTEvent ev) { this.type = type; this.ev = ev; } MochaDoomInputEvent(int type, int value) { this.type=type; this.value=value; } /** Just a friendly way to remind the child component to * position and initialize itself correctly for the first * use. */ static MochaDoomInputEvent GET_YOUR_ASS_OFF = new MochaDoomInputEvent(ConfigureNotify, null); } package hu; // Emacs style mode select -*- C++ -*- // ----------------------------------------------------------------------------- // // $Id: HU.java,v 1.32 2012/09/24 17:16:23 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: Heads-up displays // // ----------------------------------------------------------------------------- import static data.Defines.*; import static g.Keys.*; import defines.*; import static data.Limits.*; import static doom.englsh.*; import i.DoomStatusAware; import utils.C2JUtils; import v.DoomVideoRenderer; import v.IVideoScale; import v.IVideoScaleAware; import m.IDoomMenu; import m.Menu; import rr.RendererState; import rr.patch_t; import s.IDoomSound; import w.IWadLoader; import data.sounds.sfxenum_t; import doom.DoomMain; import doom.DoomStatus; import doom.event_t; import doom.evtype_t; import doom.player_t; public class HU implements DoomStatusAware, IVideoScaleAware, IHeadsUp{ public final static String rcsid = "$Id: HU.java,v 1.32 2012/09/24 17:16:23 velktron Exp $"; // MAES: Status and wad data. IWadLoader W; DoomMain<?,?> DM; IDoomMenu M; RendererState<?,?> R; DoomVideoRenderer<?,?> V; IDoomSound S; // // Locally used constants, shortcuts. // MAES: Some depend on STATE, so moved into constructor. String HU_TITLE, HU_TITLE2, HU_TITLEP, HU_TITLET; protected final static int HU_TITLEHEIGHT = 1; protected final static int HU_TITLEX = 0; protected int HU_TITLEY;// = (167 - Swap.SHORT(hu_font[0].height)); protected final static char HU_INPUTTOGGLE = 't'; protected final static int HU_INPUTX = HU_MSGX; protected int HU_INPUTY;// = (HU_MSGY + // HU_MSGHEIGHT*(Swap.SHORT(hu_font[0].height) +1)); protected final static int HU_INPUTWIDTH = 64; protected final static int HU_INPUTHEIGHT = 1; public String[] chat_macros = { HUSTR_CHATMACRO0, HUSTR_CHATMACRO1, HUSTR_CHATMACRO2, HUSTR_CHATMACRO3, HUSTR_CHATMACRO4, HUSTR_CHATMACRO5, HUSTR_CHATMACRO6, HUSTR_CHATMACRO7, HUSTR_CHATMACRO8, HUSTR_CHATMACRO9 }; @Override public void setChatMacro(int i, String s){ this.chat_macros[i]=s; } /** Needs to be seen by DoomGame */ public final static String[] player_names = { HUSTR_PLRGREEN, HUSTR_PLRINDIGO, HUSTR_PLRBROWN, HUSTR_PLRRED }; char chat_char; // remove later. player_t plr; // MAES: a whole lot of "static" stuff which really would be HU instance // status. patch_t[] hu_font = new patch_t[HU_FONTSIZE]; char[] chat_dest = new char[MAXPLAYERS]; // MAES: these used to be defined in hu_lib. We're going 100% OO here... hu_itext_t[] w_inputbuffer; hu_textline_t w_title ; hu_itext_t w_chat; boolean[] always_off = { false }; // Needs to be referenced by one of the widgets. public boolean[] chat_on = new boolean[1]; // MAES: Ugly hack which allows it to be passed as reference. Sieg heil! boolean[] message_on = new boolean[]{true}; boolean message_dontfuckwithme; boolean message_nottobefuckedwith; hu_stext_t w_message; int message_counter; // This is actually an "extern" pointing inside m_menu (Menu.java). So we // need to share Menu context. // int showMessages; // MAES: I think this is supposed to be visible by the various hu_ crap... // boolean automapactive; boolean headsupactive = false; // // Builtin map names. // The actual names can be found in DStrings.h. // protected String[] mapnames = // DOOM shareware/registered/retail (Ultimate) // names. { HUSTR_E1M1, HUSTR_E1M2, HUSTR_E1M3, HUSTR_E1M4, HUSTR_E1M5, HUSTR_E1M6, HUSTR_E1M7, HUSTR_E1M8, HUSTR_E1M9, HUSTR_E2M1, HUSTR_E2M2, HUSTR_E2M3, HUSTR_E2M4, HUSTR_E2M5, HUSTR_E2M6, HUSTR_E2M7, HUSTR_E2M8, HUSTR_E2M9, HUSTR_E3M1, HUSTR_E3M2, HUSTR_E3M3, HUSTR_E3M4, HUSTR_E3M5, HUSTR_E3M6, HUSTR_E3M7, HUSTR_E3M8, HUSTR_E3M9, HUSTR_E4M1, HUSTR_E4M2, HUSTR_E4M3, HUSTR_E4M4, HUSTR_E4M5, HUSTR_E4M6, HUSTR_E4M7, HUSTR_E4M8, HUSTR_E4M9, "NEWLEVEL", "NEWLEVEL", "NEWLEVEL", "NEWLEVEL", "NEWLEVEL", "NEWLEVEL", "NEWLEVEL", "NEWLEVEL", "NEWLEVEL" }; protected String[] mapnames2 = // DOOM 2 map names. { HUSTR_1, HUSTR_2, HUSTR_3, HUSTR_4, HUSTR_5, HUSTR_6, HUSTR_7, HUSTR_8, HUSTR_9, HUSTR_10, HUSTR_11, HUSTR_12, HUSTR_13, HUSTR_14, HUSTR_15, HUSTR_16, HUSTR_17, HUSTR_18, HUSTR_19, HUSTR_20, HUSTR_21, HUSTR_22, HUSTR_23, HUSTR_24, HUSTR_25, HUSTR_26, HUSTR_27, HUSTR_28, HUSTR_29, HUSTR_30, HUSTR_31, HUSTR_32,HUSTR_33 }; protected String[] mapnamesp = // Plutonia WAD map names. { PHUSTR_1, PHUSTR_2, PHUSTR_3, PHUSTR_4, PHUSTR_5, PHUSTR_6, PHUSTR_7, PHUSTR_8, PHUSTR_9, PHUSTR_10, PHUSTR_11, PHUSTR_12, PHUSTR_13, PHUSTR_14, PHUSTR_15, PHUSTR_16, PHUSTR_17, PHUSTR_18, PHUSTR_19, PHUSTR_20, PHUSTR_21, PHUSTR_22, PHUSTR_23, PHUSTR_24, PHUSTR_25, PHUSTR_26, PHUSTR_27, PHUSTR_28, PHUSTR_29, PHUSTR_30, PHUSTR_31, PHUSTR_32 }; protected String[] mapnamest = // TNT WAD map names. { THUSTR_1, THUSTR_2, THUSTR_3, THUSTR_4, THUSTR_5, THUSTR_6, THUSTR_7, THUSTR_8, THUSTR_9, THUSTR_10, THUSTR_11, THUSTR_12, THUSTR_13, THUSTR_14, THUSTR_15, THUSTR_16, THUSTR_17, THUSTR_18, THUSTR_19, THUSTR_20, THUSTR_21, THUSTR_22, THUSTR_23, THUSTR_24, THUSTR_25, THUSTR_26, THUSTR_27, THUSTR_28, THUSTR_29, THUSTR_30, THUSTR_31, THUSTR_32 }; char[] shiftxform; public static final char[] french_shiftxform = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, ' ', '!', '"', '#', '$', '%', '&', '"', // shift-' '(', ')', '*', '+', '?', // shift-, '_', // shift-- '>', // shift-. '?', // shift-/ '0', // shift-0 '1', // shift-1 '2', // shift-2 '3', // shift-3 '4', // shift-4 '5', // shift-5 '6', // shift-6 '7', // shift-7 '8', // shift-8 '9', // shift-9 '/', '.', // shift-; '<', '+', // shift-= '>', '?', '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', // shift-[ '!', // shift-backslash - OH MY GOD DOES WATCOM SUCK ']', // shift-] '"', '_', '\'', // shift-` 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '{', '|', '}', '~', 127 }; public static final char[] english_shiftxform = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, ' ', '!', '"', '#', '$', '%', '&', '"', // shift-' '(', ')', '*', '+', '<', // shift-, '_', // shift-- '>', // shift-. '?', // shift-/ ')', // shift-0 '!', // shift-1 '@', // shift-2 '#', // shift-3 '$', // shift-4 '%', // shift-5 '^', // shift-6 '&', // shift-7 '*', // shift-8 '(', // shift-9 ':', ':', // shift-; '<', '+', // shift-= '>', '?', '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', // shift-[ '!', // shift-backslash - OH MY GOD DOES WATCOM SUCK ']', // shift-] '"', '_', '\'', // shift-` 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '{', '|', '}', '~', 127 }; // Maes: char? char[] frenchKeyMap = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, ' ', '!', '"', '#', '$', '%', '&', '%', '(', ')', '*', '+', ';', '-', ':', '!', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', 'M', '<', '=', '>', '?', '@', 'Q', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', ',', 'N', 'O', 'P', 'A', 'R', 'S', 'T', 'U', 'V', 'Z', 'X', 'Y', 'W', '^', '\\', '$', '^', '_', '@', 'Q', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', ',', 'N', 'O', 'P', 'A', 'R', 'S', 'T', 'U', 'V', 'Z', 'X', 'Y', 'W', '^', '\\', '$', '^', 127 }; protected final char ForeignTranslation(char ch) { return ch < 128 ? frenchKeyMap[ch] : ch; } public HU(DoomStatus DM) { this.updateStatus(DM); this.w_message=new hu_stext_t(); this.w_inputbuffer=new hu_itext_t[MAXPLAYERS]; for (int i=0;i<MAXPLAYERS;i++){ this.w_inputbuffer[i]=new hu_itext_t(); } this.w_title=new hu_textline_t(); this.w_chat=new hu_itext_t(); } /** Used only for testing */ public HU() { } /** * Loads a bunch of STCFNx fonts from WAD, and sets some of the remaining * constants. * * @throws Exception */ @Override public void Init() { String xxx = new String("STCFN%03d"); int i; int j; String buffer; if (DM.language == Language_t.french) shiftxform = french_shiftxform; else shiftxform = english_shiftxform; // load the heads-up font j = HU_FONTSTART; // So it basically loads a bunch of patch_t's from memory. C2JUtils.initArrayOfObjects(hu_font, patch_t.class); for (i = 0; i < HU_FONTSIZE; i++) { buffer = String.format(xxx,j++); // hu_font[i] = ((patch_t[]) wd.CacheLumpName(buffer, PU_STATIC); hu_font[i] = (W.CachePatchName(buffer, PU_STATIC)); } // MAES: Doom's SC had a really fucked up endianness change for height. // I don't really see the point in that, as in the WAD patches appear // to be all Little Endian... mystery :-S // HU_TITLEY = (167 - Swap.SHORT(hu_font[0].height)); HU_TITLEY = (167 - hu_font[0].height); HU_INPUTY = (HU_MSGY + HU_MSGHEIGHT * hu_font[0].height + 1); } @Override public void Stop() { headsupactive = false; } @Override public void Start() { int i; String s; // MAES: fugly hax. These were compile-time inlines, // so they can either work as functions, or be set whenever the HU is started // (typically once per level). They need to be aware of game progress, // and episode numbers <1 will cause it to bomb. // MAES: hack to handle Betray in XBLA 31/5/2011 if ((DM.gamemap>32) && (DM.getGameMode()==GameMode_t.pack_xbla)){ this.HU_TITLE = mapnames[(DM.gameepisode - 1) * 9 + DM.gamemap - 2]; this.HU_TITLE2 = mapnames2[DM.gamemap - 1]; this.HU_TITLEP = mapnamesp[DM.gamemap - 2]; // fixed from HU_TITLEPw this.HU_TITLET = mapnamest[DM.gamemap - 2]; } else { this.HU_TITLE = mapnames[(DM.gameepisode - 1) * 9 + DM.gamemap - 1]; this.HU_TITLE2 = mapnames2[DM.gamemap - 1]; this.HU_TITLEP = mapnamesp[DM.gamemap - 1]; // fixed from HU_TITLEP this.HU_TITLET = mapnamest[DM.gamemap - 1]; } if (headsupactive) this.Stop(); plr = DM.players[DM.consoleplayer]; message_on[0] = false; message_dontfuckwithme = false; message_nottobefuckedwith = false; chat_on[0] = false; // create the message widget this.w_message.initSText(HU_MSGX, HU_MSGY, HU_MSGHEIGHT, hu_font, HU_FONTSTART, this.message_on); // create the map title widget this.w_title.initTextLine(HU_TITLEX, HU_TITLEY, hu_font, HU_FONTSTART); switch (DM.getGameMode()) { case shareware: case registered: case retail: s = HU_TITLE; break; case pack_plut: s = HU_TITLEP; break; case pack_tnt: s = HU_TITLET; break; case commercial: default: s = HU_TITLE2; break; } // MAES: oh great, more pointer-char magic... oh no you don't, you ugly // cow horse and reindeer lover. // while (*s) this.w_title.addCharToTextLine(*(s++)); int ptr=0; while(ptr<s.length()){ this.w_title.addCharToTextLine(s.charAt(ptr++)); } // create the chat widget this.w_chat.initIText(HU_INPUTX, HU_INPUTY, hu_font, HU_FONTSTART, chat_on); // create the inputbuffer widgets for (i = 0; i < MAXPLAYERS; i++) { w_inputbuffer[i] = new hu_itext_t(); w_inputbuffer[i].initIText(0, 0, null, 0, always_off); } headsupactive = true; } @Override public void Drawer() { this.w_message.drawSText(); this.w_chat.drawIText(); if (DM.automapactive) this.w_title.drawTextLine(false); } @Override public void Erase() { this.w_message.eraseSText(); this.w_chat.eraseIText(); this.w_title.eraseTextLine(); } @Override public void Ticker() { int i; boolean rc; char c; // tick down message counter if message is up if ((message_counter != 0) && !((--message_counter) != 0)) { message_on[0] = false; message_nottobefuckedwith = false; } if (M.getShowMessages() || message_dontfuckwithme) { // display message if necessary if (((plr.message != null) && !message_nottobefuckedwith) || ((plr.message != null) && message_dontfuckwithme)) { this.w_message.addMessageToSText(null, plr.message); plr.message = null; message_on[0] = true; message_counter = HU_MSGTIMEOUT; message_nottobefuckedwith = message_dontfuckwithme; message_dontfuckwithme = false; } } // else message_on = false; // check for incoming chat characters if (DM.netgame) { for (i = 0; i < MAXPLAYERS; i++) { if (!DM.playeringame[i]) continue; if ((i != DM.consoleplayer) && ((c = DM.players[i].cmd.chatchar) != 0)) { if (c <= HU_BROADCAST) chat_dest[i] = c; else { if (c >= 'a' && c <= 'z') c = (char) shiftxform[c]; rc = w_inputbuffer[i].keyInIText(c); if (rc && c == KEY_ENTER) { if ((w_inputbuffer[i].l.len != 0) && (chat_dest[i] == DM.consoleplayer + 1) || (chat_dest[i] == HU_BROADCAST)) { w_message.addMessageToSText(player_names[i] , w_inputbuffer[i].l.text.toString()); message_nottobefuckedwith = true; message_on[0] = true; message_counter = HU_MSGTIMEOUT; if (DM.isCommercial()) S.StartSound(null, sfxenum_t.sfx_radio); else S.StartSound(null, sfxenum_t.sfx_tink); } w_inputbuffer[i].resetIText(); } } DM.players[i].cmd.chatchar = 0; } } } } protected final int QUEUESIZE = 128; protected char[] chatchars = new char[QUEUESIZE]; protected int head = 0; protected int tail = 0; protected void queueChatChar(char c) { if (((head + 1) & (QUEUESIZE - 1)) == tail) { plr.message = HUSTR_MSGU; } else { chatchars[head] = c; head = (head + 1) & (QUEUESIZE - 1); } } @Override public char dequeueChatChar() { char c; if (head != tail) { c = chatchars[tail]; tail = (tail + 1) & (QUEUESIZE - 1); } else { c = 0; } return c; } // MAES: These were "static" inside HU_Responder, since they were meant to // represent state. protected StringBuilder lastmessage = new StringBuilder(HU_MAXLINELENGTH + 1); // protected char[] lastmessage=new char[HU_MAXLINELENGTH+1]; protected boolean shiftdown = false; protected boolean altdown = false; protected char[] destination_keys = { HUSTR_KEYGREEN, HUSTR_KEYINDIGO, HUSTR_KEYBROWN, HUSTR_KEYRED }; protected int num_nobrainers = 0; @Override public boolean Responder(event_t ev) { //System.out.println("Player "+DM.players[0].mo.x); char[] macromessage; boolean eatkey = false; char c; int i; int numplayers; numplayers = 0; // MAES: Adding BOOLEANS to ints, are we ?! for (i = 0; i < MAXPLAYERS; i++) { numplayers += (DM.playeringame[i]) ? 1 : 0; } if (ev.data1 == KEY_SHIFT) { shiftdown = (ev.type == evtype_t.ev_keydown); return false; } else if (ev.data1 == KEY_ALT || ev.data1 == KEY_ALT) { altdown = (ev.type == evtype_t.ev_keydown); return false; } if (ev.type != evtype_t.ev_keydown) return false; if (!chat_on[0]) { if (ev.data1 == HU_MSGREFRESH) { message_on[0] = true; message_counter = HU_MSGTIMEOUT; eatkey = true; } else if (DM.netgame && ev.data1 == HU_INPUTTOGGLE) { eatkey = chat_on[0] = true; w_chat.resetIText(); this.queueChatChar(HU_BROADCAST); } else if (DM.netgame && numplayers > 2) { for (i = 0; i < MAXPLAYERS; i++) { if (ev.data1 == destination_keys[i]) { if (DM.playeringame[i] && i != DM.consoleplayer) { eatkey = chat_on[0] = true; w_chat.resetIText(); this.queueChatChar((char) (i + 1)); break; } else if (i == DM.consoleplayer) { num_nobrainers++; if (num_nobrainers < 3) plr.message = HUSTR_TALKTOSELF1; else if (num_nobrainers < 6) plr.message = HUSTR_TALKTOSELF2; else if (num_nobrainers < 9) plr.message = HUSTR_TALKTOSELF3; else if (num_nobrainers < 32) plr.message = HUSTR_TALKTOSELF4; else plr.message = HUSTR_TALKTOSELF5; } } } } } else { c = (char) ev.data1; // send a macro if (altdown) { c = (char) (c - '0'); if (c > 9) return false; // fprintf(stderr, "got here\n"); macromessage = chat_macros[c].toCharArray(); // kill last message with a '\n' this.queueChatChar(KEY_ENTER); // DEBUG!!! // send the macro message int index = 0; while (macromessage[index] != 0) { this.queueChatChar(macromessage[index]); } this.queueChatChar(KEY_ENTER); // leave chat mode and notify that it was sent chat_on[0] = false; lastmessage.setLength(0); lastmessage.append(chat_macros[c]); plr.message = lastmessage.toString(); eatkey = true; } else { if (DM.language == Language_t.french) c = ForeignTranslation(c); if (shiftdown || (c >= 'a' && c <= 'z')) c = shiftxform[c]; eatkey = w_chat.keyInIText(c); if (eatkey) { // static unsigned char buf[20]; // DEBUG this.queueChatChar(c); // sprintf(buf, "KEY: %d => %d", ev->data1, c); // plr->message = buf; } if (c == KEY_ENTER) { chat_on[0] = false; if ((w_chat.l.len != 0)) { lastmessage.setLength(0); lastmessage.append( w_chat.l.text); plr.message = new String(lastmessage); } } else if (c == KEY_ESCAPE) chat_on[0] = false; } } return eatkey; } // ///////////////////////////////// STRUCTS // /////////////////////////////////// /** * Input Text Line widget * (child of Text Line widget) */ class hu_itext_t { hu_textline_t l; // text line to input on // left margin past which I am not to delete characters int lm; // pointer to boolean stating whether to update window boolean[] on; boolean laston; // last value of *->on; public hu_itext_t(){ } public void initIText(int x, int y, patch_t[] font, int startchar, boolean[] on) { this.lm = 0; // default left margin is start of text this.on = on; this.laston = true; l = new hu_textline_t(x, y, font, startchar); } // The following deletion routines adhere to the left margin restriction public void delCharFromIText() { if (this.l.len != this.lm) this.l.delCharFromTextLine(); } public void eraseLineFromIText() { while (this.lm != this.l.len) l.delCharFromTextLine(); } // Resets left margin as well public void resetIText() { this.lm = 0; this.l.clearTextLine(); } public void addPrefixToIText(char[] str) { int ptr = 0; while (str[ptr] > 0) { l.addCharToTextLine(str[ptr++]); this.lm = this.l.len; } } // Maes: String overload public void addPrefixToIText(String str) { int ptr = 0; while (str.charAt(ptr) > 0) { l.addCharToTextLine(str.charAt(ptr++)); this.lm = this.l.len; } } // wrapper function for handling general keyed input. // returns true if it ate the key public boolean keyInIText(char ch) { if (ch >= ' ' && ch <= '_') this.l.addCharToTextLine((char) ch); else if (ch == KEY_BACKSPACE) this.delCharFromIText(); else if (ch != KEY_ENTER) return false; // did not eat key return true; // ate the key } public void drawIText() { if (!this.on[0]) return; this.l.drawTextLine(true); // draw the line w/ cursor } void eraseIText() { if (this.laston && !this.on[0]) this.l.needsupdate = 4; this.l.eraseTextLine(); this.laston = this.on[0]; } } /** Scrolling Text window widget * (child of Text Line widget) */ class hu_stext_t { hu_textline_t[] lines = new hu_textline_t[HU_MAXLINES]; // text lines to draw int height; // height in lines int currline; // current line number // pointer to boolean stating whether to update window boolean[] on; boolean laston; // last value of *->on. public hu_stext_t(){ } public hu_stext_t(int x, int y, int h, patch_t[] font, int startchar, boolean[] on) { this.initSText(x, y, h, font, startchar, on); } public void initSText(int x, int y, int h, patch_t[] font, int startchar, boolean[] on) { for (int i=0;i<HU_MAXLINES;i++){ this.lines[i]=new hu_textline_t(); } this.height = h; this.on = on; this.laston = true; this.currline = 0; for (int i = 0; i < h; i++) this.lines[i].initTextLine(x, y - i * (font[0].height + 1), font, startchar); } public void addLineToSText() { // add a clear line if (++this.currline == this.height) this.currline = 0; this.lines[this.currline].clearTextLine(); // everything needs updating for (int i = 0; i < this.height; i++) this.lines[i].needsupdate = 4; } public void addMessageToSText(char[] prefix, char[] msg) { this.addLineToSText(); int ptr = 0; if ((prefix != null) && (prefix.length > 0)) { while ((ptr < prefix.length) && (prefix[ptr] > 0)) this.lines[this.currline].addCharToTextLine(prefix[ptr++]); } ptr = 0; while ((ptr < msg.length) && (msg[ptr] > 0)) this.lines[this.currline].addCharToTextLine(msg[ptr++]); } public void addMessageToSText(String prefix, String msg) { this.addLineToSText(); if ((prefix != null) && (prefix.length() > 0)) { for (int i = 0; i < prefix.length(); i++) this.lines[this.currline].addCharToTextLine(prefix.charAt(i)); } for (int i = 0; i < msg.length(); i++) this.lines[this.currline].addCharToTextLine(msg.charAt(i)); } public void drawSText() { int i, idx; hu_textline_t l; if (!this.on[0]) return; // if not on, don't draw // draw everything for (i = 0; i < this.height; i++) { idx = this.currline - i; if (idx < 0) idx += this.height; // handle queue of lines l = this.lines[idx]; // need a decision made here on whether to skip the draw l.drawTextLine(false); // no cursor, please } } public void eraseSText() { for (int i = 0; i < this.height; i++) { if (laston && !on[0]) lines[i].needsupdate = 4; this.lines[i].eraseTextLine(); } laston = on[0]; } /** * MAES: this was the only variable in HUlib.c, and only instances of * hu_textline_t ever use it. For this reason, it makes sense to have it * common (?) between all instances of hu_textline_t and set it * somewhere else. Of course, if could be made an instance method or a * HUlib object could be defined. */ protected boolean automapactive; // in AM_map.c public boolean isAutomapactive() { return automapactive; } public void setAutomapactive(boolean automapactive) { this.automapactive = automapactive; } /** * Same here. */ // TODO: boolean : whether the screen is always erased protected boolean noterased; // =viewwindowx; public boolean isNoterased() { return noterased; } public void setNoterased(boolean noterased) { this.noterased = noterased; } StringBuilder sb=new StringBuilder(); public String toString(){ sb.setLength(0); sb.append(this.lines[0].text); sb.append(this.lines[1].text); sb.append(this.lines[2].text); sb.append(this.lines[3].text); return sb.toString(); } } // Text Line widget // (parent of Scrolling Text and Input Text widgets) class hu_textline_t { // left-justified position of scrolling text window int x; int y; // MAES: was ** patch_t[] f; // font int sc; // start character char[] text = new char[HU_MAXLINELENGTH+1]; // line of text int len; // current line length // whether this line needs to be udpated int needsupdate; public hu_textline_t(){ } public void clearTextLine() { this.len = 0; C2JUtils.memset(this.text, (char)0,this.text.length); // It's actually used as a status, go figure. this.needsupdate = 1; } // Maes: this could as well be the contructor public void initTextLine(int x, int y, patch_t[] f, int sc) { this.x = x; this.y = y; this.f = f; this.sc = sc; this.clearTextLine(); } public hu_textline_t(int x, int y, patch_t[] f, int sc) { this.x = x; this.y = y; this.f = f; this.sc = sc; this.clearTextLine(); } public boolean addCharToTextLine(char ch) { if (len == HU_MAXLINELENGTH) return false; else { this.text[len++]=ch; this.text[len]=(char)0; // this.l[this.len] = 0; // MAES: for some reason this is set as "4", so this is a status // rather than a boolean. this.needsupdate = 4; return true; } } /** * MAES: This is much better than cluttering up the syntax everytime a * STRING must be added. * * @param s * @return */ /* public boolean addStringToTextLine(String s) { int index = 0; if (this.len == HU_MAXLINELENGTH) return false; else while ((index<s.length())&&(this.len < HU_MAXLINELENGTH)) { this.l[len]append(s.charAt(index++)); this.len++; } this.l.append((char) 0);// final padding. // MAES: for some reason this is set as "4", so this is a // status rather than a boolean. this.needsupdate = 4; return true; } */ boolean delCharFromTextLine() { if (this.len == 0) return false; else { this.text[--len]= (char)0; this.needsupdate = 4; return true; } } void drawTextLine(boolean drawcursor) { int i; int w; int x; char c; // draw the new stuff x = this.x; for (i = 0; i < this.len; i++) { c = Character.toUpperCase(text[i]); if (c != ' ' && c >= this.sc && c <= '_') { // MAES: fixed a FUCKING STUPID bug caused by SWAP.SHORT w = this.f[c - this.sc].width; if (x + w > SCREENWIDTH) break; V.DrawScaledPatch(x, y, FG,vs, f[c - sc]); x += w; } else { // Leave a space x += 4; if (x >= SCREENWIDTH) break; } } // draw the cursor if requested if (drawcursor && x + this.f['_' - this.sc].width <= SCREENWIDTH) { V.DrawScaledPatch(x, this.y, FG,vs, this.f['_' - this.sc]); } } // MAES: was "static" in C within HUlib. Which may mean it's instance // specific or global-ish. Or both. protected boolean lastautomapactive = true; // sorta called by HU_Erase and just better darn get things straight public void eraseTextLine() { int lh; // Only erases when NOT in automap and the screen is reduced, // and the text must either need updating or refreshing // (because of a recent change back from the automap) if (!DM.automapactive && (R.view.windowx != 0) && (this.needsupdate > 0)) { lh = this.f[0].height + 1; for (int y = this.y, yoffset = y * SCREENWIDTH; y < this.y + lh; y++, yoffset += SCREENWIDTH) { // Stuff is probably in am_map?? if (y < R.view.windowy || y >= R.view.windowy + R.view.height) R.VideoErase(yoffset, SCREENWIDTH); // erase entire // line else { R.VideoErase(yoffset, R.view.windowx); // erase left // border R.VideoErase(yoffset + R.view.windowx + R.view.width, R.view.windowx); // erase right border } } } lastautomapactive = DM.automapactive; if (this.needsupdate != 0) this.needsupdate--; } } @Override public patch_t[] getHUFonts() { return this.hu_font; } @Override public void updateStatus(DoomStatus<?,?> DM) { this.DM = DM.DM; this.W = DM.W; this.R = (RendererState<?, ?>) DM.R; this.V=DM.V; this.S=DM.S; this.M=(Menu) DM.M; } ////////////////////////////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(); } } //$Log: HU.java,v $ //Revision 1.32 2012/09/24 17:16:23 velktron //Massive merge between HiColor and HEAD. There's no difference from now on, and development continues on HEAD. // //Revision 1.31.2.2 2012/09/24 16:57:43 velktron //Addressed generics warnings. // //Revision 1.31.2.1 2012/09/19 17:43:06 velktron //Aware of new ViewVars structure. // //Revision 1.31 2011/11/01 22:17:46 velktron //Cleaned up a bit, implements IHeadsUp // //Revision 1.30 2011/10/23 18:11:58 velktron //Generic compliance for DoomVideoInterface // //Revision 1.29 2011/10/07 16:05:22 velktron //Now using g.Keys for key input stuff. // //Revision 1.28 2011/05/31 23:46:18 velktron //Fixed scaling. // //Revision 1.27 2011/05/31 21:42:30 velktron //Handling for map33 // //Revision 1.26 2011/05/24 17:45:08 velktron //IHeadsUp interface, setChatMacro method. // //Revision 1.25 2011/05/23 16:56:44 velktron //Migrated to VideoScaleInfo. // //Revision 1.24 2011/05/21 14:42:32 velktron //Adapted to use new gamemode system. // //Revision 1.23 2011/05/20 18:27:12 velktron //DoomMenu -> IDoomMenu // //Revision 1.22 2011/05/20 18:24:19 velktron //FINALLY fixed a stupid bug that broke HU messages. // //Revision 1.21 2011/05/18 16:52:40 velktron //Changed to DoomStatus package hu; import rr.patch_t; import doom.event_t; public interface IHeadsUp { void Ticker(); void Erase(); void Drawer(); boolean Responder(event_t ev); patch_t[] getHUFonts(); char dequeueChatChar(); void Init(); void setChatMacro(int i, String s); void Start(); void Stop(); } package w; public class JadDecompress { public final static int WINDOW_SIZE = 4096; public final static int LOOKAHEAD_SIZE = 16; public final static int LENSHIFT = 4; /* this must be log2(LOOKAHEAD_SIZE) */ public static void decode(byte[] input, byte[] output) { /* * #ifdef JAGUAR decomp_input = input; decomp_output = output; * gpufinished = zero; gpucodestart = (int)&decomp_start; while * (!I_RefreshCompleted () ) ; #else */ int getidbyte = 0; int len; int pos; int i; int source_ptr, input_ptr = 0, output_ptr = 0; int idbyte = 0; while (true) { /* get a new idbyte if necessary */ if (getidbyte == 0) idbyte = 0xFF & input[input_ptr++]; getidbyte = (getidbyte + 1) & 7; if ((idbyte & 1) != 0) { /* decompress */ pos = (0xFF & input[input_ptr++]) << LENSHIFT; pos = pos | ((0xFF & input[input_ptr]) >> LENSHIFT); source_ptr = output_ptr - pos - 1; len = ((0xFF & input[input_ptr++]) & 0xf) + 1; if (len == 1) break; for (i = 0; i < len; i++) output[output_ptr++] = output[source_ptr++]; } else { output[output_ptr++] = input[input_ptr++]; } idbyte = idbyte >> 1; } System.out.printf("Expanded %d to %d\n", input_ptr, output_ptr); } } package w; /** killough 4/17/98: namespace tags, to prevent conflicts between resources */ public enum li_namespace { ns_global, ns_sprites, ns_flats, ns_colormaps, ns_prboom, ns_demos, ns_hires //e6y } // haleyjd 05/21/02: renamed from "namespace" package w; import java.io.IOException; import java.nio.ByteBuffer; /** A container allowing for caching of arrays of CacheableDoomObjects * * It's a massive improvement over the older system, allowing for proper * caching and auto-unpacking of arrays of CacheableDoomObjects and much * cleaner code throughout. * * The container itself is a CacheableDoomObject....can you feel the * abuse? ;-) * */ public class CacheableDoomObjectContainer<T extends CacheableDoomObject> implements CacheableDoomObject { private T[] stuff; public CacheableDoomObjectContainer(T[] stuff){ this.stuff=stuff; } public T[] getStuff(){ return stuff; } @Override public void unpack(ByteBuffer buf) throws IOException { for (int i = 0; i < stuff.length; i++) { stuff[i].unpack(buf); } } /** Statically usable method * * @param buf * @param stuff * @throws IOException */ public static void unpack(ByteBuffer buf, CacheableDoomObject[] stuff) throws IOException{ for (int i = 0; i < stuff.length; i++) { stuff[i].unpack(buf); } } } package w; import java.io.InputStream; import java.util.zip.ZipEntry; // CPhipps - changed wad init // We _must_ have the wadfiles[] the same as those actually loaded, so there // is no point having these separate entities. This belongs here. public class wadfile_info_t { public String name; // Also used as a resource identifier, so save with full path and all. public ZipEntry entry; // Secondary resource identifier e.g. files inside zip archives. public int type; // as per InputStreamSugar public wad_source_t src; public InputStream handle; public boolean cached; // Whether we use local caching e.g. for URL or zips public long maxsize=-1; // Update when known for sure. Will speed up seeking. } package w; import java.io.IOException; import java.nio.ByteBuffer; public interface IPackableDoomObject { public void pack(ByteBuffer buf) throws IOException ; } package w; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; import java.util.List; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; import utils.C2JUtils; /** * As we know, Java can be a bit awkward when handling streams e.g. you can't * really skip at will without doing some nasty crud. This class helps doing * such crud. E.g. if we are dealing with a stream that has an underlying file, * we can try and skip directly by using the file channel, otherwise we can try * (eww) closing the stream, reopening it (ASSUMING WE KNOW THE SOURCE'S URI AND * TYPE), and then skipping. * * @author Maes */ public class InputStreamSugar { public static final int UNKNOWN_TYPE = 0x0; public static final int FILE = 0x1; // Local file. Easiest case public static final int NETWORK_FILE = 0x2; public static final int ZIP_FILE = 0x4; // Zipped file public static final int BAD_URI = -1; // Bad or unparseable /** * Creates an inputstream from a local file, network resource, or zipped * file (also over a network). If an entry name is specifid AND the type is * specified to be zip, then a zipentry with that name will be sought. * * @param resource * @param contained * @param type * @return */ public static final InputStream createInputStreamFromURI(String resource, ZipEntry entry, int type) { InputStream is = null; URL u; // No entry specified or no zip type, try everything BUT zip. if (entry == null || !C2JUtils.flags(type,ZIP_FILE)) { is = getDirectInputStream(resource); } else { // Entry specified AND type specified to be zip // We might want to open even a zip file without looking // for any particular entry. if (entry != null && C2JUtils.flags(type,ZIP_FILE)) { ZipInputStream zis; // Try it as a NET zip file try { u = new URL(resource); zis = new ZipInputStream(u.openStream()); } catch (Exception e) { // Local zip file? try { // Open resource as local file-backed zip input stream, // and search proper entry. zis= new ZipInputStream(new FileInputStream(resource)); } catch (Exception e1) { // Well, it's not that either. // At this point we almost ran out of options // Try a local file and that's it. is = getDirectInputStream(resource); return is; } } // All OK? is=getZipEntryStream(zis, entry.getName()); if (is!=null) return is; } } // At this point, you'll either get a stream or jack. return getDirectInputStream(resource); } /** Match zip entries in a ZipInputStream based only on their name. * Luckily (?) ZipEntries do not keep references to their originating * streams, so opening/closing ZipInputStreams all the time won't result * in a garbage hell...I hope. * * @param zis * @param entryname * @return */ private static InputStream getZipEntryStream(ZipInputStream zis,String entryname) { ZipEntry ze = null; try { while ((ze = zis.getNextEntry()) != null) { // Directories cannot be opened if (ze.isDirectory()) continue; if (ze.getName().equals(entryname)) { return zis; } } } catch (IOException e) { // Get jack return null; } // Get jack return null; } private final static InputStream getDirectInputStream(String resource) { InputStream is = null; URL u; try { // Is it a net resource? u = new URL(resource); is = u.openStream(); } catch (Exception e) { // OK, not a valid URL or no network. We don't care. // Try opening as a local file. try { is = new FileInputStream(resource); } catch (FileNotFoundException e1) { // Well, it's not that either. // At this point we really ran out of options // and you'll get null } } return is; } /** * Attempt to do the Holy Grail of Java Streams, aka seek to a particular * position. With some types of stream, this is possible if you poke deep * enough. With others, it's not, and you can only close & reopen them * (provided you know how to do that) and then skip to a particular position * * @param is * @param pos * The desired position * @param URI * Information which can help reopen a stream, e.g. a filename, URL, * or zip file. * @peram entry If we must look into a zipfile entry * @return the skipped stream. Might be a totally different object. * @throws IOException */ public static final InputStream streamSeek(InputStream is, long pos, long size,String URI, ZipEntry entry, int type) throws IOException { if (is == null) return is; // If we know our actual position in the stream, we can aid seeking // forward /* * Too buggy :-/ pity if (knownpos>=0 && knownpos<=pos){ if * (pos==knownpos) return is; try{ final long mustskip=pos-knownpos; * long skipped=0; while (skipped<mustskip){ * skipped+=is.skip(mustskip-skipped); * System.out.printf("Must skip %d skipped %d\n",mustskip,skipped); } * return is; } catch (Exception e){ // We couldn't skip cleanly. * Swallow up and try normally. System.err.println("Couldn't skip"); } } */ // This is a more reliable method, although it's less than impressive in // results. if (size > 0) { try { long available = is.available(); long guesspos = size - available; // The stream is at a position before or equal to // our desired one. We can attempt skipping forward. if (guesspos > 0 && guesspos <= pos) { long skipped=0; long mustskip=pos-guesspos; // Repeat skipping until proper amount reached while (skipped<mustskip) skipped+=is.skip(mustskip-skipped); return is; } } catch (Exception e) { // We couldn't skip cleanly. Swallow up and try normally. } } // Cast succeeded if (is instanceof FileInputStream) { try { ((FileInputStream) is).getChannel().position(pos); return is; } catch (IOException e) { // Ouch. Do a dumb close & reopening. is.close(); is = createInputStreamFromURI(URI, null, 1); is.skip(pos); return is; } } // Cast succeeded if (is instanceof ZipInputStream) { // ZipInputStreams are VERY dumb. so... is.close(); is = createInputStreamFromURI(URI,entry,type); is.skip(pos); return is; } try { // Is it a net resource? We have to reopen it :-/ // long a=System.nanoTime(); URL u = new URL(URI); InputStream nis = u.openStream(); nis.skip(pos); is.close(); // long b=System.nanoTime(); // System.out.printf("Network stream seeked WITH closing %d\n",(b-a)/1000); return nis; } catch (Exception e) { } // TODO: zip handling? return is; } public static List<ZipEntry> getAllEntries(ZipInputStream zis) throws IOException { ArrayList<ZipEntry> zes = new ArrayList<ZipEntry>(); ZipEntry z; while ((z = zis.getNextEntry()) != null) { zes.add(z); } return zes; } /** Attempts to return a stream size estimate. Only guaranteed to work 100% * for streams representing local files, and zips (if you have the entry). * * @param is * @param z * @return */ public static long getSizeEstimate(InputStream is, ZipEntry z) { if (is instanceof FileInputStream) { try { return ((FileInputStream) is).getChannel().size(); } catch (IOException e) { } } if (is instanceof FileInputStream) { if (z != null) return z.getSize(); } // Last ditch try { return is.available(); } catch (IOException e) { try { return is.available(); } catch (IOException e1) { return -1; } } } } package w; public enum animenum_t { ANIM_ALWAYS, ANIM_RANDOM, ANIM_LEVEL } package w; import java.io.IOException; import rr.patch_t; public interface IWadLoader { /** * W_Reload Flushes any of the reloadable lumps in memory and reloads the * directory. * * @throws Exception */ public abstract void Reload() throws Exception; /** * W_InitMultipleFiles * * Pass a null terminated list of files to use (actually a String[] array in Java). * * All files are optional, but at least one file must be found. * * Files with a .wad extension are idlink files with multiple lumps. * * Other files are single lumps with the base filename for the lump name. * * Lump names can appear multiple times. * The name searcher looks backwards, so a later file does override all earlier ones. * * @param filenames * @param coalesce Choose whether to coalesce or not sprites, flats etc. * */ public abstract void InitMultipleFiles(String[] filenames, boolean coalesce) throws Exception; /** * W_InitFile * * Just initialize from a single file. * * @param filename * @param coalesce Choose whether to coalesce or not sprites, flats etc. * */ public abstract void InitFile(String filename, boolean coalesce) throws Exception; /** * W_NumLumps * * Returns the total number of lumps loaded in this Wad manager. Awesome. * */ public abstract int NumLumps(); /** * Returns actual lumpinfo_t object for a given name. Useful if you want to * access something on a file, I guess? * * @param name * @return */ public abstract lumpinfo_t GetLumpinfoForName(String name); /** * W_GetNumForName * Calls W_CheckNumForName, but bombs out if not found. */ public abstract int GetNumForName(String name); /** * * @param lumpnum * @return */ public abstract String GetNameForNum(int lumpnum); // // W_LumpLength // Returns the buffer size needed to load the given lump. // public abstract int LumpLength(int lump); /** * W_CacheLumpNum Modified to read a lump as a specific type of * CacheableDoomObject. If the class is not identified or is null, then a * generic DoomBuffer object is left in the lump cache and returned. * @param <T> */ public abstract <T> T CacheLumpNum(int lump, int tag, Class<T> what); // MAES 24/8/2011: superseded by auto-allocating version with proper // container-based caching. @Deprecated public abstract void CacheLumpNumIntoArray(int lump, int tag, Object[] array, Class what) throws IOException; /** * Return a cached lump based on its name, as raw bytes, no matter what. * It's rare, but has its uses. * * @param name * @param tag * @param what * @return */ public abstract byte[] CacheLumpNameAsRawBytes(String name, int tag); /** * Return a cached lump based on its num, as raw bytes, no matter what. * It's rare, but has its uses. * * @param name * @param tag * @param what * @return */ public abstract byte[] CacheLumpNumAsRawBytes(int num, int tag); /** Get a DoomBuffer of the specified lump name * * @param name * @param tag * @return */ public abstract DoomBuffer CacheLumpName(String name, int tag); /** Get a DoomBuffer of the specified lump num * * @param lump * @return */ public abstract DoomBuffer CacheLumpNumAsDoomBuffer(int lump); /** * Specific method for loading cached patches by name, since it's by FAR the * most common operation. * * @param name * @return */ public abstract patch_t CachePatchName(String name); /** * Specific method for loading cached patches, since it's by FAR the most * common operation. * * @param name * @param tag * @return */ public abstract patch_t CachePatchName(String name, int tag); /** * Specific method for loading cached patches by number. * * @param num * @return */ public abstract patch_t CachePatchNum(int num); public abstract <T extends CacheableDoomObject> T CacheLumpName(String name, int tag, Class<T> what); /** A lump with size 0 is a marker. This means that it * can/must be skipped, and if we want actual data we must * read the next one. * * @param lump * @return */ public abstract boolean isLumpMarker(int lump); public abstract String GetNameForLump(int lump); public abstract int CheckNumForName(String name/* , int namespace */); /** Return ALL possible results for a given name, in order to resolve name clashes without * using namespaces * * @param name * @return */ public abstract int[] CheckNumsForName(String name); public abstract lumpinfo_t GetLumpInfo(int i); /** A way to cleanly close open file handles still pointed at by lumps. * Is also called upon finalize */ public void CloseAllHandles(); /** Null the disk lump associated with a particular object, * if any. This will NOT induce a garbage collection, unless * you also null any references you have to that object. * * @param lump */ void UnlockLumpNum(int lump); void UnlockLumpNum(CacheableDoomObject lump); public <T extends CacheableDoomObject> T[] CacheLumpNumIntoArray(int lump, int num, Class<T> what); /** Verify whether a certain lump number is valid and has * the expected name. * * @param lump * @param lumpname * @return */ boolean verifyLumpName(int lump, String lumpname); /** The index of a known loaded wadfile * * @param wad1 * @return */ public abstract int GetWadfileIndex(wadfile_info_t wad1); /** The number of loaded wadfile * * @return */ public abstract int GetNumWadfiles(); /** Force a lump (in memory) to be equal to a dictated content. Useful * for when you are e.g. repairing palette lumps or doing other sanity * checks. * * @param lump * @param obj */ void InjectLumpNum(int lump, CacheableDoomObject obj); /** Read a lump into a bunch of bytes straight. No caching, no frills. * * @param lump * @return */ byte[] ReadLump(int lump); /** Use your own buffer, of proper size of course. * * @param lump * @param buf */ void ReadLump(int lump, byte[] buf); /** Use your own buffer, of proper size AND offset. * * @param lump * @param buf */ void ReadLump(int lump, byte[] buf, int offset); } package w; import java.io.DataInputStream; import java.io.IOException; /** This is for objects that can be read from disk, but cannot * self-determine their own length for some reason. * * @author Maes * */ public interface AidedReadableDoomObject { public void read(DataInputStream f, int len) throws IOException ; } package w; import java.io.InputStream; /* typedef struct { // WARNING: order of some fields important (see info.c). char name[9]; int size; // killough 4/17/98: namespace tags, to prevent conflicts between resources enum { ns_global=0, ns_sprites, ns_flats, ns_colormaps, ns_prboom, ns_demos, ns_hires //e6y } li_namespace; // haleyjd 05/21/02: renamed from "namespace" wadfile_info_t *wadfile; int position; wad_source_t source; int flags; //e6y } lumpinfo_t; */ public class lumpinfo_t implements Cloneable{ public String name; public InputStream handle; public long position; public long size; // A 32-bit hash which should be enough for searching through hashtables. public int hash; // A 64-bit hash that just maps an 8-char string to a long num, good for hashing // or for direct comparisons. //public long stringhash; // Intepreting the first 32 bits of their name as an int. Used in initsprites. public int intname; // public int next; //public int index; // For BOOM compatibility public li_namespace namespace; public wadfile_info_t wadfile; public int hashCode(){ return hash; } public String toString(){ return (name +" "+ Integer.toHexString(hash)); } public lumpinfo_t clone(){ lumpinfo_t tmp=new lumpinfo_t(); tmp.name=name; // Well... a reference will do. tmp.handle=handle; tmp.position=position; tmp.size=size; tmp.hash=hash; tmp.intname=intname; tmp.namespace=namespace; tmp.wadfile=wadfile; return tmp; } } package w; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; public class wadheader_t implements IReadableDoomObject, IWritableDoomObject { public String type; public int numentries; public int tablepos; public boolean big_endian=false; public void read(DataInputStream f) throws IOException{ type=DoomIO.readNullTerminatedString(f,4); if (!big_endian){ numentries=(int) DoomIO.readUnsignedLEInt(f); tablepos=(int) DoomIO.readUnsignedLEInt(f); } else { numentries=f.readInt(); tablepos=f.readInt(); } } public static int sizeof(){ return 16; } @Override public void write(DataOutputStream dos) throws IOException { DoomIO.writeString(dos, type, 4); if (!big_endian){ DoomIO.writeLEInt(dos, (int) numentries); DoomIO.writeLEInt(dos, (int) tablepos); } else { dos.writeInt((int) numentries); dos.writeInt((int) tablepos); } } } package w; import java.io.DataOutputStream; import java.io.IOException; public interface IWritableDoomObject { public void write(DataOutputStream dos) throws IOException ; } package w; // CPhipps - defined enum in wider scope // Ty 08/29/98 - add source field to identify where this lump came from public enum wad_source_t { // CPhipps - define elements in order of 'how new/unusual' source_iwad, // iwad file load source_pre, // predefined lump source_auto_load, // lump auto-loaded by config file source_pwad, // pwad file load source_lmp, // lmp file load source_net // CPhipps //e6y // ,source_deh_auto_load ,source_deh ,source_err } package w; import java.io.DataOutputStream; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.util.ArrayList; import java.util.List; /** A very simple WAD building utility class, just enough to lay some organized lumps on disk. Not particularly optimized * * It works simply by adding IWritableDoomObjects, IPackableDoomObjects and raw byte[] data to a list. Then, when the time * to write everything to disk comes, a header, index table etc. are created as well. * * */ public class WadBuilder { protected File file; protected wadheader_t header; protected List<IWritableDoomObject> lumps; protected DataOutputStream dos; protected FileOutputStream fos; protected int indexpos,totalsize; public WadBuilder(String file,String type) { this.header=new wadheader_t(); this.lumps=new ArrayList<IWritableDoomObject>(); this.header.type=type; this.file=new File(file); this.indexpos=0; } public void start() throws IOException { if (file.exists()) file.delete(); fos=new FileOutputStream(file,false); dos=new DataOutputStream(fos); // This header is only temporary, until we know how many lumps and data we've actually written. header.write(dos); } /** Add a zero-sized marker * * @param lumpname * @throws IOException */ public void add(String lumpname) throws IOException{ filelump_t lump=new filelump_t(lumpname,0,dos.size()); lumps.add(lump); } /** Add and write immediately a raw byte[] bunch of data. * * @param lumpname * @param data * @throws IOException */ public void add(String lumpname, byte[] data) throws IOException{ filelump_t lump=new filelump_t(lumpname,data.length,dos.size()); lumps.add(lump); dos.write(data); } /** Add and write immediately an object implementing the IWritableDoomObject interface. * * @param lumpname * @param stuff * @throws IOException */ public void add(String lumpname, IWritableDoomObject stuff) throws IOException{ long pos=dos.size(); // Save current stream position stuff.write(dos); // The new lump can be created ONLY after the write filelump_t lump=new filelump_t(lumpname,dos.size()-pos,pos); lumps.add(lump); } /** Add and write immediately an array of objects implementing the IWritableDoomObject interface. * Objects will be written contiguously. * * @param lumpname * @param stuff * @throws IOException */ public void add(String lumpname, IWritableDoomObject stuff[]) throws IOException{ long pos=dos.size(); // Save current stream position DoomIO.writeListOfObjects(dos, stuff,stuff.length); // The new lump can be created ONLY after the write filelump_t lump=new filelump_t(lumpname,dos.size()-pos,pos); lumps.add(lump); } /** When called, the WAD index will be written to the file, the header will be * updated for the total number of entries and index position, and the file closed. * * @throws IOException */ public void close() throws IOException{ this.indexpos=dos.size(); // Time to write the index DoomIO.writeListOfObjects(dos, this.lumps, this.lumps.size()); this.totalsize=dos.size(); // Amend header this.header.numentries=lumps.size(); this.header.tablepos=this.indexpos; // Write amended header fos.getChannel().position(0); header.write(dos); // Close streams dos.close(); fos.close(); } /** Directly copy a bunch of lumps from an IWadLoader object to this WAD file. * * @param W * @param lumplist * @throws IOException */ public void add(IWadLoader W,String[] lumplist) throws IOException{ byte[] data; for (String lumpname: lumplist){ data=W.CacheLumpNameAsRawBytes(lumpname, 0); this.add(lumpname,data); } } } package w; public class name8 { private byte[] s; static byte[] ss=new byte[9]; public int[] x; public long hash; public name8(String name){ s=new byte[9]; x=new int[2]; // in case the name was a full 8 chars this.s[8] = 0; byte[] tmp=name.getBytes(); System.arraycopy(tmp, 0, this.s, 0, Math.min(8,tmp.length)); this.x[0]=byteArrayToInt(s,0); this.x[1]=byteArrayToInt(s,4); this.hash=byteArrayToLong(s,0); } /** Returns a 64-bit number that maps directly to the ASCII * 8-bit representation of a fixed-length 8 char string. * It's for all effects and purposes a unique 64-bit hash, and can be used to * speed up comparisons. * * @param name * @return */ public static long getLongHash(String name){ // in case the name was a full 8 chars for (int i=0;i<ss.length;i++){ ss[i]=0; } byte[] tmp=name.getBytes(); // We must effectively limit hashes to 31 bits to be able to use them. System.arraycopy(tmp, 0, ss, 0, Math.min(8,tmp.length)); return byteArrayToLong(ss,0); } public static int getIntName(String name){ // in case the name was a full 8 chars for (int i=0;i<ss.length;i++){ ss[i]=0; } byte[] tmp=name.getBytes(); System.arraycopy(tmp, 0, ss, 0, Math.min(4,tmp.length)); return byteArrayToInt(ss,0); } public static int byteArrayToInt(byte[] src, int ofs){ return (src[ofs]<<24)|(src[ofs+1]<<16)|(src[ofs+2]<<8)|src[ofs+3]; } public static long byteArrayToLong(byte[] src, int ofs){ return (((long)byteArrayToInt(src, 0)<<32)|byteArrayToInt(src, 4)); } /** Probably has horrible performance... * * @param src * @param ofs * @return */ public static int stringToInt(String src, int ofs){ byte[] s=new byte[9]; for (int i=0;i<src.length();i++){ s[i]=(byte) src.charAt(i); } return (s[ofs]<<24)|(s[ofs+1]<<16)|(s[ofs+2]<<8)|s[ofs+3]; } } package w; public interface IReadWriteDoomObject extends IReadableDoomObject, IWritableDoomObject{ } package w; import java.io.IOException; import java.nio.ByteBuffer; /** All objects that can be deserialized from raw byte buffers such as those * read from WAD lumps should implement this method, so that the WadLoader * can cache them, and recursive calls to sub-objects can be made. * * E.g. an object of type A consists of a header and a list of objects of type B. * Calling A.unpack(buf) will cause A to unmarshal its own header, set the list of * B objects, and then call B.unpack() for each of them, by passing the same buffer * along. * * This system works cleanly, and allows to simulate Doom's "cached memory" while * returning proper objects of the correct type and keeping close to Java's * "correct" way of doing things. * * * For example, if a patch_t is read from disk, the WadLoader uses its unpack() method * to read it from a lump read from disk, and creates a new patch_t object, which is placed * in the lump cache (which holds CacheableDoomObject, incidentally). The next time this * same patch_t is requested, the reference to the already cached patch_t will be returned, * if it hasn't been forcedly flushed from the cache. Voila', lump caching! * * The principle can be applied to ARRAYS of similar objects too: using the same buffer, * iterative serial unpacking is possible, while still mantaining a "cached" reference * to their array (TODO: actually, this needs to be implemented more efficiently. Look in * WadLoader) * * The opposite would be a "PackableDoomObject", aka objects that can pack themselves into * a byte buffer for transmission purposes, although Doom doesn't really need to write as * much as it needs reading. * * For the purpose of saving/loading games, which need to read/write to variable disk * structures ALL the time, use the ReadableDoomObject/WritableDoomObject interfaces. * Their difference is that they are highly mutable and supposed to be read from files * or input/output streams, and that a continuous reference to them as deserialized * objects (e.g. in the caching mechanism) is not needed. * * * @author Velktron * */ public interface CacheableDoomObject { public void unpack(ByteBuffer buf) throws IOException ; } package w; public enum statenum_t { NoState(-1), StatCount(0), ShowNextLoc(1); private int value; private statenum_t(int val){ this.value=val; } public int getValue() { return value; } } package w; /* Copyright (C) 1997-2001 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. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ //Created on 24.07.2004 by RST. //$Id: DoomIO.java,v 1.4 2016/06/06 13:45:44 velktron Exp $ import java.io.*; import java.nio.ByteOrder; import java.nio.charset.Charset; import java.util.List; import m.Swap; /** * An extension of RandomAccessFile, which handles readString/WriteString specially * and offers several Doom related (and cross-OS) helper functions for reading/writing * arrays of multiple objects or fixed-length strings from/to disk. * * TO DEVELOPERS: this is the preferrered method of I/O for anything implemented. * In addition, Doomfiles can be passed to objects implementing the IReadableDoomObject * and IWritableDoomObject interfaces, which will "autoread" or "autowrite" themselves * to the implied stream. * * TODO: in the light of greater future portabililty and compatibility in certain * environments, PERHAPS this should have been implemented using Streams. Perhaps * it's possible to change the underlying implementation without (?) changing too * much of the exposed interface, but it's not a priority for me right now. * */ public class DoomIO { private DoomIO(){ } /** Writes a Vector to a RandomAccessFile. */ public static void writeVector(DataOutputStream dos,float v[]) throws IOException { for (int n = 0; n < 3; n++) dos.writeFloat(v[n]); } /** Writes a Vector to a RandomAccessFile. */ public static float[] readVector(DataInputStream dis) throws IOException { float res[] = { 0, 0, 0 }; for (int n = 0; n < 3; n++) res[n] = dis.readFloat(); return res; } /** Reads a length specified string from a file. */ public static final String readString(DataInputStream dis) throws IOException { int len = dis.readInt(); if (len == -1) return null; if (len == 0) return ""; byte bb[] = new byte[len]; dis.read(bb, 0, len); return new String(bb, 0, len,Charset.forName("ISO-8859-1")); } /** MAES: Reads a specified number of bytes from a file into a new String. * With many lengths being implicit, we need to actually take the loader by the hand. * * @param len * @return * @throws IOException */ public final static String readString(DataInputStream dis,int len) throws IOException { if (len == -1) return null; if (len == 0) return ""; byte bb[] = new byte[len]; dis.read(bb, 0, len); return new String(bb, 0, len); } public static String readString(InputStream f,int len) throws IOException { if (len == -1) return null; if (len == 0) return ""; byte bb[] = new byte[len]; f.read(bb, 0, len); return new String(bb, 0, len,Charset.forName("ISO-8859-1")); } /** MAES: Reads a specified number of bytes from a file into a new, NULL TERMINATED String. * With many lengths being implicit, we need to actually take the loader by the hand. * * @param len * @return * @throws IOException */ public static final String readNullTerminatedString(InputStream dis,int len) throws IOException { if (len == -1) return null; if (len == 0) return ""; byte bb[] = new byte[len]; int terminator=len; dis.read(bb, 0, len); for (int i=0;i<bb.length;i++){ if (bb[i]==0) { terminator=i; break; // stop on first null } } // This is the One True Encoding for Doom. return new String(bb, 0, terminator,Charset.forName("ISO-8859-1")); } /** MAES: Reads multiple strings with a specified number of bytes from a file. * If the array is not large enough, only partial reads will occur. * * @param len * @return * @throws IOException */ public static final String[] readMultipleFixedLengthStrings(DataInputStream dis,String[] dest, int num, int len) throws IOException { // Some sanity checks... if (num<=0 || len < 0) return null; if (len == 0) { for (int i=0;i<dest.length;i++){ dest[i]=new String(""); } return dest; } for (int i=0;i<num;i++){ dest[i]=readString(dis,len); } return dest; } /** Writes a length specified string (Pascal style) to a file. * * */ public static void writeString(DataOutputStream dos,String s) { try { if (s == null) { dos.writeInt(-1); return; } dos.writeInt(s.length()); if (s.length() != 0) dos.writeBytes(s); } catch (Exception e){ System.err.println("writeString "+s+" to DoomFile failed!"); } } /** Writes a String with a specified len to a file. * This is useful for fixed-size String fields in * files. Any leftover space will be filled with 0x00s. * * @param s * @param len * @throws IOException */ public static void writeString(DataOutputStream dos,String s,int len) throws IOException { if (s==null) return; if (s.length() != 0){ byte[] dest=s.getBytes("ISO-8859-1"); dos.write(dest,0,Math.min(len,dest.length)); // Fill in with 0s if something's left. if (dest.length<len){ for (int i=0;i<len-dest.length;i++){ dos.write((byte)0x00); } } } } public static void readObjectArray(DataInputStream dis,IReadableDoomObject[] s,int len) throws IOException { if ((s==null)||(len==0)) return; for (int i=0;i<Math.min(len,s.length);i++){ s[i].read(dis); } } public static void readObjectArrayWithReflection(DataInputStream dis,IReadableDoomObject[] s,int len) throws Exception { if (len==0) return; Class<?> c=s.getClass().getComponentType(); for (int i=0;i<Math.min(len,s.length);i++){ if (s[i]==null) s[i]=(IReadableDoomObject) c.newInstance(); s[i].read(dis); } } public static void readObjectArray(DataInputStream dis,IReadableDoomObject[] s,int len, Class<?> c) throws Exception { if ((s==null)||(len==0)) return; for (int i=0;i<Math.min(len,s.length);i++){ if (s[i]==null) { s[i]=(IReadableDoomObject) c.newInstance(); } s[i].read(dis); } } public static final void readIntArray(DataInputStream dis,int[] s,int len, ByteOrder bo) throws IOException { if ((s==null)||(len==0)) return; for (int i=0;i<Math.min(len,s.length);i++){ s[i]=dis.readInt(); if (bo==ByteOrder.LITTLE_ENDIAN){ s[i]=Swap.LONG(s[i]); } } } public static final void readShortArray(DataInputStream dis,short[] s,int len, ByteOrder bo) throws IOException { if ((s==null)||(len==0)) return; for (int i=0;i<Math.min(len,s.length);i++){ s[i]=dis.readShort(); if (bo==ByteOrder.LITTLE_ENDIAN){ s[i]=Swap.SHORT(s[i]); } } } public static final void readIntArray(DataInputStream dis,int[] s,ByteOrder bo) throws IOException { readIntArray(dis,s,s.length,bo); } public static final void readShortArray(DataInputStream dis,short[] s,ByteOrder bo) throws IOException { readShortArray(dis,s,s.length,bo); } public static void readBooleanArray(DataInputStream dis,boolean[] s,int len) throws IOException { if ((s==null)||(len==0)) return; for (int i=0;i<Math.min(len,s.length);i++){ s[i]=dis.readBoolean(); } } /** Reads an array of "int booleans" into an array or * proper booleans. 4 bytes per boolean are used! * * @param s * @param len * @throws IOException */ public final static void readBooleanIntArray(DataInputStream dis,boolean[] s,int len) throws IOException { if ((s==null)||(len==0)) return; for (int i=0;i<Math.min(len,s.length);i++){ s[i]=readIntBoolean(dis); } } public static final void readBooleanIntArray(DataInputStream dis,boolean[] s) throws IOException { readBooleanIntArray(dis,s,s.length); } public static final void writeBoolean(DataOutputStream dos,boolean[] s,int len) throws IOException { if ((s==null)||(len==0)) return; for (int i=0;i<Math.min(len,s.length);i++){ dos.writeBoolean(s[i]); } } public static final void writeObjectArray(DataOutputStream dos,IWritableDoomObject[] s,int len) throws IOException { if ((s==null)||(len==0)) return; for (int i=0;i<Math.min(len,s.length);i++){ s[i].write(dos); } } public static final void writeListOfObjects(DataOutputStream dos,List<IWritableDoomObject> s,int len) throws IOException { if ((s==null)||(len<=0)) return; for (int i=0;i<Math.min(len,s.size());i++){ s.get(i).write(dos); } } public static final void writeListOfObjects(DataOutputStream dos,IWritableDoomObject s[],int len) throws IOException { if ((s==null)||(len<=0)) return; for (int i=0;i<Math.min(len,s.length);i++){ s[i].write(dos); } } public final static void readBooleanArray(DataInputStream dis,boolean[] s) throws IOException { readBooleanArray(dis,s,s.length); } public final static void readIntBooleanArray(DataInputStream dis,boolean[] s) throws IOException { readBooleanIntArray(dis,s,s.length); } public static final void writeCharArray(DataOutputStream dos,char[] charr,int len) throws IOException { if ((charr==null)||(len==0)) return; for (int i=0;i<Math.min(len,charr.length);i++){ dos.writeChar(charr[i]); } } /** Will read an array of proper Unicode chars. * * @param charr * @param len * @throws IOException */ public static final void readCharArray(DataInputStream dis,char[] charr,int len) throws IOException { if ((charr==null)||(len==0)) return; for (int i=0;i<Math.min(len,charr.length);i++){ charr[i]=dis.readChar(); } } /** Will read a bunch of non-unicode chars into a char array. * Useful when dealing with legacy text files. * * @param charr * @param len * @throws IOException */ public static final void readNonUnicodeCharArray(DataInputStream dis,char[] charr,int len) throws IOException { if ((charr==null)||(len==0)) return; for (int i=0;i<Math.min(len,charr.length);i++){ charr[i]=(char) dis.readUnsignedByte(); } } /** Writes an item reference. public void writeItem(gitem_t item) throws IOException { if (item == null) writeInt(-1); else writeInt(item.index); } */ /** Reads the item index and returns the game item. public gitem_t readItem() throws IOException { int ndx = readInt(); if (ndx == -1) return null; else return GameItemList.itemlist[ndx]; } * @throws IOException */ public static final long readUnsignedLEInt(DataInputStream dis) throws IOException{ int tmp=dis.readInt(); return 0xFFFFFFFFL&Swap.LONG(tmp); } public static final int readLEInt(DataInputStream dis) throws IOException{ int tmp=dis.readInt(); return Swap.LONG(tmp); } public static final int readLEInt(InputStream dis) throws IOException{ int tmp=new DataInputStream(dis).readInt(); return Swap.LONG(tmp); } public static final void writeLEInt(DataOutputStream dos,int value) throws IOException{ dos.writeInt(Swap.LONG(value)); } // 2-byte number public static int SHORT_little_endian_TO_big_endian(int i) { return ((i>>8)&0xff)+((i << 8)&0xff00); } // 4-byte number public static int INT_little_endian_TO_big_endian(int i) { return((i&0xff)<<24)+((i&0xff00)<<8)+((i&0xff0000)>>8)+((i>>24)&0xff); } public static final short readLEShort(DataInputStream dis) throws IOException { short tmp=dis.readShort(); return Swap.SHORT(tmp); } /** Reads a "big boolean" using 4 bytes. * * @return * @throws IOException */ public static final boolean readIntBoolean(DataInputStream dis) throws IOException { return (dis.readInt()!=0); } } package w; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; public class wadinfo_t implements IReadableDoomObject,IWritableDoomObject{ // Should be "IWAD" or "PWAD". String identification; long numlumps; long infotableofs; /** Reads the wadinfo_t from the file.*/ public void read(DataInputStream f) throws IOException { identification = DoomIO.readString(f,4); numlumps=DoomIO.readUnsignedLEInt(f); infotableofs=DoomIO.readUnsignedLEInt(f); } @Override public void write(DataOutputStream dos) throws IOException { DoomIO.writeString(dos, identification, 4); DoomIO.writeLEInt(dos, (int)numlumps); DoomIO.writeLEInt(dos, (int)infotableofs); } } package w; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; /** filelumps are on-disk structures. lumpinfos are almost the same, but are memory only. * * @author Maes * */ public class filelump_t implements IReadableDoomObject, IWritableDoomObject { public long filepos; public long size; // Is INT 32-bit in file! public String name; // Whatever appears inside the wadfile public String actualname; // Sanitized name, e.g. after compression markers public boolean big_endian=false; // E.g. Jaguar public boolean compressed=false; // Compressed lump public filelump_t(String name, long size, long filepos) { this.filepos=filepos; this.size=size; this.name=name; } public filelump_t() { } public void read(DataInputStream f) throws IOException{ // MAES: Byte Buffers actually make it convenient changing byte order on-the-fly. // But RandomAccessFiles (and inputsteams) don't :-S if (!big_endian){ filepos=DoomIO.readUnsignedLEInt(f); size=DoomIO.readUnsignedLEInt(f); } else { filepos=f.readInt(); size=f.readInt(); } // Names used in the reading subsystem should be upper case, // but check for compressed status first name=DoomIO.readNullTerminatedString(f,8); char[] stuff= name.toCharArray(); // It's a compressed lump if (stuff[0] > 0x7F) { this.compressed=true; stuff[0]&=0x7F; } actualname=new String(stuff).toUpperCase(); } public static int sizeof(){ return (4+4+8); } @Override public void write(DataOutputStream dos) throws IOException { if (!big_endian){ DoomIO.writeLEInt(dos, (int) filepos); DoomIO.writeLEInt(dos, (int) size); } else { dos.writeInt((int) filepos); dos.writeInt((int) size); } DoomIO.writeString(dos, name, 8); } } // Emacs style mode select -*- C++ -*- // ----------------------------------------------------------------------------- // // $Id: WadLoader.java,v 1.66 2016/07/04 13:50:50 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: // Handles WAD file header, directory, lump I/O. // // ----------------------------------------------------------------------------- package w; import java.io.BufferedInputStream; import java.io.ByteArrayInputStream; import java.io.DataInputStream; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Hashtable; import java.util.List; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; import rr.patch_t; import utils.C2JUtils; import static data.Defines.*; import i.*; public class WadLoader implements IWadLoader { //// FIELDS /** DoomSystem object */ protected IDoomSystem I; /** Location of each lump on disk. */ public lumpinfo_t[] lumpinfo; public int numlumps; /** * MAES: probably array of byte[]??? void** lumpcache; * * Actually, loaded objects will be deserialized here as the general type * "CacheableDoomObject" (in the worst case they will be byte[] or * ByteBuffer). * * Not to brag, but this system is FAR superior to the inline unmarshaling * used in other projects ;-) */ private CacheableDoomObject[] lumpcache; private boolean[] preloaded; /** Added for Boom compliance */ private List<wadfile_info_t> wadfiles; /** * #define strcmpi strcasecmp MAES: this is just capitalization. However we * can't manipulate String object in Java directly like this, so this must * be a return type. * * TODO: maybe move this in utils? */ public String strupr(String s) { return s.toUpperCase(); } /* ditto */ public void strupr(char[] s) { for (int i = 0; i < s.length; i++) { s[i] = Character.toUpperCase(s[i]); } } int reloadlump; /** Offset inside an input stream to allow us to read lumps as "wad archives" or "WADs withing WADs". The catch is that * each such lump will have to be handled with its own reader. * */ int wadarchive_offset; /** Standard constructor * * @param I */ public WadLoader(IDoomSystem I) { this(); this.I = I; } /** A default constructor with a built-in dummy system */ public WadLoader() { lumpinfo = new lumpinfo_t[0]; zone= new Hashtable<CacheableDoomObject, Integer>(); wadfiles=new ArrayList<wadfile_info_t>(); this.I=new DummySystem(); } /** A simplified constructor for opening a single local file without coalescing * * @param wadfile */ public WadLoader(String wadfile) { this(); try { this.InitFile(wadfile,false); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } // MAES: was char* String reloadname; /** * W_AddFile: * * This is where lumps are actually read + loaded from a file. * * All files are optional, but at least one file must be * found (PWAD, if all required lumps are present). * Files with a .wad extension are wadlink files * with multiple lumps. * Other files are single lumps with the base filename * for the lump name. * * If filename starts with a tilde, the file is handled * specially to allow map reloads. * But: the reload feature is a fragile hack... * * @param uri * @param entry * @throws type */ private void AddFile(String uri,ZipEntry entry,int type) throws Exception { wadinfo_t header = new wadinfo_t(); int lump_p; // MAES: was lumpinfo_t* , but we can use it as an array // pointer. InputStream handle,storehandle; long length; int startlump; filelump_t[] fileinfo = new filelump_t[1]; // MAES: was * filelump_t singleinfo = new filelump_t(); // handle reload indicator. if (uri.charAt(0) == '~') { uri = uri.substring(1); reloadname = uri; reloadlump = numlumps; } // open the resource and add to directory // It can be any streamed type handled by the "sugar" utilities. try { handle = InputStreamSugar.createInputStreamFromURI(uri,entry,type); } catch (Exception e) { I.Error(" couldn't open resource %s \n", uri); return; } // Create and set wadfile info wadfile_info_t wadinfo=new wadfile_info_t(); wadinfo.handle= handle; wadinfo.name=uri; wadinfo.entry=entry; wadinfo.type=type; // System.out.println(" adding " + filename + "\n"); // We start at the number of lumps. This allows appending stuff. startlump = this.numlumps; String checkname=(wadinfo.entry!=null?wadinfo.entry.getName():uri); // If not "WAD" then we check for single lumps. if (!C2JUtils.checkForExtension(checkname,"wad")) { fileinfo[0] = singleinfo; singleinfo.filepos = 0; singleinfo.size = InputStreamSugar.getSizeEstimate(handle,wadinfo.entry); // Single lumps. Only use 8 characters singleinfo.actualname=singleinfo.name = C2JUtils.removeExtension(uri).toUpperCase(); // MAES: check out certain known types of extension if (C2JUtils.checkForExtension(uri,"lmp")) wadinfo.src=wad_source_t.source_lmp; else if (C2JUtils.checkForExtension(uri,"deh")) wadinfo.src=wad_source_t.source_deh; else if (C2JUtils.checkForExtension(uri,null)) wadinfo.src=wad_source_t.source_deh; numlumps++; } else { // MAES: 14/06/10 this is historical, for this is the first time I // implement reading something from RAF into Doom's structs. // Kudos to the JAKE2 team who solved this problem before me. // MAES: 25/10/11: In retrospect, this solution, while functional, was // inelegant and limited. DataInputStream dis=new DataInputStream(handle); // Read header in one go. Usually doesn't cause trouble? header.read(dis); if (header.identification.compareTo("IWAD") != 0) { // Homebrew levels? if (header.identification.compareTo("PWAD") != 0) { I.Error("Wad file %s doesn't have IWAD or PWAD id\n",checkname); } else wadinfo.src=wad_source_t.source_pwad; // modifiedgame = true; } else wadinfo.src=wad_source_t.source_iwad; length = header.numlumps; // Init everything: fileinfo = C2JUtils.createArrayOfObjects(filelump_t.class,(int)length); dis.close(); handle=InputStreamSugar.streamSeek(handle,header.infotableofs,wadinfo.maxsize,uri,entry,type); // FIX: sometimes reading from zip files doesn't work well, so we pre-cache the TOC byte[] TOC=new byte[(int) (length*filelump_t.sizeof())]; int read=0; while (read<TOC.length){ // Make sure we have all of the TOC, sometimes ZipInputStream "misses" bytes. // when wrapped. read+=handle.read(TOC,read,TOC.length-read); } ByteArrayInputStream bais=new ByteArrayInputStream(TOC); // MAES: we can't read raw structs here, and even less BLOCKS of // structs. dis=new DataInputStream(bais); DoomIO.readObjectArray(dis,fileinfo, (int) length); numlumps += header.numlumps; wadinfo.maxsize=estimateWadSize(header,lumpinfo); } // end loading wad // At this point, a WADFILE or LUMPFILE been successfully loaded, // and so is added to the list this.wadfiles.add(wadinfo); // Fill in lumpinfo // MAES: this was a realloc(lumpinfo, numlumps*sizeof(lumpinfo_t)), // so we have to increase size and copy over. Maybe this should be // an ArrayList? int oldsize = lumpinfo.length; lumpinfo_t[] newlumpinfo = new lumpinfo_t[numlumps]; try { C2JUtils.initArrayOfObjects(newlumpinfo, lumpinfo_t.class); System.arraycopy(lumpinfo, 0, newlumpinfo, 0, oldsize); } catch (Exception e) { // if (!lumpinfo) I.Error("Couldn't realloc lumpinfo"); } // Bye bye, old lumpinfo! lumpinfo = newlumpinfo; // MAES: lump_p was an alias for lumpinfo[startlump]. I know it's a // bit crude as an approximation but heh... lump_p = startlump; // MAES: if reloadname is null, handle is stored...else an invalid // handle? storehandle = (reloadname != null) ? null : handle; // This iterates through single files. int fileinfo_p = 0; for (int i = startlump; i < numlumps; i++, lump_p++, fileinfo_p++) { lumpinfo[lump_p].handle = storehandle; lumpinfo[lump_p].position = fileinfo[fileinfo_p].filepos; lumpinfo[lump_p].size = fileinfo[fileinfo_p].size; // Make all lump names uppercase. Searches should also be uppercase only. lumpinfo[lump_p].name = fileinfo[fileinfo_p].name.toUpperCase(); lumpinfo[lump_p].hash =lumpinfo[lump_p].name.hashCode(); // lumpinfo[lump_p].stringhash = name8.getLongHash(strupr(lumpinfo[lump_p].name)); // LumpNameHash(lumpinfo[lump_p].name); lumpinfo[lump_p].intname = name8.getIntName(strupr(lumpinfo[lump_p].name)); //System.out.println(lumpinfo[lump_p]); lumpinfo[lump_p].wadfile=wadinfo; // MAES: Add Boom provenience info } if (reloadname != null) handle.close(); } /** Try to guess a realistic wad size limit based only on the number of lumps and their * STATED contents, in case it's not possible to get an accurate stream size otherwise. * Of course, they may be way off with deliberately malformed files etc. * * @param header * @param lumpinfo2 * @return */ private long estimateWadSize(wadinfo_t header, lumpinfo_t[] lumpinfo) { long maxsize=header.infotableofs+header.numlumps*16; for (int i=0;i<lumpinfo.length;i++){ if ((lumpinfo[i].position+lumpinfo[i].size) >maxsize){ maxsize=lumpinfo[i].position+lumpinfo[i].size; } } return maxsize; } /* (non-Javadoc) * @see w.IWadLoader#Reload() */ @SuppressWarnings("null") public void Reload() throws Exception { wadinfo_t header = new wadinfo_t(); int lumpcount; int lump_p; // Maes: same as in W_WADload int i; DataInputStream handle = null; int length; filelump_t[] fileinfo; if (reloadname == null) return; try { handle = new DataInputStream(new BufferedInputStream(new FileInputStream(reloadname))); } catch (Exception e) { I.Error("W_Reload: couldn't open %s", reloadname); } header.read(handle); // Actual number of lumps in file... lumpcount = (int) header.numlumps; header.infotableofs = header.infotableofs; length = lumpcount; fileinfo = new filelump_t[length]; handle.reset(); handle.skip(header.infotableofs); // MAES: we can't read raw structs here, and even less BLOCKS of // structs. DoomIO.readObjectArrayWithReflection(handle,fileinfo, (int) length); /* * for (int j=0;j<length;j++){ fileinfo[j].load (handle); } */ // numlumps += header.numlumps; // read (handle, fileinfo, length); // Fill in lumpinfo lump_p = reloadlump; int fileinfo_p = 0; for (i = reloadlump; i < reloadlump + lumpcount; i++, lump_p++, fileinfo_p++) { if (lumpcache[i] != null) { // That's like "freeing" it, right? lumpcache[i] = null; preloaded[i] = false; } lumpinfo[lump_p].position = fileinfo[fileinfo_p].filepos; lumpinfo[lump_p].size = fileinfo[fileinfo_p].size; } } /* (non-Javadoc) * @see w.IWadLoader#InitMultipleFiles(java.lang.String[]) */ public void InitMultipleFiles(String[] filenames,boolean coalesce) throws Exception { int size; // open all the files, load headers, and count lumps numlumps = 0; // will be realloced as lumps are added lumpinfo = new lumpinfo_t[0]; for (String s : filenames) { if (s != null){ if (C2JUtils.testReadAccess(s)) { // Resource is readable, guess type. int type=C2JUtils.guessResourceType(s); if (C2JUtils.flags(type,InputStreamSugar.ZIP_FILE)){ addZipFile(s, type); } else { this.AddFile(s,null, type); } System.out.printf("\tadded %s (zipped: %s network: %s)\n",s, C2JUtils.flags(type, InputStreamSugar.ZIP_FILE), C2JUtils.flags(type, InputStreamSugar.NETWORK_FILE)); } else System.err.printf("Couldn't open resource %s\n",s); } } if (numlumps == 0) I.Error("W_InitFiles: no files found"); if (coalesce) { CoalesceMarkedResource("S_START", "S_END", li_namespace.ns_sprites); CoalesceMarkedResource("F_START", "F_END", li_namespace.ns_flats); // CoalesceMarkedResource("P_START", "P_END", li_namespace.ns_flats); } // set up caching size = numlumps; lumpcache = new CacheableDoomObject[size]; preloaded = new boolean[size]; if (lumpcache == null) I.Error("Couldn't allocate lumpcache"); this.InitLumpHash(); } /** * @param s * @param type * @throws IOException * @throws Exception */ protected void addZipFile(String s, int type) throws IOException, Exception { // Get entries BufferedInputStream is=new BufferedInputStream( InputStreamSugar.createInputStreamFromURI(s, null, type) ); ZipInputStream zip=new ZipInputStream(is); List<ZipEntry> zes=InputStreamSugar.getAllEntries(zip); zip.close(); for (ZipEntry zz:zes){ // The name of a zip file will be used as an identifier if (!zz.isDirectory()) this.AddFile(s,zz, type); } } public void InitFile(String filename,boolean coalesce) throws Exception { String[] names = new String[1]; names[0] = filename; // names[1] = null; InitMultipleFiles(names,coalesce); } /* (non-Javadoc) * @see w.IWadLoader#NumLumps() */ public final int NumLumps() { return numlumps; } /** * W_CheckNumForName2 Returns -1 if name not found. * * A slightly better implementation, uses string hashes * as direct comparators (though 64-bit long descriptors * could be used). It's faster than the old method, but * still short from the Hashtable's performance by * an order of magnitude. * * @param name * @return * * UNUSED public int CheckNumForName2(String name) { // scan backwards so patch lump files take precedence int lump_p = numlumps; // make the name into two integers for easy compares // case insensitive long hash = name8.getLongHash(name); // System.out.print("Looking for "+name + " with hash " // +Long.toHexString(hash)); while (lump_p-- != 0) if (lumpinfo[lump_p].stringhash == hash) { // System.out.print(" found "+lumpinfo[lump_p]+"\n" ); return lump_p; } // TFB. Not found. return -1; } */ /** * Old, shitty method for CheckNumForName. It's an overly literal * translation of how the C original worked, which was none too good * even without the overhead of converting a string to * its integer representation. It's so bad, that it's two orders * of magnitude slower than a Hashtable implemetation, and one from * a direct hash/longname comparison with linear search. * * @param name * @return * public int CheckNumForName3(String name) { int v1; int v2; // lumpinfo_t lump_p; int lump_p; // make the name into two integers for easy compares // case insensitive name8 union = new name8(strupr(name)); v1 = union.x[0]; v2 = union.x[1]; // scan backwards so patch lump files take precedence lump_p = numlumps; while (lump_p-- != 0) { int a = name8.stringToInt(lumpinfo[lump_p].name, 0); int b = name8.stringToInt(lumpinfo[lump_p].name, 4); if ((a == v1) && (b == v2)) { return lump_p; } } // TFB. Not found. return -1; } */ /* (non-Javadoc) * @see w.IWadLoader#GetLumpinfoForName(java.lang.String) */ public lumpinfo_t GetLumpinfoForName(String name) { int v1; int v2; // lumpinfo_t lump_p; int lump_p; // make the name into two integers for easy compares // case insensitive name8 union = new name8(strupr(name)); v1 = union.x[0]; v2 = union.x[1]; // scan backwards so patch lump files take precedence lump_p = numlumps; while (lump_p-- != 0) { int a = name8.stringToInt(lumpinfo[lump_p].name, 0); int b = name8.stringToInt(lumpinfo[lump_p].name, 4); if ((a == v1) && (b == v2)) { return lumpinfo[lump_p]; } } // TFB. Not found. return null; } /* (non-Javadoc) * @see w.IWadLoader#GetNumForName(java.lang.String) */ public int GetNumForName(String name) { int i; i = CheckNumForName(name.toUpperCase()); if (i == -1) { Exception e = new Exception(); e.printStackTrace(); System.err.println("Error:" + name + "not found"); System.err.println("Hash:" + Long.toHexString(name8.getLongHash(name))); I.Error("W_GetNumForName: %s not found!", name); } return i; } /* (non-Javadoc) * @see w.IWadLoader#GetNameForNum(int) */ public String GetNameForNum(int lumpnum) { if (lumpnum>=0 && lumpnum<this.numlumps){ return this.lumpinfo[lumpnum].name; } return null; } // // W_LumpLength // Returns the buffer size needed to load the given lump. // /* (non-Javadoc) * @see w.IWadLoader#LumpLength(int) */ public int LumpLength(int lump) { if (lump >= numlumps) I.Error("W_LumpLength: %i >= numlumps", lump); return (int) lumpinfo[lump].size; } @Override public final byte[] ReadLump(int lump){ lumpinfo_t l=lumpinfo[lump]; byte[] buf=new byte[(int) l.size]; ReadLump(lump, buf,0); return buf; } @Override public final void ReadLump(int lump, byte[] buf) { ReadLump(lump, buf, 0); } /** * W_ReadLump Loads the lump into the given buffer, which must be >= * W_LumpLength(). SKIPS CACHING * * @throws IOException */ @Override public final void ReadLump(int lump, byte[] buf, int offset) { int c=0; lumpinfo_t l; InputStream handle = null; if (lump >= this.numlumps) { I.Error("W_ReadLump: %i >= numlumps", lump); return; } l = lumpinfo[lump]; if (l.handle == null) { // reloadable file, so use open / read / close try { // FIXME: reloadable files can only be that. Files. handle = InputStreamSugar.createInputStreamFromURI(this.reloadname,null,0); } catch (Exception e) { e.printStackTrace(); I.Error("W_ReadLump: couldn't open %s", reloadname); } } else handle = l.handle; try { handle=InputStreamSugar.streamSeek(handle,l.position, l.wadfile.maxsize,l.wadfile.name,l.wadfile.entry,l.wadfile.type); // read buffered. Unfortunately that interferes badly with // guesstimating the actual stream position. BufferedInputStream bis=new BufferedInputStream(handle,8192); while (c<l.size) c+= bis.read(buf,offset+c, (int) (l.size-c)); // Well, that's a no-brainer. //l.wadfile.knownpos=l.position+c; if (c < l.size) System.err.printf("W_ReadLump: only read %d of %d on lump %d %d\n", c, l.size, lump,l.position); if (l.handle == null) handle.close(); else l.handle=handle; I.BeginRead (); return; // ??? I_EndRead (); } catch (Exception e) { e.printStackTrace(); I.Error("W_ReadLump: could not read lump " + lump); e.printStackTrace(); return; } } /** The most basic of the Wadloader functions. Will attempt to read a lump * off disk, based on the specific class type (it will call the unpack() * method). If not possible to call the unpack method, it will leave a * DoomBuffer object in its place, with the raw byte contents. It's * * */ @SuppressWarnings("unchecked") public <T> T CacheLumpNum(int lump, int tag, Class<T> what) { if (lump >= numlumps) { I.Error("W_CacheLumpNum: %i >= numlumps", lump); } // Nothing cached here... // SPECIAL case : if no class is specified (null), the lump is re-read anyway // and you get a raw doombuffer. Plus, it won't be cached. if ((lumpcache[lump] == null)||(what==null)) { // read the lump in // System.out.println("cache miss on lump "+lump); // Fake Zone system: mark this particular lump with the tag specified // ptr = Z_Malloc (W_LumpLength (lump), tag, &lumpcache[lump]); // Read as a byte buffer anyway. ByteBuffer thebuffer = ByteBuffer.wrap(ReadLump(lump)); // Class type specified if (what != null) { try { // Can it be uncached? If so, deserialize it. if (implementsInterface(what, w.CacheableDoomObject.class)) { // MAES: this should be done whenever single lumps // are read. DO NOT DELEGATE TO THE READ OBJECTS THEMSELVES. // In case of sequential reads of similar objects, use // CacheLumpNumIntoArray instead. thebuffer.rewind(); lumpcache[lump] = (CacheableDoomObject) what.newInstance(); ((CacheableDoomObject) lumpcache[lump]).unpack((ByteBuffer) thebuffer); // Track it for freeing Track(lumpcache[lump],lump); if (what == patch_t.class) { ((patch_t) lumpcache[lump]).name = this.lumpinfo[lump].name; } } else { // replace lump with parsed object. lumpcache[lump] = (CacheableDoomObject) thebuffer; // Track it for freeing Track((CacheableDoomObject)thebuffer,lump); } } catch (Exception e) { System.err.println("Could not auto-instantiate lump " + lump + " of class " + what); e.printStackTrace(); } } else { // Class not specified? Then gimme a containing DoomBuffer! DoomBuffer db = new DoomBuffer(thebuffer); lumpcache[lump] = db; } } else { // System.out.println("cache hit on lump " + lump); // Z.ChangeTag (lumpcache[lump],tag); } return (T) lumpcache[lump]; } /** A very useful method when you need to load a lump which can consist * of an arbitrary number of smaller fixed-size objects (assuming that you * know their number/size and the size of the lump). Practically used * by the level loader, to handle loading of sectors, segs, things, etc. * since their size/lump/number relationship is well-defined. * * It possible to do this in other ways, but it's extremely convenient this way. * * MAES 24/8/2011: This method is deprecated, Use the much more convenient * and slipstreamed generic version, which also handles caching of arrays * and auto-allocation. * * @param lump The lump number to load. * @param tag Caching tag * @param array The array with objects to load. Its size implies how many to read. * @return */ @Deprecated public void CacheLumpNumIntoArray(int lump, int tag, Object[] array, Class what) throws IOException { if (lump >= numlumps) { I.Error("W_CacheLumpNum: %i >= numlumps", lump); } // Nothing cached here... if ((lumpcache[lump] == null)) { // read the lump in //System.out.println("cache miss on lump " + lump); // Read as a byte buffer anyway. ByteBuffer thebuffer = ByteBuffer.wrap(ReadLump(lump)); // Store the buffer anyway (as a DoomBuffer) lumpcache[lump] = new DoomBuffer(thebuffer); // Track it (as ONE lump) Track(lumpcache[lump],lump); } else { //System.out.println("cache hit on lump " + lump); // Z.ChangeTag (lumpcache[lump],tag); } // Class type specified. If the previously cached stuff is a // "DoomBuffer" we can go on. if ((what != null) && (lumpcache[lump].getClass() == DoomBuffer.class)) { try { // Can it be uncached? If so, deserialize it. FOR EVERY OBJECT. ByteBuffer b = ((DoomBuffer) (lumpcache[lump])).getBuffer(); b.rewind(); for (int i = 0; i < array.length; i++) { if (implementsInterface(what, w.CacheableDoomObject.class)) { ((CacheableDoomObject) array[i]).unpack(b); } } // lumpcache[lump]=array; } catch (Exception e) { System.err.println("Could not auto-unpack lump " + lump + " into an array of objects of class " + what); e.printStackTrace(); } } return; } /** A very useful method when you need to load a lump which can consist * of an arbitrary number of smaller fixed-size objects (assuming that you * know their number/size and the size of the lump). Practically used * by the level loader, to handle loading of sectors, segs, things, etc. * since their size/lump/number relationship is well-defined. * * It possible to do this in other (more verbose) ways, but it's * extremely convenient this way, as a lot of common and repetitive code * is only written once, and generically, here. Trumps the older * method in v 1.43 of WadLoader, which is deprecated. * * @param lump The lump number to load. * @param num number of objects to read * * @return a properly sized array of the correct type. */ public <T extends CacheableDoomObject> T[] CacheLumpNumIntoArray(int lump, int num, Class<T> what){ if (lump >= numlumps) { I.Error("CacheLumpNumIntoArray: %i >= numlumps", lump); } if (!implementsInterface(what, CacheableDoomObject.class)){ I.Error("CacheLumpNumIntoArray: %s does not implement CacheableDoomObject", what.getName()); } // Nothing cached here... if ((lumpcache[lump] == null)&&(what!=null)) { //System.out.println("cache miss on lump " + lump); // Read as a byte buffer anyway. ByteBuffer thebuffer = ByteBuffer.wrap(ReadLump(lump)); T[] stuff=(T[]) C2JUtils.createArrayOfObjects(what, num); // Store the buffer anyway (as a CacheableDoomObjectContainer) lumpcache[lump] = new CacheableDoomObjectContainer<T>(stuff); // Auto-unpack it, if possible. try { thebuffer.rewind(); lumpcache[lump].unpack(thebuffer); } catch (Exception e) { System.err.println("Could not auto-unpack lump " + lump + " into an array of objects of class " + what); e.printStackTrace(); } // Track it (as ONE lump) Track(lumpcache[lump],lump); } else { //System.out.println("cache hit on lump " + lump); // Z.ChangeTag (lumpcache[lump],tag); } if (lumpcache[lump]==null) return null; return (T[]) ((CacheableDoomObjectContainer<T>)lumpcache[lump]).getStuff(); } public CacheableDoomObject CacheLumpNum(int lump) { return lumpcache[lump]; } /** Tells us if a class implements a certain interface. * If you know of a better way, be my guest. * * @param what * @param which * @return */ protected boolean implementsInterface(Class<?> what, Class<?> which) { Class<?>[] shit = what.getInterfaces(); for (int i = 0; i < shit.length; i++) { if (shit[i].equals(which)) return true; } return false; } /* (non-Javadoc) * @see w.IWadLoader#CacheLumpNameAsRawBytes(java.lang.String, int) */ public byte[] CacheLumpNameAsRawBytes(String name, int tag) { return ((DoomBuffer) this.CacheLumpNum(this.GetNumForName(name), tag, null)).getBuffer().array(); } /* (non-Javadoc) * @see w.IWadLoader#CacheLumpNumAsRawBytes(int, int) */ public byte[] CacheLumpNumAsRawBytes(int num, int tag) { return ((DoomBuffer) this.CacheLumpNum(num, tag, null)).getBuffer().array(); } /* (non-Javadoc) * @see w.IWadLoader#CacheLumpName(java.lang.String, int) */ public DoomBuffer CacheLumpName(String name, int tag) { return (DoomBuffer) this.CacheLumpNum(this.GetNumForName(name), tag, DoomBuffer.class); } public DoomBuffer CacheLumpNumAsDoomBuffer(int lump) { return (DoomBuffer) this.CacheLumpNum(lump, 0, DoomBuffer.class); } /* (non-Javadoc) * @see w.IWadLoader#CachePatchName(java.lang.String) */ public patch_t CachePatchName(String name) { return (patch_t) this.CacheLumpNum(this.GetNumForName(name), PU_CACHE, patch_t.class); } /* (non-Javadoc) * @see w.IWadLoader#CachePatchName(java.lang.String, int) */ public patch_t CachePatchName(String name, int tag) { return (patch_t) this.CacheLumpNum(this.GetNumForName(name), tag, patch_t.class); } /* (non-Javadoc) * @see w.IWadLoader#CachePatchNum(int, int) */ public patch_t CachePatchNum(int num) { return (patch_t) this.CacheLumpNum(num, PU_CACHE, patch_t.class); } /* (non-Javadoc) * @see w.IWadLoader#CacheLumpName(java.lang.String, int, java.lang.Class) */ public <T extends CacheableDoomObject> T CacheLumpName(String name, int tag, Class<T> what) { return this.CacheLumpNum(this.GetNumForName(name.toUpperCase()), tag, what); } // // W_Profile // /* USELESS char[][] info = new char[2500][10]; int profilecount; void Profile() throws IOException { int i; // memblock_t block = null; Object ptr; char ch; FileWriter f; int j; String name; for (i = 0; i < numlumps; i++) { ptr = lumpcache[i]; if ((ptr == null)) { ch = ' '; continue; } else { // block = (memblock_t *) ( (byte *)ptr - sizeof(memblock_t)); if (block.tag < PU_PURGELEVEL) ch = 'S'; else ch = 'P'; } info[i][profilecount] = ch; } profilecount++; f = new FileWriter(new File("waddump.txt")); // name[8] = 0; for (i = 0; i < numlumps; i++) { name = lumpinfo[i].name; f.write(name); for (j = 0; j < profilecount; j++) f.write(" " + info[i][j]); f.write("\n"); } f.close(); } */ /* (non-Javadoc) * @see w.IWadLoader#isLumpMarker(int) */ public boolean isLumpMarker(int lump){ return (lumpinfo[lump].size==0); } /* (non-Javadoc) * @see w.IWadLoader#GetNameForLump(int) */ public String GetNameForLump(int lump){ return lumpinfo[lump].name; } // /////////////////// HASHTABLE SYSTEM /////////////////// // // killough 1/31/98: Initialize lump hash table // /** * Maes 12/12/2010: Some credit must go to Killough for first * Introducing the hashtable system into Boom. On early releases I had * copied his implementation, but it proved troublesome later on and slower * than just using the language's built-in hash table. Lesson learned, kids: * don't reinvent the wheel. * * TO get an idea of how superior using a hashtable is, on 1000000 random * lump searches the original takes 48 seconds, searching for precomputed * hashes takes 2.84, and using a Hashtable takes 0.2 sec. * * And the best part is that Java provides a perfectly reasonable implementation. * */ Hashtable<String, Integer> doomhash; protected void InitLumpHash() { doomhash = new Hashtable<String, Integer>(numlumps); //for (int i = 0; i < numlumps; i++) // lumpinfo[i].index = -1; // mark slots empty // Insert nodes to the beginning of each chain, in first-to-last // lump order, so that the last lump of a given name appears first // in any chain, observing pwad ordering rules. killough for (int i = 0; i < numlumps; i++) { // hash function: doomhash.put(lumpinfo[i].name.toUpperCase(), new Integer(i)); } } /* (non-Javadoc) * @see w.IWadLoader#CheckNumForName(java.lang.String) */ public int CheckNumForName(String name/* , int namespace */) { Integer r = doomhash.get(name); // System.out.print("Found "+r); if (r != null) return r.intValue(); // System.out.print(" found "+lumpinfo[i]+"\n" ); return -1; } /* (non-Javadoc) * @see w.IWadLoader#CheckNumForName(java.lang.String) */ public int[] CheckNumsForName(String name) { list.clear(); // Dumb search, no chained hashtables I'm afraid :-/ // Move backwards, so list is compiled with more recent ones first. for (int i=numlumps-1;i>=0;i--){ if (name.compareToIgnoreCase(lumpinfo[i].name)==0) list.add(i); } final int num=list.size(); int[] result=new int[num]; for (int i=0;i<num;i++){ result[i]=list.get(i); } // Might be empty/null, so check that out. return result; } private final ArrayList<Integer> list=new ArrayList<Integer>(); @Override public lumpinfo_t GetLumpInfo(int i) { return this.lumpinfo[i]; } @Override public void CloseAllHandles(){ ArrayList<InputStream> d=new ArrayList<InputStream>(); for (int i=0;i<this.lumpinfo.length;i++){ if (!d.contains(lumpinfo[i].handle)) d.add(lumpinfo[i].handle); } int count=0; for (InputStream e:d){ try { e.close(); //System.err.printf("%s file handle closed",e.toString()); count++; } catch (IOException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } } //System.err.printf("%d file handles closed",count); } @Override public void finalize(){ CloseAllHandles(); } public static final int ns_global=0; public static final int ns_flats=1; public static final int ns_sprites=2; /** * Based on Boom's W_CoalesceMarkedResource * Sort of mashes similar namespaces together so that they form * a continuous space (single start and end, e.g. so that multiple * S_START and S_END as well as special DEUTEX lumps mash together * under a common S_START/S_END boundary). Also also sort of performs * a "bubbling down" of marked lumps at the end of the namespace. * * It's convenient for sprites, but can be replaced by alternatives * for flats. * * killough 4/17/98: add namespace tags * * @param start_marker * @param end_marker * @param namespace * @return */ public int CoalesceMarkedResource(String start_marker, String end_marker, li_namespace namespace) { int result = 0; lumpinfo_t[] marked = new lumpinfo_t[numlumps]; // C2JUtils.initArrayOfObjects(marked, lumpinfo_t.class); int num_marked = 0, num_unmarked = 0; boolean is_marked = false, mark_end = false; lumpinfo_t lump; // Scan for specified start mark for (int i=0;i<numlumps;i++){ lump=lumpinfo[i]; if (IsMarker(start_marker,lump.name)) // start marker found { // If this is the first start marker, add start marker to marked lumps // System.err.printf("%s identified as starter mark for %s index %d\n",lump.name, // start_marker,i); if (num_marked==0) { marked[num_marked]=new lumpinfo_t(); marked[num_marked].name=new String(start_marker); marked[num_marked].size = 0; // killough 3/20/98: force size to be 0 marked[num_marked].namespace =li_namespace.ns_global; // killough 4/17/98 marked[num_marked].handle=lump.handle; // No real use for this yet marked[num_marked].wadfile = lump.wadfile; num_marked = 1; //System.err.printf("%s identified as FIRST starter mark for %s index %d\n",lump.name, // start_marker,i); } is_marked = true; // start marking lumps } else if (IsMarker(end_marker, lump.name)) // end marker found { // System.err.printf("%s identified as end mark for %s index %d\n",lump.name, // end_marker,i); mark_end = true; // add end marker below is_marked = false; // stop marking lumps } else if (is_marked || lump.namespace == namespace) { // if we are marking lumps, // move lump to marked list // sf: check for namespace already set // sf 26/10/99: // ignore sprite lumps smaller than 8 bytes (the smallest possible) // in size -- this was used by some dmadds wads // as an 'empty' graphics resource if(namespace != li_namespace.ns_sprites || lump.size > 8) { marked[num_marked] = lump.clone(); // System.err.printf("Marked %s as %d for %s\n",lump.name,num_marked,namespace); marked[num_marked++].namespace = namespace; // killough 4/17/98 result++; } } else lumpinfo[num_unmarked++] = lump.clone(); // else move down THIS list } // Append marked list to end of unmarked list System.arraycopy(marked, 0, lumpinfo, num_unmarked, num_marked); numlumps = num_unmarked + num_marked; // new total number of lumps if (mark_end) // add end marker { lumpinfo[numlumps].size = 0; // killough 3/20/98: force size to be 0 //lumpinfo[numlumps].wadfile = NULL; lumpinfo[numlumps].namespace = li_namespace.ns_global; // killough 4/17/98 lumpinfo[numlumps++].name=end_marker; } return result; } public final static boolean IsMarker(String marker, String name) { // Safeguard against nameless marker lumps e.g. in Galaxia.wad if (name==null || name.length()==0) return false; boolean result= name.equalsIgnoreCase(marker) || // doubled first character test for single-character prefixes only // FF_* is valid alias for F_*, but HI_* should not allow HHI_* (marker.charAt(1) == '_' && name.charAt(0) == marker.charAt(0) && name.substring(1).equalsIgnoreCase(marker)); return result; } @Override public void UnlockLumpNum(int lump) { lumpcache[lump]=null; } @Override public void InjectLumpNum(int lump, CacheableDoomObject obj){ lumpcache[lump]=obj; } //// Merged remnants from LumpZone here. Hashtable<CacheableDoomObject, Integer> zone; /** Add a lump to the tracking */ public void Track(CacheableDoomObject lump, int index){ zone.put(lump, index); } @Override public void UnlockLumpNum(CacheableDoomObject lump){ // Remove it from the reference Integer lumpno=zone.remove(lump); // Force nulling. This should trigger garbage collection, // and reclaim some memory, provided you also nulled any other // reference to a certain lump. Therefore, make sure you null // stuff right after calling this method, if you want to make sure // that they won't be referenced anywhere else. if (lumpno!=null) { lumpcache[lumpno]=null; //System.out.printf("Lump %d %d freed\n",lump.hashCode(),lumpno); } } @Override public boolean verifyLumpName(int lump, String lumpname) { // Lump number invalid if (lump<0 || lump>numlumps-1) return false; String name=GetNameForLump(lump); // Expected lump name not found if (name==null || lumpname.compareToIgnoreCase(name)!=0) return false; // Everything should be OK now... return true; } @Override public int GetWadfileIndex(wadfile_info_t wad1) { return wadfiles.indexOf(wad1); } @Override public int GetNumWadfiles() { return wadfiles.size(); } } //$Log: WadLoader.java,v $ //Revision 1.66 2016/07/04 13:50:50 velktron //GPL license header // //Revision 1.65 2016/06/06 13:45:44 velktron //New features to support basic WAD building/writing. // //Revision 1.64 2014/03/28 00:55:32 velktron //Cleaner, generic-based design to minimize warnings and suppressions. <T extends CacheableDoomObject> used whenever possible. // //Revision 1.63 2013/06/03 10:36:33 velktron //Jaguar handling (actualname) // //Revision 1.62.2.1 2013/01/09 14:24:12 velktron //Uses the rest of the crap // //Revision 1.62 2012/11/08 17:16:12 velktron //Made GetLumpForNum generic. // //Revision 1.61 2012/09/25 16:33:36 velktron //Dummy Doomsystem for easy testing. // //Revision 1.60 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.57.2.5 2012/09/19 21:46:46 velktron //Simpler call for getPATCH // //Revision 1.57.2.4 2012/09/04 15:08:34 velktron //New GetNumsForName function. // //Revision 1.57.2.3 2012/06/14 22:38:20 velktron //Uses new disk flasher. // //Revision 1.57.2.2 2011/12/08 00:40:40 velktron //Fix for Galaxia.wad nameless lumps. // //Revision 1.57.2.1 2011/12/05 12:05:13 velktron //Fixed a vexing bug with ZIP file header & TOC reading. // //Revision 1.57 2011/11/09 19:07:40 velktron //Adapted to handling ZIP files // //Revision 1.56 2011/11/03 21:14:30 velktron //Added -FINALLY!- resource access testing before adding them -_- // //Revision 1.55 2011/11/01 22:09:11 velktron //Some more progress on URI handling. Not essential/not breaking. // //Revision 1.54 2011/10/25 19:45:51 velktron //More efficient use of bis ;-) // //Revision 1.53 2011/10/25 19:42:48 velktron //Added advanced streaming input support and more convenient byte[] ReadLump methods. // //Revision 1.52 2011/10/24 02:07:08 velktron //DoomFile model abandoned. Now streams are used whenever possible, with possible future expandability to use e.g. URL streams or other types of resources other than RandomAccessFiles. // //Revision 1.51 2011/10/23 22:50:42 velktron //Added InjectLumpNum function to force generated contents. // //Revision 1.50 2011/10/23 18:16:31 velktron //Cleanup, moved logs to the end of the file // //Revision 1.49 2011/10/19 12:34:29 velktron //Using extractFileBase in C2JUtils now, got rid of filelength // //Revision 1.48 2011/09/29 15:18:31 velktron //Resource coalescing correctly handles wadfiles // //Revision 1.47 2011/09/27 15:57:09 velktron //Full wadinfo (lump "ownership") system in place, borrowed from prBoom+ with a twist ;-) // //Revision 1.46 2011/09/16 11:17:22 velktron //Added verifyLumpName function // //Revision 1.45 2011/09/02 16:29:59 velktron //Minor interface change // //Revision 1.44 2011/08/24 14:55:42 velktron //Deprecated old CacheLumpNumIntoArray method, much cleaner system introduced. // //Revision 1.43 2011/08/23 16:10:20 velktron //Got rid of Z remnants, commenter out Profile (useless as it is) // //Revision 1.42 2011/08/23 16:08:43 velktron //Integrated Zone functionality in WadLoader, Boom-like UnlockLump. Makes things MUCH easier. // //Revision 1.41 2011/08/02 13:49:56 velktron //Fixed missing handle on generated lumpinfo_t // //Revision 1.40 2011/08/01 22:09:14 velktron //Flats coalescing. // //Revision 1.39 2011/08/01 21:42:56 velktron //Added BOOM CoaleseResources function. // //Revision 1.38 2011/07/13 16:34:18 velktron //Started adding some BOOM wad handling stuff. Still WIP though. // //Revision 1.37 2011/07/05 13:26:30 velktron //Added handle closing functionality. // //Revision 1.36 2011/06/12 21:52:11 velktron //Made CheckNumForName uppercase-proof, at last. // //Revision 1.35 2011/06/03 16:35:27 velktron //Default fakezone // //Revision 1.34 2011/06/02 14:23:20 velktron //Added ability to "peg" an IZone manager. // //Revision 1.33 2011/05/23 17:00:39 velktron //Got rid of verbosity // //Revision 1.32 2011/05/22 21:08:28 velktron //Added better filename handling. // //Revision 1.31 2011/05/18 16:58:11 velktron //Changed to DoomStatus // //Revision 1.30 2011/05/13 11:20:07 velktron //Why the hell did this not implement IReadableDoomObject? // //Revision 1.29 2011/05/13 11:17:48 velktron //Changed default read buffer behavior. Now it's ALWAYS reset when reading from disk, and not up to the CacheableDoomObject. This does not affect bulk/stream reads. // //Revision 1.28 2011/05/10 10:39:18 velktron //Semi-playable Techdemo v1.3 milestone // //Revision 1.27 2011/01/26 00:04:45 velktron //DEUTEX flat support, Unrolled drawspan precision fix. // //Revision 1.26 2011/01/10 16:40:54 velktron //Some v1.3 commits: OSX fix, limit-removing flat management (to fix), // //Revision 1.25 2010/12/22 01:23:15 velktron //Definitively fixed plain DrawColumn. //Fixed PATCH/TEXTURE and filelump/wadloader capitalization. //Brought back some testers. // //Revision 1.24 2010/12/14 17:55:59 velktron //Fixed weapon bobbing, added translucent column drawing, separated rendering commons. // //Revision 1.23 2010/12/13 16:03:20 velktron //More fixes in the wad loading code // //Revision 1.22 2010/12/12 21:27:17 velktron //Fixed hashtable bug. Now using Java's one, faster AND easier to follow. // //Revision 1.21 2010/10/08 16:55:50 velktron //Duh // //Revision 1.20 2010/09/27 02:27:29 velktron //BEASTLY update // //Revision 1.19 2010/09/24 17:58:39 velktron //Menus and HU functional -mostly. // //Revision 1.18 2010/09/23 20:36:45 velktron //*** empty log message *** // //Revision 1.17 2010/09/23 15:11:57 velktron //A bit closer... // //Revision 1.16 2010/09/22 16:40:02 velktron //MASSIVE changes in the status passing model. //DoomMain and DoomGame unified. //Doomstat merged into DoomMain (now status and game functions are one). // //Most of DoomMain implemented. Possible to attempt a "classic type" start but will stop when reading sprites. // //Revision 1.15 2010/09/13 15:39:17 velktron //Moving towards an unified gameplay approach... // //Revision 1.14 2010/09/09 01:13:19 velktron //MUCH better rendering and testers. // //Revision 1.13 2010/09/07 16:23:00 velktron //*** empty log message *** // //Revision 1.12 2010/09/03 15:30:34 velktron //More work on unified renderer // //Revision 1.11 2010/09/02 15:56:54 velktron //Bulk of unified renderer copyediting done. // //Some changes like e.g. global separate limits class and instance methods for seg_t and node_t introduced. // //Revision 1.10 2010/08/30 15:53:19 velktron //Screen wipes work...Finale coded but untested. //GRID.WAD included for testing. // //Revision 1.9 2010/08/23 14:36:08 velktron //Menu mostly working, implemented Killough's fast hash-based GetNumForName, although it can probably be finetuned even more. // //Revision 1.8 2010/08/13 14:06:36 velktron //Endlevel screen fully functional! // //Revision 1.7 2010/08/11 16:31:34 velktron //Map loading works! Check out LevelLoaderTester for more. // //Revision 1.6 2010/08/10 16:41:57 velktron //Threw some work into map loading. // //Revision 1.5 2010/07/22 15:37:53 velktron //MAJOR changes in Menu system. // //Revision 1.4 2010/07/15 14:01:49 velktron //Added reflector Method stuff for function pointers. // //Revision 1.3 2010/07/06 15:20:23 velktron //Several changes in the WAD loading routine. Now lumps are directly unpacked as "CacheableDoomObjects" and only defaulting will result in "raw" DoomBuffer reads. // //Makes caching more effective. // //Revision 1.2 2010/06/30 11:44:40 velktron //Added a tester for patches (one of the most loosely-coupled structs in Doom!) //and fixed some minor stuff all around. // //Revision 1.1 2010/06/30 08:58:50 velktron //Let's see if this stuff will finally commit.... // // //Most stuff is still being worked on. For a good place to start and get an //idea of what is being done, I suggest checking out the "testers" package. // //Revision 1.1 2010/06/29 11:07:34 velktron //Release often, release early they say... // //Commiting ALL stuff done so far. A lot of stuff is still broken/incomplete, //and there's still mixed C code in there. I suggest you load everything up in //Eclpise and see what gives from there. // //A good place to start is the testers/ directory, where you can get an idea of //how a few of the implemented stuff works. package w; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.CharBuffer; import java.nio.DoubleBuffer; import java.nio.FloatBuffer; import java.nio.IntBuffer; import java.nio.LongBuffer; import java.nio.ShortBuffer; /** Very similar to the concept of ReadableDoomObjects * but made to work with byte buffers instead. * * This is normally NOT used to pass data around: I am * using it as a workaround to store raw byte buffers * into a "CacheableDoomObject" array, as Java * doesn't seem to like storing both ByteBuffers and * CacheableDoomObjects in the same array. WTF... * * @author admin * */ public class DoomBuffer implements CacheableDoomObject { public DoomBuffer(){ } public DoomBuffer(ByteBuffer b){ this.buffer=b; } private ByteBuffer buffer; public static void readObjectArray(ByteBuffer buf,CacheableDoomObject[] s,int len) throws IOException { if ((s==null)||(len==0)) return; for (int i=0;i<Math.min(len,s.length);i++){ s[i].unpack(buf); } } public static void readIntArray(ByteBuffer buf,int[] s,int len) throws IOException { if ((s==null)||(len==0)) return; for (int i=0;i<Math.min(len,s.length);i++){ s[i]=buf.getInt(); } } public static void putIntArray(ByteBuffer buf,int[] s,int len,ByteOrder bo) throws IOException { buf.order(bo); if ((s==null)||(len==0)) return; for (int i=0;i<Math.min(len,s.length);i++){ buf.putInt(s[i]); } } public static void putBooleanIntArray(ByteBuffer buf,boolean[] s,int len,ByteOrder bo) throws IOException { buf.order(bo); if ((s==null)||(len==0)) return; for (int i=0;i<Math.min(len,s.length);i++){ buf.putInt(s[i]?1:0); } } public static void putBooleanInt(ByteBuffer buf,boolean s,ByteOrder bo) throws IOException { buf.order(bo); buf.putInt(s?1:0); } public static void readCharArray(ByteBuffer buf,char[] s,int len) throws IOException { if ((s==null)||(len==0)) return; for (int i=0;i<Math.min(len,s.length);i++){ s[i]=buf.getChar(); } } public static void readShortArray(ByteBuffer buf,short[] s,int len) throws IOException { if ((s==null)||(len==0)) return; for (int i=0;i<Math.min(len,s.length);i++){ s[i]=buf.getShort(); } } public void readShortArray(short[] s,int len) throws IOException { if ((s==null)||(len==0)) return; for (int i=0;i<Math.min(len,s.length);i++){ s[i]=this.buffer.getShort(); } } public void readCharArray(char[] s,int len) throws IOException { if ((s==null)||(len==0)) return; for (int i=0;i<Math.min(len,s.length);i++){ s[i]=this.buffer.getChar(); } } public void readCharArray(int[] s,int len) throws IOException { if ((s==null)||(len==0)) return; for (int i=0;i<Math.min(len,s.length);i++){ s[i]=this.buffer.getChar(); } } /** Reads a length specified string from a buffer. */ public static String readString(ByteBuffer buf) throws IOException { int len = buf.getInt(); if (len == -1) return null; if (len == 0) return ""; byte bb[] = new byte[len]; buf.get(bb, 0, len); return new String(bb, 0, len); } /** MAES: Reads a specified number of bytes from a buffer into a new String. * With many lengths being implicit, we need to actually take the loader by the hand. * * @param buf * @param len * @return * @throws IOException */ public static String getString(ByteBuffer buf, int len) throws IOException { if (len == -1) return null; if (len == 0) return ""; byte bb[] = new byte[len]; buf.get(bb, 0, len); return new String(bb, 0, len); } /** MAES: Reads a maximum specified number of bytes from a buffer into a new String, * considering the bytes as representing a null-terminated, C-style string. * * @param buf * @param len * @return * @throws IOException */ public static String getNullTerminatedString(ByteBuffer buf, int len) throws IOException { if (len == -1) return null; if (len == 0) return ""; byte bb[] = new byte[len]; buf.get(bb, 0, len); // Detect null-termination. for (int i=0;i<len;i++){ if (bb[i]==0x00){ len=i; break; } } return new String(bb, 0, len); } /** MAES: WRITES a maximum specified number of characters from a String to a buffer, using C-style * null Termination. * * @param buf * @param str * @param len * @return * @throws IOException */ public static int putNullTerminatedString(ByteBuffer buf, String str, int len) throws IOException { if (str==null || len<=0) return 0; if (str.length() != 0){ byte[] str8=str.getBytes("ISO-8859-1"); buf.put(str8,0,Math.min(len,str.length())); // Fill in with 0s if something's left. if (str8.length<len){ for (int i=0;i<len-str8.length;i++){ buf.put((byte)0x00); } } return str8.length; } return 0; } /** MAES: Reads a specified number of bytes from a buffer into a new String. * With many lengths being implicit, we need to actually take the loader by the hand. * * @param buf * @param len * @return * @throws IOException */ public static char[] getCharSeq(ByteBuffer buf, int len) throws IOException { return (getString(buf,len)).toCharArray(); } @Override public void unpack(ByteBuffer buf) throws IOException { this.buffer=buf; } public ByteBuffer getBuffer() { return buffer; } public void setOrder(ByteOrder bo){ this.buffer.order(bo); } public void rewind() { this.buffer.rewind(); } public static int getBEInt(byte b3,byte b2, byte b1,byte b0) { return (b3<<24|b2<<16|b1<<8|b0); } public static int getBEInt(byte[] buf,int offset) { return (buf[offset]<<24|buf[offset+1]<<16|buf[offset+2]<<8|buf[offset+3]); } public static int getBEInt(byte[] buf) { return (buf[0]<<24|buf[1]<<16|buf[2]<<8|buf[3]); } public static int getLEInt(byte b0,byte b1, byte b2,byte b3) { return (b3<<24|b2<<16|b1<<8|b0); } public static int getLEInt(byte[] buf) { return (buf[3]<<24|buf[2]<<16|buf[1]<<24|buf[0]); } public static short getBEShort(byte[] buf) { return (short) (buf[0]<<8|buf[1]); } public static short getLEShort(byte[] buf) { return (short) (buf[0]<<8|buf[1]); } } package w; import java.io.DataInputStream; import java.io.IOException; import java.io.InputStream; /** This is an interface implemented by objects that must be read form disk. * Every object is supposed to do its own umarshalling. This way, * structured and hierchical reads are possible. Another superior innovation * of Mocha Doom ;-) Err....ok :-p * * @author Velktron * */ public interface IReadableDoomObject { public void read(DataInputStream f) throws IOException ; } package g; import java.lang.reflect.Array; import p.intercept_t; import rr.line_t; import data.mapthing_t; import doom.DoomStatus; /* Emacs style mode select -*- C++ -*- *----------------------------------------------------------------------------- * * * PrBoom: a Doom port merged with LxDoom and LSDLDoom * based on BOOM, a modified and improved DOOM engine * Copyright (C) 1999 by * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman * Copyright (C) 1999-2000 by * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze * Copyright 2005, 2006 by * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * DESCRIPTION: * * Overflow emulation code, totally ripped off prBoom+! Sieg Heil! * *--------------------------------------------------------------------- */ public class Overflow { String[] overflow_cfgname = { "overrun_spechit_emulate", "overrun_reject_emulate", "overrun_intercept_emulate", "overrun_playeringame_emulate", "overrun_donut_emulate", "overrun_missedbackside_emulate" }; class overrun_param_t{ boolean warn; boolean emulate; boolean tmp_emulate; int promted; int shit_happens; } class intercepts_overrun_t { int len; long addr; boolean int16_array; } public static final int OVERFLOW_SPECHIT=0; public static final int OVERFLOW_REJECT=1; public static final int OVERFLOW_INTERCEPT=2; public static final int OVERFLOW_PLYERINGAME=3; public static final int OVERFLOW_DONUT=4; public static final int OVERFLOW_MISSEDBACKSIDE=5; public static final int OVERFLOW_MAX=6; public static final int MAXINTERCEPTS_ORIGINAL=128; public static final boolean EMULATE(int overflow) { return (overflows[overflow].emulate || overflows[overflow].tmp_emulate); } public static final boolean PROCESS(int overflow) { return (overflows[overflow].warn || EMULATE(overflow)); } static overrun_param_t[] overflows=new overrun_param_t[OVERFLOW_MAX]; public static intercepts_overrun_t[] intercepts_overrun; /* static void ShowOverflowWarning(int overflow, int fatal, String ... params) { overflows[overflow].shit_happens = true; if (overflows[overflow].warn && !overflows[overflow].promted) { va_list argptr; char buffer[1024]; static const char *name[OVERFLOW_MAX] = { "SPECHIT", "REJECT", "INTERCEPT", "PLYERINGAME", "DONUT", "MISSEDBACKSIDE"}; static const char str1[] = "Too big or not supported %s overflow has been detected. " "Desync or crash can occur soon " "or during playback with the vanilla engine in case you're recording demo.%s%s"; static const char str2[] = "%s overflow has been detected.%s%s"; static const char str3[] = "%s overflow has been detected. " "The option responsible for emulation of this overflow is switched off " "hence desync or crash can occur soon " "or during playback with the vanilla engine in case you're recording demo.%s%s"; overflows[overflow].promted = true; sprintf(buffer, (fatal ? str1 : (EMULATE(overflow) ? str2 : str3)), name[overflow], "\nYou can change PrBoom behaviour for this overflow through in-game menu.", params); va_start(argptr, params); I_vWarning(buffer, argptr); va_end(argptr); } } */ // e6y // // Intercepts Overrun emulation // See more information on: // doomworld.com/vb/doom-speed-demos/35214-spechits-reject-and-intercepts-overflow-lists // // Thanks to Simon Howard (fraggle) for refactor the intercepts // overrun code so that it should work properly on big endian machines // as well as little endian machines. // Overwrite a specific memory location with a value. /* static void InterceptsMemoryOverrun(int location, int value) { int i, offset; int index; long addr; i = 0; offset = 0; // Search down the array until we find the right entry while (intercepts_overrun[i].len != 0) { if (offset + intercepts_overrun[i].len > location) { addr = intercepts_overrun[i].addr; // Write the value to the memory location. // 16-bit and 32-bit values are written differently. if (addr != NULL) { if (intercepts_overrun[i].int16_array) { index = (location - offset) / 2; ((short *) addr)[index] = value & 0xffff; ((short *) addr)[index + 1] = (value >> 16) & 0xffff; } else { index = (location - offset) / 4; ((int *) addr)[index] = value; } } break; } offset += intercepts_overrun[i].len; ++i; } } */ /* void InterceptsOverrun(int num_intercepts, intercept_t intercept) { if (num_intercepts > MAXINTERCEPTS_ORIGINAL && demo_compatibility && PROCESS(OVERFLOW_INTERCEPT)) { ShowOverflowWarning(OVERFLOW_INTERCEPT, false, ""); if (EMULATE(OVERFLOW_INTERCEPT)) { int location = (num_intercepts - MAXINTERCEPTS_ORIGINAL - 1) * 12; // Overwrite memory that is overwritten in Vanilla Doom, using // the values from the intercept structure. // // Note: the .d.{thing,line} member should really have its // address translated into the correct address value for // Vanilla Doom. InterceptsMemoryOverrun(location, intercept.frac); InterceptsMemoryOverrun(location + 4, intercept.isaline); InterceptsMemoryOverrun(location + 8, (int) intercept.d.thing); } } } */ // e6y // playeringame overrun emulation // it detects and emulates overflows on vex6d.wad\bug_wald(toke).lmp, etc. // http://www.doom2.net/doom2/research/runningbody.zip static boolean PlayeringameOverrun(final mapthing_t mthing, DoomStatus DS) { if (mthing.type == 0 && PROCESS(OVERFLOW_PLYERINGAME)) { ShowOverflowWarning(OVERFLOW_PLYERINGAME, DS.players[4].didsecret, ""); if (EMULATE(OVERFLOW_PLYERINGAME)) { return true; } } return false; } // // spechit overrun emulation // // Spechit overrun magic value. public static final int DEFAULT_SPECHIT_MAGIC =0x01C09C98; class spechit_overrun_param_t { line_t line; line_t[] spechit; int numspechit; int[] tmbbox; int[] tmfloorz; int[] tmceilingz; boolean crushchange; boolean nofit; } long spechit_baseaddr = 0; // e6y // Code to emulate the behavior of Vanilla Doom when encountering an overrun // of the spechit array. // No more desyncs on compet-n\hr.wad\hr18*.lmp, all strain.wad\map07 demos etc. // http://www.doomworld.com/vb/showthread.php?s=&threadid=35214 // See more information on: // doomworld.com/vb/doom-speed-demos/35214-spechits-reject-and-intercepts-overflow-lists /* static void SpechitOverrun(spechit_overrun_param_t params) { int numspechit = params.numspechit; if (demo_compatibility && numspechit > 8) { line_t[] spechit = params.spechit; ShowOverflowWarning(OVERFLOW_SPECHIT, numspechit > (compatibility_level == dosdoom_compatibility || compatibility_level == tasdoom_compatibility ? 10 : 14), "\n\nThe list of LineID leading to overrun:\n%d, %d, %d, %d, %d, %d, %d, %d, %d.", spechit[0].iLineID, spechit[1].iLineID, spechit[2].iLineID, spechit[3].iLineID, spechit[4].iLineID, spechit[5].iLineID, spechit[6].iLineID, spechit[7].iLineID, spechit[8].iLineID); if (EMULATE(OVERFLOW_SPECHIT)) { unsigned int addr; if (spechit_baseaddr == 0) { int p; // This is the first time we have had an overrun. Work out // what base address we are going to use. // Allow a spechit value to be specified on the command line. // // Use the specified magic value when emulating spechit overruns. // p = M_CheckParm("-spechit"); if (p > 0) { //baseaddr = atoi(myargv[p+1]); M_StrToInt(myargv[p+1], (long*)&spechit_baseaddr); } else { spechit_baseaddr = DEFAULT_SPECHIT_MAGIC; } } // Calculate address used in doom2.exe addr = spechit_baseaddr + (params.line - lines) * 0x3E; if (compatibility_level == dosdoom_compatibility || compatibility_level == tasdoom_compatibility) { // There are no more desyncs in the following dosdoom demos: // flsofdth.wad\fod3uv.lmp - http://www.doomworld.com/sda/flsofdth.htm // hr.wad\hf181430.lmp - http://www.doomworld.com/tas/hf181430.zip // hr.wad\hr181329.lmp - http://www.doomworld.com/tas/hr181329.zip // icarus.wad\ic09uv.lmp - http://competn.doom2.net/pub/sda/i-o/icuvlmps.zip switch(numspechit) { case 9: *(params.tmfloorz) = addr; break; case 10: *(params.tmceilingz) = addr; break; default: fprintf(stderr, "SpechitOverrun: Warning: unable to emulate" "an overrun where numspechit=%i\n", numspechit); break; } } else { switch(numspechit) { case 9: case 10: case 11: case 12: params.tmbbox[numspechit-9] = addr; break; case 13: *(params.nofit) = addr; break; case 14: *(params.crushchange) = addr; break; default: lprintf(LO_ERROR, "SpechitOverrun: Warning: unable to emulate" " an overrun where numspechit=%i\n", numspechit); break; } } } } } */ // // reject overrun emulation // // padding the reject table if it is too short // totallines must be the number returned by P_GroupLines() // an underflow will be padded with zeroes, or a doom.exe z_zone header // // e6y // reject overrun emulation code // It's emulated successfully if the size of overflow no more than 16 bytes. // No more desync on teeth-32.wad\teeth-32.lmp. // http://www.doomworld.com/vb/showthread.php?s=&threadid=35214 public static byte[] RejectOverrun(int rejectlump, final byte[] rejectmatrix, int totallines, int numsectors) { int required; byte []newreject; byte pad; int length=rejectmatrix.length; required = (numsectors * numsectors + 7) / 8; if (length < required) { // allocate a new block and copy the reject table into it; zero the rest // PU_LEVEL => will be freed on level exit newreject = new byte[required]; System.arraycopy(rejectmatrix, 0, newreject, 0, length); // e6y // PrBoom 2.2.5 and 2.2.6 padded a short REJECT with 0xff // This command line switch is needed for all potential demos // recorded with these versions of PrBoom on maps with too short REJECT // I don't think there are any demos that will need it but yes that seems sensible // pad = prboom_comp[PC_REJECT_PAD_WITH_FF].state ? 0xff : 0; // MAES: obviously we don't need that. pad=0; Array.setByte(newreject,length,pad); // unlock the original lump, it is no longer needed //W_UnlockLumpNum(rejectlump); rejectlump = -1; if (demo_compatibility && PROCESS(OVERFLOW_REJECT)) { ShowOverflowWarning(OVERFLOW_REJECT, (required - length > 16) || (length%4 != 0), ""); if (EMULATE(OVERFLOW_REJECT)) { // merged in RejectOverrunAddInt(), and the 4 calls to it, here unsigned int rejectpad[4] = { 0, // size, will be filled in using totallines 0, // part of the header of a doom.exe z_zone block 50, // DOOM_CONST_PU_LEVEL 0x1d4a11 // DOOM_CONST_ZONEID }; unsigned int i, pad = 0, *src = rejectpad; byte *dest = newreject + length; rejectpad[0] = ((totallines*4+3)&~3)+24; // doom.exe zone header size // copy at most 16 bytes from rejectpad // emulating a 32-bit, little-endian architecture (can't memmove) for (i = 0; i < (unsigned int)(required - length) && i < 16; i++) { // 16 hard-coded if (!(i&3)) // get the next 4 bytes to copy when i=0,4,8,12 pad = *src++; *dest++ = pad & 0xff; // store lowest-significant byte pad >>= 8; // rotate the next byte down } } } lprintf(LO_WARN, "P_LoadReject: REJECT too short (%u<%u) - padded\n", length, required); } } // // Read Access Violation emulation. // // C:\>debug // -d 0:0 // // DOS 6.22: // 0000:0000 (57 92 19 00) F4 06 70 00-(16 00) // DOS 7.1: // 0000:0000 (9E 0F C9 00) 65 04 70 00-(16 00) // Win98: // 0000:0000 (9E 0F C9 00) 65 04 70 00-(16 00) // DOSBox under XP: // 0000:0000 (00 00 00 F1) ?? ?? ?? 00-(07 00) #define DOS_MEM_DUMP_SIZE 10 unsigned char mem_dump_dos622[DOS_MEM_DUMP_SIZE] = { 0x57, 0x92, 0x19, 0x00, 0xF4, 0x06, 0x70, 0x00, 0x16, 0x00}; unsigned char mem_dump_win98[DOS_MEM_DUMP_SIZE] = { 0x9E, 0x0F, 0xC9, 0x00, 0x65, 0x04, 0x70, 0x00, 0x16, 0x00}; unsigned char mem_dump_dosbox[DOS_MEM_DUMP_SIZE] = { 0x00, 0x00, 0x00, 0xF1, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00}; unsigned char *dos_mem_dump = mem_dump_dos622; static int GetMemoryValue(unsigned int offset, void *value, int size) { static int firsttime = true; if (firsttime) { int p, i, val; firsttime = false; i = 0; if ((p = M_CheckParm("-setmem")) && (p < myargc-1)) { if (!strcasecmp(myargv[p + 1], "dos622")) dos_mem_dump = mem_dump_dos622; if (!strcasecmp(myargv[p + 1], "dos71")) dos_mem_dump = mem_dump_win98; else if (!strcasecmp(myargv[p + 1], "dosbox")) dos_mem_dump = mem_dump_dosbox; else { while (++p != myargc && *myargv[p] != '-' && i < DOS_MEM_DUMP_SIZE) { M_StrToInt(myargv[p], &val); dos_mem_dump[i++] = (unsigned char)val; } } } } if (value) { switch (size) { case 1: *((unsigned char*)value) = *((unsigned char*)(&dos_mem_dump[offset])); return true; case 2: *((unsigned short*)value) = *((unsigned short*)(&dos_mem_dump[offset])); return true; case 4: *((unsigned int*)value) = *((unsigned int*)(&dos_mem_dump[offset])); return true; } } return false; } // // donut overrun emulation (linedef action #9) // #define DONUT_FLOORPIC_DEFAULT 0x16 int DonutOverrun(fixed_t *pfloorheight, short *pfloorpic) { if (demo_compatibility && PROCESS(OVERFLOW_DONUT)) { ShowOverflowWarning(OVERFLOW_DONUT, 0, ""); if (EMULATE(OVERFLOW_DONUT)) { if (pfloorheight && pfloorpic) { GetMemoryValue(0, pfloorheight, 4); GetMemoryValue(8, pfloorpic, 2); // bounds-check floorpic if ((*pfloorpic) <= 0 || (*pfloorpic) >= numflats) { *pfloorpic = MIN(numflats - 1, DONUT_FLOORPIC_DEFAULT); } return true; } } } return false; } // // MissedBackSideOverrun // int MissedBackSideOverrun(sector_t *sector, seg_t *seg) { if (demo_compatibility && PROCESS(OVERFLOW_MISSEDBACKSIDE)) { if (seg) { ShowOverflowWarning(OVERFLOW_MISSEDBACKSIDE, 0, "\n\nLinedef %d has two-sided flag set, but no second sidedef", seg.linedef.iLineID); } else { ShowOverflowWarning(OVERFLOW_MISSEDBACKSIDE, 0, ""); } if (EMULATE(OVERFLOW_MISSEDBACKSIDE)) { if (sector) { GetMemoryValue(0, §or.floorheight, 4); GetMemoryValue(4, §or.ceilingheight, 4); return true; } } } return false; } } package g; import defines.*; import doom.event_t; import doom.gameaction_t; // Emacs style mode select -*- C++ -*- //----------------------------------------------------------------------------- // // $Id: DoomGameInterface.java,v 1.4 2010/12/20 17:15:08 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: // Duh. // //----------------------------------------------------------------------------- public interface DoomGameInterface { // // GAME // public void DeathMatchSpawnPlayer (int playernum); public void InitNew (skill_t skill, int episode, int map); /** 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); public void DeferedPlayDemo (String demo); /** Can be called by the startup code or M_Responder, calls P_SetupLevel or W_EnterWorld. */ public void LoadGame (String name); public void DoLoadGame (); /** Called by M_Responder. */ public void SaveGame (int slot, String description); /** Only called by startup code. */ public void RecordDemo (String name); public void BeginRecording (); public void PlayDemo (String name); public void TimeDemo (String name); public boolean CheckDemoStatus (); public void ExitLevel (); public void SecretExitLevel() ; public void WorldDone() ; public void Ticker() ; public boolean Responder (event_t ev); public void ScreenShot() ; public gameaction_t getGameAction(); public void setGameAction(gameaction_t ga); public boolean getPaused(); public void setPaused(boolean on); } package g; import static data.Defines.VERSION; import static data.Limits.*; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.nio.ByteBuffer; import defines.*; import doom.DoomStatus; import utils.C2JUtils; import w.CacheableDoomObject; import w.DoomBuffer; import w.DoomIO; import w.IReadableDoomObject; import w.IWritableDoomObject; /** represents the header of Doom savegame, so that basic info can be checked quickly. * * To load the whole game and check if there are final mistakes, you must go through it all. * Savegames need to be aware of ALL status and context, so maybe they should be inner classes? * */ public class DoomSaveGame implements CacheableDoomObject, IReadableDoomObject, IWritableDoomObject{ public DoomSaveGame(){ playeringame=new boolean[MAXPLAYERS]; } public String name; // max size SAVEGAMENAME public String vcheck; // These are for DS public int gameskill; public int gameepisode; public int gamemap; public boolean[] playeringame; /** what bullshit, stored as 24-bit integer?! */ public int leveltime; // These help checking shit. public boolean wrongversion; public boolean properend; @Override public void unpack(ByteBuffer buf) throws IOException { name=DoomBuffer.getNullTerminatedString(buf, SAVESTRINGSIZE); vcheck=DoomBuffer.getNullTerminatedString(buf, VERSIONSIZE); String vcheckb= ("version "+VERSION); // no more unpacking, and report it. if (wrongversion = !(vcheckb.equalsIgnoreCase(vcheck))) return; gameskill = buf.get(); gameepisode = buf.get(); gamemap = buf.get(); for (int i=0 ; i<MAXPLAYERS ; i++) playeringame[i] = buf.get()!=0; // load a base level (this doesn't advance the pointer?) //G_InitNew (gameskill, gameepisode, gamemap); // get the times int a = C2JUtils.toUnsignedByte(buf.get()); int b = C2JUtils.toUnsignedByte(buf.get()); int c = C2JUtils.toUnsignedByte(buf.get()); // Quite anomalous, leveltime is stored as a BIG ENDIAN, 24-bit unsigned integer :-S leveltime = (a<<16) + (b<<8) + c; // Mark this position... buf.mark(); buf.position(buf.limit()-1); if (buf.get() != 0x1d) properend=false; else properend=true; buf.reset(); // We've loaded whatever consistutes "header" info, the rest must be unpacked by proper // methods in the game engine itself. } @Override public void write(DataOutputStream f) throws IOException { DoomIO.writeString(f,name,SAVESTRINGSIZE); DoomIO.writeString(f,vcheck,VERSIONSIZE); f.writeByte(gameskill); f.writeByte(gameepisode); f.writeByte(gamemap); for (int i=0 ; i<MAXPLAYERS ; i++) f.writeBoolean(playeringame[i]); // load a base level (this doesn't advance the pointer?) //G_InitNew (gameskill, gameepisode, gamemap); // get the times byte a = (byte) (0x0000FF&(leveltime>>>16)); byte b = (byte) (0x00FF&(leveltime>>>8)); byte c = (byte) (0x00FF&(leveltime)); // Quite anomalous, leveltime is stored as a BIG ENDIAN, 24-bit unsigned integer :-S f.writeByte(a); f.writeByte(b); f.writeByte(c); // TODO: after this point, we should probably save some packed buffers representing raw state... // needs further study. // The end. f.writeByte(0x1d); } @Override public void read(DataInputStream f) throws IOException { name=DoomIO.readNullTerminatedString(f,SAVESTRINGSIZE); vcheck=DoomIO.readNullTerminatedString(f,VERSIONSIZE); String vcheckb= ("version "+VERSION); // no more unpacking, and report it. if (wrongversion = !(vcheckb.equalsIgnoreCase(vcheck))) return; gameskill = f.readByte(); gameepisode = f.readByte(); gamemap = f.readByte(); playeringame=new boolean[MAXPLAYERS]; for (int i=0 ; i<MAXPLAYERS ; i++) playeringame[i] = f.readBoolean(); // load a base level (this doesn't advance the pointer?) //G_InitNew (gameskill, gameepisode, gamemap); // get the times int a = f.readUnsignedByte(); int b = f.readUnsignedByte(); int c = f.readUnsignedByte(); // Quite anomalous, leveltime is stored as a BIG ENDIAN, 24-bit unsigned integer :-S leveltime = (a<<16) + (b<<8) + c; // Mark this position... //long mark=f.getFilePointer(); //f.seek(f.length()-1); //if (f.readByte() != 0x1d) properend=false; else // properend=true; //f.seek(mark); long available=f.available(); f.skip(available-1); if (f.readByte() != 0x1d) properend=false; else properend=true; // We've loaded whatever consistutes "header" info, the rest must be unpacked by proper // methods in the game engine itself. } public void toStat(DoomStatus DS){ System.arraycopy(this.playeringame, 0, DS.playeringame, 0, this.playeringame.length); DS.gameskill=skill_t.values()[this.gameskill]; DS.gameepisode=this.gameepisode; DS.gamemap=this.gamemap; DS.leveltime=this.leveltime; } public void fromStat(DoomStatus DS){ System.arraycopy(DS.playeringame, 0, this.playeringame, 0, DS.playeringame.length); this.gameskill=DS.gameskill.ordinal(); this.gameepisode=DS.gameepisode; this.gamemap=DS.gamemap; this.leveltime=DS.leveltime; } } package g; // //DOOM keyboard definition. //This is the stuff configured by Setup.Exe. //Most key data are simple ascii (uppercased). // //NOTE: the codes used here are arbitrary, non-system specificm //however they must match what's used in the default.cfg files //and throughout the code. Once the system input barrier has been //crossed, Doom only understands these codes. It's the job of //the system-specific interface to produce the correct mappings. public class Keys { public static final char KEY_NULL = 0; // null key, triggers nothing public static final char KEY_ESCAPE = 27; public static final char KEY_SPACE = 32; public static final char KEY_NUMLOCK = (0x80+69); public static final char KEY_SCROLLLOCK = (0x80+70); public static final char KEY_MINUS = 45; public static final char KEY_EQUALS = 61; public static final char KEY_BACKSPACE = 8; public static final char KEY_TAB = 9; public static final char KEY_ENTER = 13; // // scancodes 71-83 (non-extended) // public static final char KEY_KEYPAD7 = (0x80+71); public static final char KEY_KEYPAD8 = (0x80+72); public static final char KEY_KEYPAD9 = (0x80+73); public static final char KEY_MINUSPAD = (0x80+74); public static final char KEY_KEYPAD4 = (0x80+75); public static final char KEY_KEYPAD5 = (0x80+76); public static final char KEY_KEYPAD6 = (0x80+77); public static final char KEY_PLUSPAD = (0x80+78); public static final char KEY_KEYPAD1 = (0x80+79); public static final char KEY_KEYPAD2 = (0x80+80); public static final char KEY_KEYPAD3 = (0x80+81); public static final char KEY_KEYPAD0 = (0x80+82); public static final char KEY_KPADDEL = (0x80+83); // windows95 keys... public static final char KEY_LEFTWIN = (0x80+91); public static final char KEY_RIGHTWIN = (0x80+92); public static final char KEY_MENU = (0x80+93); // // scancodes 71-83 EXTENDED are remapped // to these by the keyboard handler (just add 30) // public static final char KEY_KPADSLASH = (0x80+100); //extended scancode 53 '/' remapped public static final char KEY_HOME = (0x80+101); public static final char KEY_UPARROW = (0x80+102); public static final char KEY_PGUP = (0x80+103); public static final char KEY_LEFTARROW = (0x80+105); public static final char KEY_RIGHTARROW = (0x80+107); public static final char KEY_END = (0x80+109); public static final char KEY_DOWNARROW = (0x80+110); public static final char KEY_PGDN = (0x80+111); public static final char KEY_INS = (0x80+112); public static final char KEY_DEL = (0x80+113); public static final char KEY_F1 = (0x80+0x3b); public static final char KEY_F2 = (0x80+0x3c); public static final char KEY_F3 = (0x80+0x3d); public static final char KEY_F4 = (0x80+0x3e); public static final char KEY_F5 = (0x80+0x3f); public static final char KEY_F6 = (0x80+0x40); public static final char KEY_F7 = (0x80+0x41); public static final char KEY_F8 = (0x80+0x42); public static final char KEY_F9 = (0x80+0x43); public static final char KEY_F10 = (0x80+0x44); public static final char KEY_F11 = (0x80+0x57); public static final char KEY_F12 = (0x80+0x58); public static final char KEY_PAUSE = 255; // these ones must be non-extended scancodes (rctrl,rshift,lalt) public static final char KEY_SHIFT = (0x80+54); public static final char KEY_CTRL = (0x80+29); public static final char KEY_ALT = (0x80+56); public static final char KEY_CAPSLOCK = (0x80+58); public static final char KEY_CONSOLE = (int)'`'; // public static final char KEY_OPENBRACKETS // public static final char KEY_CLOSEBRACKETS } package testers; import m.VarsManager; public class TestSettingsManager { public static void main(String argv[]){ VarsManager SM=new VarsManager(); SM.LoadDefaults("default.cfg"); System.out.println(SM.getSetting("use_mouse").getBoolean()); SM.putSetting("jump_height", "56", false); System.out.println(SM.getSetting("jump_height").getInteger()); System.out.println(SM.getSetting("use_mouse").getBoolean()); SM.putSetting("crap_setting", "false", true); System.out.println(SM.getSetting("crap_setting").getInteger()); System.out.println(SM.getSetting("key_left").getChar()); SM.SaveDefaults("default.cfg"); } } package testers; import java.io.File; import java.io.FileOutputStream; import s.DMXSound; import s.DSP; import w.WadLoader; public class LinearSoundInterpolation { public static void main(String[] argv) throws Exception{ WadLoader W=new WadLoader(); W.InitMultipleFiles(new String[]{"c:\\iwads\\doom1.wad"}); int sfxlump = W.GetNumForName("dspistol"); DMXSound dmx= W.CacheLumpNum(sfxlump, 0, DMXSound.class); File out=new File("original.raw"); FileOutputStream fos=new FileOutputStream(out); fos.write(dmx.data); fos.close(); fos=new FileOutputStream("linear.raw"); // KRUDE if (dmx.speed==11025){ //Plain linear interpolation. dmx.data=DSP.crudeResample(dmx.data,2); //DSP.filter(dmx.data,SAMPLERATE, SAMPLERATE/4); dmx.datasize=dmx.data.length; } fos.write(dmx.data); fos.close(); } } package testers; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileOutputStream; import w.*; /** DUMPS a WAD's LUMPS to disk. Any questions?! * */ public class WadDumper { public static void main(String[] argv) { try { IWadLoader W=new WadLoader(); W.InitMultipleFiles(argv,false); BufferedOutputStream bos; int numlumps=W.NumLumps(); for (int i=0;i<numlumps;i++){ byte[] crap=W.ReadLump(i); String name=W.GetNameForLump(i); File f=new File(String.format("%s.lmp",name)); bos=new BufferedOutputStream(new FileOutputStream(f)); bos.write(crap); bos.close(); } } catch (Exception e){ e.printStackTrace(); } } } package testers; import pooling.AudioChunkPool; import s.AudioChunk; public class TestAudioChunkPooling { public static final int TESTS=10000; public static void main(String[] argv){ AudioChunk[] chunks=new AudioChunk[TESTS]; AudioChunkPool chunkpool=new AudioChunkPool(); long a=System.nanoTime(); for (int i=0;i<TESTS;i++){ chunks[i]=new AudioChunk(); } for (int i=0;i<TESTS;i++){ chunks[i]=new AudioChunk(); } for (int i=0;i<TESTS;i++){ chunks[i]=new AudioChunk(); } for (int i=0;i<TESTS;i++){ chunks[i]=new AudioChunk(); } long b=System.nanoTime(); System.out.println("Time: "+(float)(b-a)/1000000000f); a=System.nanoTime(); for (int i=0;i<TESTS;i++){ chunks[i]=chunkpool.checkOut(); } for (int i=0;i<TESTS;i++){ chunkpool.checkIn(chunks[i]); chunks[i]=chunkpool.checkOut(); } for (int i=0;i<TESTS;i++){ chunkpool.checkIn(chunks[i]); chunks[i]=chunkpool.checkOut(); } for (int i=0;i<TESTS;i++){ chunkpool.checkIn(chunks[i]); chunks[i]=chunkpool.checkOut(); } b=System.nanoTime(); System.out.println("Time: "+(float)(b-a)/1000000000f); } } package testers; import java.nio.ByteBuffer; import javax.sound.sampled.AudioFormat; import javax.sound.sampled.AudioSystem; import javax.sound.sampled.DataLine; import javax.sound.sampled.SourceDataLine; import s.SpeakerSound; import w.WadLoader; import defines.GameMission_t; import doom.DoomStatus; public class TestSpeakerSound { public static void main(String[] argv) throws Exception { WadLoader W=new WadLoader(); W.InitMultipleFiles(new String[] {"C:\\iwads\\doom1.wad"}); SpeakerSound sp=(SpeakerSound) W.CacheLumpName("DPSAWUP", 0, SpeakerSound.class); byte[] stuff=sp.toRawSample(); AudioFormat format = new AudioFormat(11025,8,1,false,true); DataLine.Info info = new DataLine.Info(SourceDataLine.class, format); SourceDataLine line=null; if (AudioSystem.isLineSupported(info)) try { line= (SourceDataLine) AudioSystem.getSourceDataLine(format); line.open(format); } catch (Exception e){ e.printStackTrace(); System.err.print( "Could not play signed 16 data\n"); } if (line!=null) System.err.print(" configured audio device\n" ); line.start(); line.write(stuff, 0,stuff.length); line.drain(); Thread.sleep(1000); } } package testers; import static data.Defines.PU_STATIC; import hu.HU; import java.nio.ByteBuffer; import m.FixedFloat; import rr.patch_t; import rr.vertex_t; import data.mapvertex_t; import defines.*; import doom.DoomContext; import doom.DoomStatus; import utils.C2JUtils; import w.*; /** This is a very simple tester for the WadLoader and HU modules. * We use the same exact methods used in the C source code, only * with a more OO approach. * * */ public class PatchLoaderTester { public static void main(String[] argv) { try { WadLoader W=new WadLoader(); W.InitMultipleFiles(new String[] {"doom1.wad"}); patch_t wall=W.CachePatchName("BAL1d0"); System.out.println(wall.leftoffset); } catch (Exception e){ e.printStackTrace(); } } } package testers; import static data.Defines.PU_STATIC; import hu.HU; import java.nio.ByteBuffer; import m.FixedFloat; import rr.vertex_t; import data.mapvertex_t; import defines.*; import doom.DoomContext; import doom.DoomStatus; import utils.C2JUtils; import w.*; /** This is a very simple tester for the WadLoader and HU modules. * We use the same exact methods used in the C source code, only * with a more OO approach. * * */ public class WadCoalescerTester { public static void main(String[] argv) { try { WadLoader W=new WadLoader(); W.InitMultipleFiles(new String[] {"doom1.wad"/*,"sprite2.wad"*/}); //W.AddFile("bitter.wad"); //W.CoalesceMarkedResource("F_START", "F_END",li_namespace.ns_flats); //W.CoalesceMarkedResource("S_START", "S_END",li_namespace.ns_sprites); System.out.println("Total lumps read: "+W.numlumps); for (int i=0;i<W.numlumps;i++){ System.out.println(W.lumpinfo[i].name+" "+i); } } catch (Exception e){ e.printStackTrace(); } } } package testers; import m.DoomRandom; import m.IRandom; import s.ClassicDoomSoundDriver; import s.SpeakerDoomSoundDriver; import s.SuperDoomSoundDriver; import data.sounds.sfxenum_t; import doom.DoomStatus; import w.WadLoader; public class TestSuperSound { public static void main(String[] argv) throws Exception{ DoomStatus DS=new DoomStatus(); WadLoader W=new WadLoader(); IRandom RND=new DoomRandom(); DS.W=W; DS.RND=RND; W.InitMultipleFiles(new String[]{"doom1.wad"}); SuperDoomSoundDriver sound=new SuperDoomSoundDriver(DS,4); sound.InitSound(); sound.SetChannels(3); Thread.sleep(1000); //sound.StartSound(1, 127, 127, 127, 0); for (int i=0;i<1000;i++){ Thread.sleep(1000/35); if (i%10==0) sound.StartSound(sfxenum_t.sfx_plpain.ordinal(), 127, 127, 127, 0); //if (i%50==0) sound.StartSound(sfxenum_t.sfx_barexp.ordinal(), 127, 0, 127, 0); //if (i%35==0) sound.StartSound(sfxenum_t.sfx_plpain.ordinal(), 127, 255, 127, 0); //if (i%71==0) sound.StartSound(sfxenum_t.sfx_oof.ordinal(), 127, 192, 127, 0); sound.UpdateSound(); sound.SubmitSound(); DS.gametic++; } sound.ShutdownSound(); } } package testers; import static data.Defines.KEY_F1; import static data.Defines.PU_STATIC; import static data.Limits.MAXEVENTS; import hu.HU; import i.DoomSystem; import i.IDoomSystem; import i.InputListener; import java.awt.image.BufferedImage; import java.awt.image.IndexColorModel; import javax.swing.JFrame; import awt.OldAWTDoom; import rr.patch_t; import s.DummySoundDriver; import m.IDoomMenu; import m.Menu; import m.DoomRandom; import utils.C2JUtils; import v.BufferedRenderer; import v.SimpleRenderer; import w.DoomBuffer; import w.WadLoader; import data.Defines; import data.Defines.GameMission_t; import data.Defines.GameMode_t; import data.Defines.Language_t; import data.Defines.gamestate_t; import doom.DoomContext; import doom.DoomMain; import doom.DoomStatus; import doom.event_t; import doom.player_t; import doom.ticcmd_t; import doom.wbstartstruct_t; /** This is a very simple tester for Menu module */ public class AWTMenuTester { public static final int WIDTH=320; public static final int HEIGHT=200; public static void main(String[] argv) { try { // Create a Wad file loader. WadLoader W=new WadLoader(); W.InitMultipleFiles(new String[] {"doom1.wad"}); System.out.println("Total lumps read: "+W.numlumps); patch_t help1=W.CachePatchName("TITLEPIC", PU_STATIC); // Read the paletter. DoomBuffer palette = W.CacheLumpName("PLAYPAL", PU_STATIC); // Create a video renderer BufferedRenderer V=new BufferedRenderer(WIDTH,HEIGHT); V.Init(); byte[] pal=palette.getBuffer().array(); IDoomSystem I=new DoomSystem(); DoomMain DM=new DoomMain(); // Create the frame. OldAWTDoom frame = new OldAWTDoom(DM,V,pal); DM.I=I; DM.VI=frame; DM.S=new DummySoundDriver(); frame.DM=DM; DM.W=W; DM.V=V; DM.DM=DM; HU HU=new HU(DM); DM.language=Language_t.english; HU.Init(); DM.HU=HU; DM.gameepisode=1; DM.gamemap=1; DM.gamemission=GameMission_t.doom; DM.gamemode=GameMode_t.shareware; DM.wminfo=new wbstartstruct_t(); // Simulate being in the mid of a level. DM.usergame=true; DM.gamestate=gamestate_t.GS_LEVEL; C2JUtils.initArrayOfObjects(DM.players,player_t.class); DM.RND=new DoomRandom(); DM.players[0].cmd=new ticcmd_t(); DM.players[0].itemcount=1337; DM.players[0].killcount=1337; DM.players[0].secretcount=1337; DM.wminfo.plyr[0].in=true; DM.wminfo.plyr[0].sitems=1337; DM.wminfo.plyr[0].skills=1337; DM.wminfo.plyr[0].stime=28595; DM.wminfo.plyr[0].ssecret=1337; DM.playeringame[0]=true; DM.wminfo.last=6; DM.wminfo.epsd=0; DM.wminfo.maxitems=100; DM.wminfo.maxkills=100; DM.wminfo.maxsecret=100; DM.wminfo.partime=28595; IDoomMenu M=DM.M=new Menu(DM); M.Init(); frame.InitGraphics(); long a=System.nanoTime(); DM.R.menuactive=true; for (int i=0;i<100000;i++){ int ba=DM.I.GetTime(); while (ba-DM.I.GetTime()==0){ //Don't do that! frame.setVisible(true); Thread.sleep(1); } frame.GetEvent(); for ( ; DM.eventtail != DM.eventhead ; DM.eventtail = (++DM.eventtail)&(MAXEVENTS-1) ) { event_t ev = DM.events[DM.eventtail]; //System.out.println(ev); if (ev!=null) ((Menu)M).Responder(ev); } V.DrawPatch(0,0,0,help1); M.Ticker(); M.Drawer(); DM.gametic++; frame.FinishUpdate(); if (i%100==0){ long b=System.nanoTime(); System.out.println(i +" frames in " +((b-a)/1e09) +" = "+i/((b-a)/1e09) + " fps"); } System.out.print(frame.processEvents()); } } catch (Exception e){ e.printStackTrace(); } } } package testers; import static data.Defines.*; import static data.Limits.*; import static m.fixed_t.FRACBITS; import i.InputListener; import java.awt.Frame; import java.awt.GraphicsDevice; import java.awt.image.BufferedImage; import java.awt.image.IndexColorModel; import javax.swing.JFrame; import m.DoomRandom; import p.LevelLoader; import p.mobj_t; import st.StatusBar; import utils.C2JUtils; import v.BufferedRenderer; import w.DoomBuffer; import w.WadLoader; import automap.DoomAutoMap; import automap.Map; import data.Defines; import data.Defines.GameMission_t; import data.Defines.GameMode_t; import data.Defines.skill_t; import doom.DoomContext; import doom.DoomStatus; import doom.event_t; import doom.player_t; import doom.ticcmd_t; import doom.wbstartstruct_t; import doom.weapontype_t; /** This is a very simple tester for the Automap. Combined with status bar + Level loader. */ public class AutoMapTester3 { public static final int WIDTH=320; public static void main(String[] argv) { try { WadLoader W=new WadLoader(); W.InitMultipleFiles(new String[] {"doom1.wad"}); //W.AddFile("bitter.wad"); System.out.println("Total lumps read: "+W.numlumps); DoomBuffer palette = W.CacheLumpName("PLAYPAL", PU_STATIC); byte[] pal=palette.getBuffer().array(); BufferedRenderer V=new BufferedRenderer(WIDTH,200); V.Init(); BufferedImage bi=new BufferedImage(V.getWidth(),V.getHeight(),BufferedImage.TYPE_INT_RGB); V.setPalette(pal); V.mapInternalRasterToBufferedImage(bi); DoomStatus ds = new DoomStatus(); ds.gameepisode=1; ds.gamemap=1; ds.gamemission=GameMission_t.doom; ds.gamemode=GameMode_t.shareware; ds.wminfo=new wbstartstruct_t(); C2JUtils.initArrayOfObjects(ds.players,player_t.class); Defines.SCREENWIDTH=WIDTH; Defines.SCREENHEIGHT=200; DoomContext DC=new DoomContext(); DC.DS=ds; DC.W=W; DC.V=V; DC.RND=new DoomRandom(); ds.players[0].cmd=new ticcmd_t(); ds.players[0].itemcount=1337; ds.players[0].killcount=1337; ds.players[0].secretcount=1337; ds.players[0].weaponowned[0]=true; ds.players[0].weaponowned[1]=true; ds.players[0].weaponowned[2]=true; ds.players[0].weaponowned[3]=true; ds.players[0].readyweapon=weapontype_t.wp_pistol; ds.players[0].health[0]=100; ds.players[0].armorpoints[0]=100; ds.players[0].ammo[0]=400; ds.players[0].maxammo[0]=400; ds.players[0].ammo[1]=100; ds.players[0].maxammo[1]=100; ds.players[0].ammo[2]=100; ds.players[0].maxammo[2]=100; ds.players[0].ammo[3]=600; ds.players[0].maxammo[3]=600; ds.players[0].cards[0]=true; ds.players[0].cards[2]=true; ds.players[0].cards[4]=true; ds.players[0].mo=new mobj_t(); ds.players[0].mo.x=1056<<FRACBITS; ds.players[0].mo.y=-3616<<FRACBITS; ds.players[0].powers[pw_allmap]=100; ds.deathmatch=false; ds.statusbaractive=true; ds.wminfo.plyr[0].in=true; ds.wminfo.plyr[0].sitems=1337; ds.wminfo.plyr[0].skills=1337; ds.wminfo.plyr[0].stime=28595; ds.wminfo.plyr[0].ssecret=1337; ds.playeringame[0]=true; ds.wminfo.last=6; ds.wminfo.epsd=0; ds.wminfo.maxitems=100; ds.wminfo.maxkills=100; ds.wminfo.maxsecret=100; ds.wminfo.partime=28595; StatusBar ST=new StatusBar(DC); ST.Start(); LevelLoader PL=new LevelLoader(DC); PL.SetupLevel(1, 1, 0, skill_t.sk_hard); DC.LL=PL; DC.ST=ST; DoomAutoMap AM=new Map(DC); AM.Start(); ST.Responder(new event_t('i')); ST.Responder(new event_t('d')); ST.Responder(new event_t('d')); ST.Responder(new event_t('t')); AM.Responder(new event_t(Map.AM_FOLLOWKEY)); AM.Responder(new event_t(Map.AM_ZOOMOUTKEY)); AM.Responder(new event_t(Map.AM_GRIDKEY)); CrappyDisplay frame = new CrappyDisplay(bi); frame.setTitle("MochaDoom"); InputListener in = new InputListener(); frame.addComponentListener(in); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.pack(); frame.setLocationRelativeTo(null); //frame.setUndecorated(true); frame.setVisible(true); frame.setBounds(frame.getX(), frame.getY(), WIDTH, 240); long a=System.nanoTime(); int TICKS=10000; for (int i=0;i<TICKS;i++){ if ((i%20)<10) AM.Responder(new event_t(Map.AM_ZOOMINKEY)); else AM.Responder(new event_t(Map.AM_ZOOMOUTKEY)); if ((i%25)<12) { AM.Responder(new event_t(Map.AM_PANUPKEY)); AM.Responder(new event_t(Map.AM_PANRIGHTKEY)); } else { AM.Responder(new event_t(Map.AM_PANDOWNKEY)); AM.Responder(new event_t(Map.AM_PANLEFTKEY)); } AM.Ticker(); AM.Drawer(); ST.Ticker(); ST.Drawer(false,true); V.changePalette((i/(2000/14))%14); V.remap(0); frame.update(null); /*File outputFile = new File( "tic"+i+".png"); ImageIO.write(bi, "PNG", outputFile); */ // V.takeScreenShot(0, "tic"+i,icm); //AM.Responder(new event_t(Map.AM_PANLEFTKEY)); } long b=System.nanoTime(); System.out.println(TICKS +" tics in " +((b-a)/1e09) +" = "+TICKS/((b-a)/1e09) + " fps"); /* V.takeScreenShot(0, "tic1",icm); for (int i=20;i<150;i++){ EL.Ticker(); EL.Drawer(); if (i==100){ ds.players[0].cmd.buttons=1; // simulate attack ds.players[0].attackdown=false; // simulate attack } if (i==120){ ds.players[0].cmd.buttons=1; // simulate attack ds.players[0].attackdown=false; // simulate attack } V.takeScreenShot(0,( "tic"+i),icm); } */ } catch (Exception e){ e.printStackTrace(); } } public static void initFullScreen(GraphicsDevice gd, Frame gf) { // initialize the main app frame gf = new Frame("Game Frame"); gf.setUndecorated(true); // disable repaint mechanism gf.setIgnoreRepaint(true); // the next call shows the window gd.setFullScreenWindow(gf); } } package testers; import static data.Defines.KEY_F1; import static data.Defines.PU_STATIC; import java.awt.image.BufferedImage; import java.awt.image.IndexColorModel; import javax.swing.JFrame; import m.IDoomMenu; import m.Menu; import m.DoomRandom; import utils.C2JUtils; import v.BufferedRenderer; import w.DoomBuffer; import w.WadLoader; import data.Defines; import data.Defines.GameMission_t; import data.Defines.GameMode_t; import doom.DoomContext; import doom.DoomStatus; import doom.event_t; import doom.player_t; import doom.ticcmd_t; import doom.wbstartstruct_t; /** This is a very simple tester for the End Level screen drawer. * * */ public class MenuVideoTester { public static final int WIDTH=320; public static void main(String[] argv) { try { WadLoader W=new WadLoader(); W.InitMultipleFiles(new String[] {"doom1.wad"}); //W.AddFile("bitter.wad"); System.out.println("Total lumps read: "+W.numlumps); DoomBuffer palette = W.CacheLumpName("PLAYPAL", PU_STATIC); byte[] pal=palette.getBuffer().array(); IndexColorModel icm=new IndexColorModel(8, 256,pal, 0, false); Defines.SCREENWIDTH=WIDTH; Defines.SCREENHEIGHT=200; BufferedRenderer V=new BufferedRenderer(WIDTH,200,icm); V.Init(); IndexColorModel[] icms=new IndexColorModel[pal.length/768]; BufferedImage[] pals=new BufferedImage[icms.length]; for (int i=0;i<icms.length;i++){ icms[i]=new IndexColorModel(8, 256,pal, i*768, false); pals[i]=new BufferedImage(icms[i],V.screenbuffer[0].getRaster(), false, null); } DoomStatus ds = new DoomStatus(); ds.gameepisode=1; ds.gamemap=1; ds.gamemission=GameMission_t.doom; ds.gamemode=GameMode_t.shareware; ds.wminfo=new wbstartstruct_t(); C2JUtils.initArrayOfObjects(ds.players,player_t.class); DoomContext DC=new DoomContext(); DC.DS=ds; DC.W=W; DC.V=V; DC.RND=new DoomRandom(); ds.players[0].cmd=new ticcmd_t(); ds.players[0].itemcount=1337; ds.players[0].killcount=1337; ds.players[0].secretcount=1337; ds.wminfo.plyr[0].in=true; ds.wminfo.plyr[0].sitems=1337; ds.wminfo.plyr[0].skills=1337; ds.wminfo.plyr[0].stime=28595; ds.wminfo.plyr[0].ssecret=1337; ds.playeringame[0]=true; ds.wminfo.last=6; ds.wminfo.epsd=0; ds.wminfo.maxitems=100; ds.wminfo.maxkills=100; ds.wminfo.maxsecret=100; ds.wminfo.partime=28595; JFrame frame = new JFrame("MochaDoom"); CrappyDisplay shit = new CrappyDisplay(pals); frame.add(shit); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.pack(); frame.setLocationRelativeTo(null); //frame.setUndecorated(true); frame.setVisible(true); frame.setBounds(frame.getX(), frame.getY(), WIDTH, 240); IDoomMenu M=new Menu(DC); M.Init(); for (int i=0;i<20;i++){ M.Ticker(); M.Drawer(); } V.takeScreenShot(0, "menutic19",icm); ds.menuactive=true; for (int i=20;i<150;i++){ M.Ticker(); M.Drawer(); if (i==40){ M.Responder(new event_t(Defines.KEY_DOWNARROW)); } if (i==60){ M.Responder(new event_t(Defines.KEY_DOWNARROW)); } if (i==80){ M.Responder(new event_t(Defines.KEY_ESCAPE)); } if (i==100){ M.Responder(new event_t(KEY_F1)); } if (i==120){ M.Responder(new event_t(Defines.KEY_ESCAPE)); } V.takeScreenShot(0,( "menutic"+i),icm); } }catch (Exception e){ e.printStackTrace(); } } } package testers; import rr.patch_t; import w.*; /** A tester for loading patches off cached memory and directly from disk, * proving how direct WAD access is possible, too. * Will need to implement reading column pixel data and dumping it into a file somewhat... * */ public class DiskPatchReader { public static void main(String[] argv) throws Exception { WadLoader W=new WadLoader(); W.InitMultipleFiles(new String[] {"C:\\iwads\\doom1.wad"}); System.out.println("Total lumps read: "+W.numlumps); System.out.println("Num for WALL00_1: "+W.GetNumForName("WALL00_1")); // We prepare a ByteBuffer to receive a "SECTORS" object. Deserializing it is // another matter. //ByteBuffer bb=W.CacheLumpName("WALL00_1", 0); // patch_t wall= W.CachePatchName("WALL00_1"); lumpinfo_t lump= W.GetLumpinfoForName("WALL00_1"); System.out.println(lump.name); System.out.println(lump.position); System.out.println(lump.size); // Now open Doom1.wad standalone... //DoomFile f=new DoomFile("doom1.wad","r"); //patch_t wall1=new patch_t(); //f.seek(lump.position); //wall1.read(f); //System.out.println(wall1.height); System.out.println("Num for HELP1: "+W.GetNumForName("HELP1")); //bb=W.CacheLumpName("HELP1", 0); patch_t stbar= W.CachePatchName("HELP1"); System.out.println(stbar.height); System.out.println(stbar.width); stbar=(patch_t)W.CacheLumpName("HELP1", 0,stbar.getClass()); } } package testers; import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.SocketTimeoutException; import java.util.LinkedList; import n.BasicNetworkInterface; import doom.DoomContext; import doom.DoomMain; import doom.doomdata_t; import doom.ticcmd_t; /** Tester for Net included from DW's work * * $Log * */ public class NetTester { static DoomMain D1, D2; static Object synchronizer = new Object(); public static class NetInterfaceTester extends BasicNetworkInterface { DoomContext DC; public NetInterfaceTester(DoomContext DC) { super(DC); // TODO Auto-generated constructor stub } private doomdata_t sendData = new doomdata_t(); private doomdata_t recvData = new doomdata_t(); @Override public void sendSocketPacket(DatagramSocket ds, DatagramPacket dp) throws IOException { DoomMain receiver; if (DM.consoleplayer == 0) { receiver = D2; } else { receiver = D1; } NetInterfaceTester N = (NetInterfaceTester)receiver.DNI; N.addReceivedPacket(dp); sendData.unpack(dp.getData()); //System.out.println("Player "+DM.consoleplayer+" SENT: "+dataToStr(sendData)); } /*class Elem<E> { E e; E next = null; public Elem(E e) { this.e = e; } } Elem<DatagramPacket> head = new Elem<DatagramPacket>(null);*/ //ArrayList<DatagramPacket> al = new ArrayList<DatagramPacket>(); LinkedList<DatagramPacket> al = new LinkedList<DatagramPacket>(); public void addReceivedPacket(DatagramPacket dp) { synchronized(synchronizer) { al.addLast(dp); } } @Override public void socketGetPacket(DatagramSocket ds, DatagramPacket dp) throws IOException { synchronized(synchronizer) { if (al.size() < 1) throw new SocketTimeoutException(); DatagramPacket pop = al.removeFirst(); dp.setData(pop.getData()); dp.setLength(pop.getLength()); dp.setSocketAddress(pop.getSocketAddress()); recvData.unpack(dp.getData()); } //System.out.println("Player "+DM.consoleplayer+" RECV: "+dataToStr(recvData)); } public String dataToStr(doomdata_t dt) { StringBuffer sb = new StringBuffer(); sb.append("STIC: "+dt.starttic+" NTIC: "+dt.numtics+" CMDS:\r\n"); for (int i = 0; i < dt.numtics; i++) { ticcmd_t tc = dt.cmds[i]; sb.append(" FMOVE: "+tc.forwardmove+" CONS: "+tc.consistancy+"\r\n"); } return sb.toString(); } } public static void main(String[] args) { D1=new DoomMain(); D1.Init(); D1.DNI = new NetInterfaceTester(D1); D1.myargv = new String[] {"", "-net", "1", "localhost"}; D1.myargc = D1.myargv.length; // Bump argcount +1 to maintain CheckParm behavior D2=new DoomMain(); D2.Init(); D2.DNI = new NetInterfaceTester(D2); D2.myargv = new String[] {"", "-net", "2", "localhost"}; D2.myargc = D2.myargv.length; // Bump argcount +1 to maintain CheckParm behavior new Thread() { public void run() { D1.Start (); } }.start(); try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } new Thread() { public void run() { D2.Start (); } }.start(); } } package testers; import m.fixed_t; public class Mul implements Operation { @Override public void invoke(fixed_t a, fixed_t b) { a.FixedMul(b); } } package testers; import w.*; /** This is a very simple tester for the End Level screen drawer. * * */ public class LumpGetterTester { public static void main(String[] argv) { try { WadLoader W=new WadLoader(); W.InitMultipleFiles(new String[] {"C:\\iwads\\doom1.wad"}); //W.AddFile("bitter.wad"); System.out.println("Total lumps read: "+W.numlumps); int random; int TESTS=10000; int[] tests=new int[TESTS]; int hash; long a=System.nanoTime(); for (int i=0;i<TESTS;i++){ random =(int) (Math.random()*W.numlumps); String what=W.lumpinfo[random].name; hash=W.lumpinfo[random].hash; tests[i] = W.CheckNumForName3(what); if (!W.lumpinfo[tests[i]].name.equalsIgnoreCase(what)) System.err.println("Mismatch"); } long b=System.nanoTime(); System.out.println(b-a); } catch (Exception e){ e.printStackTrace(); } } } package testers; import static data.Defines.*; import static data.Limits.*; import static m.fixed_t.FRACBITS; import i.InputListener; import java.awt.Frame; import java.awt.GraphicsDevice; import java.awt.image.BufferedImage; import java.awt.image.IndexColorModel; import javax.swing.JFrame; import m.IDoomMenu; import m.Menu; import m.DoomRandom; import p.Actions; import p.LevelLoader; import p.mobj_t; import st.StatusBar; import utils.C2JUtils; import v.BufferedRenderer; import w.DoomBuffer; import w.WadLoader; import automap.DoomAutoMap; import automap.Map; import data.Defines; import data.Defines.GameMission_t; import data.Defines.GameMode_t; import data.Defines.skill_t; import doom.DoomContext; import doom.DoomMain; import doom.DoomStatus; import doom.event_t; import doom.player_t; import doom.ticcmd_t; import doom.wbstartstruct_t; import doom.weapontype_t; /** This is a very simple tester for the Automap. Combined with status bar + Level loader. */ public class AutoMapTester2 { public static final int WIDTH=320; public static void main(String[] argv) { try { WadLoader W=new WadLoader(); W.InitMultipleFiles(new String[] {"doom1.wad"}); //W.AddFile("bitter.wad"); System.out.println("Total lumps read: "+W.numlumps); DoomBuffer palette = W.CacheLumpName("PLAYPAL", PU_STATIC); byte[] pal=palette.getBuffer().array(); IndexColorModel icm=new IndexColorModel(8, 256,pal, 0, false); Defines.SCREENWIDTH=WIDTH; Defines.SCREENHEIGHT=200; BufferedRenderer V=new BufferedRenderer(WIDTH,200,icm); V.Init(); IndexColorModel[] icms=new IndexColorModel[palette.getBuffer().limit()/768]; BufferedImage[] pals=new BufferedImage[icms.length]; for (int i=0;i<icms.length;i++){ icms[i]=new IndexColorModel(8, 256,pal, i*768, false); }/* pals[i]=new BufferedImage(icms[i],V.screenbuffer[0].getRaster(), false, null); }*/ pals=V.getBufferedScreens(0, icms); //=V.getBufferedScreens(0,icm);>= numlumps DoomMain ds = new DoomMain(); ds.gameepisode=1; ds.gamemap=1; ds.gamemission=GameMission_t.doom; ds.gamemode=GameMode_t.shareware; ds.wminfo=new wbstartstruct_t(); C2JUtils.initArrayOfObjects(ds.players,player_t.class); ds.DM=ds; ds.W=W; ds.V=V; ds.RND=new DoomRandom(); ds.players[0].cmd=new ticcmd_t(); ds.players[0].itemcount=1337; ds.players[0].killcount=1337; ds.players[0].secretcount=1337; ds.players[0].weaponowned[0]=true; ds.players[0].weaponowned[1]=true; ds.players[0].weaponowned[2]=true; ds.players[0].weaponowned[3]=true; ds.players[0].readyweapon=weapontype_t.wp_pistol; ds.players[0].health[0]=100; ds.players[0].armorpoints[0]=100; ds.players[0].ammo[0]=400; ds.players[0].maxammo[0]=400; ds.players[0].ammo[1]=100; ds.players[0].maxammo[1]=100; ds.players[0].ammo[2]=100; ds.players[0].maxammo[2]=100; ds.players[0].ammo[3]=600; ds.players[0].maxammo[3]=600; ds.players[0].cards[0]=true; ds.players[0].cards[2]=true; ds.players[0].cards[4]=true; ds.players[0].mo=new mobj_t(); ds.players[0].mo.x=1056<<FRACBITS; ds.players[0].mo.y=-3616<<FRACBITS; ds.players[0].powers[pw_allmap]=100; ds.deathmatch=false; ds.statusbaractive=true; ds.wminfo.plyr[0].in=true; ds.wminfo.plyr[0].sitems=1337; ds.wminfo.plyr[0].skills=1337; ds.wminfo.plyr[0].stime=28595; ds.wminfo.plyr[0].ssecret=1337; ds.playeringame[0]=true; ds.wminfo.last=6; ds.wminfo.epsd=0; ds.wminfo.maxitems=100; ds.wminfo.maxkills=100; ds.wminfo.maxsecret=100; ds.wminfo.partime=28595; StatusBar ST=new StatusBar(ds); ds.ST=ST; ST.Start(); LevelLoader LL=new LevelLoader(ds); LL.SetupLevel(1, 1, 0, skill_t.sk_hard); ds.LL=LL; ds.ST=ST; DoomAutoMap AM=new Map(ds); ds.AM=AM; AM.Start(); Actions P=new Actions(ds); ds.P=P; ST.Responder(new event_t('i')); ST.Responder(new event_t('d')); ST.Responder(new event_t('d')); ST.Responder(new event_t('t')); AM.Responder(new event_t(Map.AM_FOLLOWKEY)); AM.Responder(new event_t(Map.AM_ZOOMOUTKEY)); AM.Responder(new event_t(Map.AM_GRIDKEY)); //BufferedImage bi=((BufferedRenderer)V).screenbuffer[0]; //BufferedImage bi2=((BufferedRenderer)V).cloneScreen(0, icm2); CrappyDisplay frame = new CrappyDisplay(pals); frame.setTitle("MochaDoom"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.pack(); frame.setLocationRelativeTo(null); //frame.setUndecorated(true); frame.setVisible(true); frame.setBounds(frame.getX(), frame.getY(), WIDTH, 240); long a=System.nanoTime(); int TICKS=10000; for (int i=0;i<TICKS;i++){ if ((i%20)<10) AM.Responder(new event_t(Map.AM_ZOOMINKEY)); else AM.Responder(new event_t(Map.AM_ZOOMOUTKEY)); if ((i%25)<12) { AM.Responder(new event_t(Map.AM_PANUPKEY)); AM.Responder(new event_t(Map.AM_PANRIGHTKEY)); } else { AM.Responder(new event_t(Map.AM_PANDOWNKEY)); AM.Responder(new event_t(Map.AM_PANLEFTKEY)); } AM.Ticker(); AM.Drawer(); ST.Ticker(); ST.Drawer(false,true); frame.setPalette((i/(10000/14))%14); frame.processEvents(); frame.update(); //frame.update(); //frame.update(shit.getGraphics()); } long b=System.nanoTime(); System.out.println(TICKS +" tics in " +((b-a)/1e09) +" = "+TICKS/((b-a)/1e09) + " fps"); } catch (Exception e){ e.printStackTrace(); } } } package testers; /** Tester for bots...heh goodnight. * */ import static p.MapUtils.InterceptVector; import java.awt.Point; import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.SocketTimeoutException; import java.util.ArrayList; import java.util.HashSet; import java.util.LinkedList; import n.BasicNetworkInterface; import p.divline_t; import p.intercept_t; import p.mobj_t; import rr.line_t; import rr.sector_t; import awt.OldAWTDoom; import b.BotGame; import b.Reachable; import b.SearchNode_t; import b.bot_t; import b.BotGame.ObjKind; import b.BotGame.ReachableGroup; import b.BotGame.SeenNode; import b.BotPaths.BotPath; import b.SearchNode_t.BlockingSector; import defines.card_t; import doom.DoomContext; import doom.DoomMain; import doom.doomdata_t; import doom.player_t; import doom.think_t; import doom.thinker_t; import doom.ticcmd_t; public class BotTester { static DoomMain D4, D2, D1; static Object synchronizer = new Object(); public static class NetInterfaceTester extends BasicNetworkInterface { DoomContext DC; public NetInterfaceTester(DoomContext DC) { super(DC); // TODO Auto-generated constructor stub } private doomdata_t sendData = new doomdata_t(); private doomdata_t recvData = new doomdata_t(); @Override public void sendSocketPacket(DatagramSocket ds, DatagramPacket dp) throws IOException { DoomMain receiver; if (DM.consoleplayer == 0) { receiver = D2; } else { receiver = D4; } NetInterfaceTester N = (NetInterfaceTester)receiver.DNI; N.addReceivedPacket(dp); sendData.unpack(dp.getData()); //System.out.println("Player "+DM.consoleplayer+" SENT: "+dataToStr(sendData)); } /*class Elem<E> { E e; E next = null; public Elem(E e) { this.e = e; } } Elem<DatagramPacket> head = new Elem<DatagramPacket>(null);*/ //ArrayList<DatagramPacket> al = new ArrayList<DatagramPacket>(); LinkedList<DatagramPacket> al = new LinkedList<DatagramPacket>(); public void addReceivedPacket(DatagramPacket dp) { synchronized(synchronizer) { al.addLast(dp); } } @Override public void socketGetPacket(DatagramSocket ds, DatagramPacket dp) throws IOException { synchronized(synchronizer) { if (al.size() < 1) throw new SocketTimeoutException(); DatagramPacket pop = al.removeFirst(); dp.setData(pop.getData()); dp.setLength(pop.getLength()); dp.setSocketAddress(pop.getSocketAddress()); recvData.unpack(dp.getData()); } //System.out.println("Player "+DM.consoleplayer+" RECV: "+dataToStr(recvData)); } public String dataToStr(doomdata_t dt) { StringBuffer sb = new StringBuffer(); sb.append("STIC: "+dt.starttic+" NTIC: "+dt.numtics+" CMDS:\r\n"); for (int i = 0; i < dt.numtics; i++) { ticcmd_t tc = dt.cmds[i]; sb.append(" FMOVE: "+tc.forwardmove+" CONS: "+tc.consistancy+"\r\n"); } return sb.toString(); } } public static class DoomMainTester extends DoomMain { public player_t playerBot; public DoomMainTester(String[] args) { this.Init(); this.VI = new OldAWTDoom(this,null,null); this.DNI = new NetInterfaceTester(this); this.myargv = args; this.myargc = this.myargv.length; // Bump argcount +1 to maintain CheckParm behavior this.Start(); this.bgame.BN.B_InitNodes(); playerBot = this.players[0]; playerBot.bot = new bot_t(this.bgame, playerBot); playerBot.bot.walkReachable = new Reachable.WalkReachable(bgame, playerBot); } @Override public void DoomLoop() { System.out.println("Overriding DoomLoop"); } } public static void testE1L2() { DoomMainTester DM=new DoomMainTester(new String[] {"", "-warp", "12"}); line_t unlockLine = findLine(DM, new int[] {489, -1}); sector_t sec = DM.bgame.BSL.sectorsTaggedBy(unlockLine).get(0); sec.ceilingheight = 20709376; // this this was a bug where a 2nd round bridge was chosen instead of a 1st round in FindReachableNodes // So B_Findpath could not find a path mobj_t mo = findThing(DM, -2097152, -56623104); DM.playerBot.mo.x = 100962571; DM.playerBot.mo.y = -19975541; DM.playerBot.cards[card_t.it_redcard.ordinal()] = true; DM.bgame.FindReachableNodes(DM.playerBot); SearchNode_t srcNode = DM.bgame.botNodeArray[135][27]; SearchNode_t destNode = DM.bgame.botNodeArray[58][44]; ReachableGroup contained = (DM.playerBot.bot.botPaths.nodesContained(srcNode, destNode)); BotPath path = DM.playerBot.bot.botPaths.B_FindPath(srcNode, destNode, DM.playerBot.bot.walkReachable, DM.playerBot.bot.walkReachable, new HashSet<BlockingSector>(), contained); assrt(contained != null && path != null); /////////////////// /*SearchNode_t srcNode = DM.bgame.botNodeArray[135][27]; SearchNode_t destNode = DM.bgame.botNodeArray[67][19]; ReachableGroup contained = (DM.playerBot.bot.botPaths.nodesContained(srcNode, destNode)); */ SeenNode SN = DM.bgame.new SeenNode(DM.playerBot, mo); SN.setKind(ObjKind.item); SN.findPath(); DM.playerBot.bot.botPaths.B_FindPath(srcNode, destNode, DM.playerBot.bot.walkReachable, DM.playerBot.bot.walkReachable, new HashSet<BlockingSector>(), contained); ArrayList<SearchNode_t> srcNodes = DM.bgame.BN.findNodesFromPoint(DM.playerBot, new Point(DM.playerBot.mo.x, DM.playerBot.mo.y), DM.playerBot.bot.walkReachable /*finalReach*/); //if (srcNodes.size() == 0) // bot is really stuck! // return null; ArrayList<SearchNode_t> destNodes = SN.getNodes(); //if (destNodes.size() == 0) // return null; /*boolean contained = false; for (SearchNode_t destNode: destNodes) { //if (reachableNodes.contains(destNode)) { for (SearchNode_t srcNode: srcNodes) { if (DM.playerBot.bot.botPaths.nodesContained(srcNode, destNode)) { contained = true; } } }*/ assrt((SN.path!=null) == (contained!=null) ); System.out.println("test"); } public static void testDoomI() { testE1L2(); } public static void main(String[] args) { testDoomI(); // tested with Doom II maps D1=new DoomMainTester(new String[] {"", "-warp", "1"}); D2=new DoomMainTester(new String[] {"", "-warp", "2"}); D4=new DoomMainTester(new String[] {"", "-warp", "4"}); int[] sh = BotGame.shiftDxDy(new Point(25,25), new Point(50,50), 5); assrt(sh[0] == (int)(5/Math.sqrt(2))); assrt(sh[1] == (int)(-5/Math.sqrt(2))); // // //bot should not be able to traverse the two poles at startup of level 02 // Point inPoint = new Point(79352584, 120685114); // Point outPoint = new Point(88774769, 121236663); // D2.players[0].mo.x = inPoint.x; // D2.players[0].mo.y = inPoint.y; // LinkedList_t<SearchNode_t> path = new LinkedList_t<SearchNode_t>(); // // Reachable.WalkReachable wr2 = new Reachable.WalkReachable(D2.bgame, D2.players[0]); // assrt(!D2.bgame.BN.B_FindPath(D2.players[0], outPoint, path, wr2, wr2)); // // // make sure we cant make through the pole with this diagonal way // SearchNode_t node1 = D2.bgame.botNodeArray[35][53]; // SearchNode_t node2 = D2.bgame.botNodeArray[36][54]; // assrt(!D2.bgame.BN.FindPathReachable(D2.players[0], node1, node2, true)); // assrt(!D2.bgame.BN.FindPathReachable(D2.players[0], D2.bgame.BN.nodeToPoint(node1), D2.bgame.BN.nodeToPoint(node2), true)); // // // test if the first switch is reachable // line_t switch1 = findLine(D2, new int[] {188, -1}); // special: 102 v1: (80740352, 77594624) // SeenNode snSw1 = D2.bgame.new SeenNode(D2.players[0], switch1); // snSw1.reachable = new Reachable.LineReachable(D2.bgame, D2.players[0], switch1); // assrt(!snSw1.isReachable()); // inPoint = new Point(79181929, 81924198); // D2.players[0].mo.x = inPoint.x; // D2.players[0].mo.y = inPoint.y; // assrt(snSw1.isReachable()); // // // // inPoint = new Point(-48443906, 31553733); // line_t switchL4 = findLine(D4, new int[] {317, -1}); // special: 102 // SeenNode snSW = D4.bgame.new SeenNode(D4.players[0], switchL4); // snSW.reachable = new Reachable.LineReachable(D4.bgame, D4.players[0], switchL4); // D4.players[0].mo.x = inPoint.x; // D4.players[0].mo.y = inPoint.y; // // assrt(snSW.getNode() != null); // // assrt(snSW.isReachable()); // // // line_t floorL4 = findLine(D4, new int[] {733, -1}); // special: 18 // SeenNode snFloor = D4.bgame.new SeenNode(D4.players[0], floorL4); // inPoint = new Point(-111892518, 71522245); // D4.players[0].mo.x = inPoint.x; // D4.players[0].mo.y = inPoint.y; // snFloor.reachable = new Reachable.LineReachable(D4.bgame, D4.players[0], floorL4); // assrt(snFloor.isReachable()); // assrt(snFloor.findPath()); // // // in front of teleport // inPoint = new Point(-93134091, 52253824); // D4.players[0].mo.x = inPoint.x; // D4.players[0].mo.y = inPoint.y; // assrt(snFloor.findPath()); // // // line_t exitL4 = findLine(D4, new int[] {757, -1}); // special: 11 // SeenNode snExit = D4.bgame.new SeenNode(D4.players[0], exitL4); // inPoint = new Point(-127891116, 86164298); // D4.players[0].mo.x = inPoint.x; // D4.players[0].mo.y = inPoint.y; // snExit.reachable = new Reachable.LineReachable(D4.bgame, D4.players[0], exitL4); // assrt(snExit.isReachable() /*D4.bgame.BN.seenSwitchReachable(D4.players[0], inPoint, exitL4)*/); // assrt(snExit.findPath()); // //D4.bgame.BN.B_FindPath(D4.players[0], destPoint, path, walkReach, finalReach) // // //seems not to work if on the other side of the yellow door (because of the Demon?) // /*inPoint = new Point(-120594720, 85885648); // D4.players[0].mo.x = inPoint.x; // D4.players[0].mo.y = inPoint.y; // assrt(snExit.findPath());*/ // // // DoomMain D12=new DoomMainTester(new String[] {"", "-warp", "12"}); // inPoint = new Point(89169710, 117939298); // outPoint = new Point(87979765, 108184173); // Reachable.WalkReachable wr = new Reachable.WalkReachable(D12.bgame, D12.players[0]); // assrt(wr.isReachable(inPoint, outPoint)); // line_t lineD12 = findLine(D12, new int[] {1053, 1054}); // lineD12.frontsector.floorheight = 300000; // assrt(wr.isReachable(inPoint, outPoint)); // should still be considered as reachable because it is an elevator... // // // D2.P.PathTraverse(69206016, 125829120, 67108864, 125829120, 0, null); // seems OK // D2.P.PathTraverse(69206015, 125829119, 67108864, 125829120, 0, null); // seems to bug // D2.P.PathTraverse(69206017, 125829119, 67108864, 125829120, 0, null); // seems to bug // // // a rare case when changeX and changeY are both true // D1.P.PathTraverse(132120575, -56623105, 132120576, -54525952, 0, null); // seems OK // // //D2: Bug with Pathtraverse: (69206015,125829119) to (67108864,125829120) // //D2: Bug with Pathtraverse: (69206017,125829119) to (67108864,125829120) // //D2: Seems OK: (69206016,125829120) to (67108864,125829120) // // //D1: Bug with Pathtraverse: (132120575,-56623105) to (132120576,-54525952) // // traverseTester(D4); // // /*SearchNode_t srcNode = D4.bgame.BN.B_GetNodeAt(-29897546, 44775535,null); // SearchNode_t destNode = D4.bgame.BN.B_GetNodeAt(-27247827, 42609240,null); // LinkedList<intercept_t> q = D4.bgame.QueuePathTraverse(nodeToPoint(D4, srcNode), nodeToPoint(D4, destNode)); // assrt(!D4.bgame.BN.FindPathReachable(D4.players[0], srcNode, destNode, true)); // // srcNode.x = 48; // srcNode.y = 13; // destNode.x = 47; // destNode.y = 12; // assrt(!D4.bgame.BN.FindPathReachable(D4.players[0], srcNode, destNode, true)); // // D4.players[0].mo.x = -28202630; // D4.players[0].mo.y = 48802275; // // path = new LinkedList_t<SearchNode_t>(); // assrt(!D4.bgame.BN.B_FindPath(D4.players[0], new Point(-28778704, 34686148), path, D4.bgame.BN.walkReachable, D4.bgame.BN.walkReachable)); //should not have a path from "lava" to "step too high" // // for (SearchNode_t sn: path) { // System.out.println(D4.bgame.BN.nodeInfo(sn)); // }*/ // // /*new Thread() { // public void run() { // D1.Start (); // } // }.start(); // // try { // Thread.sleep(1000); // } catch (InterruptedException e) { // // TODO Auto-generated catch block // e.printStackTrace(); // } // // //D1.bgame.Got_AddBot(0,0); // // /*D2=new DoomMain(); // D2.Init(); // D2.DNI = new NetInterfaceTester(D2); // D2.myargv = new String[] {"", "-net", "2", "localhost"}; // D2.myargc = D2.myargv.length; // Bump argcount +1 to maintain CheckParm behavior // // new Thread() { // public void run() { // D1.Start (); // } // }.start(); // // try { // Thread.sleep(1000); // } catch (InterruptedException e) { // // TODO Auto-generated catch block // e.printStackTrace(); // } // // new Thread() { // public void run() { // D2.Start (); // } // }.start();*/ // } public static Point nodeToPoint(DoomMain DM, SearchNode_t node) { return new Point(DM.bgame.BN.posX2x(node.x), DM.bgame.BN.posY2y(node.y)); } public static sector_t findSector(DoomMain DM, int id/*int special, int floorheight*/) { for (sector_t sec: DM.LL.sectors) { if (/*sec.special == special && sec.floorheight == floorheight*/sec.id == id) { return sec; } } return null; } public static line_t findLine(DoomMain DM, int[] sidenum /*int special, int floorheight*/) { for (line_t line: DM.LL.lines) { if (line.sidenum.length == 2 && sidenum[0]==line.sidenum[0] && sidenum[1]==line.sidenum[1]) { return line; } } return null; } public static mobj_t findThing(DoomMain DM, int x, int y) { thinker_t currentthinker = DM.P.thinkercap.next; while (currentthinker != DM.P.thinkercap) // search through the list // of all thinkers { if (currentthinker.function/* .acp1 */== think_t./* (actionf_p1) */P_MobjThinker) { mobj_t mo = (mobj_t) currentthinker; if (mo.x == x && mo.y == y) return mo; } currentthinker = currentthinker.next; } return null; } public static void traverseTester(DoomMain DM) { sector_t secLava = findSector(DM, 24);//findSector(DM, 16, -1572864); line_t lineLava = findLine(DM, new int[] {111,112});//findSector(DM, 16, -1572864); Point p1 = new Point((lineLava.v1x + lineLava.v2x)/2, lineLava.v1y + 10); // in lava sector Point p2 = new Point((lineLava.v1x + lineLava.v2x)/2, lineLava.v1y - 10); // on step Point p3 = new Point((lineLava.v1x + lineLava.v2x)/2, lineLava.v1y); D4.bgame.pointOnLine(p3, lineLava); divline_t trac=new divline_t(); trac.x = p1.x; trac.y = p1.y; trac.dx = p2.x-p1.x; trac.dy = p2.y-p1.y; divline_t trac2=new divline_t(); trac2.x = p2.x; trac2.y = p2.y; trac2.dx = p1.x-p2.x; trac2.dy = p1.y-p2.y; divline_t dl1 = new divline_t(); dl1.MakeDivline(lineLava); InterceptVector(trac, /*new divline_t(lineLava)*/dl1); InterceptVector(trac2, /*new divline_t(lineLava)*/dl1); LinkedList<intercept_t> q = D4.bgame.QueuePathTraverse(p1, p2); assrt(q.size()==1 && q.get(0).line == lineLava); LinkedList<intercept_t> q2 = D4.bgame.CorrectedQueuePathTraverse(p1, p2); assrt(q2.size()==1 && q2.get(0).line == lineLava); LinkedList<intercept_t> q3 = D4.bgame.QueuePathTraverse(p1, p3); assrt(q3.size()==1 && q3.get(0).line == lineLava); // on one side we will see the line LinkedList<intercept_t> q4 = D4.bgame.CorrectedQueuePathTraverse(p1, p3); assrt(q4.size()==1 && q4.get(0).line == lineLava); LinkedList<intercept_t> q5 = D4.bgame.QueuePathTraverse(p3, p1); assrt(q5.size()==1 && q5.get(0).line == lineLava); LinkedList<intercept_t> q6 = D4.bgame.CorrectedQueuePathTraverse(p3, p1); assrt(q6.size()==0); // we want to ignore a line that srcPoint is on LinkedList<intercept_t> q7 = D4.bgame.QueuePathTraverse(p2, p1); assrt(q7.size()==1 && q7.get(0).line == lineLava); LinkedList<intercept_t> q8 = D4.bgame.CorrectedQueuePathTraverse(p2, p1); assrt(q8.size()==1 && q8.get(0).line == lineLava); LinkedList<intercept_t> q9 = D4.bgame.QueuePathTraverse(p3, p2); assrt(q9.size()==0 /*&& q9.get(0).line == lineLava*/); //on the other side we don't see the line LinkedList<intercept_t> q10 = D4.bgame.CorrectedQueuePathTraverse(p3, p2); assrt(q10.size()==0); LinkedList<intercept_t> q11 = D4.bgame.QueuePathTraverse(p2, p3); assrt(q11.size()==0 /*&& q11.get(0).line == lineLava*/); //on the other side we don't see the line LinkedList<intercept_t> q12 = D4.bgame.CorrectedQueuePathTraverse(p2, p3); assrt(q12.size()==1 && q12.get(0).line == lineLava); // we want to include the line destPoint is on LinkedList<BlockingSector> bs1 = D4.bgame.TraversedSecLines(p1, p2); assrt(bs1.size() == 1); LinkedList<BlockingSector> bs2 = D4.bgame.TraversedSecLines(p2, p1); assrt(bs2.size() == 1); assrt(bs1.get(0).srcSect == bs2.get(0).destSect); assrt(bs2.get(0).srcSect == bs1.get(0).destSect); // bs1 should be bs2 reversed LinkedList<BlockingSector> bs3 = D4.bgame.TraversedSecLines(p1, p3); assrt(bs3.size() == 1); LinkedList<BlockingSector> bs4 = D4.bgame.TraversedSecLines(p3, p1); assrt(bs4.size() == 0); // because we ignore the lines we start exactly on assrt(bs1.get(0).srcSect == bs3.get(0).srcSect); assrt(bs1.get(0).destSect == bs3.get(0).destSect); //bs3 should be the same as bs1 } static void assrt(boolean b) { if (!b) { System.out.println("Assert failed"); } } } package testers; import static data.Defines.PU_STATIC; import i.IDoomSystem; import java.awt.image.BufferedImage; import java.awt.image.IndexColorModel; import awt.OldAWTDoom; import m.DoomRandom; import s.IDoomSound; import s.DummySoundDriver; import utils.C2JUtils; import v.BufferedRenderer; import w.DoomBuffer; import w.WadLoader; import data.Defines; import defines.*; import doom.DoomMain; import doom.player_t; import doom.ticcmd_t; import doom.wbstartstruct_t; import f.EndLevel; /** This is a very simple tester for the End Level screen drawer. * MAES: this is mostly historical. Too many changes have occured * and it's no longer easy to operate stand-alone. * * Edit: well, maybe it is... * */ public class EndLevelTester { public static void main(String[] argv) { try { WadLoader W=new WadLoader(); W.InitMultipleFiles(new String[] {"doom1.wad"}); //W.AddFile("bitter.wad"); System.out.println("Total lumps read: "+W.numlumps); DoomBuffer palette = W.CacheLumpName("PLAYPAL", PU_STATIC); byte[] pal=palette.getBuffer().array(); IndexColorModel icm=new IndexColorModel(8, 256,pal, 0, false); BufferedRenderer V=new BufferedRenderer(Defines.SCREENWIDTH,Defines.SCREENHEIGHT,icm); V.Init(); IndexColorModel[] icms=new IndexColorModel[pal.length/768]; BufferedImage[] pals=new BufferedImage[icms.length]; for (int i=0;i<icms.length;i++){ icms[i]=new IndexColorModel(8, 256,pal, i*768, false); pals[i]=new BufferedImage(icms[i],V.screenbuffer[0].getRaster(), false, null); } DoomMain DC=new DoomMain(); DC.DM=DC; IDoomSystem I=new i.DoomSystem(); IDoomSound S=new DummySoundDriver(); DC.S=S; DC.I=I; DC.gameepisode=1; DC.gamemap=1; DC.gamemission=GameMission_t.doom; DC.gamemode=GameMode_t.shareware; DC.wminfo=new wbstartstruct_t(); C2JUtils.initArrayOfObjects(DC.players,player_t.class); DC.DM=DC; DC.W=W; DC.V=V; DC.RND=new DoomRandom(); DC.players[0].cmd=new ticcmd_t(); DC.players[0].itemcount=1337; DC.players[0].killcount=1337; DC.players[0].secretcount=1337; DC.wminfo.plyr[0].in=true; DC.wminfo.plyr[0].sitems=1337; DC.wminfo.plyr[0].skills=1337; DC.wminfo.plyr[0].stime=28595; DC.wminfo.plyr[0].ssecret=1337; DC.playeringame[0]=true; DC.wminfo.last=6; DC.wminfo.epsd=0; DC.wminfo.maxitems=100; DC.wminfo.maxkills=100; DC.wminfo.maxsecret=100; DC.wminfo.partime=28595; //JFrame frame = new JFrame("MochaDoom"); OldAWTDoom shit = new OldAWTDoom(DC, V, pal); shit.InitGraphics(); //frame.setBounds(frame.getX(), frame.getY(), WIDTH, 240); EndLevel EL=new EndLevel(DC); // EL.Start(wbstartstruct); int a,b; a=I.GetTime(); b=a; for (int i=0;i<2000;i++){ EL.Ticker(); EL.Drawer(); shit.update(shit.getGraphics()); if (i==100){ DC.players[0].cmd.buttons=1; // simulate attack DC.players[0].attackdown=false; // simulate attack } if (i==120){ DC.players[0].cmd.buttons=1; // simulate attack DC.players[0].attackdown=false; // simulate attack } // Do we still have time> while((b-a)==0) { b=I.GetTime(); } a=b; } }catch (Exception e){ e.printStackTrace(); } } } package testers; import java.io.FileNotFoundException; import java.io.IOException; import java.nio.ByteBuffer; import p.LevelLoader; import rr.SimpleTextureManager; import rr.TextureManager; import s.DummySoundDriver; import s.IDoomSound; import savegame.VanillaDSG; import savegame.VanillaDSGHeader; import m.MenuMisc; import defines.GameMission_t; import defines.GameMode_t; import defines.skill_t; import demo.VanillaDoomDemo; import doom.DoomMain; import doom.DoomStatus; import w.*; public class SaveGameHeaderTester { public static void main(String[] argv) throws Exception { ByteBuffer buf=MenuMisc.ReadFile("doomsav0.dsg"); DoomFile f=new DoomFile("doomsav0.dsg","r"); DoomStatus DS=new DoomStatus(); VanillaDSGHeader vdsg=new VanillaDSGHeader(); try { vdsg.read(f); f.seek(0); WadLoader W=new WadLoader(); W.InitMultipleFiles(new String[] {"C:\\DOOMS\\doom1.wad"}); //W.AddFile("bitter.wad"); System.out.println("Total lumps read: "+W.numlumps); System.out.println("NUm for E1M1: "+W.GetNumForName("E1M1")); DS.gameepisode=1; DS.gamemap=1; DS.gamemission=GameMission_t.doom; DS.setGameMode(GameMode_t.shareware); IDoomSound S=new DummySoundDriver(); DS.S=S; DS.W=W; LevelLoader LL=new LevelLoader(DS); DS.LL=LL; TextureManager TM=new SimpleTextureManager(DS); DS.TM=TM; LL.updateStatus(DS); TM.InitFlats(); TM.InitTextures(); //HU hu=new HU(DS); //hu.Init(); LL.SetupLevel(1, 1, 0, skill_t.sk_hard); VanillaDSG DSG=new VanillaDSG(); DSG.updateStatus(DS); DS.playeringame[0]=true; DS.players[0].updateStatus(DS); DSG.doLoad(f); // W.AddFile("bitter.wad"); System.out.println(vdsg); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } package testers; import java.awt.image.BufferedImage; import java.awt.image.DataBufferByte; import java.io.BufferedInputStream; import java.io.ByteArrayInputStream; import java.io.FileInputStream; import java.io.FileOutputStream; import java.nio.ByteBuffer; import java.nio.ByteOrder; import javax.imageio.ImageIO; public class PaletteShower15 { public static final int BLOCKSIZE=16; public static final int SHIFT=3; public static void main(String[] argv) { try { BufferedInputStream bis; bis=new BufferedInputStream(new FileInputStream(argv[0])); int size=bis.available(); int blocks=size/512; byte[] palbuf=new byte[512]; ByteBuffer bb= ByteBuffer.wrap(palbuf); bb.order(ByteOrder.LITTLE_ENDIAN); bb.mark(); for (int i=0;i<blocks;i++){ bis.read(palbuf); BufferedImage bim=new BufferedImage(16*BLOCKSIZE,16*BLOCKSIZE,BufferedImage.TYPE_INT_ARGB); for (int j=0;j<256;j++){ short shcol=bb.getShort(); int r=(0x7C00&shcol)>>10; int g=(0x3E0&shcol)>>5; int b=(0x01F&shcol); System.out.printf("%x %d %d %d\n",shcol,r,g,b); int colo= 0xFF000000|(r<<(16+SHIFT))| (g<<(8+SHIFT))| (b<<SHIFT); for (int x=0;x<BLOCKSIZE;x++){ for (int y=0;y<BLOCKSIZE;y++){ bim.setRGB(BLOCKSIZE*(j%16)+x, BLOCKSIZE*(j/16)+y, colo); } } } FileOutputStream f=new FileOutputStream(String.format("%s-%d.PNG",argv[0],i)); ImageIO.write(bim,"PNG",f); bb.reset(); } bis.close(); } catch (Exception e){ e.printStackTrace(); } } } package testers; import p.LevelLoader; import rr.UnifiedRenderer; import utils.C2JUtils; import w.WadLoader; import data.Defines.GameMission_t; import data.Defines.GameMode_t; import data.Defines.skill_t; import doom.DoomContext; import doom.DoomStatus; import doom.player_t; import doom.wbstartstruct_t; /** This is a very simple tester for the WadLoader and HU modules. * We use the same exact methods used in the C source code, only * with a more OO approach. * * */ public class RenderDataLoader { public static void main(String[] argv) { try { WadLoader W=new WadLoader(); W.InitMultipleFiles(new String[] {"doom1.wad"}); System.out.println("Total lumps read: "+W.numlumps); DoomStatus ds = new DoomStatus(); ds.gameepisode=1; ds.gamemap=1; ds.gamemission=GameMission_t.doom; ds.gamemode=GameMode_t.shareware; ds.wminfo=new wbstartstruct_t(); C2JUtils.initArrayOfObjects(ds.players,player_t.class); DoomContext DC=new DoomContext(); DC.DS=ds; DC.W=W; LevelLoader LL=new LevelLoader(DC); DC.LL=LL; LL.SetupLevel(1, 1, 0, skill_t.sk_hard); UnifiedRenderer RD=new UnifiedRenderer(DC); RD.InitData(); RD.RenderPlayerView(ds.players[0]); } catch (Exception e){ e.printStackTrace(); } } } package testers; import m.FixedFloat; import m.fixed_t; import static m.fixed_t.*; class FPTest{ public static final int PRECISION=16; public static void main(String argv[]) { byte aa=(byte) 129; fixed_t a=new fixed_t(0x8FFF0000); fixed_t b=new fixed_t(0xFFFFFFFF); // a=F2F(32393.244141f); // b=F2F(2.5f); /*System.out.println(Integer.toHexString(a.val)); System.out.println(Integer.toHexString(b.val)); System.out.println(FixedFloat.toFloat(a.val)); System.out.println(FixedFloat.toFloat(b.val)); System.out.println(FixedFloat.toDouble(a.val)); System.out.println(FixedFloat.toDouble(b.val));*/ int c=FixedFloat.toFixed(2.512344f); System.out.println(Integer.toHexString(c)); System.out.println(FixedFloat.toFloat(c)); System.out.println(Integer.toBinaryString(Float.floatToRawIntBits(FixedFloat.toFloat(c)))); int d=FixedFloat.toFixed(32768.0125d); int e=FixedFloat.toFixed(Double.NEGATIVE_INFINITY); int f=FixedFloat.toFixed(-2.5123d); System.out.println(FixedFloat.toFloat(d)); System.out.println(FixedFloat.toFloat(e)); System.out.println(FixedFloat.toFloat(f)); System.out.println(FixedFloat.toDouble(d)); System.out.println(FixedFloat.toDouble(e)); System.out.println(FixedFloat.toDouble(f)); int g=FixedFloat.toFixed(10.0); int h=FixedFloat.toFixed(3.0); System.out.println(FixedFloat.toFloat(FixedDiv(g,h))); //System.out.println(Integer.toBinaryString(Float.floatToRawIntBits(FixedFloat.toDouble(c)))); /* fixed_t.FixedMul(a,b,b); System.out.println(Integer.toHexString(b.get())); a=F2F(2.5f); b=F2F(2.5f); // a.set(a.val+b.val-(fixed_t.FixedMul(F2F(1.5f),a)).val); System.out.println(Integer.toHexString(a.get())); a=F2F(10000.0f); b=F2F(0.5657f); System.out.println(Integer.toHexString(a.val)); System.out.println(Integer.toHexString(b.val)); a=fixed_t.FixedDiv(a,b); System.out.println(Integer.toHexString(a.val)); */ } public static fixed_t F2F(float f){ fixed_t tmp; int ing; float frac; ing=(int)Math.floor(f); //System.out.println("Int: "+(int)(f)); //System.out.println("Hex: "+Integer.toHexString(ing)); //System.out.println("Frac: "+(f-Math.floor(f))); //System.out.println("Frac hex: "+Integer.toHexString(FixedDecimal(f))); tmp= new fixed_t((ing<<16)|FixedDecimal(f)); //System.out.println(Integer.toHexString(tmp.val)); return tmp; } public static char FixedDecimal(float f){ char fixint_value=0; float decimal_part= (float) (f-Math.floor(f)); for ( int i = 1; i <= PRECISION; i++) { if (decimal_part > 1.f/(float)(i + 1.0)) { decimal_part -= 1.f/(float)(i + 1.0); fixint_value |= (1 << PRECISION - i); } } return fixint_value; } public static double Value(int fixed){ double dec=(fixed>>FRACBITS); double frac=0;char fixint_value=0; for ( int i = FRACBITS; i >=0; i--) { if (((fixed>>FRACBITS)&(0x0001))==1) { frac+=1/(2+(FRACBITS-i)); } } return (dec+frac); } } package testers; import i.IVideo; public class IVideoTester { public static void main(String argv[]){ IVideo IV=new IVideo(); IV.InitExpand(); IV.InitExpand2(); IV.InitExpand(); } } package testers; import static data.Defines.KEY_BACKSPACE; import static data.Defines.KEY_DOWNARROW; import static data.Defines.KEY_ENTER; import static data.Defines.KEY_EQUALS; import static data.Defines.KEY_ESCAPE; import static data.Defines.KEY_F1; import static data.Defines.KEY_F10; import static data.Defines.KEY_F11; import static data.Defines.KEY_F12; import static data.Defines.KEY_F2; import static data.Defines.KEY_F3; import static data.Defines.KEY_F4; import static data.Defines.KEY_F5; import static data.Defines.KEY_F6; import static data.Defines.KEY_F7; import static data.Defines.KEY_F8; import static data.Defines.KEY_F9; import static data.Defines.KEY_LEFTARROW; import static data.Defines.KEY_MINUS; import static data.Defines.KEY_PAUSE; import static data.Defines.KEY_RALT; import static data.Defines.KEY_RCTRL; import static data.Defines.KEY_RIGHTARROW; import static data.Defines.KEY_RSHIFT; import static data.Defines.KEY_TAB; import static data.Defines.KEY_UPARROW; import i.InputListener; import java.awt.AWTEvent; import java.awt.Canvas; import java.awt.Color; import java.awt.Dimension; import java.awt.Event; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Image; import java.awt.event.InputEvent; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.awt.image.BufferedImage; import java.util.LinkedList; import javax.swing.JFrame; import doom.event_t; import doom.evtype_t; public class CrappyDisplay extends JFrame implements KeyListener{ /** * */ private static final long serialVersionUID = 1L; Canvas drawhere; BufferedImage[] bi; int palette=0; Dimension size; Image crap; //InputListener in; Graphics2D g2d; public CrappyDisplay(BufferedImage[] bi) { this.setBackground(Color.black); this.bi=bi; size = new Dimension(); size.width = bi[0].getWidth(); size.height = bi[0].getHeight(); this.setSize(size); drawhere=new Canvas(); drawhere.setSize(size); drawhere.setBounds(0, 0, drawhere.getWidth(),drawhere.getHeight()); drawhere.setBackground(Color.black); this.add(drawhere); this.addKeyListener(this); //this.addComponentListener(in); this.setVisible(true); g2d = (Graphics2D)drawhere.getGraphics(); } public CrappyDisplay(BufferedImage bi) { this.bi=new BufferedImage[]{bi}; size = new Dimension(); size.width = bi.getWidth(); size.height = bi.getHeight(); this.setSize(size); drawhere=new Canvas(); drawhere.setSize(size); drawhere.setBounds(0, 0, drawhere.getWidth(),drawhere.getHeight()); drawhere.setBackground(Color.black); this.add(drawhere); //in = new InputListener(); //this.addComponentListener(in); this.setVisible(true); g2d = (Graphics2D)drawhere.getGraphics(); } public void setPalette(int pal){ this.palette=pal; } public void update(Graphics g) { //Graphics2D g2d = (Graphics2D)drawhere.getGraphics(); //voli.getGraphics().drawImage(bi,0,0,null); g2d.drawImage(bi[palette],0,0,this); } public void update() { Graphics2D g2d = (Graphics2D)drawhere.getGraphics(); //voli.getGraphics().drawImage(bi,0,0,null); g2d.drawImage(bi[palette],0,0,this); } public String processEvents(){ StringBuffer tmp=new StringBuffer(); event_t event; while ( (event=InputListener.nextEvent()) != null ) { tmp.append(event.type.ordinal()+"\n"); } return tmp.toString(); } public int xlatekey(KeyEvent e) { int rc; switch(rc = e.getKeyCode()) //Event.XKeycodeToKeysym(X_display, X_event.xkey.keycode, 0)) { case KeyEvent.VK_LEFT: rc = KEY_LEFTARROW; break; case KeyEvent.VK_RIGHT: rc = KEY_RIGHTARROW; break; case KeyEvent.VK_DOWN: rc = KEY_DOWNARROW; break; case KeyEvent.VK_UP: rc = KEY_UPARROW; break; case KeyEvent.VK_ESCAPE: rc = KEY_ESCAPE; break; case KeyEvent.VK_ENTER: rc = KEY_ENTER; break; case KeyEvent.VK_TAB: rc = KEY_TAB; break; case KeyEvent.VK_F1: rc = KEY_F1; break; case KeyEvent.VK_F2: rc = KEY_F2; break; case KeyEvent.VK_F3: rc = KEY_F3; break; case KeyEvent.VK_F4: rc = KEY_F4; break; case KeyEvent.VK_F5: rc = KEY_F5; break; case KeyEvent.VK_F6: rc = KEY_F6; break; case KeyEvent.VK_F7: rc = KEY_F7; break; case KeyEvent.VK_F8: rc = KEY_F8; break; case KeyEvent.VK_F9: rc = KEY_F9; break; case KeyEvent.VK_F10: rc = KEY_F10; break; case KeyEvent.VK_F11: rc = KEY_F11; break; case KeyEvent.VK_F12: rc = KEY_F12; break; //case Event.BACK_SPACE: case KeyEvent.VK_DELETE: rc = KEY_BACKSPACE; break; case KeyEvent.VK_PAUSE: rc = KEY_PAUSE; break; case KeyEvent.KEY_PRESSED: switch(e.getKeyCode()){ case (KeyEvent.VK_EQUALS): rc = KEY_EQUALS; break; case KeyEvent.VK_SUBTRACT: case KeyEvent.VK_MINUS: rc = KEY_MINUS; break; case KeyEvent.SHIFT_DOWN_MASK: rc = KEY_RSHIFT; break; case KeyEvent.CTRL_DOWN_MASK: rc = KEY_RCTRL; break; case KeyEvent.ALT_DOWN_MASK: rc = KEY_RALT; break; } default: /*if (rc >= KeyEvent.VK_SPACE && rc <= KeyEvent.VK_DEAD_TILDE) rc = (int) (rc - KeyEvent.FOCUS_EVENT_MASK + ' ');*/ if (rc >= KeyEvent.VK_A && rc <= KeyEvent.VK_Z) rc = rc-KeyEvent.VK_A +'a'; break; } System.out.println("Typed "+e.getKeyCode()+" char "+e.getKeyChar()+" mapped to "+Integer.toHexString(rc)); return rc; } public void keyPressed(KeyEvent e) { if (!((e.getModifiersEx() & InputEvent.ALT_GRAPH_DOWN_MASK) != 0)) { addEvent(new event_t(evtype_t.ev_keydown, xlatekey(e))); } } public void keyReleased(KeyEvent e) { // addEvent(new event_t(evtype_t.ev_keyup,xlatekey(e))); } public void keyTyped(KeyEvent e) { /* if ((e.getModifiersEx() & InputEvent.ALT_GRAPH_DOWN_MASK) != 0) { addEvent(new event_t(evtype_t.ev_keydown, xlatekey(e))); addEvent(new event_t(evtype_t.ev_keyup, xlatekey(e))); } */ } static void addEvent(event_t ev) { synchronized (eventQueue) { eventQueue.addLast(ev); } } public static event_t nextEvent() { event_t ev; synchronized (eventQueue) { ev = (!eventQueue.isEmpty())?(event_t)eventQueue.removeFirst():null; } return ev; } // modifications of eventQueue must be thread safe! private static LinkedList<event_t> eventQueue = new LinkedList<event_t>(); } package testers; import static data.Defines.PU_STATIC; import static data.Limits.MAXEVENTS; import n.DummyNetworkHandler; import hu.HU; import i.DoomSystem; import i.IDoomSystem; import p.Actions; import p.ILevelLoader; import p.LevelLoader; import automap.Map; import awt.AWTDoom; import rr.ParallelRenderer; import rr.SimpleTextureManager; import rr.SpriteManager; import s.DummySoundDriver; import st.StatusBar; import timing.MilliTicker; import m.IDoomMenu; import m.Menu; import m.DoomRandom; import v.BufferedRenderer; import v.DoomVideoRenderer; import v.GammaTables; import v.IVideoScale; import v.VideoScaleInfo; import w.DoomBuffer; import w.WadLoader; import defines.*; import data.Tables; import doom.CommandLine; import doom.DoomMain; import doom.event_t; import doom.ticcmd_t; import doom.wbstartstruct_t; /** This is a very simple tester for Menu module */ public class AWTParallelRenderTester1 { static IVideoScale VSI=new VideoScaleInfo(4.0f); public static void main(String[] argv) { try { Tables.InitTables(); // Create a Wad file loader. WadLoader W=new WadLoader(); W.InitMultipleFiles(new String[] {"doom1.wad"}); System.out.println("Total lumps read: "+W.numlumps); // Read the palette. DoomBuffer palette = W.CacheLumpName("PLAYPAL", PU_STATIC); // Create a video renderer DoomVideoRenderer V=new BufferedRenderer(VSI.getScreenWidth(),VSI.getScreenHeight()); V.setVideoScale(VSI); V.initScaling(); V.Init(); byte[] pal=palette.getBuffer().array(); V.createPalettes(pal, GammaTables.gammatables, 14, 256, 3, 5); IDoomSystem I=new DoomSystem(); DoomMain DM=new DoomMain(); CommandLine CM=new CommandLine(argv); DM.CM=CM; DM.TICK=new MilliTicker(); DM.setVideoScale(VSI); DM.initScaling(); DM.singletics=true; DM.setTicdup(1); DM.DGN=new DummyNetworkHandler(); // Create the frame. AWTDoom frame = new AWTDoom(DM,V); frame.InitGraphics(); DM.I=I; DM.VI=frame; DM.S=new DummySoundDriver(); frame.DM=DM; DM.W=W; DM.V=V; DM.DM=DM; HU HU=new HU(DM); DM.language=Language_t.english; HU.Init(); DM.HU=HU; DM.gameepisode=1; DM.gamemap=1; DM.gamemission=GameMission_t.doom; DM.setGameMode(GameMode_t.shareware); DM.wminfo=new wbstartstruct_t(); // Simulate being in the mid of a level. DM.usergame=true; DM.gamestate=gamestate_t.GS_LEVEL; DM.gameskill=skill_t.sk_hard; //C2JUtils.initArrayOfObjects(DM.players,player_t.class); DM.RND=new DoomRandom(); DM.players[0].cmd=new ticcmd_t(); DM.players[0].itemcount=1337; DM.players[0].killcount=1337; DM.players[0].secretcount=1337; DM.wminfo.plyr[0].in=true; DM.wminfo.plyr[0].sitems=1337; DM.wminfo.plyr[0].skills=1337; DM.wminfo.plyr[0].stime=28595; DM.wminfo.plyr[0].ssecret=1337; DM.playeringame[0]=true; DM.wminfo.last=6; DM.wminfo.epsd=0; DM.wminfo.maxitems=100; DM.wminfo.maxkills=100; DM.wminfo.maxsecret=100; DM.wminfo.partime=28595; IDoomMenu M=DM.M=new Menu(DM); Map AM=new Map(DM); DM.AM=AM; StatusBar ST=(StatusBar) (DM.ST=new StatusBar(DM)); ILevelLoader LL=DM.LL=new LevelLoader(DM); DM.P=new Actions(DM); DM.SM=new SpriteManager(DM); DM.R=new ParallelRenderer(DM,2,1); DM.TM=new SimpleTextureManager(DM); DM.P.updateStatus(DM); LL.updateStatus(DM); M.updateStatus(DM); ST.updateStatus(DM); AM.updateStatus(DM); DM.initializeVideoScaleStuff(); DM.R.Init(); DM.P.Init(); DM.players[0].updateStatus(DM); DM.players[0].PlayerReborn(); ST.Init(); M.Init(); ST.Start(); LL.SetupLevel(1, 1, 0, skill_t.sk_hard); AM.LevelInit(); AM.Start(); DM.R.SetViewSize(11, 0); DM.R.ExecuteSetViewSize(); DM.TM.setSkyTexture(DM.TM.CheckTextureNumForName("SKY1")); long a=System.nanoTime(); DM.menuactive=false; DM.automapactive=true; DM.R.FillBackScreen(); DM.R.DrawViewBorder(); // Center on "bloody mess" in E1M1 DM.players[0].mo.y+=420<<16; //DM.players[0].mo.x+=1650<<16; int pl=0; for (int i=0;i<20000;i++){ frame.GetEvent(); for ( ; DM.eventtail != DM.eventhead ; DM.eventtail = (++DM.eventtail)&(MAXEVENTS-1) ) { event_t ev = DM.events[DM.eventtail]; //System.out.println(ev); if (ev!=null){ AM.Responder(ev); M.Responder(ev); ST.Responder(ev); } } //V.DrawPatch(0,0,0,help1); //M.Ticker(); //M.Drawer(); AM.Ticker(); //AM.Drawer(); ST.Ticker(); DM.players[0].viewz=(40)<<16; //DM.players[0].mo.x=ox+(int) ((12864<<16)*Math.cos(2*Math.PI*i/500.0)); //DM.players[0].mo.y=oy+(int) ((64<<16)*Math.sin(2*Math.PI*i/500.0)); //DM.players[0].mo.angle= ((long)(0xFFFFFFFFL*(Math.atan2(DM.players[0].mo.y-oy,DM.players[0].mo.x-ox)+Math.PI)/(2*Math.PI)))&0xFFFFFFFFL; DM.players[0].mo.angle=(DM.players[0].mo.angle-0x800000)&0xFFFFFFFFL; //System.out.println(">>>>>>>>>>>>>>>>>> VIEW ANGLE "+360.0*(DM.players[0].mo.angle>>19)/8192.0); DM.R.RenderPlayerView(DM.players[0]); //ST.Drawer(false,true); //System.out.println("Rendered"+DM.gametic); DM.gametic++; frame.FinishUpdate(); if (i%200==0){ long b=System.nanoTime(); //frame.setPalette((pl++)%14); System.out.println((200) +" frames in " +((b-a)/1e09) +" = "+200/((b-a)/1e09) + " fps"); a=b; } // System.out.print(frame.processEvents()); /*while (ba-DM.I.GetTime()>-1){ Thread.sleep(10); }*/ } } catch (Exception e){ e.printStackTrace(); } } } package testers; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.RandomAccessFile; import demo.VanillaDoomDemo; import utils.C2JUtils; import w.*; public class JaguarFile { static final String URI="E://jagdoom/doom.wad"; public static void main(String[] argv) throws IOException { DataInputStream dis=new DataInputStream(new FileInputStream("E://jagdoom/doom.wad")); DataOutputStream dos=new DataOutputStream(new FileOutputStream("E://jagdoom/jagdoom.wad")); wadheader_t header=new wadheader_t(); header.big_endian=true; header.read(dis); header.big_endian=false; header.write(dos); System.out.println(header.type); System.out.println(header.numentries); System.out.println(header.tablepos); filelump_t[] stuff=new filelump_t[header.numentries]; C2JUtils.initArrayOfObjects(stuff); byte[] data=new byte[header.tablepos-wadheader_t.sizeof()]; dis.read(data); dos.write(data); int marker=dis.readInt(); dos.writeInt(marker); for (filelump_t f:stuff){ f.big_endian=true; f.read(dis); System.out.printf("%s %s %d %d %s\n",f.name,f.actualname,f.filepos,f.size,f.compressed); f.big_endian=false; f.write(dos); } dis.close(); dos.close(); FileOutputStream fos; FileInputStream fis =new FileInputStream("E://jagdoom/doom.wad"); long size= InputStreamSugar.getSizeEstimate(fis, null); int k=0; for (filelump_t f:stuff){ //if (k>2) break; k++; InputStreamSugar.streamSeek(fis, f.filepos, size, URI, null, InputStreamSugar.FILE); byte[] input=new byte[(int) Math.min(f.size,fis.available())]; byte[] output; fis.read(input); if (f.compressed){ System.out.printf("Decompressing %s expecting %d\n",f.actualname,f.size); output=new byte[(int) f.size]; JadDecompress.decode(input, output); fos=new FileOutputStream( String.format("E://jagdoom/DEC_%s.lmp",f.actualname)); } else { output=input; fos=new FileOutputStream( String.format("E://jagdoom/%s.lmp",f.name)); } fos.write(output); } } } package testers; import static data.Defines.KEY_F1; import static data.Defines.PU_STATIC; import static data.Limits.MAXEVENTS; import hu.HU; import i.DoomSystem; import i.IDoomSystem; import i.InputListener; import java.awt.image.BufferedImage; import java.awt.image.IndexColorModel; import javax.swing.JFrame; import p.Actions; import p.LevelLoader; import automap.Map; import awt.OldAWTDoom; import rr.UnifiedRenderer; import rr.patch_t; import s.DummySoundDriver; import st.StatusBar; import m.IDoomMenu; import m.Menu; import m.DoomRandom; import utils.C2JUtils; import v.BufferedRenderer; import v.SimpleRenderer; import w.DoomBuffer; import w.WadLoader; import data.Defines; import defines.*; import doom.DoomContext; import doom.DoomMain; import doom.DoomStatus; import doom.event_t; import doom.player_t; import doom.ticcmd_t; import doom.wbstartstruct_t; /** This is a very simple tester for Menu module */ public class AWTAutomapTester { public static final int WIDTH=320; public static final int HEIGHT=200; public static void main(String[] argv) { try { // Create a Wad file loader. WadLoader W=new WadLoader(); W.InitMultipleFiles(new String[] {"doom1.wad"}); System.out.println("Total lumps read: "+W.numlumps); patch_t help1=W.CachePatchName("TITLEPIC", PU_STATIC); // Read the paletter. DoomBuffer palette = W.CacheLumpName("PLAYPAL", PU_STATIC); // Create a video renderer BufferedRenderer V=new BufferedRenderer(WIDTH,HEIGHT); V.Init(); byte[] pal=palette.getBuffer().array(); IDoomSystem I=new DoomSystem(); DoomMain DM=new DoomMain(); // Create the frame. OldAWTDoom frame = new OldAWTDoom(DM,V,pal); frame.InitGraphics(); DM.I=I; DM.VI=frame; DM.S=new DummySoundDriver(); frame.DM=DM; DM.W=W; DM.V=V; DM.DM=DM; DM.language=Language_t.english; DM.gameepisode=1; DM.gamemap=1; DM.gamemission=GameMission_t.doom; DM.gamemode=GameMode_t.shareware; DM.wminfo=new wbstartstruct_t(); // Simulate being in the mid of a level. DM.usergame=true; DM.gamestate=gamestate_t.GS_LEVEL; DM.gameskill=skill_t.sk_hard; //C2JUtils.initArrayOfObjects(DM.players,player_t.class); DM.RND=new DoomRandom(); DM.players[0].cmd=new ticcmd_t(); DM.players[0].itemcount=1337; DM.players[0].killcount=1337; DM.players[0].secretcount=1337; DM.wminfo.plyr[0].in=true; DM.wminfo.plyr[0].sitems=1337; DM.wminfo.plyr[0].skills=1337; DM.wminfo.plyr[0].stime=28595; DM.wminfo.plyr[0].ssecret=1337; DM.playeringame[0]=true; DM.wminfo.last=6; DM.wminfo.epsd=0; DM.wminfo.maxitems=100; DM.wminfo.maxkills=100; DM.wminfo.maxsecret=100; DM.wminfo.partime=28595; DM.PlayerReborn(0); IDoomMenu M=DM.M=new Menu(DM); Map AM=(Map) (DM.AM=new Map(DM)); StatusBar ST=(StatusBar) (DM.ST=new StatusBar(DM)); LevelLoader LL=DM.LL=new LevelLoader(DM); DM.P=new Actions(DM); DM.R=new UnifiedRenderer(DM); HU HU=DM.HU=new HU(DM); DM.P.updateStatus(DM); M.updateStatus(DM); ST.updateStatus(DM); AM.updateStatus(DM); HU.updateStatus(DM); LL.updateStatus(DM); DM.R.Init(); DM.P.Init(); HU.Init(); LL.SetupLevel(1, 2, 0, skill_t.sk_hard); ST.Init(); M.Init(); ST.Start(); AM.LevelInit(); AM.Start(); HU.Start(); // M.StartControlPanel(); long a=System.nanoTime(); DM.menuactive=false; DM.automapactive=false; for (int i=0;i<100000;i++){ int ba=DM.I.GetTime(); while (ba-DM.I.GetTime()==0){ // Don't do that! frame.setVisible(true); Thread.sleep(1); } frame.GetEvent(); for ( ; DM.eventtail != DM.eventhead ; DM.eventtail = (++DM.eventtail)&(MAXEVENTS-1) ) { event_t ev = DM.events[DM.eventtail]; //System.out.println(ev); if (ev!=null){ AM.Responder(ev); M.Responder(ev); HU.Responder(ev); ST.Responder(ev); } } //V.DrawPatch(0,0,0,help1); AM.Ticker(); AM.Drawer(); HU.Ticker(); HU.Drawer(); ST.Ticker(); ST.Drawer(false,true); M.Ticker(); M.Drawer(); DM.gametic++; frame.FinishUpdate(); if (i%100==0){ long b=System.nanoTime(); System.out.println(i +" frames in " +((b-a)/1e09) +" = "+i/((b-a)/1e09) + " fps"); } System.out.print(frame.processEvents()); } } catch (Exception e){ e.printStackTrace(); } } } package testers; import static data.Defines.PU_STATIC; import static data.Limits.MAXEVENTS; import n.DummyNetworkHandler; import hu.HU; import i.DoomSystem; import i.IDoomSystem; import p.Actions; import p.ILevelLoader; import p.LevelLoader; import automap.Map; import awt.AWTDoom; import rr.ParallelRenderer2; import rr.SimpleTextureManager; import rr.SpriteManager; import s.DummySoundDriver; import st.StatusBar; import timing.MilliTicker; import m.IDoomMenu; import m.Menu; import m.DoomRandom; import v.BufferedRenderer; import v.DoomVideoRenderer; import v.GammaTables; import v.IVideoScale; import v.VideoScaleInfo; import w.DoomBuffer; import w.WadLoader; import defines.*; import data.Tables; import doom.CommandLine; import doom.DoomMain; import doom.event_t; import doom.ticcmd_t; import doom.wbstartstruct_t; /** This is a very simple tester for Menu module */ public class AWTParallelRenderTester2 { static IVideoScale VSI=new VideoScaleInfo(4.0f); public static void main(String[] argv) { try { Tables.InitTables(); // Create a Wad file loader. WadLoader W=new WadLoader(); W.InitMultipleFiles(new String[] {"doom1.wad"}); System.out.println("Total lumps read: "+W.numlumps); // Read the palette. DoomBuffer palette = W.CacheLumpName("PLAYPAL", PU_STATIC); // Create a video renderer DoomVideoRenderer<?> V=new BufferedRenderer(VSI.getScreenWidth(),VSI.getScreenHeight()); V.setVideoScale(VSI); V.initScaling(); V.Init(); byte[] pal=palette.getBuffer().array(); V.createPalettes(pal, GammaTables.gammatables, 14, 256, 3, 5); IDoomSystem I=new DoomSystem(); DoomMain DM=new DoomMain(); CommandLine CM=new CommandLine(argv); DM.CM=CM; DM.TICK=new MilliTicker(); DM.setVideoScale(VSI); DM.initScaling(); DM.singletics=true; DM.setTicdup(1); DM.DGN=new DummyNetworkHandler(); // Create the frame. AWTDoom frame = new AWTDoom(DM,(DoomVideoRenderer<byte[]>) V); frame.InitGraphics(); DM.I=I; DM.VI=frame; DM.S=new DummySoundDriver(); frame.DM=DM; DM.W=W; DM.V=V; DM.DM=DM; HU HU=new HU(DM); DM.language=Language_t.english; HU.Init(); DM.HU=HU; DM.gameepisode=1; DM.gamemap=1; DM.gamemission=GameMission_t.doom; DM.setGameMode(GameMode_t.shareware); DM.wminfo=new wbstartstruct_t(); // Simulate being in the mid of a level. DM.usergame=true; DM.gamestate=gamestate_t.GS_LEVEL; DM.gameskill=skill_t.sk_hard; //C2JUtils.initArrayOfObjects(DM.players,player_t.class); DM.RND=new DoomRandom(); DM.players[0].cmd=new ticcmd_t(); DM.players[0].itemcount=1337; DM.players[0].killcount=1337; DM.players[0].secretcount=1337; DM.wminfo.plyr[0].in=true; DM.wminfo.plyr[0].sitems=1337; DM.wminfo.plyr[0].skills=1337; DM.wminfo.plyr[0].stime=28595; DM.wminfo.plyr[0].ssecret=1337; DM.playeringame[0]=true; DM.wminfo.last=6; DM.wminfo.epsd=0; DM.wminfo.maxitems=100; DM.wminfo.maxkills=100; DM.wminfo.maxsecret=100; DM.wminfo.partime=28595; IDoomMenu M=DM.M=new Menu(DM); Map AM=new Map(DM); DM.AM=AM; StatusBar ST=(StatusBar) (DM.ST=new StatusBar(DM)); ILevelLoader LL=DM.LL=new LevelLoader(DM); DM.P=new Actions(DM); DM.SM=new SpriteManager(DM); DM.R=new ParallelRenderer2(DM,2,1); DM.TM=new SimpleTextureManager(DM); DM.P.updateStatus(DM); LL.updateStatus(DM); M.updateStatus(DM); ST.updateStatus(DM); AM.updateStatus(DM); DM.initializeVideoScaleStuff(); DM.R.Init(); DM.P.Init(); DM.players[0].updateStatus(DM); DM.players[0].PlayerReborn(); ST.Init(); M.Init(); ST.Start(); LL.SetupLevel(1, 1, 0, skill_t.sk_hard); AM.LevelInit(); AM.Start(); DM.R.SetViewSize(11, 0); DM.R.ExecuteSetViewSize(); DM.TM.setSkyTexture(DM.TM.CheckTextureNumForName("SKY1")); long a=System.nanoTime(); DM.menuactive=false; DM.automapactive=true; DM.R.FillBackScreen(); DM.R.DrawViewBorder(); // Center on "bloody mess" in E1M1 DM.players[0].mo.y+=420<<16; //DM.players[0].mo.x+=1650<<16; int pl=0; for (int i=0;i<20000;i++){ frame.GetEvent(); for ( ; DM.eventtail != DM.eventhead ; DM.eventtail = (++DM.eventtail)&(MAXEVENTS-1) ) { event_t ev = DM.events[DM.eventtail]; //System.out.println(ev); if (ev!=null){ AM.Responder(ev); M.Responder(ev); ST.Responder(ev); } } //V.DrawPatch(0,0,0,help1); //M.Ticker(); //M.Drawer(); AM.Ticker(); //AM.Drawer(); ST.Ticker(); DM.players[0].viewz=(40)<<16; //DM.players[0].mo.x=ox+(int) ((12864<<16)*Math.cos(2*Math.PI*i/500.0)); //DM.players[0].mo.y=oy+(int) ((64<<16)*Math.sin(2*Math.PI*i/500.0)); //DM.players[0].mo.angle= ((long)(0xFFFFFFFFL*(Math.atan2(DM.players[0].mo.y-oy,DM.players[0].mo.x-ox)+Math.PI)/(2*Math.PI)))&0xFFFFFFFFL; DM.players[0].mo.angle=(DM.players[0].mo.angle-0x800000)&0xFFFFFFFFL; //System.out.println(">>>>>>>>>>>>>>>>>> VIEW ANGLE "+360.0*(DM.players[0].mo.angle>>19)/8192.0); DM.R.RenderPlayerView(DM.players[0]); //ST.Drawer(false,true); //System.out.println("Rendered"+DM.gametic); DM.gametic++; frame.FinishUpdate(); if (i%200==0){ long b=System.nanoTime(); //frame.setPalette((pl++)%14); System.out.println((200) +" frames in " +((b-a)/1e09) +" = "+200/((b-a)/1e09) + " fps"); a=b; } // System.out.print(frame.processEvents()); /*while (ba-DM.I.GetTime()>-1){ Thread.sleep(10); }*/ } } catch (Exception e){ e.printStackTrace(); } } } package testers; import static data.SineCosine.*; import m.FixedFloat; /** This is a class that tests what happens with BAM angles used in Doom * Normally, they were supposed to be unsigned 32-bit types, mapping * the [0...360) range to a continuum of 32-bit integer values. * * However, in Java we have no unsigned integer type. * A workaround is to declare angles as ints, and be careful about what operations we perform. * The general rules are: * * a) Angles below ANG180 (1st and 2nd qdt) will compare just fine. * b) Angles in the 3rd and 4th quadrant will compare OK too. * c) However angles from different semiplanes won't compare correctly: they need to be extended to longs. * d) Alternatively, comparisons can be done with a specialized function -> need to benchmark that. * e) Sums and subtractions of angles will work ok with ints, as far as the bit content is concerned. * When they need to be compared however, the above limitations apply. * f) Multiplications with work OK with either type. * g) Divisions for cumulative angles between 180 and -0.0 degrees will fail due to signs. * g) So, the best thing would be to expand angles to longs and use the extra value range. * * Some benchmark facts: * 1) Addition/subtraction between 32-bit and 64-bit operands are, on average, the same speed. * 2) Multiplication is twice as fast with 32-bit operands. * 2) Casting to int is usually slightly faster than ANDing with a 32-bit mask and storing as long. * 3) Using a special method for division between ints can be almost as 1.5x as fast * than performing long divisions on 32-bit limited numbers. * 4) The only thing that's undeliably faster in 64-bit mode are comparisons between "32 bit unsigned" * numbers (5x as fast), and the overall. * * Using 32-bit ints clearly improves memory bandwidth and storage, but makes comparisons problematic. * If most of the angle code consists of additions and comparisons, I'd say keep BAM angles 64-bit. * Using code that's multiplication or division intensive may benefit from 32-bit BAM angles... * eventally code can be locally optimized depending on what's being done. * */ public class TestBAM { public final static int PASSES=2; public final static int TESTS=5000000; public final static int ANG90=0x40000000; public final static int ANG180=0x80000000; public final static int ANG270=0xC0000000; public final static int ANG45=ANG90/2; public final static int ANG135=ANG270/2; public final static long LANG90=0x40000000; public final static long LANG180=0x80000000; public final static long LANG270=0xC0000000L; public final static long LANG45=LANG90/2; public final static long LANG135=LANG270/2; public final static long ANGLETOFINESHIFT=19; public final static int BITS31=0x7FFFFFFF; public static void main(String argv[]){ System.out.println("ANG90>ANG45: "+(ANG90>ANG45)); System.out.println("LANG180> ANG90 "+ (toLong(ANG180)>ANG90)); System.out.println("ANG180>ANG90 "+ (ANG180>ANG90)); // this resolves wrong, because ANG180 is "negative" in signed int notation. System.out.println("ANG180>ANG90 with GT"+ GT(ANG180,ANG90)); // this resolves wrong, because ANG180 is "negative" in signed int notation. System.out.println("ANG270>ANG180 " + (ANG270>ANG180)); // but this resolves correctly, because ANG270 is negative but "larger". System.out.println("ANG270>ANG180 " + GT(ANG270,ANG180)); // but this resolves correctly, because ANG270 is negative but "larger". System.out.println(ANG45>ANG270); // this resolves wrong, too. It should be false. System.out.println((ANG45>toLong(ANG270))); // this resolves wrong, too. It should be false. System.out.println((ANG270-ANG45)>ANG180); // this resolves correctly. System.out.println("ZILCH "+((ANG270-ANG45)>ANG90)); // this, however, won't. System.out.println((0-ANG45)>ANG180); // this resolves correctly too. System.out.println((ANG270-ANG180)>ANG45); // correct. System.out.println((ANG270-ANG180)==ANG90); // This is true, and correct. System.out.println((ANG270/2)>ANG90); // This won't work, because it will resolve to a negative. System.out.println((ANG270/2)>ANG180); // This won't work either. System.out.println((toLong(ANG270)/2)>toLong(ANG180)); // Only this will work. System.out.println(ANG45>>ANGLETOFINESHIFT); System.out.println(ANG90>>ANGLETOFINESHIFT); System.out.println("ANG90 mult 3 == ANG270" +GE(ANG90*5,ANG90)); System.out.println(ANG180*2); System.out.println((long)ANG180/2); System.out.println(ANG270*2 == ANG180); System.out.println(FixedFloat.toFloat((int) (ANG45>>>ANGLETOFINESHIFT))+" "+finesine[(int) (ANG45>>>ANGLETOFINESHIFT)]); System.out.println(FixedFloat.toFloat((int) (ANG270>>>ANGLETOFINESHIFT))+" "+finesine[(int) (ANG270>>>ANGLETOFINESHIFT)]); System.out.println(FixedFloat.toFloat((int) ((ANG45*6)>>>ANGLETOFINESHIFT))+" "+finesine[(int) (ANG45*6)>>>ANGLETOFINESHIFT]); System.out.println(FixedFloat.toFloat((int) (ANG45>>>ANGLETOFINESHIFT))+" "+finesine[(int) (ANG45>>>ANGLETOFINESHIFT)]); System.out.println(FixedFloat.toFloat((int) (ANG270>>>ANGLETOFINESHIFT))+" "+finesine[(int) (ANG270>>>ANGLETOFINESHIFT)]); System.out.println(FixedFloat.toFloat((int) ((ANG45*6)>>>ANGLETOFINESHIFT))+" "+finesine[(int) (ANG45*6)>>>ANGLETOFINESHIFT]); int[] BAM32=new int[TESTS]; long[] BAM64=new long[TESTS]; for (int i=0;i<TESTS;i++){ // Keep only 32 bits. BAM64[i]=(Double.doubleToLongBits(Math.random())&0xFFFFFFFFL); BAM32[i]=(int) (BAM64[i]); } int errors=0; for (int i=0;i<TESTS-1;i++){ // Keep only 32 bits. if ((BAM64[i]<BAM64[i+1])!=(!GE(BAM32[i],BAM32[i+1]))) errors++; } System.out.println("Comparison Errors "+ errors); errors=0; for (int i=0;i<TESTS-1;i++){ // Keep only 32 bits. if ((int)(BAM64[i]*BAM64[i+1])!=(BAM32[i]*BAM32[i+1])) errors++; } System.out.println("Multiplication Errors "+ errors); errors=0; // Preventing overflow errors is easy. for (int i=0;i<TESTS-1;i++){ // Keep only 32 bits. if ((toLong(BAM32[i])/2)!=((BAM64[i]/2)&0x0FFFFFFFFL)) errors++; } System.out.println("Overflow Errors "+ errors); errors=0; for (int i=0;i<TESTS-1;i++){ // Keep only 32 bits. if ((int)(BAM64[i]/BAM64[i+1])!=(BAM32[i]/BAM32[i+1])) errors++; } System.out.println("Division Errors "+ errors); errors=0; for (int i=1;i<TESTS-1;i++){ // Keep only 32 bits. if ((int)(BAM64[i]/i)!=BAMDiv(BAM32[i],i)) errors++; //System.out.println(BAMDiv(BAM32[i],i)); } System.out.println("Division with BAMDiv Errors "+ errors); /*for (int i=0;i<tantoangle.length;i++){ System.out.println(FixedFloat.toFloat(tantoangle[i])); }*/ for (int p=0;p<PASSES;p++){ timingTests(BAM32,BAM64); } } private static void timingTests(int[] bAM32, long[] bAM64) { long a,b; int errors; int results1[]=new int[TESTS]; int results2[]=new int[TESTS]; long results3[]=new long[TESTS]; a=System.nanoTime(); for (int i=0;i<TESTS-1;i++){ // Keep only 32 bits. results1[i]=bAM32[i]+bAM32[i+1]; //System.out.println(BAMDiv(BAM32[i],i)); } b=System.nanoTime(); System.out.println("Time for "+TESTS+" 32-bit additions: "+ (b-a)); a=System.nanoTime(); for (int i=0;i<TESTS-1;i++){ // Keep only 32 bits. results2[i]=(int) (bAM64[i]+bAM64[i+1]); //System.out.println(BAMDiv(BAM32[i],i)); } b=System.nanoTime(); System.out.println("Time for "+TESTS+" 64-bit additions (int storage with cast to 32-bit): "+ (b-a)); a=System.nanoTime(); for (int i=0;i<TESTS-1;i++){ // Keep only 32 bits. results3[i]=(bAM64[i]+bAM64[i+1])&0xFFFFFFFFL; //System.out.println(BAMDiv(BAM32[i],i)); } b=System.nanoTime(); System.out.println("Time for "+TESTS+" 64-bit additions (long storage with 32-bit cutoff): "+ (b-a)); errors=0; for (int i=1;i<TESTS-1;i++){ // Keep only 32 bits. if (results1[i]!=results2[i]) errors++; //System.out.println(BAMDiv(BAM32[i],i)); } System.out.println("Errors "+ errors); a=System.nanoTime(); for (int i=0;i<TESTS-1;i++){ // Keep only 32 bits. results1[i]=bAM32[i]*bAM32[i+1]; //System.out.println(BAMDiv(BAM32[i],i)); } b=System.nanoTime(); System.out.println("Time for "+TESTS+" 32-bit multiplications : "+ (b-a)); a=System.nanoTime(); for (int i=0;i<TESTS-1;i++){ // Keep only 32 bits. results2[i]=(int)(bAM64[i]*bAM64[i+1]); //System.out.println(BAMDiv(BAM32[i],i)); } b=System.nanoTime(); System.out.println("Time for "+TESTS+" 64-bit multiplications (with result cast to int): "+ (b-a)); errors=0; for (int i=1;i<TESTS-1;i++){ // Keep only 32 bits. if (results1[i]!=results2[i]) errors++; //System.out.println(BAMDiv(BAM32[i],i)); } System.out.println("Errors "+ errors); a=System.nanoTime(); for (int i=1;i<TESTS-1;i++){ // Keep only 32 bits. results1[i]=BAMDiv(bAM32[i],50); //System.out.println(BAMDiv(BAM32[i],i)); } b=System.nanoTime(); System.out.println("Time for "+TESTS+" 32-bit divisions with BAMDiv: "+ (b-a)); a=System.nanoTime(); for (int i=1;i<TESTS-1;i++){ // Keep only 32 bits. results2[i]=(int)(bAM64[i]/50); //System.out.println(BAMDiv(BAM32[i],i)); } b=System.nanoTime(); System.out.println("Time for "+TESTS+" 64-bit divisions (with result cast to int): "+ (b-a)); errors=0; for (int i=1;i<TESTS-1;i++){ // Keep only 32 bits. results1[i]=(GE(bAM32[i],bAM32[i+1])?1:0); //System.out.println(BAMDiv(BAM32[i],i)); } System.out.println("Time for "+TESTS+" 32-bit comparisons): "+ (b-a)); a=System.nanoTime(); for (int i=1;i<TESTS-1;i++){ // Keep only 32 bits. results2[i]=(bAM64[i]>=bAM64[i+1]?1:0); //System.out.println(BAMDiv(BAM32[i],i)); } b=System.nanoTime(); System.out.println("Time for "+TESTS+" 64-bit comparisons (with result cast to int): "+ (b-a)); errors=0; for (int i=1;i<TESTS-1;i++){ // Keep only 32 bits. if (results1[i]!=results2[i]) errors++; //System.out.println(BAMDiv(BAM32[i],i)); } System.out.println("Errors "+ errors); } public static final long toLong(int a){ return(0xFFFFFFFFL&a); } /** Compare BAM angles in 32-bit format * "Greater or Equal" bam0>bam1 * */ public static final boolean GE(int bam0, int bam1){ // Handle easy case. if (bam0==bam1) return true; // bam0 is greater than 180 degrees. if (bam0<0 && bam1>=0) return true; // bam1 is greater than 180 degrees. if (bam0>=0 && bam1<0) return false; // Both "greater than 180", No other way to compare. bam0&=BITS31; bam1&=BITS31; return bam0>bam1; } public static final boolean GT(int bam0, int bam1){ // bam0 is greater than 180 degrees. if (bam0<0 && bam1>=0) return true; // bam1 is greater than 180 degrees. if (bam0>=0 && bam1<0) return false; // Both "greater than 180", No other way to compare. bam0&=BITS31; bam1&=BITS31; return bam0>bam1; } public static final int BAMDiv(int bam0, int bam1){ // bam0 is greater than 180 degrees. if (bam0>=0) return bam0/bam1; // bam0 is greater than 180 degrees. // We have to make is so that ANG270 0xC0000000 becomes ANG135, aka 60000000 if (bam1>=0) return (int) ((long)(0x0FFFFFFFFL&bam0)/bam1); return (int) ((long)(0x0FFFFFFFFL&bam0)/(0x0FFFFFFFFL&bam1)); } } package testers; import demo.VanillaDoomDemo; import w.*; public class DemoLumpTester { public static void main(String[] argv) { WadLoader W=new WadLoader(); try { W.InitMultipleFiles(new String[] {"C://DOOMS/e1m1.wad"}); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } //W.AddFile("bitter.wad"); System.out.println("Total lumps read: "+W.numlumps); System.out.println("NUm for DEMO1: "+W.GetNumForName("DEMO1")); VanillaDoomDemo demo=(VanillaDoomDemo) W.CacheLumpName("DEMO1",0,VanillaDoomDemo.class); System.out.println(demo); } } package testers; import m.DoomRandom; import m.IRandom; import s.ClassicDoomSoundDriver; import s.SpeakerDoomSoundDriver; import data.sounds.sfxenum_t; import doom.DoomStatus; import w.WadLoader; public class TestClassicSound { public static void main(String[] argv) throws Exception{ DoomStatus DS=new DoomStatus(); WadLoader W=new WadLoader(); IRandom RND=new DoomRandom(); DS.W=W; DS.RND=RND; W.InitMultipleFiles(new String[]{"C:\\iwads\\doom1.wad"}); SpeakerDoomSoundDriver sound=new SpeakerDoomSoundDriver(DS,4); sound.InitSound(); sound.SetChannels(3); Thread.sleep(1000); //sound.StartSound(1, 127, 127, 127, 0); for (int i=0;i<1000;i++){ Thread.sleep(1000/35); if (i%10==0) sound.StartSound(sfxenum_t.sfx_plpain.ordinal(), 127, 127, 127, 0); //if (i%50==0) sound.StartSound(sfxenum_t.sfx_barexp.ordinal(), 127, 0, 127, 0); //if (i%35==0) sound.StartSound(sfxenum_t.sfx_plpain.ordinal(), 127, 255, 127, 0); //if (i%71==0) sound.StartSound(sfxenum_t.sfx_oof.ordinal(), 127, 192, 127, 0); sound.UpdateSound(); sound.SubmitSound(); DS.gametic++; } sound.ShutdownSound(); } } package testers; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import w.WadLoader; public class TestInvoke { /** * @param args * @throws Exception */ public static void main(String[] args) throws Exception { Method shit=WadLoader.class.getDeclaredMethod("InitFile", String.class); Method cunt=WadLoader.class.getDeclaredMethod("LumpLength", int.class); Method nothing=WadLoader.class.getDeclaredMethod("Reload",null); WadLoader crap=new WadLoader(); shit.invoke(crap, "~DOOM1.WAD"); System.out.println(cunt.invoke(crap, 0)); byte[] ahhh=crap.CacheLumpName("PLAYPAL", 0).getBuffer().array(); System.out.println(ahhh.length); ahhh=crap.CacheLumpName("PLAYPAL", 0).getBuffer().array(); System.out.println(ahhh.length); // This should flush the cache nothing.invoke(crap, (Object[])null); ahhh=crap.CacheLumpName("PLAYPAL", 0).getBuffer().array(); System.out.println(ahhh.length); } } package testers; import static data.Defines.PU_STATIC; import java.awt.image.BufferedImage; import java.awt.image.IndexColorModel; import javax.swing.JFrame; import m.DoomRandom; import m.IRandom; import rr.patch_t; import v.BufferedRenderer; import w.DoomBuffer; import w.WadLoader; import data.Defines; import doom.DoomMain; import f.Wiper; /** This is a very simple tester for the Automap. Combined with status bar + Level loader. */ public class AssWipeTester { public static int WIDTH=320; public static void main(String[] argv) { try { DoomMain DC=new DoomMain(); WadLoader W=new WadLoader(DC.I); IRandom RND=new DoomRandom(); DC.RND=RND; DC.W=W; W.InitMultipleFiles(new String[] {"doom1.wad"}); //W.AddFile("bitter.wad"); System.out.println("Total lumps read: "+W.numlumps); BufferedRenderer V=new BufferedRenderer(640,200); DC.V=V; // Get the palettes DoomBuffer palette = W.CacheLumpName("PLAYPAL", PU_STATIC); byte[] pal=palette.getBuffer().array(); IndexColorModel icm=new IndexColorModel(8, 256,pal, 0, false); V.setIcm(icm); V.Init(); patch_t titlepic=W.CachePatchName("TITLEPIC", PU_STATIC); patch_t credit=W.CachePatchName("HELP1", PU_STATIC); Wiper wipe=new Wiper(DC); Defines.SCREENWIDTH=640; Defines.SCREENHEIGHT=200; // "Hook" on screen 0. BufferedImage bi=((BufferedRenderer)V).screenbuffer[0]; //BufferedImage[] pals=V.getBufferedScreens(0, icms); CrappyDisplay frame = new CrappyDisplay(bi); frame.setTitle("MochaDoom"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.pack(); frame.setLocationRelativeTo(null); //frame.setUndecorated(true); frame.setVisible(true); frame.setBounds(frame.getX(), frame.getY(), WIDTH, 240); int tck=DC.I.GetTime(); long a=System.nanoTime(); int TICKS=10; int frames=0; for (int i=0;i<TICKS;i++){ V.DrawPatch(0, 0, 0, titlepic); wipe.StartScreen(0, 0, Defines.SCREENWIDTH, Defines.SCREENHEIGHT); V.DrawPatch(0, 0, 0, credit); wipe.EndScreen(0, 0, Defines.SCREENWIDTH, Defines.SCREENHEIGHT); int wipestart = DC.I.GetTime () - 1; int nowtime; int tics; boolean done; patch_t tmp=credit; credit=titlepic; titlepic=tmp; //wipe.ScreenWipe(Wiper.wipe.Melt.ordinal(), 0, 0, Defines.SCREENWIDTH, Defines.SCREENHEIGHT, TICKS-i); //ST.Drawer(false,true); //Thread.sleep(200); do { do { nowtime = DC.I.GetTime (); tics = nowtime - wipestart; } while (tics<1); wipestart = nowtime; //V.DrawPatch(0, 0, 0, titlepic); //V.DrawPatch(320, 0, 0, titlepic); done = wipe.ScreenWipe(Wiper.wipe.Melt.ordinal() , 0, 0, Defines.SCREENWIDTH, Defines.SCREENHEIGHT, tics); //I_UpdateNoBlit (); //M_Drawer (); // menu is drawn even on top of wipes //System.out.println(i); frame.update(null); frames++; } while (!done); long b=System.nanoTime(); System.out.println(frames +" frames in " +((b-a)/1e09) +" = "+frames/((b-a)/1e09) + " fps"); } } catch (Exception e){ e.printStackTrace(); } } } package testers; import java.awt.image.BufferedImage; import java.io.BufferedInputStream; import java.io.FileInputStream; import java.io.FileOutputStream; import javax.imageio.ImageIO; public class PaletteShower { public static final int BLOCKSIZE=16; public static final int SHIFT=0; public static void main(String[] argv) { try { BufferedInputStream bis; bis=new BufferedInputStream(new FileInputStream(argv[0])); int size=bis.available(); int blocks=size/768; byte[] palbuf=new byte[768]; for (int i=0;i<blocks;i++){ bis.read(palbuf); BufferedImage bim=new BufferedImage(16*BLOCKSIZE,16*BLOCKSIZE,BufferedImage.TYPE_INT_ARGB); /* * for (int j=0;j<256;j++){ int color= 0xFF000000|(palbuf[j]<<(16+SHIFT))| (palbuf[j]<<(8+SHIFT))| (palbuf[j]<<SHIFT); } */ for (int j=0;j<256;j++){ int colo= 0xFF000000|(0xFF0000&palbuf[j*3]<<(16+SHIFT))| (0xFF00&palbuf[1+j*3]<<(8+SHIFT))| (0xFF&palbuf[2+j*3]<<SHIFT); for (int x=0;x<BLOCKSIZE;x++){ for (int y=0;y<BLOCKSIZE;y++){ bim.setRGB(BLOCKSIZE*(j%16)+x, BLOCKSIZE*(j/16)+y, colo); } } } FileOutputStream f=new FileOutputStream(String.format("%s-%d.PNG",argv[0],i)); ImageIO.write(bim,"PNG",f); } bis.close(); } catch (Exception e){ e.printStackTrace(); } } } package testers; import static data.SineCosine.finesine; import static data.Tables.*; import static m.fixed_t.FRACUNIT; import static m.fixed_t.FRACBITS; import static m.fixed_t.FixedDiv; import static m.fixed_t.FixedMul; import data.Tables; import m.fixed_t; public class TestScale { private static int detailshift=0; private static long rw_normalangle; private static long viewangle; private static int projection=0xF0000000; private static int rw_distance=0xFF000; private static final int divs=10; private static int viewx=0, viewy=0; private static int dist=256*FRACUNIT; public static void main(String[] argv){ int[][] octants={{dist,0},{dist,dist},{0,dist},{-dist,dist},{-dist,0},{-dist,-dist},{0,-dist},{dist,-dist}}; for (int i=0;i<octants.length;i++){ long ang = PointToAngle (octants[i][0],octants[i][1]); System.out.print((ang*360.0)/0xFFFFFFFFL); System.out.print(" "+(ang>>FRACBITS)); System.out.print(" "+(Long.toHexString(ang))); System.out.print(" "+(Long.toBinaryString(ang+(ANG45*9)/2))); int rot = (int) (((ang+(ANG45*9)/2)&BITS32)>>29); System.out.print(" "+rot+"\n"); } /* long angle=0; projection=0; int divisions=(int) (0xFFFFFFFFL/divs); for (int i=0;i<divs;i++){ for (int j=0;j<divs;j++){ System.out.println(i+" "+j+" "+rw_normalangle+" "+viewangle+" "+ScaleFromGlobalAngle(viewangle)); viewangle+=divisions; } rw_normalangle+=divisions; }*/ } /** * R_ScaleFromGlobalAngle * Returns the texture mapping scale * for the current line (horizontal span) * at the given angle. * rw_distance must be calculated first. */ public static int ScaleFromGlobalAngle (long visangle) { int scale; // fixed_t int anglea; int angleb; int sinea; int sineb; int num; // fixed_t int den; // UNUSED /* { fixed_t dist; fixed_t z; fixed_t sinv; fixed_t cosv; sinv = finesine[(visangle-rw_normalangle)>>ANGLETOFINESHIFT]; dist = FixedDiv (rw_distance, sinv); cosv = finecosine[(viewangle-visangle)>>ANGLETOFINESHIFT]; z = abs(FixedMul (dist, cosv)); scale = FixedDiv(projection, z); return scale; } */ anglea = (int) (ANG90 +visangle-viewangle); angleb = (int) (ANG90 +visangle-rw_normalangle); // both sines are allways positive sinea = finesine[anglea>>>ANGLETOFINESHIFT]; sineb = finesine[angleb>>>ANGLETOFINESHIFT]; num = FixedMul(projection,sineb)<<detailshift; den = FixedMul(rw_distance,sinea); if (den > num>>16) { scale = FixedDiv (num, den); if (scale > 64*FRACUNIT) scale = 64*FRACUNIT; // Minimum scale. else if (scale < 256) scale = 256; } else // Top saturation scale scale = 64*FRACUNIT; return scale; } /** * R_PointToAngle * To get a global angle from cartesian coordinates, * the coordinates are flipped until they are in * the first octant of the coordinate system, then * the y (<=x) is scaled and divided by x to get a * tangent (slope) value which is looked up in the * tantoangle[] table. * * @param xx (fixed_t) * @param yy (fixed_t) */ public static long PointToAngle ( int x, int y ) { // MAES: note how we don't use &BITS32 here. That is because we know that the maximum possible // value of tantoangle is angle // This way, we are actually working with vectors emanating from our current position. x-= viewx; y-= viewy; if ( (x==0) && (y==0) ) return 0; if (x>= 0) { // x >=0 if (y>= 0) { // y>= 0 if (x>y) { // octant 0 return tantoangle[ SlopeDiv(y,x)]; } else { // octant 1 return (ANG90-1-tantoangle[ SlopeDiv(x,y)]); } } else { // y<0 y = -y; if (x>y) { // octant 8 return -tantoangle[SlopeDiv(y,x)]; } else { // octant 7 return ANG270+tantoangle[ SlopeDiv(x,y)]; } } } else { // x<0 x = -x; if (y>= 0) { // y>= 0 if (x>y) { // octant 3 return (ANG180-1-tantoangle[ SlopeDiv(y,x)]); } else { // octant 2 return (ANG90+ tantoangle[ SlopeDiv(x,y)]); } } else { // y<0 y = -y; if (x>y) { // octant 4 return (ANG180+tantoangle[ SlopeDiv(y,x)]); } else { // octant 5 return (ANG270-1-tantoangle[ SlopeDiv(x,y)]); } } } // This is actually unreachable. // return 0; } /** R_PointToDist * * @param x fixed_t * @param y fixed_t * @return */ public static int PointToDist ( int x, int y ) { int angle=0; int dx; int dy; int temp; int dist; dx = Math.abs(x - viewx); dy = Math.abs(y - viewy); // Sanity check, else it's going to bomb. /* if (dx==0){ // if (dy>0) angle= Tables.toBAMIndex(ANG90); else if (dy<0) angle= Tables.toBAMIndex(ANG270); else angle= 0; } else { */ if (dy>dx) { temp = dx; dx = dy; dy = temp; } /* If both dx and dy are zero, this is going to bomb. Fixeddiv will return MAXINT aka 7FFFFFFF, >> DBITS will make it 3FFFFFF, addding ANG90 will make if 43FFFFFF and angletofineshi */ angle = (int) (((tantoangle[ FixedDiv(dy,dx)>>DBITS ]+ANG90)&BITS32) >> ANGLETOFINESHIFT); // use as cosine dist = FixedDiv (dx, finesine[angle] ); return dist; } } package testers; import doom.event_t; import m.cheatseq_t; public class TestCheats { public static void main(String argv[]){ char cheat_ammo_seq[] = { 0xb2, 0x26, 0xf2, 0x66, 0xa2, 0xff // idkfa }; cheatseq_t idkfa=new cheatseq_t( cheat_ammo_seq); //event_t[] keystokes={new event_t('i'),new event_t('d') ,new event_t('k') ,new event_t('f') ,new event_t('a') }; //idkfa.sequence=cheat_ammo_seq System.out.println(idkfa.CheckCheat('i')); System.out.println(idkfa.CheckCheat('d')); System.out.println(idkfa.CheckCheat('k')); System.out.println(idkfa.CheckCheat('f')); System.out.println(idkfa.CheckCheat('a')); System.out.println(idkfa.CheckCheat('i')); System.out.println(idkfa.CheckCheat('d')); System.out.println(idkfa.CheckCheat('k')); System.out.println(idkfa.CheckCheat('f')); System.out.println(idkfa.CheckCheat('b')); System.out.println(idkfa.CheckCheat('i')); System.out.println(idkfa.CheckCheat('d')); System.out.println(idkfa.CheckCheat('k')); System.out.println(idkfa.CheckCheat('f')); System.out.println(idkfa.CheckCheat('a')); } } package testers; import m.FixedFloat; import m.fixed_t; class FPBench{ public static final int PRECISION=16; public static void main(String argv[]) { final int tests=500000; for (int j=0;j<2;j++){ float[] a=new float[tests]; float[] b=new float[tests]; float[] c=new float[tests]; fixed_t[] fa=new fixed_t[tests]; fixed_t[] fb=new fixed_t[tests]; fixed_t[] fc=new fixed_t[tests]; long tan_0=System.nanoTime(); for (int i=0;i<tests;i++){ a[i]=((float)(Math.random()*65535.0-32678.0)); b[i]=((float)(Math.random()*65535.0-32678.0)); } long tan_1=System.nanoTime(); System.out.println("Allocate "+tests+" random IEEE floats: \t" + (tan_1-tan_0)); tan_0=System.nanoTime(); for (int i=0;i<tests;i++){ fa[i]=F2F(a[i]); fb[i]=F2F(b[i]); } tan_1=System.nanoTime(); System.out.println("Convert "+tests+" random IEEE floats into fixed_t (creates new): \t" + (tan_1-tan_0)); tan_0=System.nanoTime(); for (int i=0;i<tests;i++){ F2F(a[i],fa[i]); F2F(b[i],fb[i]); } tan_1=System.nanoTime(); System.out.println("Convert "+tests+" random IEEE floats into fixed_t (reuses objects): \t" + (tan_1-tan_0)); tan_0=System.nanoTime(); for (int i=0;i<tests;i++){ fa[i].set(FixedFloat.toFixed(a[i])); fb[i].set(FixedFloat.toFixed(b[i])); } tan_1=System.nanoTime(); System.out.println("Convert "+tests+" random IEEE floats into fixed_t (new method, reuses objects): \t" + (tan_1-tan_0)); tan_0=System.nanoTime(); for (int i=0;i<tests;i++){ c[i]=a[i]*b[i]; } tan_1=System.nanoTime(); System.out.println("Perform "+tests+" random IEEE floats multiplications: \t" + (tan_1-tan_0)); tan_0=System.nanoTime(); for (int i=0;i<tests;i++){ fc[i]=new fixed_t(fixed_t.FixedMul(fa[i],fb[i])); } tan_1=System.nanoTime(); System.out.println("Perform "+tests+" random fixed_t multiplications (new objects): \t"+ (tan_1-tan_0)); System.out.println(tan_1-tan_0); tan_0=System.nanoTime(); for (int i=0;i<tests;i++){ fc[i].val=(fixed_t.FixedMulInt(fa[i],fb[i])); } tan_1=System.nanoTime(); System.out.println("Perform "+tests+" random fixed_t multiplications (integer return): \t" + (tan_1-tan_0)); tan_0=System.nanoTime(); for (int i=0;i<tests;i++){ fixed_t.FixedMul(fa[i],fb[i],fc[i]); } tan_1=System.nanoTime(); System.out.println("Perform "+tests+" random fixed_t multiplications (in-place): \t"+ (tan_1-tan_0)); tan_0=System.nanoTime(); for (int i=0;i<tests;i++){ c[i]=a[i]/b[i]; } tan_1=System.nanoTime(); System.out.println("Perform "+tests+" random IEEE floats divisions: \t" + (tan_1-tan_0)); tan_0=System.nanoTime(); for (int i=0;i<tests;i++){ // fc[i]=fixed_t.FixedDiv(fa[i],fb[i]); } tan_1=System.nanoTime(); System.out.println("Perform "+tests+" random fixed_t divisions (new objects): \t"+ (tan_1-tan_0)); tan_0=System.nanoTime(); for (int i=0;i<tests;i++){ //fixed_t.FixedDiv(fa[i],fb[i],fc[i]); } tan_1=System.nanoTime(); System.out.println("Perform "+tests+" random fixed_t divisions (in-place): \t"+ (tan_1-tan_0)); } } public static fixed_t F2F(float f){ fixed_t tmp; int ing; ing=(int)Math.floor(f); tmp= new fixed_t((ing<<16)|FixedDecimal(f)); //System.out.println(Integer.toHexString(tmp.val)); return tmp; } public static void F2F(float f, fixed_t tmp){ int ing; ing=(int)Math.floor(f); tmp.set((ing<<16)|FixedDecimal(f)); } public static char FixedDecimal(float f){ char fixint_value=0; float decimal_part= (float) (f-Math.floor(f)); for ( int i = 1; i <= PRECISION; i++) { if (decimal_part >= 1.f/(float)(i + 1)) { decimal_part -= 1.f/(float)(i + 1); fixint_value |= (1 << PRECISION - i); } } return fixint_value; } } package testers; import m.fixed_t; public interface Operation { public void invoke(fixed_t a,fixed_t b); } package testers; import java.util.Arrays; public class TestPrimitiveClassDetection { public static void main(String argv[]) throws InstantiationException, IllegalAccessException{ Object shit; Object shiiit=new double[5]; shit=shiiit; System.out.println(shit.getClass()); int status=-1; double[] something = null; if (shit instanceof double[]) something=(double[])shit; System.out.println(something.length); } } package testers; import static data.Defines.PU_STATIC; import static data.Limits.MAXEVENTS; import n.DummyNetworkHandler; import hu.HU; import i.DoomSystem; import i.IDoomSystem; import p.Actions; import p.ILevelLoader; import p.LevelLoader; import automap.Map; import awt.AWTDoom; import rr.SimpleTextureManager; import rr.SpriteManager; import rr.UnifiedRenderer; import s.DummySoundDriver; import st.StatusBar; import timing.MilliTicker; import m.IDoomMenu; import m.Menu; import m.DoomRandom; import v.BufferedRenderer16; import v.DoomVideoRenderer; import v.GammaTables; import v.IVideoScale; import v.VideoScaleInfo; import w.DoomBuffer; import w.WadLoader; import defines.*; import data.Tables; import doom.CommandLine; import doom.DoomMain; import doom.event_t; import doom.ticcmd_t; import doom.wbstartstruct_t; /** This is a very simple tester for Menu module */ public class AWTRenderViewTester { static IVideoScale VSI=new VideoScaleInfo(3.0f); public static void main(String[] argv) { try { Tables.InitTables(); // Create a Wad file loader. WadLoader W=new WadLoader(); W.InitMultipleFiles(new String[] {"doom1.wad","COLORS15.lmp"}); System.out.println("Total lumps read: "+W.numlumps); // Read the palette. DoomBuffer palette = W.CacheLumpName("PLAYPAL", PU_STATIC); // Create a video renderer DoomVideoRenderer<short[]> V=new BufferedRenderer16(VSI.getScreenWidth(),VSI.getScreenHeight()); V.setVideoScale(VSI); V.initScaling(); V.Init(); byte[] pal=palette.getBuffer().array(); V.createPalettes(pal, GammaTables.gammatables, 14, 256, 3, 5); IDoomSystem I=new DoomSystem(); DoomMain DM=new DoomMain(); DM.CM=new CommandLine(argv); DM.TICK=new MilliTicker(); DM.setVideoScale(VSI); DM.initScaling(); DM.singletics=true; DM.setTicdup(1); DM.DGN=new DummyNetworkHandler(); // Create the frame. AWTDoom frame = new AWTDoom(DM,(DoomVideoRenderer<short[]>) V); frame.InitGraphics(); DM.I=I; DM.VI=frame; DM.S=new DummySoundDriver(); frame.DM=DM; DM.W=W; DM.V=V; DM.DM=DM; HU HU=new HU(DM); DM.language=Language_t.english; HU.Init(); DM.HU=HU; DM.gameepisode=1; DM.gamemap=1; DM.gamemission=GameMission_t.doom; DM.setGameMode(GameMode_t.shareware); DM.wminfo=new wbstartstruct_t(); // Simulate being in the mid of a level. DM.usergame=true; DM.gamestate=gamestate_t.GS_LEVEL; DM.gameskill=skill_t.sk_hard; //C2JUtils.initArrayOfObjects(DM.players,player_t.class); DM.RND=new DoomRandom(); DM.players[0].cmd=new ticcmd_t(); DM.players[0].itemcount=1337; DM.players[0].killcount=1337; DM.players[0].secretcount=1337; DM.wminfo.plyr[0].in=true; DM.wminfo.plyr[0].sitems=1337; DM.wminfo.plyr[0].skills=1337; DM.wminfo.plyr[0].stime=28595; DM.wminfo.plyr[0].ssecret=1337; DM.playeringame[0]=true; DM.wminfo.last=6; DM.wminfo.epsd=0; DM.wminfo.maxitems=100; DM.wminfo.maxkills=100; DM.wminfo.maxsecret=100; DM.wminfo.partime=28595; IDoomMenu M=DM.M=new Menu(DM); Map AM=new Map(DM); DM.AM=AM; StatusBar ST=(StatusBar) (DM.ST=new StatusBar(DM)); ILevelLoader LL=DM.LL=new LevelLoader(DM); DM.P=new Actions(DM); DM.SM=new SpriteManager(DM); DM.R=new UnifiedRenderer(DM); DM.TM=new SimpleTextureManager(DM); DM.P.updateStatus(DM); LL.updateStatus(DM); M.updateStatus(DM); ST.updateStatus(DM); AM.updateStatus(DM); //DM.R.updateStatus(DM); // Interesting bug: initializing the Unified renderer BEFORE applying // scale info, results in VERY dark visplanes. // This can't be done as easily with the Parallel because a lot more // stuff will break. DM.initializeVideoScaleStuff(); DM.R.Init(); DM.P.Init(); DM.players[0].updateStatus(DM); DM.players[0].PlayerReborn(); ST.Init(); M.Init(); ST.Start(); LL.SetupLevel(1, 1, 0, skill_t.sk_hard); AM.LevelInit(); AM.Start(); DM.R.SetViewSize(11, 1); DM.R.ExecuteSetViewSize(); DM.TM.setSkyTexture(DM.TM.CheckTextureNumForName("SKY1")); long a=System.nanoTime(); DM.menuactive=false; DM.automapactive=true; DM.R.FillBackScreen(); DM.R.DrawViewBorder(); // Center on "bloody mess" in E1M1 DM.players[0].mo.y+=420<<16; //DM.players[0].mo.x+=1650<<16; int pl=0; for (int i=0;i<20000;i++){ frame.GetEvent(); for ( ; DM.eventtail != DM.eventhead ; DM.eventtail = (++DM.eventtail)&(MAXEVENTS-1) ) { event_t ev = DM.events[DM.eventtail]; //System.out.println(ev); if (ev!=null){ AM.Responder(ev); M.Responder(ev); ST.Responder(ev); } } //V.DrawPatch(0,0,0,help1); //M.Ticker(); //M.Drawer(); AM.Ticker(); //AM.Drawer(); ST.Ticker(); DM.players[0].viewz=(40)<<16; //DM.players[0].mo.x=ox+(int) ((12864<<16)*Math.cos(2*Math.PI*i/500.0)); //DM.players[0].mo.y=oy+(int) ((64<<16)*Math.sin(2*Math.PI*i/500.0)); //DM.players[0].mo.angle= ((long)(0xFFFFFFFFL*(Math.atan2(DM.players[0].mo.y-oy,DM.players[0].mo.x-ox)+Math.PI)/(2*Math.PI)))&0xFFFFFFFFL; DM.players[0].mo.angle=(DM.players[0].mo.angle-0x800000)&0xFFFFFFFFL; //System.out.println(">>>>>>>>>>>>>>>>>> VIEW ANGLE "+360.0*(DM.players[0].mo.angle>>19)/8192.0); DM.R.RenderPlayerView(DM.players[0]); //ST.Drawer(false,true); //System.out.println("Rendered"+DM.gametic); DM.gametic++; frame.FinishUpdate(); if (i%200==0){ long b=System.nanoTime(); //frame.setPalette((pl++)%14); System.out.println((200) +" frames in " +((b-a)/1e09) +" = "+200/((b-a)/1e09) + " fps"); a=b; } // System.out.print(frame.processEvents()); /*while (ba-DM.I.GetTime()>-1){ Thread.sleep(10); }*/ } } catch (Exception e){ e.printStackTrace(); } } } package testers; import m.fixed_t; public class Add implements Operation { @Override public void invoke(fixed_t a, fixed_t b) { a.add(b); } } package testers; import java.util.HashMap; import pooling.RoguePatchMap; import pooling.RoguePatchMap2; public class TestPatchMaps { public static final int TESTS=2000000; public static final int SET=30; public static final int REPS=50; static byte[][][] stuff=new byte[SET][1][1]; static byte[][][] stuff2=new byte[TESTS][][]; // A normal hashmap.... static HashMap<Integer,byte[][]> map1= new HashMap<Integer,byte[][]> (); // And a NEW and ENHANCED implementation. static RoguePatchMap map2=new RoguePatchMap(); public static void main(String[] argv){ for (int i=0;i<REPS;i++){ System.out.printf("Put %d in Hashmap: %d\n",TESTS,testPutHashmap()); } for (int i=0;i<REPS;i++){ System.out.printf("Put %d in Patchmap: %d\n",TESTS,testPutPatchmap()); } for (int i=0;i<REPS;i++){ System.out.printf("Get %d from Hashmap: %d\n",TESTS,testGetHashmap()); } for (int i=0;i<REPS;i++){ System.out.printf("Get %d from Patchmap: %d\n",TESTS,testGetPatchmap()); } } private static long testPutHashmap(){ long a=System.nanoTime(); for (int i=0;i<TESTS;i++){ map1.put(i%SET, stuff[i%SET]); } long b=System.nanoTime(); return (b-a); } private static long testGetHashmap(){ long a=System.nanoTime(); for (int i=0;i<TESTS;i++){ stuff2[i]=map1.get(i%SET); } long b=System.nanoTime(); return (b-a); } private static long testPutPatchmap(){ long a=System.nanoTime(); for (int i=0;i<TESTS;i++){ map2.put(i%SET, stuff[i%SET]); } long b=System.nanoTime(); return (b-a); } private static long testGetPatchmap(){ long a=System.nanoTime(); for (int i=0;i<TESTS;i++){ stuff2[i]=map2.get(i%SET); } long b=System.nanoTime(); return (b-a); } } package testers; import static data.Defines.PU_STATIC; import static data.Defines.pw_allmap; import static m.fixed_t.FRACBITS; import java.awt.image.IndexColorModel; import automap.DoomAutoMap; import automap.Map; import p.LevelLoader; import p.mobj_t; import m.DoomRandom; import st.StatusBar; import data.Defines; import defines.*; import doom.DoomContext; import doom.DoomMain; import doom.DoomStatus; import doom.event_t; import doom.evtype_t; import doom.player_t; import doom.ticcmd_t; import doom.wbstartstruct_t; import doom.weapontype_t; import utils.C2JUtils; import v.SimpleRenderer; import w.*; /** This is a very simple tester for the Automap. Combined with status bar + Level loader. */ public class AutoMapTester { public static void main(String[] argv) { try { WadLoader W=new WadLoader(); W.InitMultipleFiles(new String[] {"doom1.wad"}); //W.AddFile("bitter.wad"); System.out.println("Total lumps read: "+W.numlumps); SimpleRenderer V=new SimpleRenderer(320,200); V.Init(); DoomBuffer palette = W.CacheLumpName("PLAYPAL", PU_STATIC); byte[] pal=palette.getBuffer().array(); IndexColorModel icm=new IndexColorModel(8, 256,pal, 0, false); DoomMain DM=new DoomMain(); DM.gameepisode=1; DM.gamemap=1; DM.gamemission=GameMission_t.doom; DM.gamemode=GameMode_t.shareware; DM.wminfo=new wbstartstruct_t(); C2JUtils.initArrayOfObjects(DM.players,player_t.class); Defines.SCREENWIDTH=320; Defines.SCREENHEIGHT=200; DM.DM=(DoomMain) DM; DM.W=W; DM.V=V; DM.RND=new DoomRandom(); DM.players[0].cmd=new ticcmd_t(); DM.players[0].itemcount=1337; DM.players[0].killcount=1337; DM.players[0].secretcount=1337; DM.players[0].weaponowned[0]=true; DM.players[0].weaponowned[1]=true; DM.players[0].weaponowned[2]=true; DM.players[0].weaponowned[3]=true; DM.players[0].readyweapon=weapontype_t.wp_pistol; DM.players[0].health[0]=100; DM.players[0].armorpoints[0]=666; DM.players[0].ammo[0]=666; DM.players[0].maxammo[0]=666; DM.players[0].ammo[1]=666; DM.players[0].maxammo[1]=666; DM.players[0].ammo[2]=666; DM.players[0].maxammo[2]=666; DM.players[0].ammo[3]=666; DM.players[0].maxammo[3]=666; DM.players[0].cards[0]=true; DM.players[0].cards[2]=true; DM.players[0].cards[4]=true; DM.players[0].mo=new mobj_t(); DM.players[0].mo.x=1056<<FRACBITS; DM.players[0].mo.y=-3616<<FRACBITS; DM.players[0].powers[pw_allmap]=100; DM.deathmatch=false; DM.statusbaractive=true; DM.wminfo.plyr[0].in=true; DM.wminfo.plyr[0].sitems=1337; DM.wminfo.plyr[0].skills=1337; DM.wminfo.plyr[0].stime=28595; DM.wminfo.plyr[0].ssecret=1337; DM.playeringame[0]=true; DM.wminfo.last=6; DM.wminfo.epsd=0; DM.wminfo.maxitems=100; DM.wminfo.maxkills=100; DM.wminfo.maxsecret=100; DM.wminfo.partime=28595; StatusBar ST=(StatusBar) (DM.ST=new StatusBar(DM)); ST.Start(); LevelLoader PL=new LevelLoader(DM); PL.SetupLevel(1, 1, 0, skill_t.sk_hard); DM.LL=PL; DM.ST=ST; DoomAutoMap AM=DM.AM=new Map(DM); AM.Start(); ST.Responder(new event_t('i')); ST.Responder(new event_t('d')); ST.Responder(new event_t('d')); ST.Responder(new event_t('t')); AM.Responder(new event_t(Map.AM_FOLLOWKEY)); AM.Responder(new event_t(Map.AM_ZOOMINKEY)); AM.Responder(new event_t(Map.AM_GRIDKEY)); AM.updateStatus(DM); ST.updateStatus(DM); for (int i=0;i<100;i++){ DM.players[0].health[0]--; DM.players[0].ammo[0]=i; DM.players[0].damagecount=1; // if zero, it won't update. AM.Responder(new event_t(evtype_t.ev_keyup,Map.AM_MSGENTERED)); if ((i%20)<10) AM.Responder(new event_t(Map.AM_ZOOMINKEY)); else AM.Responder(new event_t(Map.AM_ZOOMOUTKEY)); if ((i%25)<12) { AM.Responder(new event_t(Map.AM_PANUPKEY)); AM.Responder(new event_t(Map.AM_PANRIGHTKEY)); } else { AM.Responder(new event_t(Map.AM_PANDOWNKEY)); AM.Responder(new event_t(Map.AM_PANLEFTKEY)); } AM.Ticker(); AM.Drawer(); ST.Ticker(); ST.Drawer(false,true); V.takeScreenShot(0, "tic"+i,icm); //AM.Responder(new event_t(Map.AM_PANLEFTKEY)); } } catch (Exception e){ e.printStackTrace(); } } } package testers; import hu.HU; import m.DummyMenu; import m.IDoomMenu; import m.MenuMisc; import demo.VanillaDoomDemo; import doom.CommandLine; import doom.DoomStatus; import w.*; public class DefaultsTester { public static void main(String[] argv) { DoomStatus DS=new DoomStatus(); m.AbstractDoomMenu dummy= new DummyMenu(); DS.M=dummy; DS.HU=new HU(); DS.CM=new CommandLine(argv); MenuMisc.LoadDefaults(DS); MenuMisc.defaultfile="ghetto.cfg"; MenuMisc.SaveDefaults(DS); } } package testers; import java.lang.reflect.Method; import m.FixedFloat; import m.fixed_t; import w.WadLoader; /** A simple benchmark proving how using reflection is slower than both simply switching * on a status flag, or using "invokable" objects. This affected the choice of invokable objects * and simple switching whenever Doom used function pointers. * */ public class TestReflection { /** * @param args * @throws Exception * @throws */ public static void main(String[] args) throws Exception { int TESTS = 50000000; // "function pointer" to fadd Method fadd = fixed_t.class.getDeclaredMethod("add", fixed_t.class); Method fmul = fixed_t.class.getDeclaredMethod("FixedMul", fixed_t.class); Operation fadd2=new Add(); Operation fmul2=new Mul(); Operation todo; fixed_t a = new fixed_t(FixedFloat.toFixed(1.0)); fixed_t b = new fixed_t(FixedFloat.toFixed(1.1)); Method operation = fadd; { long ta = System.nanoTime(); for (int i = 0; i < TESTS; i++) { operation.invoke(a, b); } long tb = System.nanoTime(); System.out.println("Adding with reflection :"+(tb - ta)); } System.out.println(FixedFloat.toDouble(a.val)); a = new fixed_t(FixedFloat.toFixed(1.0)); operation = fmul; { long ta = System.nanoTime(); for (int i = 0; i < TESTS; i++) { operation.invoke(a, b); } long tb = System.nanoTime(); System.out.println("Multiplying with reflection :"+(tb - ta)); } System.out.println(FixedFloat.toDouble(a.val)); /** add=0, mul=1 */ int op = 0; a = new fixed_t(FixedFloat.toFixed(1.0)); { long ta = System.nanoTime(); for (int i = 0; i < TESTS; i++) { switch (op) { case 0: a.add(b); break; case 1: a.FixedMul(b); break; } } long tb = System.nanoTime(); System.out.println("Adding with switching :"+(tb - ta)); } System.out.println(FixedFloat.toDouble(a.val)); op=1; a = new fixed_t(FixedFloat.toFixed(1.0)); { long ta = System.nanoTime(); for (int i = 0; i < TESTS; i++) { switch (op) { case 0: a.add(b); case 1: a.FixedMul(b); } } long tb = System.nanoTime(); System.out.println("Multiplying with switching :"+(tb - ta)); } System.out.println(FixedFloat.toDouble(a.val)); a = new fixed_t(FixedFloat.toFixed(1.0)); todo=fadd2; { long ta = System.nanoTime(); for (int i = 0; i < TESTS; i++) { todo.invoke(a,b); } long tb = System.nanoTime(); System.out.println("Adding with invokable object :"+(tb - ta)); } System.out.println(FixedFloat.toDouble(a.val)); a = new fixed_t(FixedFloat.toFixed(1.0)); todo=fmul2; { long ta = System.nanoTime(); for (int i = 0; i < TESTS; i++) { todo.invoke(a,b); } long tb = System.nanoTime(); System.out.println("Multiplying with invokable object :"+(tb - ta)); } System.out.println(FixedFloat.toDouble(a.val)); operation = fadd; { long ta = System.nanoTime(); for (int i = 0; i < TESTS; i++) { operation.invoke(a, b); } long tb = System.nanoTime(); System.out.println("Adding with reflection 2:"+(tb - ta)); } System.out.println(FixedFloat.toDouble(a.val)); a = new fixed_t(FixedFloat.toFixed(1.0)); operation = fmul; { long ta = System.nanoTime(); for (int i = 0; i < TESTS; i++) { operation.invoke(a, b); } long tb = System.nanoTime(); System.out.println("Multiplying with reflection 2:"+(tb - ta)); } System.out.println(FixedFloat.toDouble(a.val)); } } package testers; import m.Swap; public class TestSwap { /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub short a=(short)0xBEEF; char c=(char)0xBABE; int b=0XDEADBABE; System.out.printf("%X\n",a); System.out.printf("%X\n",b); System.out.printf("%X\n",(int)c); a=Swap.SHORT(a); b=Swap.LONG(b); c=(char)Swap.SHORT(c); System.out.printf("%X\n",a); System.out.printf("%X\n",b); System.out.printf("%X\n",(int)c); c=(char)Swap.SHORT(a); System.out.printf("%X\n",(int)c); char aa=Swap.USHORT((char)a); System.out.printf("%X\n",(int)a); System.out.printf("%X\n",(int)aa); } } package testers; import static data.Defines.PU_STATIC; import hu.HU; import java.nio.ByteBuffer; import m.FixedFloat; import rr.vertex_t; import data.mapvertex_t; import defines.*; import doom.DoomContext; import doom.DoomStatus; import utils.C2JUtils; import w.*; /** This is a very simple tester for the WadLoader and HU modules. * We use the same exact methods used in the C source code, only * with a more OO approach. * * */ public class WadLoaderTester { public static void main(String[] argv) { try { IWadLoader W=new WadLoader(); W.InitMultipleFiles(new String[] {"doom1.wad"}); //W.AddFile("bitter.wad"); System.out.println("Total lumps read: "+W.NumLumps()); System.out.println("NUm for E1M1: "+W.GetNumForName("E1M1")); System.out.println("NUm for SECTORS: "+W.GetNumForName("SECTORS")); System.out.println("NUm for SSECTORS: "+W.GetNumForName("SSECTORS")); int lump=W.GetNumForName("VERTEXES"); System.out.println("NUm for VERTEXES: "+W.GetNumForName("VERTEXES")); // We prepare a ByteBuffer to receive a "SECTORS" object. Deserializing it is // another matter. ByteBuffer bb=W.CacheLumpName("SECTORS", 0).getBuffer(); System.out.println("Num for THINGS: "+W.GetNumForName("THINGS")); DoomStatus ds = new DoomStatus(); ds.gameepisode=1; ds.gamemap=1; ds.gamemission=GameMission_t.doom; ds.W=W; // Testing "Heads Up" clases. Notice how we pass "doomstate" and "WadLoader" as instances, // instead of globals. OO all the way, baby! // Determine number of lumps: // total lump length / vertex record length. int numvertexes = W.LumpLength (lump) / mapvertex_t.sizeOf(); // Allocate zone memory for buffer. vertex_t[] vertexes; // Load data into cache. // MAES: we now have a mismatch between memory/disk: in memory, we need an array. // On disk, we have a single lump/blob. Thus, we need to find a way to deserialize this... vertexes= W.CacheLumpNumIntoArray(lump,numvertexes,vertex_t.class); // Copy and convert vertex coordinates, // internal representation as fixed. for (int i=0 ; i<numvertexes ; i++) { //vertexes[i].x = ml[i].x<<FRACBITS; //vertexes[i].y = ml[i].y<<FRACBITS; System.out.println(vertexes[i].x+" , "+vertexes[i].y+" "+FixedFloat.toDouble(vertexes[i].x)+" , "+FixedFloat.toDouble(vertexes[i].y)); //System.out.println(ml[i].x+" , "+ml[i].y+" "+FPTest.Value(vertexes[i].x)+" , "+FPTest.Value(vertexes[i].y)); } } catch (Exception e){ e.printStackTrace(); } } } package testers; public class TestLightLevels { public static final int LIGHTLEVELS= 32; public static final int MAXLIGHTSCALE =48; public static final int NUMCOLORMAPS =32; public static final int LIGHTBRIGHT =2; public static final int DISTMAP =2; public static void main(String[] argv){ for (int i = 0; i < LIGHTLEVELS; i++) { int startmap = ((LIGHTLEVELS-LIGHTBRIGHT-i)*2)*NUMCOLORMAPS/LIGHTLEVELS; for (int j = 0; j < MAXLIGHTSCALE; j++) { int level = startmap - j/ DISTMAP; if (level < 0) level = 0; if (level >= NUMCOLORMAPS) level = NUMCOLORMAPS - 1; System.out.printf("%d ",level); } System.out.printf("\n"); } } } package testers; import static data.Defines.KEY_F1; import static data.Defines.PU_STATIC; import static data.Defines.pw_allmap; import static m.fixed_t.FRACBITS; import i.InputListener; import i.IDoomSystem; import java.applet.Applet; import java.awt.Canvas; import java.awt.Color; import java.awt.Dimension; import java.awt.Font; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Image; import java.awt.image.BufferedImage; import java.awt.image.DataBufferByte; import java.awt.image.IndexColorModel; import java.io.IOException; import javax.imageio.ImageIO; import m.IDoomMenu; import m.Menu; import m.DoomRandom; import p.LevelLoader; import p.mobj_t; import rr.patch_t; import st.StatusBar; import utils.C2JUtils; import v.BufferedRenderer; import w.DoomBuffer; import w.WadLoader; import automap.DoomAutoMap; import automap.Map; import data.Defines; import data.Defines.GameMission_t; import data.Defines.GameMode_t; import data.Defines.skill_t; import doom.DoomContext; import doom.DoomStatus; import doom.event_t; import doom.player_t; import doom.ticcmd_t; import doom.wbstartstruct_t; import doom.weapontype_t; import f.EndLevel; import f.Wiper; public class DoomApplet extends Applet { public static final int WIDTH=320; public static final int HEIGHT=200; public static final int TICKS=5000; IndexColorModel[] icms; Canvas drawhere; BufferedImage[] bi; int palette=0; Dimension size; Image crap; InputListener in; Graphics2D g2d; BufferedImage[] pals; /** * */ private static final long serialVersionUID = 1L; IWadLoader W; BufferedRenderer V; DoomStatus ds; DoomContext DC; StatusBar ST; Map AM; Menu M; EndLevel EL; private patch_t titlepic; private patch_t help1; private Wiper wipe; public void start(){ } public void doStuff(){ // Do a screen wipe WipeYoAss(); /* DoMenu(); doAutoMap(); doEndLevel();*/ } private void doAutoMap() { V.screens[0]=((DataBufferByte)pals[0].getRaster().getDataBuffer()).getData(); ST.Responder(new event_t('i')); ST.Responder(new event_t('d')); ST.Responder(new event_t('d')); ST.Responder(new event_t('t')); AM.Responder(new event_t(Map.AM_FOLLOWKEY)); AM.Responder(new event_t(Map.AM_ZOOMOUTKEY)); AM.Responder(new event_t(Map.AM_GRIDKEY)); long a=System.nanoTime(); for (int i=0;i<TICKS;i++){ if ((i%20)<10) AM.Responder(new event_t(Map.AM_ZOOMINKEY)); else AM.Responder(new event_t(Map.AM_ZOOMOUTKEY)); if ((i%25)<12) { AM.Responder(new event_t(Map.AM_PANUPKEY)); AM.Responder(new event_t(Map.AM_PANRIGHTKEY)); } else { AM.Responder(new event_t(Map.AM_PANDOWNKEY)); AM.Responder(new event_t(Map.AM_PANLEFTKEY)); } V.DrawPatch(0, 0, 0, titlepic); AM.Ticker(); AM.Drawer(); ST.Ticker(); ST.Drawer(false,true); this.setPalette((i/(TICKS/14))%14); this.processEvents(); //System.out.println(V.isRasterNull(0)); this.update(); } } private void dump(int i) { try { V.takeScreenShot(0, "C:\\SHIT"+i+".PNG",icms[0]); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } public void init(){ // size size = new Dimension(); size.width = WIDTH; size.height = HEIGHT; this.setSize(size); // Canvas drawhere=new Canvas(); drawhere.setSize(size); drawhere.setBounds(0, 0, drawhere.getWidth(),drawhere.getHeight()); drawhere.setBackground(Color.black); this.add(drawhere); drawhere.setVisible(true); // Listener in = new InputListener(); this.addComponentListener(in); // Graphics g2d = (Graphics2D)drawhere.getGraphics(); //this.setSize(new Dimension(WIDTH,)); this.setVisible(true); W=new WadLoader(); try { W.InitMultipleFiles(new String[] {"doom.wad"}); } catch (Exception e) { e.printStackTrace(); } help1=W.CachePatchName("HELP1", PU_STATIC); System.out.println(help1.height); System.out.println(help1.width); System.out.println(help1); System.out.println(W.GetNumForName("TITLEPIC")); System.out.println(W.lumpinfo[W.GetNumForName("TITLEPIC")].size); titlepic=W.CachePatchName("HELP1", PU_STATIC); System.out.println(titlepic.height); System.out.println(titlepic.width); System.out.println(help1); System.out.println(W.GetNumForName("HELP1")); System.out.println(W.lumpinfo[W.GetNumForName("HELP1")].size); DoomBuffer palette = W.CacheLumpName("PLAYPAL", PU_STATIC); byte[] pal=palette.getBuffer().array(); IndexColorModel icm=new IndexColorModel(8, 256,pal, 0, false); Defines.SCREENWIDTH=WIDTH; Defines.SCREENHEIGHT=HEIGHT; V=new BufferedRenderer(WIDTH,HEIGHT,icm); V.Init(); icms=new IndexColorModel[palette.getBuffer().limit()/768]; for (int i=0;i<icms.length;i++){ icms[i]=new IndexColorModel(8, 256,pal, i*768, false); } pals=new BufferedImage[icms.length]; pals=V.getBufferedScreens(0, icms); /* for (int i=0;i<icms.length;i++){ icms[i]=new IndexColorModel(8, 256,pal, i*768, false); pals[i]=new BufferedImage(icms[i],V.screenbuffer[0].getRaster(), false, null); } */ this.Init(pals); ds=new DoomStatus(); initDoomState(ds); DC=new DoomContext(); DC.DS=ds; DC.W=W; DC.V=V; DC.RND=new DoomRandom(); ST=new StatusBar(DC); ST.Start(); LevelLoader PL=new LevelLoader(DC); try { PL.SetupLevel(1, 1, 0, skill_t.sk_hard); } catch (Exception e) { e.printStackTrace(); } DC.LL=PL; DC.ST=ST; AM=new Map(DC); AM.Start(); M= new Menu(DC); M.Init(); wipe=new Wiper(DC); EL=new EndLevel(DC); EL.Start(DC.DS.wminfo); doStuff(); } private static void initDoomState(DoomStatus ds) { C2JUtils.initArrayOfObjects(ds.players,player_t.class); ds.gameepisode=1; ds.gamemap=1; ds.gamemission=GameMission_t.doom; ds.gamemode=GameMode_t.shareware; ds.wminfo=new wbstartstruct_t(); ds.players[0].cmd=new ticcmd_t(); ds.players[0].itemcount=1337; ds.players[0].killcount=1337; ds.players[0].secretcount=1337; ds.players[0].weaponowned[0]=true; ds.players[0].weaponowned[1]=true; ds.players[0].weaponowned[2]=true; ds.players[0].weaponowned[3]=true; ds.players[0].readyweapon=weapontype_t.wp_pistol; ds.players[0].health[0]=100; ds.players[0].armorpoints[0]=100; ds.players[0].ammo[0]=400; ds.players[0].maxammo[0]=400; ds.players[0].ammo[1]=100; ds.players[0].maxammo[1]=100; ds.players[0].ammo[2]=100; ds.players[0].maxammo[2]=100; ds.players[0].ammo[3]=600; ds.players[0].maxammo[3]=600; ds.players[0].cards[0]=true; ds.players[0].cards[2]=true; ds.players[0].cards[4]=true; ds.players[0].mo=new mobj_t(); ds.players[0].mo.x=1056<<FRACBITS; ds.players[0].mo.y=-3616<<FRACBITS; ds.players[0].powers[pw_allmap]=100; ds.deathmatch=false; ds.statusbaractive=true; ds.wminfo.plyr[0].in=true; ds.wminfo.plyr[0].sitems=1337; ds.wminfo.plyr[0].skills=1337; ds.wminfo.plyr[0].stime=28595; ds.wminfo.plyr[0].ssecret=1337; ds.playeringame[0]=true; ds.wminfo.last=6; ds.wminfo.epsd=0; ds.wminfo.maxitems=100; ds.wminfo.maxkills=100; ds.wminfo.maxsecret=100; ds.wminfo.partime=28595; } /* public void paint(Graphics g){ g2d=(Graphics2D) g; g2d.drawImage(bi[palette],0,0,this); }*/ public void paint(Graphics g){ /* if (g==null) this.update(null); else super.paint(g); //g2d.drawImage(bi[palette],0,0,this); * */ } public void Init(BufferedImage[] bi) { this.setBackground(Color.black); this.bi=bi; } public void Init(BufferedImage bi) { this.bi=new BufferedImage[]{bi}; size = new Dimension(); size.width = bi.getWidth(); size.height = bi.getHeight(); this.setSize(size); drawhere=new Canvas(); drawhere.setSize(size); drawhere.setBounds(0, 0, drawhere.getWidth(),drawhere.getHeight()); drawhere.setBackground(Color.black); this.add(drawhere); in = new InputListener(); this.addComponentListener(in); g2d = (Graphics2D)drawhere.getGraphics(); } public void setPalette(int pal){ this.palette=pal; } public void update() { g2d=(Graphics2D) drawhere.getGraphics(); g2d.drawImage(bi[palette],0,0,this); //drawhere.repaint(); //super.update(this.getGraphics()); } public void update(Graphics g) { // g2d.drawImage(bi[palette],0,0,this); } public String processEvents(){ StringBuffer tmp=new StringBuffer(); event_t event; while ( (event=InputListener.nextEvent()) != null ) { tmp.append(event.type.ordinal()+"\n"); } return tmp.toString(); } public void WipeYoAss(){ int WIPES=1; for (int i=0;i<WIPES;i++){ V.DrawPatch(0, 0, 0, help1); // Grab "help" wipe.StartScreen(0, 0, Defines.SCREENWIDTH, Defines.SCREENHEIGHT); V.DrawPatch(0, 0, 0, titlepic); wipe.EndScreen(0, 0, Defines.SCREENWIDTH, Defines.SCREENHEIGHT); int wipestart = IDoomSystem.GetTime () - 1; int nowtime; int tics; boolean done; do { do { nowtime = IDoomSystem.GetTime (); tics = nowtime - wipestart; } while (tics<1); wipestart = nowtime; //V.DrawPatch(0, 0, 0, titlepic); V.DrawPatch(0, 0, 0, help1); //V.DrawPatch(320, 0, 0, titlepic); done = wipe.ScreenWipe(Wiper.wipe.Melt.ordinal() , 0, 0, Defines.SCREENWIDTH, Defines.SCREENHEIGHT, tics); //I_UpdateNoBlit (); //M_Drawer (); // menu is drawn even on top of wipes //System.out.println(V.isRasterNull(0)); this.update(); } while (!done); } } public void DoMenu(){ ds.menuactive=true; for (int i=0;i<600;i++){ if (i==40){ System.out.println("Pressing enter"); M.Responder(new event_t(Defines.KEY_DOWNARROW)); } if (i==60){ System.out.println("Pressing down"); M.Responder(new event_t(Defines.KEY_DOWNARROW)); } if (i==80){ System.out.println("Pressing escape"); M.Responder(new event_t(Defines.KEY_ESCAPE)); } if (i==100){ System.out.println("Pressing up"); M.Responder(new event_t(Defines.KEY_UPARROW)); } if (i==120){ System.out.println("Pressing up"); M.Responder(new event_t(Defines.KEY_UPARROW)); } if (i==140){ System.out.println("Pressing escape"); M.Responder(new event_t(Defines.KEY_ESCAPE)); } if (i==160){ System.out.println("Pressing up"); M.Responder(new event_t(Defines.KEY_UPARROW)); } if (i==160){ System.out.println("Pressing up"); M.Responder(new event_t(Defines.KEY_UPARROW)); } if (i==300 || i==500|i==550|i==600){ System.out.println("Pressing F1"); M.Responder(new event_t(KEY_F1)); System.out.println("pressed "); } if (i==400 || i==650){ System.out.println("Pressing escape"); M.Responder(new event_t(Defines.KEY_ESCAPE)); } //V.takeScreenShot(0,( "menutic"+i),icm); try { Thread.sleep(14); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } V.DrawPatch(0,0,0,help1); M.Ticker(); M.Drawer(); System.out.println(V.isRasterNull(0)); this.update(); //System.out.print(frame.processEvents()); } } public void doEndLevel(){ int a,b; a=IDoomSystem.GetTime(); b=a; for (int i=0;i<2000;i++){ EL.Ticker(); EL.Drawer(); this.update(); if (i==100){ ds.players[0].cmd.buttons=1; // simulate attack ds.players[0].attackdown=false; // simulate attack } if (i==120){ ds.players[0].cmd.buttons=1; // simulate attack ds.players[0].attackdown=false; // simulate attack } // Do we still have time> while((b-a)<1) { b=IDoomSystem.GetTime(); } a=b; } } } package testers; import v.FastTranspose; public class TestFastTranspose { /** * @param args */ public static void main(String[] args) { int length=11; byte[] x=new byte[1<<(length<<1)]; byte[] scratch=new byte[1<<(FastTranspose.BL<<1)]; for(int i=0;i< 1<<(length<<1); i++ ) x[i]=(byte) ((i%256)-128); FastTranspose.blocktranspose(x, scratch, length); } } package testers; import java.io.IOException; import java.io.OutputStream; import java.io.Writer; import s.QMusToMid; import s.QMusToMid.Track; public class QMusToMidTester extends QMusToMid { //_D_: used by me to test and fix bugs //FileInputStream fisTest; //int nbTest = 0; public QMusToMidTester() { super(); /*try { fisTest = new FileInputStream("C:\\Users\\David\\Desktop\\qmus2mid\\test.mid"); //fisTest.read(new byte[106]); //debut track 0 fisTest.read(new byte[6977]); //debut track 1 } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); }*/ } public static void fwrite(byte[] bytes, int offset, int size, int count, Object file) throws IOException { //nbTest += size; if (file instanceof OutputStream) { //.getClass(). . isAssignableFrom(OutputStream.class)) ((OutputStream)file).write(bytes, offset, size); for (int i = 0; i < size; i++) { //System.out.println(bytes[i]); //byte b = (byte)fisTest.read(); //if (b != bytes[offset+i]) { //System.out.println("Woawww byte #: "+(offset+i)+" "+b+ " VS "+bytes[offset+i]); //} } } if (file instanceof Writer) { // if (file.getClass().isAssignableFrom(Writer.class)) char[] ch = new char[bytes.length]; for (int i = 0; i < bytes.length; i++) { //System.out.println(bytes[i]); ch[i] = (char)toUnsigned(bytes[i]); if ((byte)ch[i] == 0x3F) { System.out.println("testtt"); } //char c = (char)fisTest.read(); //if (c != ch[i]) { // System.out.println("Woawww"); //} } ((Writer)file).write(ch, offset, size); } } public void TWriteByte( int MIDItrack, byte byte_, Track track[] ) { super.TWriteByte(MIDItrack, byte_, track); //_D_: this was only added by me to test and fix bugs before this worked /*try { if (MIDItrack == 1) { int b; b = fisTest.read(); if ((byte)b != byte_) { //System.out.println("Woawww"); } } } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); }*/ } } package testers; import javax.swing.*; import java.awt.event.*; import java.awt.BorderLayout; import java.awt.Dimension; public class KeyEventDemo extends JPanel implements KeyListener, ActionListener { /** * */ private static final long serialVersionUID = 1L; JTextArea displayArea; JTextField typingArea; static final String newline = "\n"; public KeyEventDemo() { super(new BorderLayout()); JButton button = new JButton("Clear"); button.addActionListener(this); typingArea = new JTextField(20); typingArea.addKeyListener(this); //Uncomment this if you wish to turn off focus //traversal. The focus subsystem consumes //focus traversal keys, such as Tab and Shift Tab. //If you uncomment the following line of code, this //disables focus traversal and the Tab events will //become available to the key event listener. //typingArea.setFocusTraversalKeysEnabled(false); displayArea = new JTextArea(); displayArea.setEditable(false); JScrollPane scrollPane = new JScrollPane(displayArea); scrollPane.setPreferredSize(new Dimension(375, 125)); add(typingArea, BorderLayout.PAGE_START); add(scrollPane, BorderLayout.CENTER); add(button, BorderLayout.PAGE_END); } /** Handle the key typed event from the text field. */ public void keyTyped(KeyEvent e) { displayInfo(e, "KEY TYPED: "); } /** Handle the key pressed event from the text field. */ public void keyPressed(KeyEvent e) { displayInfo(e, "KEY PRESSED: "); } /** Handle the key released event from the text field. */ public void keyReleased(KeyEvent e) { displayInfo(e, "KEY RELEASED: "); } /** Handle the button click. */ public void actionPerformed(ActionEvent e) { //Clear the text components. displayArea.setText(""); typingArea.setText(""); //Return the focus to the typing area. typingArea.requestFocusInWindow(); } /* * We have to jump through some hoops to avoid * trying to print non-printing characters * such as Shift. (Not only do they not print, * but if you put them in a String, the characters * afterward won't show up in the text area.) */ protected void displayInfo(KeyEvent e, String s){ String keyString, modString, tmpString, actionString, locationString; //You should only rely on the key char if the event //is a key typed event. int id = e.getID(); if (id == KeyEvent.KEY_TYPED) { char c = e.getKeyChar(); keyString = "key character = '" + c + "'"; } else { int keyCode = e.getKeyCode(); keyString = "key code = " + keyCode + " (" + KeyEvent.getKeyText(keyCode) + ")"; } int modifiers = e.getModifiersEx(); modString = "modifiers = " + modifiers; tmpString = KeyEvent.getModifiersExText(modifiers); if (tmpString.length() > 0) { modString += " (" + tmpString + ")"; } else { modString += " (no modifiers)"; } actionString = "action key? "; if (e.isActionKey()) { actionString += "YES"; } else { actionString += "NO"; } locationString = "key location: "; int location = e.getKeyLocation(); if (location == KeyEvent.KEY_LOCATION_STANDARD) { locationString += "standard"; } else if (location == KeyEvent.KEY_LOCATION_LEFT) { locationString += "left"; } else if (location == KeyEvent.KEY_LOCATION_RIGHT) { locationString += "right"; } else if (location == KeyEvent.KEY_LOCATION_NUMPAD) { locationString += "numpad"; } else { // (location == KeyEvent.KEY_LOCATION_UNKNOWN) locationString += "unknown"; } displayArea.append(s + newline + " " + keyString + newline + " " + modString + newline + " " + actionString + newline + " " + locationString + newline); displayArea.setCaretPosition(displayArea.getDocument().getLength()); } /** * Create the GUI and show it. For thread safety, * this method should be invoked from the * event-dispatching thread. */ private static void createAndShowGUI() { //Make sure we have nice window decorations. JFrame.setDefaultLookAndFeelDecorated(true); //Create and set up the window. JFrame frame = new JFrame("KeyEventDemo"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); //Create and set up the content pane. JComponent newContentPane = new KeyEventDemo(); newContentPane.setOpaque(true); //content panes must be opaque frame.setContentPane(newContentPane); //Display the window. frame.pack(); frame.setVisible(true); } public static void main(String[] args) { //Schedule a job for the event-dispatching thread: //creating and showing this application's GUI. javax.swing.SwingUtilities.invokeLater(new Runnable() { public void run() { createAndShowGUI(); } }); } } package testers; import static data.Defines.KEY_F1; import static data.Defines.PU_STATIC; import hu.HU; import i.InputListener; import java.awt.image.BufferedImage; import java.awt.image.IndexColorModel; import javax.swing.JFrame; import rr.patch_t; import m.IDoomMenu; import m.Menu; import m.DoomRandom; import utils.C2JUtils; import v.BufferedRenderer; import v.SimpleRenderer; import w.DoomBuffer; import w.WadLoader; import data.Defines; import data.Defines.GameMission_t; import data.Defines.GameMode_t; import data.Defines.Language_t; import doom.DoomContext; import doom.DoomMain; import doom.DoomStatus; import doom.event_t; import doom.player_t; import doom.ticcmd_t; import doom.wbstartstruct_t; /** This is a very simple tester for Menu module */ public class MenuTester { public static final int WIDTH=320; public static void main(String[] argv) { try { WadLoader W=new WadLoader(); W.InitMultipleFiles(new String[] {"doom1.wad"}); //W.AddFile("bitter.wad"); System.out.println("Total lumps read: "+W.numlumps); patch_t help1=W.CachePatchName("TITLEPIC", PU_STATIC); DoomBuffer palette = W.CacheLumpName("PLAYPAL", PU_STATIC); byte[] pal=palette.getBuffer().array(); IndexColorModel icm=new IndexColorModel(8, 256,pal, 0, false); Defines.SCREENWIDTH=320; Defines.SCREENHEIGHT=200; BufferedRenderer V=new BufferedRenderer(320,200,icm); V.Init(); IndexColorModel[] icms=new IndexColorModel[palette.getBuffer().limit()/768]; BufferedImage[] pals=new BufferedImage[icms.length]; for (int i=0;i<icms.length;i++){ icms[i]=new IndexColorModel(8, 256,pal, i*768, false); pals[i]=new BufferedImage(icms[i],V.screenbuffer[0].getRaster(), false, null); } DoomMain DM=new DoomMain(); DM.W=W; DM.V=V; HU HU=new HU(DM); HU.Init(); DM.HU=HU; DM.gameepisode=1; DM.gamemap=1; DM.gamemission=GameMission_t.doom; DM.gamemode=GameMode_t.shareware; DM.wminfo=new wbstartstruct_t(); DM.usergame=true; C2JUtils.initArrayOfObjects(DM.players,player_t.class); DM.RND=new DoomRandom(); DM.players[0].cmd=new ticcmd_t(); DM.players[0].itemcount=1337; DM.players[0].killcount=1337; DM.players[0].secretcount=1337; DM.wminfo.plyr[0].in=true; DM.wminfo.plyr[0].sitems=1337; DM.wminfo.plyr[0].skills=1337; DM.wminfo.plyr[0].stime=28595; DM.wminfo.plyr[0].ssecret=1337; DM.playeringame[0]=true; DM.wminfo.last=6; DM.wminfo.epsd=0; DM.wminfo.maxitems=100; DM.wminfo.maxkills=100; DM.wminfo.maxsecret=100; DM.wminfo.partime=28595; IDoomMenu M=DM.M=new Menu(DM); DM.language=Language_t.english; M.Init(); CrappyDisplay frame = new CrappyDisplay(pals); frame.setTitle("MochaDoom"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.pack(); frame.setLocationRelativeTo(null); //frame.setUndecorated(true); frame.setVisible(true); frame.setBounds(frame.getX(), frame.getY(), WIDTH, 240); DM.menuactive=true; for (int i=0;i<10000;i++){ int a=DM.I.GetTime(); while (a-DM.I.GetTime()==0){ frame.setVisible(true); Thread.sleep(1); } event_t ev=CrappyDisplay.nextEvent(); //System.out.println(ev); if (ev!=null) ((Menu)M).Responder(ev); /* if (i==40){ System.out.println("Pressing enter"); M.Responder(new event_t(Defines.KEY_DOWNARROW)); } if (i==60){ System.out.println("Pressing down"); M.Responder(new event_t(Defines.KEY_DOWNARROW)); } if (i==80){ System.out.println("Pressing escape"); M.Responder(new event_t(Defines.KEY_ESCAPE)); } if (i==100){ System.out.println("Pressing up"); M.Responder(new event_t(Defines.KEY_UPARROW)); } if (i==120){ System.out.println("Pressing up"); M.Responder(new event_t(Defines.KEY_UPARROW)); } if (i==140){ System.out.println("Pressing escape"); M.Responder(new event_t(Defines.KEY_ESCAPE)); } if (i==160){ System.out.println("Pressing up"); M.Responder(new event_t(Defines.KEY_UPARROW)); } if (i==160){ System.out.println("Pressing up"); M.Responder(new event_t(Defines.KEY_UPARROW)); } if (i==300 || i==500|i==550|i==600){ System.out.println("Pressing F1"); M.Responder(new event_t(KEY_F1)); System.out.println("pressed "); } if (i==400 || i==650){ System.out.println("Pressing escape"); M.Responder(new event_t(Defines.KEY_ESCAPE)); } */ //V.takeScreenShot(0,( "menutic"+i),icm); V.DrawPatch(0,0,0,help1); M.Ticker(); M.Drawer(); DM.gametic++; frame.update(null); System.out.print(frame.processEvents()); } } catch (Exception e){ e.printStackTrace(); } } } package testers; import hu.HU; import p.LevelLoader; import rr.SimpleTextureManager; import rr.TextureManager; import s.DummySoundDriver; import s.IDoomSound; import utils.C2JUtils; import w.WadLoader; import defines.*; import doom.DoomContext; import doom.DoomStatus; import doom.player_t; import doom.wbstartstruct_t; /** This is a very simple tester for the WadLoader and HU modules. * We use the same exact methods used in the C source code, only * with a more OO approach. * * */ public class LevelLoaderTester { public static void main(String[] argv) { try { WadLoader W=new WadLoader(); W.InitMultipleFiles(new String[] {"C:\\DOOMS\\doom1.wad"}); //W.AddFile("bitter.wad"); System.out.println("Total lumps read: "+W.numlumps); System.out.println("NUm for E1M1: "+W.GetNumForName("E1M1")); DoomStatus DS = new DoomStatus(); DS.gameepisode=1; DS.gamemap=1; DS.gamemission=GameMission_t.doom; DS.gamemode=GameMode_t.shareware; IDoomSound S=new DummySoundDriver(); DS.S=S; DS.W=W; LevelLoader LL=new LevelLoader(DS); DS.LL=LL; TextureManager TM=new SimpleTextureManager(DS); DS.TM=TM; LL.updateStatus(DS); TM.InitFlats(); TM.InitTextures(); //HU hu=new HU(DS); //hu.Init(); LL.SetupLevel(1, 1, 0, skill_t.sk_hard); } catch (Exception e){ e.printStackTrace(); } } } package testers; import rr.MultiPatchSynthesizer; import rr.column_t; public class TestPatchSynthesis { public static void main(String[] argv){ final byte[] crap={1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16}; final boolean[] shit={true,true,true, false, false, false, true, true,true, false,true,false, true,false,true, true}; column_t fuck=MultiPatchSynthesizer.getColumnStream(crap, shit, crap.length); System.out.println("FUCK" +fuck); } } package testers; import m.DoomRandom; import m.IRandom; /** Another microbenchmark. According to this, casting "unsigned bytes" to chars is * faster when performed with a logical function, rather than with arithmetic or * direct logical operations. Inlining perhaps? Furthermore, it has almost no penalty * compared to not casting or to use built-in casting. So it's possible to use a * "toUnsigned" function of sorts, whenever you need to treat signed bytes as unsigned * without expanding them to chars in memory. * */ public class Unsigned { public static void main(String[] argv){ int TESTS=100000000; IRandom r=new DoomRandom(); byte[] values=new byte[TESTS]; for (int i=0;i<TESTS;i++) { values[i]=(byte)r.P_Random(); } byte ub=(byte) 0xFF; char us=(char) ub; System.out.println((int)us); us= 0xFFFF; ub=(byte) us; System.out.println(ub); System.out.println((int)((char)ub)); System.out.println((int)us); long a=System.nanoTime(); for (int i=0;i<TESTS;i++) { us=(char) (0x00FF&values[i]); } long b=System.nanoTime(); System.out.println("Time for "+TESTS+" byte to \"unsigned byte\" casts (with logical ops)"+((b-a)/1e09)); a=System.nanoTime(); for (int i=0;i<TESTS;i++) { us=unsigned(values[i]); } b=System.nanoTime(); System.out.println("Time for "+TESTS+" byte to \"unsigned byte\" casts (with num. function) "+((b-a)/1e09)); a=System.nanoTime(); for (int i=0;i<TESTS;i++) { us=unsigned2(values[i]); } b=System.nanoTime(); System.out.println("Time for "+TESTS+" byte to \"unsigned byte\" casts (with log. function)"+((b-a)/1e09)); a=System.nanoTime(); for (int i=0;i<TESTS;i++) { ub=values[i]; us= (char) ub; } b=System.nanoTime(); System.out.println("Time for "+TESTS+" byte to \"unsigned byte\" casts (no casting)"+((b-a)/1e09)); } public static final char unsigned(byte b){ return (char) ((b>0)?b:(-b+128)); } public static char unsigned2(byte b){ return (char) (0x00FF&b); } } package testers; import m.DoomRandom; import m.IRandom; import s.ClassicDoomSoundDriver; import s.ClipSFXModule; import s.SpeakerDoomSoundDriver; import data.sounds.sfxenum_t; import doom.DoomStatus; import w.WadLoader; public class TestClipSound { public static void main(String[] argv) throws Exception{ DoomStatus DS=new DoomStatus(); WadLoader W=new WadLoader(); IRandom RND=new DoomRandom(); DS.W=W; DS.RND=RND; W.InitMultipleFiles(new String[]{"doom1.wad"}); ClipSFXModule sound=new ClipSFXModule(DS,4); sound.InitSound(); sound.SetChannels(4); Thread.sleep(1000); //sound.StartSound(1, 127, 127, 127, 0); for (int i=0;i<1000;i++){ Thread.sleep(1000/35); if (i%70==0) sound.StartSound(sfxenum_t.sfx_plpain.ordinal(), 127, 127, 127, 0); if (i%90==0) sound.StartSound(sfxenum_t.sfx_barexp.ordinal(), 127, 0, 127, 0); if (i%35==0) sound.StartSound(sfxenum_t.sfx_plpain.ordinal(), 127, 255, 127, 0); if (i%35==0) sound.StartSound(sfxenum_t.sfx_oof.ordinal(), 127, 192, 127, 0); sound.UpdateSound(); sound.SubmitSound(); System.out.println(DS.gametic++); } sound.ShutdownSound(); } } package testers; import p.Actions; import p.mobj_t; import pooling.AudioChunkPool; import pooling.MobjPool; import s.AudioChunk; public class TestMobjPooling { public static final int TESTS=1000000; public static void main(String[] argv){ mobj_t[] chunks=new mobj_t[TESTS]; MobjPool chunkpool=new MobjPool(new Actions()); long a=System.nanoTime(); for (int i=0;i<TESTS;i++){ chunks[i]=new mobj_t(); } for (int i=0;i<TESTS;i++){ chunks[i]=new mobj_t(); } for (int i=0;i<TESTS;i++){ chunks[i]=new mobj_t(); } for (int i=0;i<TESTS;i++){ chunks[i]=new mobj_t(); } long b=System.nanoTime(); System.out.println("Time: "+(float)(b-a)/1000000000f); a=System.nanoTime(); for (int i=0;i<TESTS;i++){ chunks[i]=chunkpool.checkOut(); } for (int i=0;i<TESTS;i++){ chunkpool.checkIn(chunks[i]); chunks[i]=chunkpool.checkOut(); } for (int i=0;i<TESTS;i++){ chunkpool.checkIn(chunks[i]); chunks[i]=chunkpool.checkOut(); } for (int i=0;i<TESTS;i++){ chunkpool.checkIn(chunks[i]); chunks[i]=chunkpool.checkOut(); } b=System.nanoTime(); System.out.println("Time: "+(float)(b-a)/1000000000f); } } package testers; import java.awt.image.BufferedImage; import java.awt.image.DataBufferByte; import java.io.BufferedInputStream; import java.io.ByteArrayInputStream; import java.io.FileInputStream; import java.io.FileOutputStream; import java.nio.ByteBuffer; import java.nio.ByteOrder; import javax.imageio.ImageIO; public class PaletteShower12 { public static final int BLOCKSIZE=16; public static final int SHIFT=4; public static void main(String[] argv) { try { BufferedInputStream bis; bis=new BufferedInputStream(new FileInputStream(argv[0])); int size=bis.available(); int blocks=size/512; byte[] palbuf=new byte[512]; ByteBuffer bb= ByteBuffer.wrap(palbuf); bb.order(ByteOrder.LITTLE_ENDIAN); bb.mark(); for (int i=0;i<blocks;i++){ bis.read(palbuf); BufferedImage bim=new BufferedImage(16*BLOCKSIZE,16*BLOCKSIZE,BufferedImage.TYPE_INT_ARGB); for (int j=0;j<256;j++){ short shcol=bb.getShort(); int r=(0xF000&shcol)>>12; int g=(0xF00&shcol)>>8; int b=(0xF0&shcol)>>4; System.out.printf("%x %d %d %d\n",shcol,r,g,b); int colo= 0xFF000000|(r<<(16+SHIFT))| (g<<(8+SHIFT))| (b<<SHIFT); for (int x=0;x<BLOCKSIZE;x++){ for (int y=0;y<BLOCKSIZE;y++){ bim.setRGB(BLOCKSIZE*(j%16)+x, BLOCKSIZE*(j/16)+y, colo); } } } FileOutputStream f=new FileOutputStream(String.format("%s-%d.PNG",argv[0],i)); ImageIO.write(bim,"PNG",f); bb.reset(); } bis.close(); } catch (Exception e){ e.printStackTrace(); } } } package defines; /** Mission packs - might be useful for TC stuff? * */ public enum GameMission_t { doom, // DOOM 1 doom2, // DOOM 2 pack_tnt, // TNT mission pack pack_plut, // Plutonia pack none } package defines; /** Move clipping aid for LineDefs. */ public enum slopetype_t { ST_HORIZONTAL, ST_VERTICAL, ST_POSITIVE, ST_NEGATIVE } package defines; public enum skill_t { sk_baby, sk_easy, sk_medium, sk_hard, sk_nightmare } package defines; /** The current state of the game: whether we are playing, gazing at the intermission screen, the game final animation, or a demo. */ public enum gamestate_t { GS_LEVEL, GS_INTERMISSION, GS_FINALE, GS_DEMOSCREEN, GS_MINUS_ONE // hack used for the "-1" state } package defines; /** * Key cards. */ public enum card_t { it_bluecard, it_yellowcard, it_redcard, it_blueskull, it_yellowskull, it_redskull, NUMCARDS } package defines; //Ammunition types defined. public enum ammotype_t { am_clip, // Pistol / chaingun ammo. am_shell, // Shotgun / double barreled shotgun. am_cell, // Plasma rifle, BFG. am_misl, // Missile launcher. NUMAMMO, am_noammo // Unlimited for chainsaw / fist. } package defines; /** Identify language to use, software localization. */ public enum Language_t { english, french, german, unknown } package defines; /** Game mode handling - identify IWAD version to handle IWAD dependend animations etc. */ public enum GameMode_t { shareware, // DOOM 1 shareware, E1, M9 registered, // DOOM 1 registered, E3, M27 commercial, // DOOM 2 retail, E1 M34 // DOOM 2 german edition not handled retail, // DOOM 1 retail, E4, M36 pack_tnt, // TNT mission pack pack_plut, // Plutonia pack pack_xbla, // XBLA Doom. How you got hold of it, I don't care :-p indetermined // Well, no IWAD found. } ; package defines; public enum statenum_t { S_NULL, S_LIGHTDONE, S_PUNCH, S_PUNCHDOWN, S_PUNCHUP, S_PUNCH1, S_PUNCH2, S_PUNCH3, S_PUNCH4, S_PUNCH5, S_PISTOL, S_PISTOLDOWN, S_PISTOLUP, S_PISTOL1, S_PISTOL2, S_PISTOL3, S_PISTOL4, S_PISTOLFLASH, S_SGUN, S_SGUNDOWN, S_SGUNUP, S_SGUN1, S_SGUN2, S_SGUN3, S_SGUN4, S_SGUN5, S_SGUN6, S_SGUN7, S_SGUN8, S_SGUN9, S_SGUNFLASH1, S_SGUNFLASH2, S_DSGUN, S_DSGUNDOWN, S_DSGUNUP, S_DSGUN1, S_DSGUN2, S_DSGUN3, S_DSGUN4, S_DSGUN5, S_DSGUN6, S_DSGUN7, S_DSGUN8, S_DSGUN9, S_DSGUN10, S_DSNR1, S_DSNR2, S_DSGUNFLASH1, S_DSGUNFLASH2, S_CHAIN, S_CHAINDOWN, S_CHAINUP, S_CHAIN1, S_CHAIN2, S_CHAIN3, S_CHAINFLASH1, S_CHAINFLASH2, S_MISSILE, S_MISSILEDOWN, S_MISSILEUP, S_MISSILE1, S_MISSILE2, S_MISSILE3, S_MISSILEFLASH1, S_MISSILEFLASH2, S_MISSILEFLASH3, S_MISSILEFLASH4, S_SAW, S_SAWB, S_SAWDOWN, S_SAWUP, S_SAW1, S_SAW2, S_SAW3, S_PLASMA, S_PLASMADOWN, S_PLASMAUP, S_PLASMA1, S_PLASMA2, S_PLASMAFLASH1, S_PLASMAFLASH2, S_BFG, S_BFGDOWN, S_BFGUP, S_BFG1, S_BFG2, S_BFG3, S_BFG4, S_BFGFLASH1, S_BFGFLASH2, S_BLOOD1, S_BLOOD2, S_BLOOD3, S_PUFF1, S_PUFF2, S_PUFF3, S_PUFF4, S_TBALL1, S_TBALL2, S_TBALLX1, S_TBALLX2, S_TBALLX3, S_RBALL1, S_RBALL2, S_RBALLX1, S_RBALLX2, S_RBALLX3, S_PLASBALL, S_PLASBALL2, S_PLASEXP, S_PLASEXP2, S_PLASEXP3, S_PLASEXP4, S_PLASEXP5, S_ROCKET, S_BFGSHOT, S_BFGSHOT2, S_BFGLAND, S_BFGLAND2, S_BFGLAND3, S_BFGLAND4, S_BFGLAND5, S_BFGLAND6, S_BFGEXP, S_BFGEXP2, S_BFGEXP3, S_BFGEXP4, S_EXPLODE1, S_EXPLODE2, S_EXPLODE3, S_TFOG, S_TFOG01, S_TFOG02, S_TFOG2, S_TFOG3, S_TFOG4, S_TFOG5, S_TFOG6, S_TFOG7, S_TFOG8, S_TFOG9, S_TFOG10, S_IFOG, S_IFOG01, S_IFOG02, S_IFOG2, S_IFOG3, S_IFOG4, S_IFOG5, S_PLAY, S_PLAY_RUN1, S_PLAY_RUN2, S_PLAY_RUN3, S_PLAY_RUN4, S_PLAY_ATK1, S_PLAY_ATK2, S_PLAY_PAIN, S_PLAY_PAIN2, S_PLAY_DIE1, S_PLAY_DIE2, S_PLAY_DIE3, S_PLAY_DIE4, S_PLAY_DIE5, S_PLAY_DIE6, S_PLAY_DIE7, S_PLAY_XDIE1, S_PLAY_XDIE2, S_PLAY_XDIE3, S_PLAY_XDIE4, S_PLAY_XDIE5, S_PLAY_XDIE6, S_PLAY_XDIE7, S_PLAY_XDIE8, S_PLAY_XDIE9, S_POSS_STND, S_POSS_STND2, S_POSS_RUN1, S_POSS_RUN2, S_POSS_RUN3, S_POSS_RUN4, S_POSS_RUN5, S_POSS_RUN6, S_POSS_RUN7, S_POSS_RUN8, S_POSS_ATK1, S_POSS_ATK2, S_POSS_ATK3, S_POSS_PAIN, S_POSS_PAIN2, S_POSS_DIE1, S_POSS_DIE2, S_POSS_DIE3, S_POSS_DIE4, S_POSS_DIE5, S_POSS_XDIE1, S_POSS_XDIE2, S_POSS_XDIE3, S_POSS_XDIE4, S_POSS_XDIE5, S_POSS_XDIE6, S_POSS_XDIE7, S_POSS_XDIE8, S_POSS_XDIE9, S_POSS_RAISE1, S_POSS_RAISE2, S_POSS_RAISE3, S_POSS_RAISE4, S_SPOS_STND, S_SPOS_STND2, S_SPOS_RUN1, S_SPOS_RUN2, S_SPOS_RUN3, S_SPOS_RUN4, S_SPOS_RUN5, S_SPOS_RUN6, S_SPOS_RUN7, S_SPOS_RUN8, S_SPOS_ATK1, S_SPOS_ATK2, S_SPOS_ATK3, S_SPOS_PAIN, S_SPOS_PAIN2, S_SPOS_DIE1, S_SPOS_DIE2, S_SPOS_DIE3, S_SPOS_DIE4, S_SPOS_DIE5, S_SPOS_XDIE1, S_SPOS_XDIE2, S_SPOS_XDIE3, S_SPOS_XDIE4, S_SPOS_XDIE5, S_SPOS_XDIE6, S_SPOS_XDIE7, S_SPOS_XDIE8, S_SPOS_XDIE9, S_SPOS_RAISE1, S_SPOS_RAISE2, S_SPOS_RAISE3, S_SPOS_RAISE4, S_SPOS_RAISE5, S_VILE_STND, S_VILE_STND2, S_VILE_RUN1, S_VILE_RUN2, S_VILE_RUN3, S_VILE_RUN4, S_VILE_RUN5, S_VILE_RUN6, S_VILE_RUN7, S_VILE_RUN8, S_VILE_RUN9, S_VILE_RUN10, S_VILE_RUN11, S_VILE_RUN12, S_VILE_ATK1, S_VILE_ATK2, S_VILE_ATK3, S_VILE_ATK4, S_VILE_ATK5, S_VILE_ATK6, S_VILE_ATK7, S_VILE_ATK8, S_VILE_ATK9, S_VILE_ATK10, S_VILE_ATK11, S_VILE_HEAL1, S_VILE_HEAL2, S_VILE_HEAL3, S_VILE_PAIN, S_VILE_PAIN2, S_VILE_DIE1, S_VILE_DIE2, S_VILE_DIE3, S_VILE_DIE4, S_VILE_DIE5, S_VILE_DIE6, S_VILE_DIE7, S_VILE_DIE8, S_VILE_DIE9, S_VILE_DIE10, S_FIRE1, S_FIRE2, S_FIRE3, S_FIRE4, S_FIRE5, S_FIRE6, S_FIRE7, S_FIRE8, S_FIRE9, S_FIRE10, S_FIRE11, S_FIRE12, S_FIRE13, S_FIRE14, S_FIRE15, S_FIRE16, S_FIRE17, S_FIRE18, S_FIRE19, S_FIRE20, S_FIRE21, S_FIRE22, S_FIRE23, S_FIRE24, S_FIRE25, S_FIRE26, S_FIRE27, S_FIRE28, S_FIRE29, S_FIRE30, S_SMOKE1, S_SMOKE2, S_SMOKE3, S_SMOKE4, S_SMOKE5, S_TRACER, S_TRACER2, S_TRACEEXP1, S_TRACEEXP2, S_TRACEEXP3, S_SKEL_STND, S_SKEL_STND2, S_SKEL_RUN1, S_SKEL_RUN2, S_SKEL_RUN3, S_SKEL_RUN4, S_SKEL_RUN5, S_SKEL_RUN6, S_SKEL_RUN7, S_SKEL_RUN8, S_SKEL_RUN9, S_SKEL_RUN10, S_SKEL_RUN11, S_SKEL_RUN12, S_SKEL_FIST1, S_SKEL_FIST2, S_SKEL_FIST3, S_SKEL_FIST4, S_SKEL_MISS1, S_SKEL_MISS2, S_SKEL_MISS3, S_SKEL_MISS4, S_SKEL_PAIN, S_SKEL_PAIN2, S_SKEL_DIE1, S_SKEL_DIE2, S_SKEL_DIE3, S_SKEL_DIE4, S_SKEL_DIE5, S_SKEL_DIE6, S_SKEL_RAISE1, S_SKEL_RAISE2, S_SKEL_RAISE3, S_SKEL_RAISE4, S_SKEL_RAISE5, S_SKEL_RAISE6, S_FATSHOT1, S_FATSHOT2, S_FATSHOTX1, S_FATSHOTX2, S_FATSHOTX3, S_FATT_STND, S_FATT_STND2, S_FATT_RUN1, S_FATT_RUN2, S_FATT_RUN3, S_FATT_RUN4, S_FATT_RUN5, S_FATT_RUN6, S_FATT_RUN7, S_FATT_RUN8, S_FATT_RUN9, S_FATT_RUN10, S_FATT_RUN11, S_FATT_RUN12, S_FATT_ATK1, S_FATT_ATK2, S_FATT_ATK3, S_FATT_ATK4, S_FATT_ATK5, S_FATT_ATK6, S_FATT_ATK7, S_FATT_ATK8, S_FATT_ATK9, S_FATT_ATK10, S_FATT_PAIN, S_FATT_PAIN2, S_FATT_DIE1, S_FATT_DIE2, S_FATT_DIE3, S_FATT_DIE4, S_FATT_DIE5, S_FATT_DIE6, S_FATT_DIE7, S_FATT_DIE8, S_FATT_DIE9, S_FATT_DIE10, S_FATT_RAISE1, S_FATT_RAISE2, S_FATT_RAISE3, S_FATT_RAISE4, S_FATT_RAISE5, S_FATT_RAISE6, S_FATT_RAISE7, S_FATT_RAISE8, S_CPOS_STND, S_CPOS_STND2, S_CPOS_RUN1, S_CPOS_RUN2, S_CPOS_RUN3, S_CPOS_RUN4, S_CPOS_RUN5, S_CPOS_RUN6, S_CPOS_RUN7, S_CPOS_RUN8, S_CPOS_ATK1, S_CPOS_ATK2, S_CPOS_ATK3, S_CPOS_ATK4, S_CPOS_PAIN, S_CPOS_PAIN2, S_CPOS_DIE1, S_CPOS_DIE2, S_CPOS_DIE3, S_CPOS_DIE4, S_CPOS_DIE5, S_CPOS_DIE6, S_CPOS_DIE7, S_CPOS_XDIE1, S_CPOS_XDIE2, S_CPOS_XDIE3, S_CPOS_XDIE4, S_CPOS_XDIE5, S_CPOS_XDIE6, S_CPOS_RAISE1, S_CPOS_RAISE2, S_CPOS_RAISE3, S_CPOS_RAISE4, S_CPOS_RAISE5, S_CPOS_RAISE6, S_CPOS_RAISE7, S_TROO_STND, S_TROO_STND2, S_TROO_RUN1, S_TROO_RUN2, S_TROO_RUN3, S_TROO_RUN4, S_TROO_RUN5, S_TROO_RUN6, S_TROO_RUN7, S_TROO_RUN8, S_TROO_ATK1, S_TROO_ATK2, S_TROO_ATK3, S_TROO_PAIN, S_TROO_PAIN2, S_TROO_DIE1, S_TROO_DIE2, S_TROO_DIE3, S_TROO_DIE4, S_TROO_DIE5, S_TROO_XDIE1, S_TROO_XDIE2, S_TROO_XDIE3, S_TROO_XDIE4, S_TROO_XDIE5, S_TROO_XDIE6, S_TROO_XDIE7, S_TROO_XDIE8, S_TROO_RAISE1, S_TROO_RAISE2, S_TROO_RAISE3, S_TROO_RAISE4, S_TROO_RAISE5, S_SARG_STND, S_SARG_STND2, S_SARG_RUN1, S_SARG_RUN2, S_SARG_RUN3, S_SARG_RUN4, S_SARG_RUN5, S_SARG_RUN6, S_SARG_RUN7, S_SARG_RUN8, S_SARG_ATK1, S_SARG_ATK2, S_SARG_ATK3, S_SARG_PAIN, S_SARG_PAIN2, S_SARG_DIE1, S_SARG_DIE2, S_SARG_DIE3, S_SARG_DIE4, S_SARG_DIE5, S_SARG_DIE6, S_SARG_RAISE1, S_SARG_RAISE2, S_SARG_RAISE3, S_SARG_RAISE4, S_SARG_RAISE5, S_SARG_RAISE6, S_HEAD_STND, S_HEAD_RUN1, S_HEAD_ATK1, S_HEAD_ATK2, S_HEAD_ATK3, S_HEAD_PAIN, S_HEAD_PAIN2, S_HEAD_PAIN3, S_HEAD_DIE1, S_HEAD_DIE2, S_HEAD_DIE3, S_HEAD_DIE4, S_HEAD_DIE5, S_HEAD_DIE6, S_HEAD_RAISE1, S_HEAD_RAISE2, S_HEAD_RAISE3, S_HEAD_RAISE4, S_HEAD_RAISE5, S_HEAD_RAISE6, S_BRBALL1, S_BRBALL2, S_BRBALLX1, S_BRBALLX2, S_BRBALLX3, S_BOSS_STND, S_BOSS_STND2, S_BOSS_RUN1, S_BOSS_RUN2, S_BOSS_RUN3, S_BOSS_RUN4, S_BOSS_RUN5, S_BOSS_RUN6, S_BOSS_RUN7, S_BOSS_RUN8, S_BOSS_ATK1, S_BOSS_ATK2, S_BOSS_ATK3, S_BOSS_PAIN, S_BOSS_PAIN2, S_BOSS_DIE1, S_BOSS_DIE2, S_BOSS_DIE3, S_BOSS_DIE4, S_BOSS_DIE5, S_BOSS_DIE6, S_BOSS_DIE7, S_BOSS_RAISE1, S_BOSS_RAISE2, S_BOSS_RAISE3, S_BOSS_RAISE4, S_BOSS_RAISE5, S_BOSS_RAISE6, S_BOSS_RAISE7, S_BOS2_STND, S_BOS2_STND2, S_BOS2_RUN1, S_BOS2_RUN2, S_BOS2_RUN3, S_BOS2_RUN4, S_BOS2_RUN5, S_BOS2_RUN6, S_BOS2_RUN7, S_BOS2_RUN8, S_BOS2_ATK1, S_BOS2_ATK2, S_BOS2_ATK3, S_BOS2_PAIN, S_BOS2_PAIN2, S_BOS2_DIE1, S_BOS2_DIE2, S_BOS2_DIE3, S_BOS2_DIE4, S_BOS2_DIE5, S_BOS2_DIE6, S_BOS2_DIE7, S_BOS2_RAISE1, S_BOS2_RAISE2, S_BOS2_RAISE3, S_BOS2_RAISE4, S_BOS2_RAISE5, S_BOS2_RAISE6, S_BOS2_RAISE7, S_SKULL_STND, S_SKULL_STND2, S_SKULL_RUN1, S_SKULL_RUN2, S_SKULL_ATK1, S_SKULL_ATK2, S_SKULL_ATK3, S_SKULL_ATK4, S_SKULL_PAIN, S_SKULL_PAIN2, S_SKULL_DIE1, S_SKULL_DIE2, S_SKULL_DIE3, S_SKULL_DIE4, S_SKULL_DIE5, S_SKULL_DIE6, S_SPID_STND, S_SPID_STND2, S_SPID_RUN1, S_SPID_RUN2, S_SPID_RUN3, S_SPID_RUN4, S_SPID_RUN5, S_SPID_RUN6, S_SPID_RUN7, S_SPID_RUN8, S_SPID_RUN9, S_SPID_RUN10, S_SPID_RUN11, S_SPID_RUN12, S_SPID_ATK1, S_SPID_ATK2, S_SPID_ATK3, S_SPID_ATK4, S_SPID_PAIN, S_SPID_PAIN2, S_SPID_DIE1, S_SPID_DIE2, S_SPID_DIE3, S_SPID_DIE4, S_SPID_DIE5, S_SPID_DIE6, S_SPID_DIE7, S_SPID_DIE8, S_SPID_DIE9, S_SPID_DIE10, S_SPID_DIE11, S_BSPI_STND, S_BSPI_STND2, S_BSPI_SIGHT, S_BSPI_RUN1, S_BSPI_RUN2, S_BSPI_RUN3, S_BSPI_RUN4, S_BSPI_RUN5, S_BSPI_RUN6, S_BSPI_RUN7, S_BSPI_RUN8, S_BSPI_RUN9, S_BSPI_RUN10, S_BSPI_RUN11, S_BSPI_RUN12, S_BSPI_ATK1, S_BSPI_ATK2, S_BSPI_ATK3, S_BSPI_ATK4, S_BSPI_PAIN, S_BSPI_PAIN2, S_BSPI_DIE1, S_BSPI_DIE2, S_BSPI_DIE3, S_BSPI_DIE4, S_BSPI_DIE5, S_BSPI_DIE6, S_BSPI_DIE7, S_BSPI_RAISE1, S_BSPI_RAISE2, S_BSPI_RAISE3, S_BSPI_RAISE4, S_BSPI_RAISE5, S_BSPI_RAISE6, S_BSPI_RAISE7, S_ARACH_PLAZ, S_ARACH_PLAZ2, S_ARACH_PLEX, S_ARACH_PLEX2, S_ARACH_PLEX3, S_ARACH_PLEX4, S_ARACH_PLEX5, S_CYBER_STND, S_CYBER_STND2, S_CYBER_RUN1, S_CYBER_RUN2, S_CYBER_RUN3, S_CYBER_RUN4, S_CYBER_RUN5, S_CYBER_RUN6, S_CYBER_RUN7, S_CYBER_RUN8, S_CYBER_ATK1, S_CYBER_ATK2, S_CYBER_ATK3, S_CYBER_ATK4, S_CYBER_ATK5, S_CYBER_ATK6, S_CYBER_PAIN, S_CYBER_DIE1, S_CYBER_DIE2, S_CYBER_DIE3, S_CYBER_DIE4, S_CYBER_DIE5, S_CYBER_DIE6, S_CYBER_DIE7, S_CYBER_DIE8, S_CYBER_DIE9, S_CYBER_DIE10, S_PAIN_STND, S_PAIN_RUN1, S_PAIN_RUN2, S_PAIN_RUN3, S_PAIN_RUN4, S_PAIN_RUN5, S_PAIN_RUN6, S_PAIN_ATK1, S_PAIN_ATK2, S_PAIN_ATK3, S_PAIN_ATK4, S_PAIN_PAIN, S_PAIN_PAIN2, S_PAIN_DIE1, S_PAIN_DIE2, S_PAIN_DIE3, S_PAIN_DIE4, S_PAIN_DIE5, S_PAIN_DIE6, S_PAIN_RAISE1, S_PAIN_RAISE2, S_PAIN_RAISE3, S_PAIN_RAISE4, S_PAIN_RAISE5, S_PAIN_RAISE6, S_SSWV_STND, S_SSWV_STND2, S_SSWV_RUN1, S_SSWV_RUN2, S_SSWV_RUN3, S_SSWV_RUN4, S_SSWV_RUN5, S_SSWV_RUN6, S_SSWV_RUN7, S_SSWV_RUN8, S_SSWV_ATK1, S_SSWV_ATK2, S_SSWV_ATK3, S_SSWV_ATK4, S_SSWV_ATK5, S_SSWV_ATK6, S_SSWV_PAIN, S_SSWV_PAIN2, S_SSWV_DIE1, S_SSWV_DIE2, S_SSWV_DIE3, S_SSWV_DIE4, S_SSWV_DIE5, S_SSWV_XDIE1, S_SSWV_XDIE2, S_SSWV_XDIE3, S_SSWV_XDIE4, S_SSWV_XDIE5, S_SSWV_XDIE6, S_SSWV_XDIE7, S_SSWV_XDIE8, S_SSWV_XDIE9, S_SSWV_RAISE1, S_SSWV_RAISE2, S_SSWV_RAISE3, S_SSWV_RAISE4, S_SSWV_RAISE5, S_KEENSTND, S_COMMKEEN, S_COMMKEEN2, S_COMMKEEN3, S_COMMKEEN4, S_COMMKEEN5, S_COMMKEEN6, S_COMMKEEN7, S_COMMKEEN8, S_COMMKEEN9, S_COMMKEEN10, S_COMMKEEN11, S_COMMKEEN12, S_KEENPAIN, S_KEENPAIN2, S_BRAIN, S_BRAIN_PAIN, S_BRAIN_DIE1, S_BRAIN_DIE2, S_BRAIN_DIE3, S_BRAIN_DIE4, S_BRAINEYE, S_BRAINEYESEE, S_BRAINEYE1, S_SPAWN1, S_SPAWN2, S_SPAWN3, S_SPAWN4, S_SPAWNFIRE1, S_SPAWNFIRE2, S_SPAWNFIRE3, S_SPAWNFIRE4, S_SPAWNFIRE5, S_SPAWNFIRE6, S_SPAWNFIRE7, S_SPAWNFIRE8, S_BRAINEXPLODE1, S_BRAINEXPLODE2, S_BRAINEXPLODE3, S_ARM1, S_ARM1A, S_ARM2, S_ARM2A, S_BAR1, S_BAR2, S_BEXP, S_BEXP2, S_BEXP3, S_BEXP4, S_BEXP5, S_BBAR1, S_BBAR2, S_BBAR3, S_BON1, S_BON1A, S_BON1B, S_BON1C, S_BON1D, S_BON1E, S_BON2, S_BON2A, S_BON2B, S_BON2C, S_BON2D, S_BON2E, S_BKEY, S_BKEY2, S_RKEY, S_RKEY2, S_YKEY, S_YKEY2, S_BSKULL, S_BSKULL2, S_RSKULL, S_RSKULL2, S_YSKULL, S_YSKULL2, S_STIM, S_MEDI, S_SOUL, S_SOUL2, S_SOUL3, S_SOUL4, S_SOUL5, S_SOUL6, S_PINV, S_PINV2, S_PINV3, S_PINV4, S_PSTR, S_PINS, S_PINS2, S_PINS3, S_PINS4, S_MEGA, S_MEGA2, S_MEGA3, S_MEGA4, S_SUIT, S_PMAP, S_PMAP2, S_PMAP3, S_PMAP4, S_PMAP5, S_PMAP6, S_PVIS, S_PVIS2, S_CLIP, S_AMMO, S_ROCK, S_BROK, S_CELL, S_CELP, S_SHEL, S_SBOX, S_BPAK, S_BFUG, S_MGUN, S_CSAW, S_LAUN, S_PLAS, S_SHOT, S_SHOT2, S_COLU, S_STALAG, S_BLOODYTWITCH, S_BLOODYTWITCH2, S_BLOODYTWITCH3, S_BLOODYTWITCH4, S_DEADTORSO, S_DEADBOTTOM, S_HEADSONSTICK, S_GIBS, S_HEADONASTICK, S_HEADCANDLES, S_HEADCANDLES2, S_DEADSTICK, S_LIVESTICK, S_LIVESTICK2, S_MEAT2, S_MEAT3, S_MEAT4, S_MEAT5, S_STALAGTITE, S_TALLGRNCOL, S_SHRTGRNCOL, S_TALLREDCOL, S_SHRTREDCOL, S_CANDLESTIK, S_CANDELABRA, S_SKULLCOL, S_TORCHTREE, S_BIGTREE, S_TECHPILLAR, S_EVILEYE, S_EVILEYE2, S_EVILEYE3, S_EVILEYE4, S_FLOATSKULL, S_FLOATSKULL2, S_FLOATSKULL3, S_HEARTCOL, S_HEARTCOL2, S_BLUETORCH, S_BLUETORCH2, S_BLUETORCH3, S_BLUETORCH4, S_GREENTORCH, S_GREENTORCH2, S_GREENTORCH3, S_GREENTORCH4, S_REDTORCH, S_REDTORCH2, S_REDTORCH3, S_REDTORCH4, S_BTORCHSHRT, S_BTORCHSHRT2, S_BTORCHSHRT3, S_BTORCHSHRT4, S_GTORCHSHRT, S_GTORCHSHRT2, S_GTORCHSHRT3, S_GTORCHSHRT4, S_RTORCHSHRT, S_RTORCHSHRT2, S_RTORCHSHRT3, S_RTORCHSHRT4, S_HANGNOGUTS, S_HANGBNOBRAIN, S_HANGTLOOKDN, S_HANGTSKULL, S_HANGTLOOKUP, S_HANGTNOBRAIN, S_COLONGIBS, S_SMALLPOOL, S_BRAINSTEM, S_TECHLAMP, S_TECHLAMP2, S_TECHLAMP3, S_TECHLAMP4, S_TECH2LAMP, S_TECH2LAMP2, S_TECH2LAMP3, S_TECH2LAMP4, NUMSTATES } package automap; import i.DoomStatusAware; import v.IVideoScaleAware; import doom.event_t; public interface IAutoMap<T,V> extends IVideoScaleAware, DoomStatusAware{ // Used by ST StatusBar stuff. public final int AM_MSGHEADER =(('a'<<24)+('m'<<16)); public final int AM_MSGENTERED= (AM_MSGHEADER | ('e'<<8)); public final int AM_MSGEXITED= (AM_MSGHEADER | ('x'<<8)); // Color ranges for automap. Actual colors are bit-depth dependent. public final int REDRANGE= 16; public final int BLUERANGE =8; public final int GREENRANGE =16; public final int GRAYSRANGE =16; public final int BROWNRANGE =16; public final int YELLOWRANGE =1; public final int YOURRANGE =0; public final int WALLRANGE =REDRANGE; public final int TSWALLRANGE =GRAYSRANGE; public final int FDWALLRANGE =BROWNRANGE; public final int CDWALLRANGE =YELLOWRANGE; public final int THINGRANGE =GREENRANGE; public final int SECRETWALLRANGE =WALLRANGE; public final int GRIDRANGE =0; // Called by main loop. public boolean Responder (event_t ev); // Called by main loop. public void Ticker (); // Called by main loop, // called instead of view drawer if automap active. public void Drawer (); // Called to force the automap to quit // if the level is completed while it is up. public void Stop (); public void Start(); // Should be called in order to set a proper scaled buffer. public void Init(); } package automap; /** used only in automap */ public class mline_t { public mline_t(){ this(0,0,0,0); } public int ax,ay,bx,by; public mline_t(int ax, int ay, int bx, int by) { this.ax = ax; this.ay = ay; this.bx = bx; this.by = by; } public mline_t(double ax, double ay, double bx, double by) { this.ax = (int) ax; this.ay = (int) ay; this.bx = (int) bx; this.by = (int) by; } /* public mline_t(mpoint_t a, mpoint_t b) { this.a = a; this.b = b; } public mline_t(int ax,int ay,int bx,int by) { this.a = new mpoint_t(ax,ay); this.b = new mpoint_t(bx,by); } public mline_t(double ax,double ay,double bx,double by) { this.a = new mpoint_t(ax,ay); this.b = new mpoint_t(bx,by); } public mpoint_t a, b; public int ax; public String toString(){ return a.toString()+" - "+ b.toString(); } */ } package automap; public class fpoint_t { int x, y; public fpoint_t(){ this(0,0); } public fpoint_t(int x, int y){ this.x=x; this.y=y; } } package automap; import m.fixed_t; public class mpoint_t { public mpoint_t(fixed_t x, fixed_t y) { this.x = x.val; this.y = y.val; } public mpoint_t(int x, int y) { this.x = x; this.y = y; } public mpoint_t(double x, double y) { this.x = (int) x; this.y = (int) y; } public mpoint_t(){ this.x=0; this.y=0; } /** fixed_t */ public int x,y; public String toString(){ return (Integer.toHexString(x)+" , "+Integer.toHexString(y)); } }; package automap; // Emacs style mode select -*- C++ -*- // ----------------------------------------------------------------------------- // // $Id: Map.java,v 1.37 2012/09/24 22:36:28 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. // // // // $Log: Map.java,v $ // Revision 1.37 2012/09/24 22:36:28 velktron // Map get color // // Revision 1.36 2012/09/24 17:16:23 velktron // Massive merge between HiColor and HEAD. There's no difference from now on, and development continues on HEAD. // // Revision 1.34.2.4 2012/09/24 16:58:06 velktron // TrueColor, Generics. // // Revision 1.34.2.3 2012/09/20 14:06:43 velktron // Generic automap // // Revision 1.34.2.2 2011/11/27 18:19:19 velktron // Configurable colors, more parametrizable. // // Revision 1.34.2.1 2011/11/14 00:27:11 velktron // A barely functional HiColor branch. Most stuff broken. DO NOT USE // // Revision 1.34 2011/11/03 18:11:14 velktron // Fixed long-standing issue with 0-rot vector being reduced to pixels. Fixed // broken map panning functionality after keymap change. // // Revision 1.33 2011/11/01 23:48:43 velktron // Using FillRect // // Revision 1.32 2011/11/01 19:03:10 velktron // Using screen number constants // // Revision 1.31 2011/10/23 18:10:32 velktron // Generic compliance for DoomVideoInterface // // Revision 1.30 2011/10/07 16:08:23 velktron // Now using g.Keys and line_t // // Revision 1.29 2011/09/29 13:25:09 velktron // Eliminated "intermediate" AbstractAutoMap. Map implements IAutoMap directly. // // Revision 1.28 2011/07/28 16:35:03 velktron // Well, we don't need to know that anymore. // // Revision 1.27 2011/06/18 23:16:34 velktron // Added extreme scale safeguarding (e.g. for Europe.wad). // // Revision 1.26 2011/05/30 15:45:44 velktron // AbstractAutoMap and IAutoMap // // Revision 1.25 2011/05/24 11:31:47 velktron // Adapted to IDoomStatusBar // // Revision 1.24 2011/05/23 16:57:39 velktron // Migrated to VideoScaleInfo. // // Revision 1.23 2011/05/17 16:50:02 velktron // Switched to DoomStatus // // Revision 1.22 2011/05/10 10:39:18 velktron // Semi-playable Techdemo v1.3 milestone // // Revision 1.21 2010/12/14 17:55:59 velktron // Fixed weapon bobbing, added translucent column drawing, separated rendering // commons. // // Revision 1.20 2010/12/12 19:06:18 velktron // Tech Demo v1.1 release. // // Revision 1.19 2010/11/17 23:55:06 velktron // Kind of playable/controllable. // // Revision 1.18 2010/11/12 13:37:25 velktron // Rationalized the LUT system - now it's 100% procedurally generated. // // Revision 1.17 2010/10/01 16:47:51 velktron // Fixed tab interception. // // Revision 1.16 2010/09/27 15:07:44 velktron // meh // // Revision 1.15 2010/09/27 02:27:29 velktron // BEASTLY update // // Revision 1.14 2010/09/23 07:31:11 velktron // fuck // // Revision 1.13 2010/09/13 15:39:17 velktron // Moving towards an unified gameplay approach... // // Revision 1.12 2010/09/08 21:09:01 velktron // Better display "driver". // // Revision 1.11 2010/09/08 15:22:18 velktron // x,y coords in some structs as value semantics. Possible speed increase? // // Revision 1.10 2010/09/06 16:02:59 velktron // Implementation of palettes. // // Revision 1.9 2010/09/02 15:56:54 velktron // Bulk of unified renderer copyediting done. // // Some changes like e.g. global separate limits class and instance methods for // seg_t and node_t introduced. // // Revision 1.8 2010/09/01 15:53:42 velktron // Graphics data loader implemented....still need to figure out how column // caching works, though. // // Revision 1.7 2010/08/27 23:46:57 velktron // Introduced Buffered renderer, which makes tapping directly into byte[] screen // buffers mapped to BufferedImages possible. // // Revision 1.6 2010/08/26 16:43:42 velktron // Automap functional, biatch. // // Revision 1.5 2010/08/25 00:50:59 velktron // Some more work... // // Revision 1.4 2010/08/22 18:04:21 velktron // Automap // // Revision 1.3 2010/08/19 23:14:49 velktron // Automap // // Revision 1.2 2010/08/10 16:41:57 velktron // Threw some work into map loading. // // Revision 1.1 2010/07/20 15:52:56 velktron // LOTS of changes, Automap almost complete. Use of fixed_t inside methods // severely limited. // // Revision 1.1 2010/06/30 08:58:51 velktron // Let's see if this stuff will finally commit.... // // // Most stuff is still being worked on. For a good place to start and get an // idea of what is being done, I suggest checking out the "testers" package. // // Revision 1.1 2010/06/29 11:07:34 velktron // Release often, release early they say... // // Commiting ALL stuff done so far. A lot of stuff is still broken/incomplete, // and there's still mixed C code in there. I suggest you load everything up in // Eclpise and see what gives from there. // // A good place to start is the testers/ directory, where you can get an idea of // how a few of the implemented stuff works. // // // DESCRIPTION: the automap code // // ----------------------------------------------------------------------------- import static g.Keys.*; import static data.Limits.*; import static m.fixed_t.*; import static doom.englsh.*; import static data.Tables.*; import p.AbstractLevelLoader; import p.mobj_t; import doom.DoomMain; import doom.DoomStatus; import doom.event_t; import doom.evtype_t; import doom.player_t; import rr.patch_t; import st.IDoomStatusBar; import utils.C2JUtils; import v.DoomVideoRenderer; import static v.DoomVideoRenderer.V_NOSCALESTART; import v.IVideoScale; import w.IWadLoader; import m.cheatseq_t; import static rr.line_t.*; import static data.Defines.*; public abstract class Map<T, V> implements IAutoMap<T, V> { // ///////////////// Status objects /////////////////// IDoomStatusBar ST; IWadLoader W; DoomMain<T, V> DM; DoomVideoRenderer<T,V> V; AbstractLevelLoader LL; public final String rcsid = "$Id: Map.java,v 1.37 2012/09/24 22:36:28 velktron Exp $"; // For use if I do walls with outsides/insides public int REDS, BLUES, GREENS, GRAYS, BROWNS, YELLOWS, BLACK, WHITE; // Automap colors public int BACKGROUND, YOURCOLORS, WALLCOLORS, TSWALLCOLORS, FDWALLCOLORS, CDWALLCOLORS, THINGCOLORS, SECRETWALLCOLORS, GRIDCOLORS, XHAIRCOLORS; // drawing stuff public static final int FB = 0; public static final char AM_PANDOWNKEY = KEY_DOWNARROW; public static final char AM_PANUPKEY = KEY_UPARROW; public static final char AM_PANRIGHTKEY = KEY_RIGHTARROW; public static final char AM_PANLEFTKEY = KEY_LEFTARROW; public static final char AM_ZOOMINKEY = '='; public static final char AM_ZOOMOUTKEY = '-'; public static final char AM_STARTKEY = KEY_TAB; // KEY_TAB public static final char AM_ENDKEY = KEY_TAB; // KEY_TAB public static final char AM_GOBIGKEY = '0'; public static final char AM_FOLLOWKEY = 'f'; public static final char AM_GRIDKEY = 'g'; public static final char AM_MARKKEY = 'm'; public static final char AM_CLEARMARKKEY = 'c'; public static final int AM_NUMMARKPOINTS = 10; // (fixed_t) scale on entry public static final int INITSCALEMTOF = (int) (.2 * FRACUNIT); // how much the automap moves window per tic in frame-buffer coordinates // moves 140 pixels in 1 second public static final int F_PANINC = 4; // how much zoom-in per tic // goes to 2x in 1 second public static final int M_ZOOMIN = ((int) (1.02 * FRACUNIT)); // how much zoom-out per tic // pulls out to 0.5x in 1 second public static final int M_ZOOMOUT = ((int) (FRACUNIT / 1.02)); public Map(DoomStatus<T, V> DS) { this.updateStatus(DS); // Some initializing... this.markpoints = new mpoint_t[AM_NUMMARKPOINTS]; C2JUtils.initArrayOfObjects(markpoints, mpoint_t.class); f_oldloc = new mpoint_t(); m_paninc = new mpoint_t(); this.plr = DM.players[DM.displayplayer]; } @Override public void Init() { // Use colormap-specific colors to support extended modes. // Moved hardcoding in here. Potentially configurable. REDS = 256 - 5 * 16; BLUES = 256 - 4 * 16 + 8; GREENS = 7 * 16; GRAYS = 6 * 16; BROWNS = 4 * 16; YELLOWS = 256 - 32 + 7; BLACK = 0; WHITE = 4; // Automap colors BACKGROUND = BLACK; YOURCOLORS = WHITE; WALLCOLORS = REDS; TSWALLCOLORS = GRAYS; FDWALLCOLORS = BROWNS; CDWALLCOLORS = YELLOWS; THINGCOLORS = GREENS; SECRETWALLCOLORS = WALLCOLORS; GRIDCOLORS = (GRAYS + GRAYSRANGE / 2); XHAIRCOLORS = GRAYS; their_colors = new int[] { GREENS, GRAYS, BROWNS, REDS }; } /** translates between frame-buffer and map distances */ private final int FTOM(int x) { return FixedMul(((x) << 16), scale_ftom); } /** translates between frame-buffer and map distances */ private final int MTOF(int x) { return FixedMul((x), scale_mtof) >> 16; } /** translates between frame-buffer and map coordinates */ private final int CXMTOF(int x) { return (f_x + MTOF((x) - m_x)); } /** translates between frame-buffer and map coordinates */ private final int CYMTOF(int y) { return (f_y + (f_h - MTOF((y) - m_y))); } // the following is crap public static final short LINE_NEVERSEE = ML_DONTDRAW; // This seems to be the minimum viable scale before things start breaking // up. private static final int MINIMUM_SCALE = (int) (0.7 * FRACUNIT); // This seems to be the limit for some maps like europe.wad private static final int MINIMUM_VIABLE_SCALE = FRACUNIT >> 5; // // The vector graphics for the automap. /** * A line drawing of the player pointing right, starting from the middle. */ protected mline_t[] player_arrow; protected int NUMPLYRLINES; protected mline_t[] cheat_player_arrow; protected int NUMCHEATPLYRLINES; protected mline_t[] triangle_guy; protected int NUMTRIANGLEGUYLINES; protected mline_t[] thintriangle_guy; protected int NUMTHINTRIANGLEGUYLINES; protected void initVectorGraphics() { int R = ((8 * PLAYERRADIUS) / 7); player_arrow = new mline_t[] { new mline_t(-R + R / 8, 0, R, 0), // ----- new mline_t(R, 0, R - R / 2, R / 4), // ---- new mline_t(R, 0, R - R / 2, -R / 4), new mline_t(-R + R / 8, 0, -R - R / 8, R / 4), // >--- new mline_t(-R + R / 8, 0, -R - R / 8, -R / 4), new mline_t(-R + 3 * R / 8, 0, -R + R / 8, R / 4), // >>-- new mline_t(-R + 3 * R / 8, 0, -R + R / 8, -R / 4) }; NUMPLYRLINES = player_arrow.length; cheat_player_arrow = new mline_t[] { new mline_t(-R + R / 8, 0, R, 0), // ----- new mline_t(R, 0, R - R / 2, R / 6), // ---- new mline_t(R, 0, R - R / 2, -R / 6), new mline_t(-R + R / 8, 0, -R - R / 8, R / 6), // >---- new mline_t(-R + R / 8, 0, -R - R / 8, -R / 6), new mline_t(-R + 3 * R / 8, 0, -R + R / 8, R / 6), // >>---- new mline_t(-R + 3 * R / 8, 0, -R + R / 8, -R / 6), new mline_t(-R / 2, 0, -R / 2, -R / 6), // >>-d-- new mline_t(-R / 2, -R / 6, -R / 2 + R / 6, -R / 6), new mline_t(-R / 2 + R / 6, -R / 6, -R / 2 + R / 6, R / 4), new mline_t(-R / 6, 0, -R / 6, -R / 6), // >>-dd- new mline_t(-R / 6, -R / 6, 0, -R / 6), new mline_t(0, -R / 6, 0, R / 4), new mline_t(R / 6, R / 4, R / 6, -R / 7), // >>-ddt new mline_t(R / 6, -R / 7, R / 6 + R / 32, -R / 7 - R / 32), new mline_t(R / 6 + R / 32, -R / 7 - R / 32, R / 6 + R / 10, -R / 7) }; NUMCHEATPLYRLINES = cheat_player_arrow.length; R = (FRACUNIT); triangle_guy = new mline_t[] { new mline_t(-.867 * R, -.5 * R, .867 * R, -.5 * R), new mline_t(.867 * R, -.5 * R, 0, R), new mline_t(0, R, -.867 * R, -.5 * R) }; NUMTRIANGLEGUYLINES = triangle_guy.length; thintriangle_guy = new mline_t[] { new mline_t(-.5 * R, -.7 * R, R, 0), new mline_t(R, 0, -.5 * R, .7 * R), new mline_t(-.5 * R, .7 * R, -.5 * R, -.7 * R) }; NUMTHINTRIANGLEGUYLINES = thintriangle_guy.length; } /** Planned overlay mode */ protected int overlay = 0; protected int cheating = 0; protected boolean grid = false; protected int leveljuststarted = 1; // kluge until AM_LevelInit() is called protected int finit_width; protected int finit_height; // location of window on screen protected int f_x; protected int f_y; // size of window on screen protected int f_w; protected int f_h; /** used for funky strobing effect */ protected int lightlev; /** pseudo-frame buffer */ protected V fb; protected int amclock; /** (fixed_t) how far the window pans each tic (map coords) */ protected mpoint_t m_paninc; /** (fixed_t) how far the window zooms in each tic (map coords) */ protected int mtof_zoommul; /** (fixed_t) how far the window zooms in each tic (fb coords) */ protected int ftom_zoommul; /** (fixed_t) LL x,y where the window is on the map (map coords) */ protected int m_x, m_y; /** (fixed_t) UR x,y where the window is on the map (map coords) */ protected int m_x2, m_y2; /** (fixed_t) width/height of window on map (map coords) */ protected int m_w, m_h; /** (fixed_t) based on level size */ protected int min_x, min_y, max_x, max_y; /** (fixed_t) max_x-min_x */ protected int max_w; // /** (fixed_t) max_y-min_y */ protected int max_h; /** (fixed_t) based on player size */ protected int min_w, min_h; /** (fixed_t) used to tell when to stop zooming out */ protected int min_scale_mtof; /** (fixed_t) used to tell when to stop zooming in */ protected int max_scale_mtof; /** (fixed_t) old stuff for recovery later */ protected int old_m_w, old_m_h, old_m_x, old_m_y; /** old location used by the Follower routine */ protected mpoint_t f_oldloc; /** (fixed_t) used by MTOF to scale from map-to-frame-buffer coords */ protected int scale_mtof = INITSCALEMTOF; /** used by FTOM to scale from frame-buffer-to-map coords (=1/scale_mtof) */ protected int scale_ftom; /** the player represented by an arrow */ protected player_t plr; /** numbers used for marking by the automap */ private patch_t[] marknums = new patch_t[10]; /** where the points are */ private mpoint_t[] markpoints; /** next point to be assigned */ private int markpointnum = 0; /** specifies whether to follow the player around */ protected boolean followplayer = true; protected char[] cheat_amap_seq = { 0xb2, 0x26, 0x26, 0x2e, 0xff }; // iddt protected cheatseq_t cheat_amap = new cheatseq_t(cheat_amap_seq, 0); // MAES: STROBE cheat. It's not even cheating, strictly speaking. private char cheat_strobe_seq[] = { 0x6e, 0xa6, 0xea, 0x2e, 0x6a, 0xf6, 0x62, 0xa6, 0xff // vestrobe }; private cheatseq_t cheat_strobe = new cheatseq_t(cheat_strobe_seq, 0); private boolean stopped = true; // extern boolean viewactive; // extern byte screens[][SCREENWIDTH*SCREENHEIGHT]; /** * Calculates the slope and slope according to the x-axis of a line segment * in map coordinates (with the upright y-axis n' all) so that it can be * used with the brain-dead drawing stuff. * * @param ml * @param is */ public final void getIslope(mline_t ml, islope_t is) { int dx, dy; dy = ml.ay - ml.by; dx = ml.bx - ml.ax; if (dy == 0) is.islp = (dx < 0 ? -MAXINT : MAXINT); else is.islp = FixedDiv(dx, dy); if (dx == 0) is.slp = (dy < 0 ? -MAXINT : MAXINT); else is.slp = FixedDiv(dy, dx); } // // // public final void activateNewScale() { m_x += m_w / 2; m_y += m_h / 2; m_w = FTOM(f_w); m_h = FTOM(f_h); m_x -= m_w / 2; m_y -= m_h / 2; m_x2 = m_x + m_w; m_y2 = m_y + m_h; } // // // public final void saveScaleAndLoc() { old_m_x = m_x; old_m_y = m_y; old_m_w = m_w; old_m_h = m_h; } private void restoreScaleAndLoc() { m_w = old_m_w; m_h = old_m_h; if (!followplayer) { m_x = old_m_x; m_y = old_m_y; } else { m_x = plr.mo.x - m_w / 2; m_y = plr.mo.y - m_h / 2; } m_x2 = m_x + m_w; m_y2 = m_y + m_h; // Change the scaling multipliers scale_mtof = FixedDiv(f_w << FRACBITS, m_w); scale_ftom = FixedDiv(FRACUNIT, scale_mtof); } /** * adds a marker at the current location */ public final void addMark() { markpoints[markpointnum].x = m_x + m_w / 2; markpoints[markpointnum].y = m_y + m_h / 2; markpointnum = (markpointnum + 1) % AM_NUMMARKPOINTS; } /** * Determines bounding box of all vertices, sets global variables * controlling zoom range. */ public final void findMinMaxBoundaries() { int a; // fixed_t int b; min_x = min_y = MAXINT; max_x = max_y = -MAXINT; for (int i = 0; i < LL.numvertexes; i++) { if (LL.vertexes[i].x < min_x) min_x = LL.vertexes[i].x; else if (LL.vertexes[i].x > max_x) max_x = LL.vertexes[i].x; if (LL.vertexes[i].y < min_y) min_y = LL.vertexes[i].y; else if (LL.vertexes[i].y > max_y) max_y = LL.vertexes[i].y; } max_w = max_x - min_x; max_h = max_y - min_y; min_w = 2 * PLAYERRADIUS; // const? never changed? min_h = 2 * PLAYERRADIUS; a = FixedDiv(f_w << FRACBITS, max_w); b = FixedDiv(f_h << FRACBITS, max_h); min_scale_mtof = a < b ? a : b; if (min_scale_mtof < 0) { // MAES: safeguard against negative scaling e.g. in Europe.wad // This seems to be the limit. min_scale_mtof = MINIMUM_VIABLE_SCALE; } max_scale_mtof = FixedDiv(f_h << FRACBITS, 2 * PLAYERRADIUS); } public final void changeWindowLoc() { if (m_paninc.x != 0 || m_paninc.y != 0) { followplayer = false; f_oldloc.x = MAXINT; } m_x += m_paninc.x; m_y += m_paninc.y; if (m_x + m_w / 2 > max_x) m_x = max_x - m_w / 2; else if (m_x + m_w / 2 < min_x) m_x = min_x - m_w / 2; if (m_y + m_h / 2 > max_y) m_y = max_y - m_h / 2; else if (m_y + m_h / 2 < min_y) m_y = min_y - m_h / 2; m_x2 = m_x + m_w; m_y2 = m_y + m_h; } public final void initVariables() { int pnum = 0; DM.automapactive = true; fb = V.getScreen(DoomVideoRenderer.SCREEN_FG); f_oldloc.x = MAXINT; amclock = 0; lightlev = 0; m_paninc.x = m_paninc.y = 0; ftom_zoommul = FRACUNIT; mtof_zoommul = FRACUNIT; m_w = FTOM(f_w); m_h = FTOM(f_h); // find player to center on initially if (!DM.playeringame[pnum = DM.consoleplayer]) for (pnum = 0; pnum < MAXPLAYERS; pnum++) { System.out.println(pnum); if (DM.playeringame[pnum]) break; } plr = DM.players[pnum]; m_x = plr.mo.x - m_w / 2; m_y = plr.mo.y - m_h / 2; this.changeWindowLoc(); // for saving & restoring old_m_x = m_x; old_m_y = m_y; old_m_w = m_w; old_m_h = m_h; // inform the status bar of the change ST.Responder(st_notify); } // // // public final void loadPics() { int i; String namebuf; for (i = 0; i < 10; i++) { namebuf = ("AMMNUM" + i); marknums[i] = W.CachePatchName(namebuf); } } public final void unloadPics() { int i; for (i = 0; i < 10; i++) { W.UnlockLumpNum(marknums[i]); } } public final void clearMarks() { int i; for (i = 0; i < AM_NUMMARKPOINTS; i++) markpoints[i].x = -1; // means empty markpointnum = 0; } /** * should be called at the start of every level right now, i figure it out * myself */ public final void LevelInit() { leveljuststarted = 0; f_x = f_y = 0; f_w = finit_width; f_h = finit_height; // scanline=new byte[f_h*f_w]; this.clearMarks(); this.findMinMaxBoundaries(); scale_mtof = FixedDiv(min_scale_mtof, MINIMUM_SCALE); if (scale_mtof > max_scale_mtof) scale_mtof = min_scale_mtof; scale_ftom = FixedDiv(FRACUNIT, scale_mtof); } // // // protected final event_t st_notify = new event_t(evtype_t.ev_keyup, AM_MSGENTERED); // MAES: Was a "method static variable"...but what's the point? It's never // modified. protected final event_t st_notify_ex = new event_t(evtype_t.ev_keyup, AM_MSGEXITED); public final void Stop() { this.unloadPics(); DM.automapactive = false; // This is the only way to notify the status bar responder that we're // exiting the automap. ST.Responder(st_notify_ex); stopped = true; } // // // // More "static" stuff. protected int lastlevel = -1, lastepisode = -1; public final void Start() { if (!stopped) Stop(); stopped = false; if (lastlevel != DM.gamemap || lastepisode != DM.gameepisode) { this.LevelInit(); lastlevel = DM.gamemap; lastepisode = DM.gameepisode; } this.initVectorGraphics(); this.LevelInit(); this.initVariables(); this.loadPics(); } /** * set the window scale to the maximum size */ public final void minOutWindowScale() { scale_mtof = min_scale_mtof; scale_ftom = FixedDiv(FRACUNIT, scale_mtof); this.activateNewScale(); } /** * set the window scale to the minimum size */ public final void maxOutWindowScale() { scale_mtof = max_scale_mtof; scale_ftom = FixedDiv(FRACUNIT, scale_mtof); this.activateNewScale(); } /** These belong to AM_Responder */ protected boolean cheatstate = false, bigstate = false; /** static char buffer[20] in AM_Responder */ protected String buffer; /** MAES: brought back strobe effect */ private boolean strobe = false; /** * Handle events (user inputs) in automap mode */ public final boolean Responder(event_t ev) { boolean rc; rc = false; // System.out.println(ev.data1==AM_STARTKEY); if (!DM.automapactive) { if ((ev.data1 == AM_STARTKEY) && (ev.type == evtype_t.ev_keyup)) { this.Start(); DM.viewactive = false; rc = true; } } else if (ev.type == evtype_t.ev_keydown) { rc = true; switch (ev.data1) { case AM_PANRIGHTKEY: // pan right if (!followplayer) m_paninc.x = FTOM(F_PANINC); else rc = false; break; case AM_PANLEFTKEY: // pan left if (!followplayer) m_paninc.x = -FTOM(F_PANINC); else rc = false; break; case AM_PANUPKEY: // pan up if (!followplayer) m_paninc.y = FTOM(F_PANINC); else rc = false; break; case AM_PANDOWNKEY: // pan down if (!followplayer) m_paninc.y = -FTOM(F_PANINC); else rc = false; break; case AM_ZOOMOUTKEY: // zoom out mtof_zoommul = M_ZOOMOUT; ftom_zoommul = M_ZOOMIN; break; case AM_ZOOMINKEY: // zoom in mtof_zoommul = M_ZOOMIN; ftom_zoommul = M_ZOOMOUT; break; case AM_GOBIGKEY: bigstate = !bigstate; if (bigstate) { this.saveScaleAndLoc(); this.minOutWindowScale(); } else this.restoreScaleAndLoc(); break; case AM_FOLLOWKEY: followplayer = !followplayer; f_oldloc.x = MAXINT; plr.message = followplayer ? AMSTR_FOLLOWON : AMSTR_FOLLOWOFF; break; case AM_GRIDKEY: grid = !grid; plr.message = grid ? AMSTR_GRIDON : AMSTR_GRIDOFF; break; case AM_MARKKEY: buffer = (AMSTR_MARKEDSPOT + " " + markpointnum); plr.message = buffer; this.addMark(); break; case AM_CLEARMARKKEY: this.clearMarks(); plr.message = AMSTR_MARKSCLEARED; break; default: cheatstate = false; rc = false; } if (!DM.deathmatch && cheat_amap.CheckCheat((char) ev.data1)) { rc = false; cheating = (cheating + 1) % 3; } if (cheat_strobe.CheckCheat((char) ev.data1)) { strobe = !strobe; } } else if (ev.type == evtype_t.ev_keyup) { rc = false; switch (ev.data1) { case AM_PANRIGHTKEY: if (!followplayer) m_paninc.x = 0; break; case AM_PANLEFTKEY: if (!followplayer) m_paninc.x = 0; break; case AM_PANUPKEY: if (!followplayer) m_paninc.y = 0; break; case AM_PANDOWNKEY: if (!followplayer) m_paninc.y = 0; break; case AM_ZOOMOUTKEY: case AM_ZOOMINKEY: mtof_zoommul = FRACUNIT; ftom_zoommul = FRACUNIT; break; case AM_ENDKEY: bigstate = false; DM.viewactive = true; this.Stop(); break; } } return rc; } /** * Zooming */ private final void changeWindowScale() { // Change the scaling multipliers scale_mtof = FixedMul(scale_mtof, mtof_zoommul); scale_ftom = FixedDiv(FRACUNIT, scale_mtof); if (scale_mtof < min_scale_mtof) this.minOutWindowScale(); else if (scale_mtof > max_scale_mtof) this.maxOutWindowScale(); else this.activateNewScale(); } // // // private final void doFollowPlayer() { if (f_oldloc.x != plr.mo.x || f_oldloc.y != plr.mo.y) { m_x = FTOM(MTOF(plr.mo.x)) - m_w / 2; m_y = FTOM(MTOF(plr.mo.y)) - m_h / 2; m_x2 = m_x + m_w; m_y2 = m_y + m_h; f_oldloc.x = plr.mo.x; f_oldloc.y = plr.mo.y; // m_x = FTOM(MTOF(plr.mo.x - m_w/2)); // m_y = FTOM(MTOF(plr.mo.y - m_h/2)); // m_x = plr.mo.x - m_w/2; // m_y = plr.mo.y - m_h/2; } } // // // protected int nexttic = 0; protected int[] litelevels = { 0, 4, 7, 10, 12, 14, 15, 15 }; protected int litelevelscnt = 0; private final void updateLightLev() { // Change light level if (amclock > nexttic) { lightlev = litelevels[litelevelscnt++]; if (litelevelscnt == litelevels.length) litelevelscnt = 0; nexttic = amclock + 6 - (amclock % 6); } } /** * Updates on Game Tick */ public final void Ticker() { if (!DM.automapactive) return; amclock++; if (followplayer) this.doFollowPlayer(); // Change the zoom if necessary if (ftom_zoommul != FRACUNIT) this.changeWindowScale(); // Change x,y location if ((m_paninc.x | m_paninc.y) != 0) this.changeWindowLoc(); // Update light level if (strobe) updateLightLev(); } // private static int BUFFERSIZE=f_h*f_w; /** * Automap clipping of lines. Based on Cohen-Sutherland clipping algorithm * but with a slightly faster reject and precalculated slopes. If the speed * is needed, use a hash algorithm to handle the common cases. */ private int tmpx, tmpy;// =new fpoint_t(); private final boolean clipMline(mline_t ml, fline_t fl) { // System.out.print("Asked to clip from "+FixedFloat.toFloat(ml.a.x)+","+FixedFloat.toFloat(ml.a.y)); // System.out.print(" to clip "+FixedFloat.toFloat(ml.b.x)+","+FixedFloat.toFloat(ml.b.y)+"\n"); // These were supposed to be "registers", so they exhibit by-ref // properties. int outcode1 = 0; int outcode2 = 0; int outside; int dx; int dy; /* * fl.a.x=0; fl.a.y=0; fl.b.x=0; fl.b.y=0; */ // do trivial rejects and outcodes if (ml.ay > m_y2) outcode1 = TOP; else if (ml.ay < m_y) outcode1 = BOTTOM; if (ml.by > m_y2) outcode2 = TOP; else if (ml.by < m_y) outcode2 = BOTTOM; if ((outcode1 & outcode2) != 0) return false; // trivially outside if (ml.ax < m_x) outcode1 |= LEFT; else if (ml.ax > m_x2) outcode1 |= RIGHT; if (ml.bx < m_x) outcode2 |= LEFT; else if (ml.bx > m_x2) outcode2 |= RIGHT; if ((outcode1 & outcode2) != 0) return false; // trivially outside // transform to frame-buffer coordinates. fl.ax = CXMTOF(ml.ax); fl.ay = CYMTOF(ml.ay); fl.bx = CXMTOF(ml.bx); fl.by = CYMTOF(ml.by); // System.out.println(">>>>>> ("+fl.a.x+" , "+fl.a.y+" ),("+fl.b.x+" , "+fl.b.y+" )"); outcode1 = DOOUTCODE(fl.ax, fl.ay); outcode2 = DOOUTCODE(fl.bx, fl.by); if ((outcode1 & outcode2) != 0) return false; while ((outcode1 | outcode2) != 0) { // may be partially inside box // find an outside point if (outcode1 != 0) outside = outcode1; else outside = outcode2; // clip to each side if ((outside & TOP) != 0) { dy = fl.ay - fl.by; dx = fl.bx - fl.ax; tmpx = fl.ax + (dx * (fl.ay)) / dy; tmpy = 0; } else if ((outside & BOTTOM) != 0) { dy = fl.ay - fl.by; dx = fl.bx - fl.ax; tmpx = fl.ax + (dx * (fl.ay - f_h)) / dy; tmpy = f_h - 1; } else if ((outside & RIGHT) != 0) { dy = fl.by - fl.ay; dx = fl.bx - fl.ax; tmpy = fl.ay + (dy * (f_w - 1 - fl.ax)) / dx; tmpx = f_w - 1; } else if ((outside & LEFT) != 0) { dy = fl.by - fl.ay; dx = fl.bx - fl.ax; tmpy = fl.ay + (dy * (-fl.ax)) / dx; tmpx = 0; } if (outside == outcode1) { fl.ax = tmpx; fl.ay = tmpy; outcode1 = DOOUTCODE(fl.ax, fl.ay); } else { fl.bx = tmpx; fl.by = tmpy; outcode2 = DOOUTCODE(fl.bx, fl.by); } if ((outcode1 & outcode2) != 0) return false; // trivially outside } return true; } protected static int LEFT = 1, RIGHT = 2, BOTTOM = 4, TOP = 8; /** * MAES: the result was supposed to be passed in an "oc" parameter by * reference. Not convenient, so I made some changes... * * @param mx * @param my */ private final int DOOUTCODE(int mx, int my) { int oc = 0; if ((my) < 0) (oc) |= TOP; else if ((my) >= f_h) (oc) |= BOTTOM; if ((mx) < 0) (oc) |= LEFT; else if ((mx) >= f_w) (oc) |= RIGHT; return oc; } /** Not my idea ;-) */ protected int fuck = 0; // // Classic Bresenham w/ whatever optimizations needed for speed // private final void drawFline(fline_t fl, int color) { // MAES: wish they were registers... int x; int y; int dx; int dy; int sx; int sy; int ax; int ay; int d; // For debugging only /* * ======= /*if ( fl.ax < 0 || fl.ax >= f_w || fl.ay < 0 || fl.ay >= f_h * || fl.bx < 0 || fl.bx >= f_w || fl.by < 0 || fl.by >= f_h) >>>>>>> * 1.11 { System.err.println("fuck "+(fuck++)+" \r"); return; <<<<<<< * Map.java } */ dx = fl.bx - fl.ax; ax = 2 * (dx < 0 ? -dx : dx); sx = dx < 0 ? -1 : 1; dy = fl.by - fl.ay; ay = 2 * (dy < 0 ? -dy : dy); sy = dy < 0 ? -1 : 1; x = fl.ax; y = fl.ay; final int c = color; if (ax > ay) { d = ay - ax / 2; while (true) { PUTDOT(x, y, c); if (x == fl.bx) return; if (d >= 0) { y += sy; d -= ax; } x += sx; d += ay; } } else { d = ax - ay / 2; while (true) { PUTDOT(x, y, c); if (y == fl.by) return; if (d >= 0) { x += sx; d -= ay; } y += sy; d += ax; } } } /** Hopefully inlined */ protected abstract void PUTDOT(int xx, int yy, int cc); /** * Clip lines, draw visible parts of lines. */ protected int singlepixel = 0; private final void drawMline(mline_t ml, int color) { // fl.reset(); if (this.clipMline(ml, fl)) { // if ((fl.a.x==fl.b.x)&&(fl.a.y==fl.b.y)) singlepixel++; this.drawFline(fl, color); // draws it on frame buffer using fb // coords } } private final fline_t fl = new fline_t(); private final mline_t ml = new mline_t(); /** * Draws flat (floor/ceiling tile) aligned grid lines. */ private final void drawGrid(int color) { int x, y; // fixed_t int start, end; // fixed_t // Figure out start of vertical gridlines start = m_x; if (((start - LL.bmaporgx) % (MAPBLOCKUNITS << FRACBITS)) != 0) start += (MAPBLOCKUNITS << FRACBITS) - ((start - LL.bmaporgx) % (MAPBLOCKUNITS << FRACBITS)); end = m_x + m_w; // draw vertical gridlines ml.ay = m_y; ml.by = m_y + m_h; for (x = start; x < end; x += (MAPBLOCKUNITS << FRACBITS)) { ml.ax = x; ml.bx = x; drawMline(ml, color); } // Figure out start of horizontal gridlines start = m_y; if (((start - LL.bmaporgy) % (MAPBLOCKUNITS << FRACBITS)) != 0) start += (MAPBLOCKUNITS << FRACBITS) - ((start - LL.bmaporgy) % (MAPBLOCKUNITS << FRACBITS)); end = m_y + m_h; // draw horizontal gridlines ml.ax = m_x; ml.bx = m_x + m_w; for (y = start; y < end; y += (MAPBLOCKUNITS << FRACBITS)) { ml.ay = y; ml.by = y; drawMline(ml, color); } } protected mline_t l = new mline_t(); /** * Determines visible lines, draws them. This is LineDef based, not LineSeg * based. */ private final void drawWalls() { final int wallcolor = V.getBaseColor(WALLCOLORS + lightlev); final int fdwallcolor = V.getBaseColor(FDWALLCOLORS + lightlev); final int cdwallcolor = V.getBaseColor(CDWALLCOLORS + lightlev); final int tswallcolor = V.getBaseColor(CDWALLCOLORS + lightlev); final int secretwallcolor = V.getBaseColor(SECRETWALLCOLORS + lightlev); for (int i = 0; i < LL.numlines; i++) { l.ax = LL.lines[i].v1x; l.ay = LL.lines[i].v1y; l.bx = LL.lines[i].v2x; l.by = LL.lines[i].v2y; if ((cheating | (LL.lines[i].flags & ML_MAPPED)) != 0) { if (((LL.lines[i].flags & LINE_NEVERSEE) & ~cheating) != 0) continue; if (LL.lines[i].backsector == null) { drawMline(l, wallcolor); } else { if (LL.lines[i].special == 39) { // teleporters drawMline(l, WALLCOLORS + WALLRANGE / 2); } else if ((LL.lines[i].flags & ML_SECRET) != 0) // secret // door { if (cheating != 0) drawMline(l, secretwallcolor); else drawMline(l, wallcolor); } else if (LL.lines[i].backsector.floorheight != LL.lines[i].frontsector.floorheight) { drawMline(l, fdwallcolor); // floor level change } else if (LL.lines[i].backsector.ceilingheight != LL.lines[i].frontsector.ceilingheight) { drawMline(l, cdwallcolor); // ceiling level change } else if (cheating != 0) { drawMline(l, tswallcolor); } } } // If we have allmap... else if (plr.powers[pw_allmap] != 0) { // Some are never seen even with that! if ((LL.lines[i].flags & LINE_NEVERSEE) == 0) drawMline(l, GRAYS + 3); } } // System.out.println("Single pixel draws: "+singlepixel+" out of "+P.lines.length); // singlepixel=0; } // // Rotation in 2D. // Used to rotate player arrow line character. // private int rotx, roty; /** * Rotation in 2D. Used to rotate player arrow line character. * * @param x * fixed_t * @param y * fixed_t * @param a * angle_t -> this should be a LUT-ready BAM. */ private final void rotate(int x, int y, int a) { // int tmpx; rotx = FixedMul(x, finecosine[a]) - FixedMul(y, finesine[a]); roty = FixedMul(x, finesine[a]) + FixedMul(y, finecosine[a]); // rotx.val = tmpx; } private final void drawLineCharacter(mline_t[] lineguy, int lineguylines, int scale, // fixed_t int angle, // This should be a LUT-ready angle. int color, int x, // fixed_t int y // fixed_t ) { int i; final boolean rotate = (angle != 0); mline_t l = new mline_t(); for (i = 0; i < lineguylines; i++) { l.ax = lineguy[i].ax; l.ay = lineguy[i].ay; if (scale != 0) { l.ax = FixedMul(scale, l.ax); l.ay = FixedMul(scale, l.ay); } if (rotate) { rotate(l.ax, l.ay, angle); // MAES: assign rotations l.ax = rotx; l.ay = roty; } l.ax += x; l.ay += y; l.bx = lineguy[i].bx; l.by = lineguy[i].by; if (scale != 0) { l.bx = FixedMul(scale, l.bx); l.by = FixedMul(scale, l.by); } if (rotate) { rotate(l.bx, l.by, angle); // MAES: assign rotations l.bx = rotx; l.by = roty; } l.bx += x; l.by += y; drawMline(l, color); } } protected int their_colors[]; public final void drawPlayers() { player_t p; int their_color = -1; int color; // System.out.println(Long.toHexString(plr.mo.angle)); if (!DM.netgame) { if (cheating != 0) drawLineCharacter(cheat_player_arrow, NUMCHEATPLYRLINES, 0, toBAMIndex(plr.mo.angle), V.getBaseColor(WHITE), plr.mo.x, plr.mo.y); else drawLineCharacter(player_arrow, NUMPLYRLINES, 0, toBAMIndex(plr.mo.angle), V.getBaseColor(WHITE), plr.mo.x, plr.mo.y); return; } for (int i = 0; i < MAXPLAYERS; i++) { their_color++; p = DM.players[i]; if ((DM.deathmatch && !DM.singledemo) && p != plr) continue; if (!DM.playeringame[i]) continue; if (p.powers[pw_invisibility] != 0) color = 246; // *close* to black else color = their_colors[their_color]; drawLineCharacter(player_arrow, NUMPLYRLINES, 0, (int) p.mo.angle, V.getBaseColor(color), p.mo.x, p.mo.y); } } public final void drawThings(int colors, int colorrange) { mobj_t t; int color = V.getBaseColor(colors + lightlev); // Ain't gonna change for (int i = 0; i < LL.numsectors; i++) { // MAES: get first on the list. t = LL.sectors[i].thinglist; while (t != null) { drawLineCharacter(thintriangle_guy, NUMTHINTRIANGLEGUYLINES, 16 << FRACBITS, toBAMIndex(t.angle), color, t.x, t.y); t = (mobj_t) t.snext; } } } public final void drawMarks() { int i, fx, fy, w, h; for (i = 0; i < AM_NUMMARKPOINTS; i++) { if (markpoints[i].x != -1) { w = marknums[i].width; h = marknums[i].height; // Nothing wrong with v1.9 IWADs, but I wouldn't put my hand on // the fire for older ones. // w = 5; // because something's wrong with the wad, i guess // h = 6; // because something's wrong with the wad, i guess fx = CXMTOF(markpoints[i].x); fy = CYMTOF(markpoints[i].y); if (fx >= f_x && fx <= f_w - w && fy >= f_y && fy <= f_h - h) V.DrawScaledPatch(fx, fy, FB | V_NOSCALESTART, vs, marknums[i]); } } } protected abstract void drawCrosshair(int color); public final void Drawer() { if (!DM.automapactive) return; // System.out.println("Drawing map"); if (overlay < 1) V.FillRect(BACKGROUND, FB, 0, 0, f_w, f_h); // BACKGROUND if (grid) drawGrid(V.getBaseColor(GRIDCOLORS)); drawWalls(); drawPlayers(); if (cheating == 2) drawThings(THINGCOLORS, THINGRANGE); drawCrosshair(V.getBaseColor(XHAIRCOLORS)); drawMarks(); V.MarkRect(f_x, f_y, f_w, f_h); } @Override public void updateStatus(DoomStatus<?,?> DC) { this.V = (DoomVideoRenderer<T, V>) DC.V; this.W = DC.W; this.LL = DC.LL; this.DM = (DoomMain<T, V>) DC.DM; this.ST = DC.ST; } // //////////////////////////VIDEO SCALE STUFF // //////////////////////////////// protected int SCREENWIDTH; protected int SCREENHEIGHT; protected IVideoScale vs; public void setVideoScale(IVideoScale vs) { this.vs = vs; } public void initScaling() { this.SCREENHEIGHT = vs.getScreenHeight(); this.SCREENWIDTH = vs.getScreenWidth(); // Pre-scale stuff. finit_width = SCREENWIDTH; finit_height = SCREENHEIGHT - 32 * vs.getSafeScaling(); } public static final class HiColor extends Map<byte[], short[]> { public HiColor(DoomStatus<byte[], short[]> DS) { super(DS); } protected final void PUTDOT(int xx, int yy, int cc) { fb[(yy) * f_w + (xx)] = (short) (cc); } protected final void drawCrosshair(int color) { fb[(f_w * (f_h + 1)) / 2] = (short) color; // single point for now } } public static final class Indexed extends Map<byte[], byte[]> { public Indexed(DoomStatus<byte[], byte[]> DS) { super(DS); } protected final void PUTDOT(int xx, int yy, int cc) { fb[(yy) * f_w + (xx)] = (byte) (cc); } protected final void drawCrosshair(int color) { fb[(f_w * (f_h + 1)) / 2] = (byte) color; // single point for now } } public static final class TrueColor extends Map<byte[], int[]> { public TrueColor(DoomStatus<byte[], int[]> DS) { super(DS); } protected final void PUTDOT(int xx, int yy, int cc) { fb[(yy) * f_w + (xx)] = (int) (cc); } protected final void drawCrosshair(int color) { fb[(f_w * (f_h + 1)) / 2] = (int) color; // single point for now } } } package automap; public class islope_t { /** fixed_t */ int slp, islp; } package automap; public class fline_t { /* * public fline_t(){ a=new fpoint_t(); b=new fpoint_t(); } public fline_t(fpoint_t a, fpoint_t b){ this.a=a; this.b=b; } */ public fline_t(int ax, int ay, int bx, int by){ this.ay=ay; this.ax=ax; this.by=by; this.bx=bx; } public fline_t() { // TODO Auto-generated constructor stub } public int ax,ay,bx,by; /* public fpoint_t a, b; public void reset() { this.a.x=0; this.a.y=0; this.b.x=0; this.b.y=0; }*/ } package boom; public class prboom_comp_t { public prboom_comp_t(int minver, int maxver, boolean state, String cmd) { this.minver = minver; this.maxver = maxver; this.state = state; this.cmd = cmd; } public int minver; public int maxver; public boolean state; public String cmd; } package boom; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import w.CacheableDoomObject; /** ZDoom node? * */ public class mapseg_znod_t implements CacheableDoomObject{ public mapseg_znod_t(){ } public int v1,v2; // Those are unsigned :-/ public char linedef; public byte side; public static int sizeOf(){ return 11; } @Override public void unpack(ByteBuffer buf) throws IOException { buf.order(ByteOrder.LITTLE_ENDIAN); this.v1 = buf.getInt(); this.v2 = buf.getInt(); this.linedef=buf.getChar(); this.side=buf.get(); } }; package boom; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import w.CacheableDoomObject; /** fixed 32 bit gl_vert format v2.0+ (glBsp 1.91) */ public class mapglvertex_t implements CacheableDoomObject{ public int x, y; // fixed_t public static int sizeOf() { return 8; } @Override public void unpack(ByteBuffer buf) throws IOException { buf.order(ByteOrder.LITTLE_ENDIAN); x=buf.getInt(); y=buf.getInt(); } } package boom; /* Emacs style mode select -*- C++ -*- *----------------------------------------------------------------------------- * * * PrBoom: a Doom port merged with LxDoom and LSDLDoom * based on BOOM, a modified and improved DOOM engine * Copyright (C) 1999 by * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman * Copyright (C) 1999-2000 by * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze * Copyright 2005, 2006 by * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * DESCRIPTION: * *----------------------------------------------------------------------------- */ public interface E6Y{ /* #define HU_HUDADDX (HU_HUDX) #define HU_HUDADDY (HU_HUDY+(-1)*HU_GAPY) #define HU_CENTERMSGX (320/2) #define HU_CENTERMSGY ((200-ST_HEIGHT)/2 - 1 - LittleShort(hu_font[0].height)) #define HU_HUDADDX_D (HU_HUDX_LL) #define HU_HUDADDY_D (HU_HUDY_LL+(-1)*HU_GAPY) #define HU_MSGCENTERTIMEOUT (2*TICRATE) */ public static final String STSTR_SECRETFOUND = "A secret is revealed!"; public static final int S_CANT_GL_ARB_MULTITEXTURE= 0x10000000; public static final int S_CANT_GL_ARB_MULTISAMPLEFACTOR= 0x20000000; public static final int GL_COMBINE_ARB = 0x8570; public static final int GL_RGB_SCALE_ARB = 0x8573; public static final char NO_INDEX =0xFFFF; public static final float FOV_CORRECTION_FACTOR= 1.13776f; public static final int FOV90 =90; //public static final double DEG2RAD( a ) ( a * Pi ) / 180.0f; //#define RAD2DEG( a ) ( a / Pi ) * 180.0f; public class buf_overrun_item_t { String wadname; int map; int address; } public class camera_t { long x; long y; long z; long PrevX; long PrevY; long PrevZ; long angle; long pitch; long PrevAngle; long PrevPitch; int type; } /* extern int REAL_SCREENWIDTH; extern int REAL_SCREENHEIGHT; extern int REAL_SCREENPITCH; extern dboolean wasWiped; extern int totalleveltimes; extern int secretfound; extern int messagecenter_counter; extern int demo_skiptics; extern int demo_tics_count; extern int demo_curr_tic; extern int demo_playerscount; extern char demo_len_st[80]; extern int avi_shot_time; extern int avi_shot_num; extern const char *avi_shot_fname; extern char avi_shot_curr_fname[PATH_MAX]; extern dboolean doSkip; extern dboolean demo_stoponnext; extern dboolean demo_stoponend; extern dboolean demo_warp; extern int key_speed_up; extern int key_speed_down; extern int key_speed_default; extern int speed_step; extern int key_nextlevel; extern int key_demo_jointogame; extern int key_demo_endlevel; extern int key_walkcamera; extern int key_showalive; extern int hudadd_gamespeed; extern int hudadd_leveltime; extern int hudadd_demotime; extern int hudadd_secretarea; extern int hudadd_smarttotals; extern int hudadd_demoprogressbar; extern int movement_strafe50; extern int movement_shorttics; extern int movement_strafe50onturns; extern int movement_mouselook; extern int movement_mouseinvert; extern int movement_maxviewpitch; extern int mouse_handler; extern int mouse_doubleclick_as_use; extern int render_multisampling; extern int render_paperitems; extern int render_wipescreen; extern int mouse_acceleration; extern int demo_overwriteexisting; extern int render_fov; extern int render_aspect; extern float render_ratio; extern float render_fovratio; extern float render_fovy; extern float render_multiplier; void M_ChangeAspectRatio(void); void M_ChangeStretch(void); extern int misc_fastexit; extern int palette_ondamage; extern int palette_onbonus; extern int palette_onpowers; extern camera_t walkcamera; extern fixed_t sidemove_normal[2]; extern fixed_t sidemove_strafe50[2]; extern int PitchSign; extern int mouseSensitivity_mlook; extern angle_t viewpitch; extern float skyscale; extern float screen_skybox_zplane; extern float maxNoPitch[]; extern float tan_pitch; extern float skyUpAngle; extern float skyUpShift; extern float skyXShift; extern float skyYShift; extern dboolean mlook_or_fov; extern hu_textline_t w_hudadd; extern hu_textline_t w_centermsg; extern hu_textline_t w_precache; extern char hud_add[80]; extern char hud_centermsg[80]; void e6y_assert(const char *format, ...); void ParamsMatchingCheck(); void e6y_InitCommandLine(void); void P_WalkTicker (); void P_SyncWalkcam(dboolean sync_coords, dboolean sync_sight); void P_ResetWalkcam(void); extern dboolean sound_inited_once; void e6y_I_uSleep(unsigned long usecs); void G_SkipDemoStart(void); void G_SkipDemoStop(void); void G_SkipDemoCheck(void); int G_GotoNextLevel(void); #ifdef GL_DOOM void M_ChangeMouseLook(void); void M_ChangeMaxViewPitch(void); void M_ChangeMouseInvert(void); void M_ChangeFOV(void); void M_ChangeUseDetail(void); void M_ChangeMultiSample(void); void M_ChangeSpriteClip(void); void M_ChangeAllowBoomColormaps(void); void M_ChangeTextureUseHires(void); void M_ChangeAllowFog(void); void M_ChangeTextureHQResize(void); #endif void M_ChangeRenderPrecise(void); void M_ChangeSpeed(void); void M_ChangeScreenMultipleFactor(void); void M_ChangeInterlacedScanning(void); void M_MouseMLook(int choice); void M_MouseAccel(int choice); void M_ChangeCompTranslucency(void); void CheckPitch(signed int *pitch); void I_Init2(void); #ifdef GL_DOOM dboolean GetMouseLook(void); dboolean HaveMouseLook(void); #else #define GetMouseLook() (0) #define HaveMouseLook() (0) #endif extern float viewPitch; extern dboolean transparentpresent; void R_ClearClipSegs (void); void R_RenderBSPNode(int bspnum); typedef struct prboom_comp_s { unsigned int minver; unsigned int maxver; dboolean state; const char *cmd; } prboom_comp_t; enum { PC_MONSTER_AVOID_HAZARDS, PC_REMOVE_SLIME_TRAILS, PC_NO_DROPOFF, PC_TRUNCATED_SECTOR_SPECIALS, PC_BOOM_BRAINAWAKE, PC_PRBOOM_FRICTION, PC_REJECT_PAD_WITH_FF, PC_FORCE_LXDOOM_DEMO_COMPATIBILITY, PC_ALLOW_SSG_DIRECT, PC_TREAT_NO_CLIPPING_THINGS_AS_NOT_BLOCKING, PC_FORCE_INCORRECT_PROCESSING_OF_RESPAWN_FRAME_ENTRY, PC_FORCE_CORRECT_CODE_FOR_3_KEYS_DOORS_IN_MBF, PC_UNINITIALIZE_CRUSH_FIELD_FOR_STAIRS, PC_FORCE_BOOM_FINDNEXTHIGHESTFLOOR, PC_ALLOW_SKY_TRANSFER_IN_BOOM, PC_APPLY_GREEN_ARMOR_CLASS_TO_ARMOR_BONUSES, PC_APPLY_BLUE_ARMOR_CLASS_TO_MEGASPHERE, PC_WRONG_FIXEDDIV, PC_FORCE_INCORRECT_BOBBING_IN_BOOM, PC_BOOM_DEH_PARSER, PC_MBF_REMOVE_THINKER_IN_KILLMOBJ, PC_DO_NOT_INHERIT_FRIENDLYNESS_FLAG_ON_SPAWN, PC_DO_NOT_USE_MISC12_FRAME_PARAMETERS_IN_A_MUSHROOM, PC_MAX }; extern prboom_comp_t prboom_comp[]; int StepwiseSum(int value, int direction, int step, int minval, int maxval, int defval); enum { TT_ALLKILL, TT_ALLITEM, TT_ALLSECRET, TT_TIME, TT_TOTALTIME, TT_TOTALKILL, TT_TOTALITEM, TT_TOTALSECRET, TT_MAX }; typedef struct timetable_s { char map[16]; int kill[MAXPLAYERS]; int item[MAXPLAYERS]; int secret[MAXPLAYERS]; int stat[TT_MAX]; } timetable_t; #ifdef _WIN32 const char* WINError(void); #endif extern int stats_level; void e6y_G_DoCompleted(void); void e6y_WriteStats(void); void e6y_G_DoWorldDone(void); void I_ProcessWin32Mouse(void); void I_StartWin32Mouse(void); void I_EndWin32Mouse(void); int AccelerateMouse(int val); void MouseAccelChanging(void); extern int mlooky; extern int realtic_clock_rate; extern dboolean IsDehMaxHealth; extern dboolean IsDehMaxSoul; extern dboolean IsDehMegaHealth; extern dboolean DEH_mobjinfo_bits[NUMMOBJTYPES]; extern int deh_maxhealth; extern int deh_max_soul; extern int deh_mega_health; extern int maxhealthbonus; void e6y_G_Compatibility(void); extern dboolean zerotag_manual; extern int comperr_zerotag; extern int comperr_passuse; extern int comperr_hangsolid; dboolean compbad_get(int *compbad); dboolean ProcessNoTagLines(line_t* line, sector_t **sec, int *secnum); #define I_FindName(a) ((a)->Name) #define I_FindAttr(a) ((a)->Attribs) typedef struct { unsigned int Attribs; unsigned int Times[3*2]; unsigned int Size[2]; unsigned int Reserved[2]; char Name[PATH_MAX]; char AltName[14]; } findstate_t; char* PathFindFileName(const char* pPath); void NormalizeSlashes2(char *str); unsigned int AfxGetFileName(const char* lpszPathName, char* lpszTitle, unsigned int nMax); void AbbreviateName(char* lpszCanon, int cchMax, int bAtLeastName); //extern int viewMaxY; extern dboolean isskytexture; void D_AddDehFile (const char *file, wad_source_t source); extern int levelstarttic; void I_AfterUpdateVideoMode(void); extern int force_singletics_to; dboolean HU_DrawDemoProgress(void); #ifdef ALL_IN_ONE unsigned char* GetAllInOneLumpHandle(void); #endif #ifdef _MSC_VER int GetFullPath(const char* FileName, const char* ext, char *Buffer, size_t BufferLength); #endif void I_vWarning(const char *message, va_list argList); void I_Warning(const char *message, ...); #define PRB_MB_OK 0x00000000 #define PRB_MB_OKCANCEL 0x00000001 #define PRB_MB_ABORTRETRYIGNORE 0x00000002 #define PRB_MB_YESNOCANCEL 0x00000003 #define PRB_MB_YESNO 0x00000004 #define PRB_MB_RETRYCANCEL 0x00000005 #define PRB_MB_DEFBUTTON1 0x00000000 #define PRB_MB_DEFBUTTON2 0x00000100 #define PRB_MB_DEFBUTTON3 0x00000200 #define PRB_IDOK 1 #define PRB_IDCANCEL 2 #define PRB_IDABORT 3 #define PRB_IDRETRY 4 #define PRB_IDIGNORE 5 #define PRB_IDYES 6 #define PRB_IDNO 7 int I_MessageBox(const char* text, unsigned int type); dboolean SmoothEdges(unsigned char * buffer,int w, int h); #ifdef _WIN32 extern int mus_extend_volume; void I_midiOutSetVolumes(int volume); #endif #endif */ } package boom; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import w.CacheableDoomObject; /** * LineSeg, generated by splitting LineDefs * using partition lines selected by BSP builder. * MAES: this is the ON-DISK structure. The corresponding memory structure, * segs_t, has fixed_t members. */ public class mapseg_v4_t implements CacheableDoomObject{ public mapseg_v4_t(){ } public int v1; public int v2; public char angle; public char linedef; public char side; public char offset; public static int sizeOf(){ return 16; } @Override public void unpack(ByteBuffer buf) throws IOException { buf.order(ByteOrder.LITTLE_ENDIAN); this.v1 = buf.getInt(); this.v2 = buf.getInt(); this.angle=buf.getChar(); this.linedef=buf.getChar(); this.side=buf.getChar(); this.offset=buf.getChar(); } }; package boom; import java.io.IOException; import java.nio.ByteBuffer; import java.util.Arrays; import utils.C2JUtils; import w.CacheableDoomObject; public class DeepBSPNodesV4 implements CacheableDoomObject{ public static final byte[] DeepBSPHeader={ 'x','N','d','4',0,0,0,0 }; byte[] header=new byte[8]; mapnode_v4_t[] nodes; int numnodes; public boolean formatOK(){ return Arrays.equals(header, DeepBSPHeader); } public mapnode_v4_t[] getNodes(){ return nodes; } @Override public void unpack(ByteBuffer buf) throws IOException { int length=buf.capacity(); // Too short, not even header. if (length<8) return; numnodes=(length-8)/mapnode_v4_t.sizeOf(); if (length<1) return; buf.get(header); // read header nodes=C2JUtils.createArrayOfObjects(mapnode_v4_t.class, length); for (int i=0;i<length;i++){ nodes[i].unpack(buf); } } } package boom; import java.io.IOException; import java.nio.ByteBuffer; import java.util.Arrays; import utils.C2JUtils; import w.CacheableDoomObject; public class ZNodeSegs implements CacheableDoomObject{ private static final byte[] DeepBSPHeader={ 'x','N','d','4',0,0,0,0 }; byte[] header; mapseg_znod_t[] nodes; int numnodes; public boolean formatOK(){ return Arrays.equals(header, DeepBSPHeader); } public mapseg_znod_t[] getNodes(){ return nodes; } @Override public void unpack(ByteBuffer buf) throws IOException { int length=buf.capacity(); // Too short, not even header. if (length<8) return; numnodes=(length-8)/mapnode_v4_t.sizeOf(); if (length<1) return; buf.get(header); // read header nodes=C2JUtils.createArrayOfObjects(mapseg_znod_t.class, length); for (int i=0;i<length;i++){ nodes[i].unpack(buf); } } } package boom; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import utils.C2JUtils; import w.CacheableDoomObject; public class mapsubsector_znod_t implements CacheableDoomObject{ public long numsegs; @Override public void unpack(ByteBuffer buf) throws IOException { buf.order(ByteOrder.LITTLE_ENDIAN); this.numsegs = C2JUtils.unsigned(buf.getInt()); } public static final int sizeOf(){ return 4; } } package boom; /** cph - move compatibility levels here so we can use them in d_server.c * * @author cph * */ public final class Compatibility { /** Doom v1.2 */ public static final int doom_12_compatibility=0; public static final int doom_1666_compatibility=1; /* Doom v1.666 */ public static final int doom2_19_compatibility=2; /* Doom & Doom 2 v1.9 */ public static final int ultdoom_compatibility=3; /* Ultimate Doom and Doom95 */ public static final int finaldoom_compatibility=4; /* Final Doom */ public static final int dosdoom_compatibility=5; /* DosDoom 0.47 */ public static final int tasdoom_compatibility=6; /* TASDoom */ public static final int boom_compatibility_compatibility=7; /* Boom's compatibility mode */ public static final int boom_201_compatibility=8; /* Boom v2.01 */ public static final int boom_202_compatibility=9; /* Boom v2.02 */ public static final int lxdoom_1_compatibility=10; /* LxDoom v1.3.2+ */ public static final int mbf_compatibility=11; /* MBF */ public static final int prboom_1_compatibility=12; /* PrBoom 2.03beta? */ public static final int prboom_2_compatibility=13; /* PrBoom 2.1.0-2.1.1 */ public static final int prboom_3_compatibility=14; /* PrBoom 2.2.x */ public static final int prboom_4_compatibility=15; /* PrBoom 2.3.x */ public static final int prboom_5_compatibility=16; /* PrBoom 2.4.0 */ public static final int prboom_6_compatibility=17; /* Latest PrBoom */ public static final int MAX_COMPATIBILITY_LEVEL=18; /* Must be last entry */ /* Aliases follow */ public static final int boom_compatibility = boom_201_compatibility; /* Alias used by G_Compatibility */ public static final int best_compatibility = prboom_6_compatibility; public static final prboom_comp_t[] prboom_comp = { new prboom_comp_t(0xffffffff, 0x02020615, false, "-force_monster_avoid_hazards"), new prboom_comp_t(0x00000000, 0x02040601, false, "-force_remove_slime_trails"), new prboom_comp_t(0x02020200, 0x02040801, false, "-force_no_dropoff"), new prboom_comp_t(0x00000000, 0x02040801, false, "-force_truncated_sector_specials"), new prboom_comp_t(0x00000000, 0x02040802, false, "-force_boom_brainawake"), new prboom_comp_t(0x00000000, 0x02040802, false, "-force_prboom_friction"), new prboom_comp_t(0x02020500, 0x02040000, false, "-reject_pad_with_ff"), new prboom_comp_t(0xffffffff, 0x02040802, false, "-force_lxdoom_demo_compatibility"), new prboom_comp_t(0x00000000, 0x0202061b, false, "-allow_ssg_direct"), new prboom_comp_t(0x00000000, 0x02040601, false, "-treat_no_clipping_things_as_not_blocking"), new prboom_comp_t(0x00000000, 0x02040803, false, "-force_incorrect_processing_of_respawn_frame_entry"), new prboom_comp_t(0x00000000, 0x02040601, false, "-force_correct_code_for_3_keys_doors_in_mbf"), new prboom_comp_t(0x00000000, 0x02040601, false, "-uninitialize_crush_field_for_stairs"), new prboom_comp_t(0x00000000, 0x02040802, false, "-force_boom_findnexthighestfloor"), new prboom_comp_t(0x00000000, 0x02040802, false, "-allow_sky_transfer_in_boom"), new prboom_comp_t(0x00000000, 0x02040803, false, "-apply_green_armor_class_to_armor_bonuses"), new prboom_comp_t(0x00000000, 0x02040803, false, "-apply_blue_armor_class_to_megasphere"), new prboom_comp_t(0x02050001, 0x02050003, false, "-wrong_fixeddiv"), new prboom_comp_t(0x02020200, 0x02050003, false, "-force_incorrect_bobbing_in_boom"), new prboom_comp_t(0xffffffff, 0x00000000, false, "-boom_deh_parser"), new prboom_comp_t(0x00000000, 0x02050007, false, "-mbf_remove_thinker_in_killmobj"), new prboom_comp_t(0x00000000, 0x02050007, false, "-do_not_inherit_friendlyness_flag_on_spawn"), new prboom_comp_t(0x00000000, 0x02050007, false, "-do_not_use_misc12_frame_parameters_in_a_mushroom") }; public static final int PC_MONSTER_AVOID_HAZARDS=0; public static final int PC_REMOVE_SLIME_TRAILS=1; public static final int PC_NO_DROPOFF=2; public static final int PC_TRUNCATED_SECTOR_SPECIALS=3; public static final int PC_BOOM_BRAINAWAKE=4; public static final int PC_PRBOOM_FRICTION=5; public static final int PC_REJECT_PAD_WITH_FF=6; public static final int PC_FORCE_LXDOOM_DEMO_COMPATIBILITY=7; public static final int PC_ALLOW_SSG_DIRECT=8; public static final int PC_TREAT_NO_CLIPPING_THINGS_AS_NOT_BLOCKING=9; public static final int PC_FORCE_INCORRECT_PROCESSING_OF_RESPAWN_FRAME_ENTRY=10; public static final int PC_FORCE_CORRECT_CODE_FOR_3_KEYS_DOORS_IN_MBF=11; public static final int PC_UNINITIALIZE_CRUSH_FIELD_FOR_STAIRS=12; public static final int PC_FORCE_BOOM_FINDNEXTHIGHESTFLOOR=13; public static final int PC_ALLOW_SKY_TRANSFER_IN_BOOM=14; public static final int PC_APPLY_GREEN_ARMOR_CLASS_TO_ARMOR_BONUSES=15; public static final int PC_APPLY_BLUE_ARMOR_CLASS_TO_MEGASPHERE=16; public static final int PC_WRONG_FIXEDDIV=17; public static final int PC_FORCE_INCORRECT_BOBBING_IN_BOOM=18; public static final int PC_BOOM_DEH_PARSER=19; public static final int PC_MBF_REMOVE_THINKER_IN_KILLMOBJ=20; public static final int PC_DO_NOT_INHERIT_FRIENDLYNESS_FLAG_ON_SPAWN=21; public static final int PC_DO_NOT_USE_MISC12_FRAME_PARAMETERS_IN_A_MUSHROOM=21; public static final int PC_MAX=23; public enum PC { PC_MONSTER_AVOID_HAZARDS, PC_REMOVE_SLIME_TRAILS, PC_NO_DROPOFF, PC_TRUNCATED_SECTOR_SPECIALS, PC_BOOM_BRAINAWAKE, PC_PRBOOM_FRICTION, PC_REJECT_PAD_WITH_FF, PC_FORCE_LXDOOM_DEMO_COMPATIBILITY, PC_ALLOW_SSG_DIRECT, PC_TREAT_NO_CLIPPING_THINGS_AS_NOT_BLOCKING, PC_FORCE_INCORRECT_PROCESSING_OF_RESPAWN_FRAME_ENTRY, PC_FORCE_CORRECT_CODE_FOR_3_KEYS_DOORS_IN_MBF, PC_UNINITIALIZE_CRUSH_FIELD_FOR_STAIRS, PC_FORCE_BOOM_FINDNEXTHIGHESTFLOOR, PC_ALLOW_SKY_TRANSFER_IN_BOOM, PC_APPLY_GREEN_ARMOR_CLASS_TO_ARMOR_BONUSES, PC_APPLY_BLUE_ARMOR_CLASS_TO_MEGASPHERE, PC_WRONG_FIXEDDIV, PC_FORCE_INCORRECT_BOBBING_IN_BOOM, PC_BOOM_DEH_PARSER, PC_MBF_REMOVE_THINKER_IN_KILLMOBJ, PC_DO_NOT_INHERIT_FRIENDLYNESS_FLAG_ON_SPAWN, PC_DO_NOT_USE_MISC12_FRAME_PARAMETERS_IN_A_MUSHROOM, PC_MAX }; } package boom; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import w.CacheableDoomObject; import w.DoomBuffer; /** BSP Node structure on-disk */ public class mapnode_v4_t implements CacheableDoomObject { public mapnode_v4_t() { this.bbox = new short[2][4]; this.children = new int[2]; } /** Partition line from (x,y) to x+dx,y+dy) */ public short x, y, dx, dy; /** Bounding box for each child, clip against view frustum. */ public short[][] bbox; /** If NF_SUBSECTOR its a subsector, else it's a node of another subtree. * In simpler words: if the first bit is set, strip it and use the rest * as a subtree index. Else it's a node index. * */ public int[] children = new int[2]; public static final int sizeOf() { return (8 + 16 + 8); } @Override public void unpack(ByteBuffer buf) throws IOException { buf.order(ByteOrder.LITTLE_ENDIAN); this.x = buf.getShort(); this.y = buf.getShort(); this.dx = buf.getShort(); this.dy = buf.getShort(); DoomBuffer.readShortArray(buf, this.bbox[0], 4); DoomBuffer.readShortArray(buf, this.bbox[1], 4); DoomBuffer.readIntArray(buf, this.children, 2); } } package boom; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import w.CacheableDoomObject; public class mapsubsector_v4_t implements CacheableDoomObject{ public char numsegs; /** Index of first one, segs are stored sequentially. */ public int firstseg; @Override public void unpack(ByteBuffer buf) throws IOException { buf.order(ByteOrder.LITTLE_ENDIAN); this.numsegs = buf.getChar(); this.firstseg = buf.getInt(); } public static int sizeOf(){ return 6; } } package boom; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import w.CacheableDoomObject; import w.DoomBuffer; public class mapnode_znod_t implements CacheableDoomObject { public short x; // Partition line from (x,y) to x+dx,y+dy) public short y; public short dx; public short dy; // Bounding box for each child, clip against view frustum. public short[][] bbox; // If NF_SUBSECTOR its a subsector, else it's a node of another subtree. public int[] children; public mapnode_znod_t(){ this.bbox = new short[2][4]; this.children = new int[2]; } public static final int sizeOf() { return (8 + 16 + 8); } @Override public void unpack(ByteBuffer buf) throws IOException { buf.order(ByteOrder.LITTLE_ENDIAN); this.x = buf.getShort(); this.y = buf.getShort(); this.dx = buf.getShort(); this.dy = buf.getShort(); DoomBuffer.readShortArray(buf, this.bbox[0], 4); DoomBuffer.readShortArray(buf, this.bbox[1], 4); DoomBuffer.readIntArray(buf, this.children, 2); } } package pooling; import doom.think_t; import p.Actions; import p.mobj_t; import s.AudioChunk; /* The idea is to reuse mobjs...however in practice that * doesn't work out so well, with everything "freezing" after * a while */ public class MobjPool extends ObjectPool<mobj_t> { Actions A; public MobjPool(Actions A) { // A reasonable time limit for map objects? super(1000L); this.A=A; } protected mobj_t create() { /* for (int i=0;i<8192;i++){ locked.push(new mobj_t(A)); } return locked.pop();*/ return new mobj_t(A); } public void expire(mobj_t o) { o.function=think_t.NOP; } public boolean validate(mobj_t o) { return (o.function==think_t.NOP); } } package pooling; import s.AudioChunk; // Referenced classes of package pooling: // ObjectPool public class AudioChunkPool extends ObjectQueuePool<AudioChunk> { public AudioChunkPool() { // A reasonable time limit for Audio chunks super(10000L); } protected AudioChunk create() { return new AudioChunk(); } public void expire(AudioChunk o) { o.free = true; } public boolean validate(AudioChunk o) { return o.free; } } package pooling; public class RoguePatchMap extends GenericIntMap<byte[][]> { public RoguePatchMap(){ super(); patches = new byte[DEFAULT_CAPACITY][][]; } } package pooling; import java.util.Arrays; public abstract class GenericIntMap<K> { protected static final int DEFAULT_CAPACITY = 16; /** Concrete implementations must allocate patches * */ GenericIntMap() { lumps = new int[DEFAULT_CAPACITY]; // patches = new K[DEFAULT_CAPACITY]; } public boolean containsKey(int lump) { return indexOf(lump) >= 0; } public K get(int lump) { int index = indexOf(lump); if (index >= 0) { return patches[index]; } else { return null; } } public void put(int lump, K patch) { int index = indexOf(lump); if (index >= 0) { patches[index] = patch; } else { ensureCapacity(numEntries + 1); int newIndex = ~index; int moveCount = numEntries - newIndex; if (moveCount > 0) { System.arraycopy(lumps, newIndex, lumps, newIndex+1, moveCount); System.arraycopy(patches, newIndex, patches, newIndex+1, moveCount); } lumps[newIndex] = lump; patches[newIndex] = patch; ++ numEntries; } } protected void ensureCapacity(int cap) { while (lumps.length <= cap) { lumps = Arrays.copyOf(lumps, Math.max(lumps.length * 2, DEFAULT_CAPACITY)); } while (patches.length <= cap) { patches = Arrays.copyOf(patches, Math.max(patches.length * 2, DEFAULT_CAPACITY)); } } protected int indexOf(int lump) { return Arrays.binarySearch(lumps, 0, numEntries, lump); //for (int i=0;i<numEntries;i++) // if (lumps[i]==lump) return i; // //return -1; } protected int[] lumps; protected int numEntries; protected K[] patches; } package pooling; import java.util.Arrays; public class RoguePatchMap2 { private static final int DEFAULT_CAPACITY = 16; public RoguePatchMap2() { lumps = new int[DEFAULT_CAPACITY]; patches = new byte[DEFAULT_CAPACITY][][]; } boolean containsKey(int lump) { return indexOf(lump) >= 0; } public byte[][] get(int lump) { int index = indexOf(lump); if (index >= 0) { return patches[index]; } else { return null; } } public void put(int lump, byte[][] patch) { int index = indexOf(lump); if (index >= 0) { patches[index] = patch; } else { ensureCapacity(numEntries + 1); int newIndex = ~index; int moveCount = numEntries - newIndex; if (moveCount > 0) { System.arraycopy(lumps, newIndex, lumps, newIndex+1, moveCount); System.arraycopy(patches, newIndex, patches, newIndex+1, moveCount); } lumps[newIndex] = lump; patches[newIndex] = patch; ++ numEntries; } } private void ensureCapacity(int cap) { while (lumps.length <= cap) { lumps = Arrays.copyOf(lumps, Math.max(lumps.length * 2, DEFAULT_CAPACITY)); } while (patches.length <= cap) { patches = Arrays.copyOf(patches, Math.max(patches.length * 2, DEFAULT_CAPACITY)); } } private int indexOf(int lump) { return Arrays.binarySearch(lumps, 0, numEntries, lump); } private int[] lumps; private int numEntries; private byte[][][] patches; } package pooling; import java.util.Stack; import p.mobj_t; /** A convenient object pooling class, derived from the stock ObjectPool. * * It's about 50% faster than calling new, and MUCH faster than ObjectPool * because it doesn't do that bullshit object cleanup every so often. * */ public abstract class ObjectQueuePool<K> { private static final boolean D=false; public ObjectQueuePool(long expirationTime) { locked = new Stack<K>(); } protected abstract K create(); public abstract boolean validate(K obj); public abstract void expire(K obj); public void drain(){ locked.clear(); } public K checkOut() { K t; if(!locked.isEmpty()) { return locked.pop(); } t = create(); return t; } public void checkIn(K t) { if (D) if (t instanceof mobj_t) System.out.printf("Object %s returned to the pool\n",t.toString()); locked.push(t); } protected Stack<K> locked; // private Hashtable<K,Long> unlocked; } package pooling; import java.util.Enumeration; import java.util.Hashtable; import p.mobj_t; /** A convenient object pooling class. Currently used for AudioChunks, but * could be reused for UI events and other such things. Perhaps reusing it * for mobj_t's is possible, but risky. * */ public abstract class ObjectPool<K> { private static final boolean D=false; public ObjectPool(long expirationTime) { this.expirationTime = expirationTime; locked = new Hashtable<K,Long>(); unlocked = new Hashtable<K,Long>(); } protected abstract K create(); public abstract boolean validate(K obj); public abstract void expire(K obj); public synchronized K checkOut() { long now = System.currentTimeMillis(); K t; if(unlocked.size() > 0) { Enumeration<K> e = unlocked.keys(); // System.out.println((new StringBuilder("Pool size ")).append(unlocked.size()).toString()); while(e.hasMoreElements()) { t = e.nextElement(); if(now - ((Long)unlocked.get(t)).longValue() > expirationTime) { // object has expired if (t instanceof mobj_t) if (D) System.out.printf("Object %s expired\n",t.toString()); unlocked.remove(t); expire(t); t = null; } else { if(validate(t)) { unlocked.remove(t); locked.put(t, Long.valueOf(now)); if (D) if (t instanceof mobj_t) System.out.printf("Object %s reused\n",t.toString()); return t; } // object failed validation unlocked.remove(t); expire(t); t = null; } } } t = create(); locked.put(t, Long.valueOf(now)); return t; } public synchronized void checkIn(K t) { if (D) if (t instanceof mobj_t) System.out.printf("Object %s returned to the pool\n",t.toString()); locked.remove(t); unlocked.put(t, Long.valueOf(System.currentTimeMillis())); } private long expirationTime; protected Hashtable<K,Long> locked; private Hashtable<K,Long> unlocked; } package demo; import w.IWritableDoomObject; import defines.skill_t; public interface IDoomDemo extends IWritableDoomObject{ /** Vanilla end demo marker, to append at the end of recorded demos */ public static final int DEMOMARKER =0x80; /** Get next demo command, in its raw format. Use * its own adapters if you need it converted to a * standard ticcmd_t. * * @return */ IDemoTicCmd getNextTic(); /** Record a demo command in the IDoomDemo's native format. * Use the IDemoTicCmd's objects adaptors to convert it. * * @param tic */ void putTic(IDemoTicCmd tic); int getVersion(); void setVersion(int version); skill_t getSkill(); void setSkill(skill_t skill); int getEpisode(); void setEpisode(int episode); int getMap(); void setMap(int map); boolean isDeathmatch(); void setDeathmatch(boolean deathmatch); boolean isRespawnparm(); void setRespawnparm(boolean respawnparm); boolean isFastparm(); void setFastparm(boolean fastparm); boolean isNomonsters(); void setNomonsters(boolean nomonsters); int getConsoleplayer(); void setConsoleplayer(int consoleplayer); boolean[] getPlayeringame(); void setPlayeringame(boolean[] playeringame); void resetDemo(); } package demo; import java.io.DataOutputStream; import java.io.IOException; import java.nio.ByteBuffer; import doom.ticcmd_t; import utils.C2JUtils; import w.CacheableDoomObject; import w.IWritableDoomObject; /** A more lightweight version of ticcmd_t, which contains * too much crap and redundant data. In order to keep demo * loading and recording easier, this class contains only the * necessary stuff to read/write from/to disk during VANILLA * demos. It can be converted from/to ticcmd_t, if needed. * * @author admin * */ public class VanillaTiccmd implements CacheableDoomObject, IDemoTicCmd,IWritableDoomObject{ /** *2048 for move */ public byte forwardmove; /** *2048 for move */ public byte sidemove; /** <<16 for angle delta */ public byte angleturn; public byte buttons; /** Special note: the only occasion where we'd ever be interested * in reading ticcmd_t's from a lump is when playing back demos. * Therefore, we use this specialized reading method which does NOT, * I repeat, DOES NOT set all fields and some are read differently. * NOT 1:1 intercangeable with the Datagram methods! * */ @Override public void unpack(ByteBuffer f) throws IOException { // MAES: the original ID code for reference. // demo_p++ is a pointer inside a raw byte buffer. //cmd->forwardmove = ((signed char)*demo_p++); //cmd->sidemove = ((signed char)*demo_p++); //cmd->angleturn = ((unsigned char)*demo_p++)<<8; //cmd->buttons = (unsigned char)*demo_p++; forwardmove=f.get(); sidemove= f.get(); // Even if they use the "unsigned char" syntax, angleturn is signed. angleturn=f.get(); buttons=f.get(); } /** Ditto, we only pack some of the fields. * * @param f * @throws IOException */ public void pack(ByteBuffer f) throws IOException { f.put(forwardmove); f.put(sidemove); f.put(angleturn); f.put(buttons); } private static StringBuilder sb=new StringBuilder(); public String toString(){ sb.setLength(0); sb.append(" forwardmove "); sb.append(this.forwardmove); sb.append(" sidemove "); sb.append(this.sidemove); sb.append(" angleturn "); sb.append(this.angleturn); sb.append(" buttons "); sb.append(Integer.toHexString(this.buttons)); return sb.toString(); } @Override public void decode(ticcmd_t dest) { // Decode dest.forwardmove=this.forwardmove; dest.sidemove=this.sidemove; dest.angleturn=(short) (this.angleturn<<8); dest.buttons=(char) (C2JUtils.toUnsignedByte(this.buttons)); } @Override public void encode(ticcmd_t source) { // Note: we can get away with a simple copy because // Demoticcmds have already been "decoded". this.forwardmove=source.forwardmove; this.sidemove=source.sidemove; // OUCH!!! NASTY PRECISION REDUCTION... but it's the // One True Vanilla way. this.angleturn=(byte) (source.angleturn>>>8); this.buttons=(byte) (source.buttons&0x00FF); } @Override public void write(DataOutputStream f) throws IOException { f.writeByte(forwardmove); f.writeByte(sidemove); f.writeByte(angleturn); f.writeByte(buttons); } } package demo; import static data.Limits.MAXPLAYERS; import java.io.DataOutputStream; import java.io.IOException; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.List; import utils.C2JUtils; import w.CacheableDoomObject; import w.DoomBuffer; import w.DoomIO; import defines.skill_t; public class VanillaDoomDemo implements IDoomDemo,CacheableDoomObject{ // This stuff is in the demo header, in the order it appears // However everything is byte-sized when read from disk or to memory. public int version; public skill_t skill; public int episode; public int map; public boolean deathmatch; public boolean respawnparm; public boolean fastparm; public boolean nomonsters; public int consoleplayer; public boolean[] playeringame; // normally MAXPLAYERS (4) for vanilla. protected int p_demo; // After that, demos contain a sequence of ticcmd_t's to build dynamically at // load time or when recording. This abstraction allows arbitrary demo sizes // and easy per-step handling, and even changes/extensions. Just make sure // that ticcmd_t's are serializable! // Also, the format used in demo lumps is NOT the same as in datagrams/network // (e.g. there is no consistency) and their handling is modified. VanillaTiccmd[] commands; List<IDemoTicCmd> demorecorder; public VanillaDoomDemo(){ this.demorecorder=new ArrayList<IDemoTicCmd> (); } public void unpack(ByteBuffer b){ // Just the Header info for vanilla should be 13 bytes. // 1 byte at the end is the end-demo marker // So valid vanilla demos should have sizes that // fit the formula 14+4n, since each vanilla // demo ticcmd_t is 4 bytes. int lens=(b.limit()-13)/4; boolean vanilla=(b.limit()==(14+4*lens)); // Minimum valid vanilla demo should be 14 bytes...in theory. if (b.limit()<14) { // Use skill==null as an indicator that loading didn't go well. skill=null; return; } version=b.get(); try { skill = skill_t.values()[b.get()]; } catch (Exception e){ skill=null; } episode = b.get(); map = b.get(); deathmatch = b.get()!=0; respawnparm = b.get()!=0; fastparm = b.get()!=0; nomonsters = b.get()!=0; consoleplayer = b.get(); playeringame=new boolean[MAXPLAYERS]; for (int i=0 ; i<MAXPLAYERS ; i++) playeringame[i] = b.get()!=0; this.commands=new VanillaTiccmd[lens]; C2JUtils.initArrayOfObjects(this.commands, VanillaTiccmd.class); try { DoomBuffer.readObjectArray(b, this.commands, lens); } catch (IOException e) { skill=null; return; } } @Override public IDemoTicCmd getNextTic() { if ((commands!=null)&&(p_demo<commands.length)){ return commands[p_demo++]; } else return null; } @Override public void putTic(IDemoTicCmd tic) { demorecorder.add(tic); } @Override public int getVersion() { return version; } @Override public void setVersion(int version) { this.version = version; } @Override public skill_t getSkill() { return skill; } @Override public void setSkill(skill_t skill) { this.skill = skill; } @Override public int getEpisode() { return episode; } @Override public void setEpisode(int episode) { this.episode = episode; } @Override public int getMap() { return map; } @Override public void setMap(int map) { this.map = map; } @Override public boolean isDeathmatch() { return deathmatch; } @Override public void setDeathmatch(boolean deathmatch) { this.deathmatch = deathmatch; } @Override public boolean isRespawnparm() { return respawnparm; } @Override public void setRespawnparm(boolean respawnparm) { this.respawnparm = respawnparm; } @Override public boolean isFastparm() { return fastparm; } @Override public void setFastparm(boolean fastparm) { this.fastparm = fastparm; } @Override public boolean isNomonsters() { return nomonsters; } @Override public void setNomonsters(boolean nomonsters) { this.nomonsters = nomonsters; } @Override public int getConsoleplayer() { return consoleplayer; } @Override public void setConsoleplayer(int consoleplayer) { this.consoleplayer = consoleplayer; } @Override public boolean[] getPlayeringame() { return playeringame; } @Override public void setPlayeringame(boolean[] playeringame) { this.playeringame = playeringame; } @Override public void write(DataOutputStream f) throws IOException { f.writeByte(version); f.writeByte(skill.ordinal()); f.writeByte(episode); f.writeByte(map); f.writeBoolean(deathmatch); f.writeBoolean(respawnparm); f.writeBoolean(fastparm); f.writeBoolean(nomonsters); f.writeByte(consoleplayer); DoomIO.writeBoolean(f,this.playeringame,MAXPLAYERS); for (IDemoTicCmd i: demorecorder) { i.write(f); } f.writeByte(DEMOMARKER); // TODO Auto-generated method stub } @Override public void resetDemo() { this.p_demo=0; } /////////////////////// VARIOUS BORING GETTERS ///////////////////// } package demo; import w.IWritableDoomObject; import doom.ticcmd_t; /** Demo Tic Commands can be read/written to disk/buffers, * and are not necessarily equal to the in-game ticcmd_t. * Thus, it's necessary for them to implement some * adaptor method (both ways). * * @author admin * */ public interface IDemoTicCmd extends IWritableDoomObject{ /** Decode this IDemoTicCmd into a standard ticcmd_t. * * @param source */ public void decode(ticcmd_t dest); /** Encode this IDemoTicCmd from a standard ticcmd_t. * * @param dest */ public void encode(ticcmd_t source); } package m; import java.io.IOException; public interface ISyncLogger { public void debugStart() throws IOException; public void debugEnd(); public void sync(String format, Object ... args); } package m; import data.mobjtype_t; import doom.think_t; public interface IRandom { public int P_Random (); public int M_Random (); public void ClearRandom (); public int getIndex(); public int P_Random(int caller); public int P_Random(String message); public int P_Random(think_t caller, int sequence); public int P_Random(think_t caller, mobjtype_t type,int sequence); } package m; import static data.Limits.*; /** A fucked-up bounding box class. * Fucked-up because it's supposed to wrap fixed_t's.... no fucking way I'm doing * this with fixed_t objects. * * @author admin * */ public class BBox { public static final int BOXTOP = 0; public static final int BOXBOTTOM = 1; public static final int BOXLEFT = 2; public static final int BOXRIGHT = 3; /** (fixed_t) */ public int[] bbox; /** Points of the bbox as an object */ public BBox() { bbox = new int[4]; } // Static method public static void ClearBox(fixed_t[] box) { box[BOXRIGHT].set(MININT); box[BOXTOP].set(MININT); box[BOXLEFT].set(MAXINT); box[BOXBOTTOM].set(MAXINT); } // Instance method public void ClearBox() { bbox[BOXRIGHT]=(MININT); bbox[BOXTOP]=(MININT); bbox[BOXLEFT]=(MAXINT); bbox[BOXBOTTOM]=(MAXINT); } public static void AddToBox(fixed_t[] box, fixed_t x, fixed_t y) { if (x.compareTo(box[BOXLEFT]) < 0) box[BOXLEFT].copy(x); else if (x.compareTo(box[BOXRIGHT]) > 0) box[BOXRIGHT].copy(x); if (y.compareTo(box[BOXBOTTOM]) < 0) box[BOXBOTTOM] = y; else if (y.compareTo(box[BOXTOP]) > 0) box[BOXTOP] = y; } public void AddToBox(fixed_t x, fixed_t y) { if (x.compareTo(bbox[BOXLEFT]) < 0) bbox[BOXLEFT]=x.val; else if (x.compareTo(bbox[BOXRIGHT]) > 0) bbox[BOXRIGHT]=x.val; if (y.compareTo(bbox[BOXBOTTOM]) < 0) bbox[BOXBOTTOM] = y.val; else if (y.compareTo(bbox[BOXTOP]) > 0) bbox[BOXTOP] = y.val; } /** * MAES: Keeping with C's type (in)consistency, we also allow to input ints * -_- * * @param x * @param y */ public void AddToBox(int x, int y) { if (x < bbox[BOXLEFT]) bbox[BOXLEFT]=(x); if (x > bbox[BOXRIGHT]) bbox[BOXRIGHT]=(x); if (y < bbox[BOXBOTTOM]) bbox[BOXBOTTOM]=(y); if (y > bbox[BOXTOP]) bbox[BOXTOP]=(y); } /** * R_AddPointToBox Expand a given bbox so that it encloses a given point. * * @param x * @param y * @param box */ public static void AddPointToBox(int x, int y, fixed_t[] box) { if (x < box[BOXLEFT].val) box[BOXLEFT].set(x); if (x > box[BOXRIGHT].val) box[BOXRIGHT].set(x); if (y < box[BOXBOTTOM].val) box[BOXBOTTOM].set(y); if (y > box[BOXTOP].val) box[BOXTOP].set(y); } /** * R_AddPointToBox Expand this bbox so that it encloses a given point. * * @param x * @param y * @param box */ public void AddPointToBox(int x, int y) { if (x < bbox[BOXLEFT]) bbox[BOXLEFT]=x; if (x > bbox[BOXRIGHT]) bbox[BOXRIGHT]=x; if (y < bbox[BOXBOTTOM]) bbox[BOXBOTTOM]=y; if (y > bbox[BOXTOP]) bbox[BOXTOP]=y; } public int get(int BOXCOORDS){ return this.bbox[BOXCOORDS]; } public void set(int BOXCOORDS, int val){ this.bbox[BOXCOORDS]=val; } public static void ClearBox(int[] bbox) { bbox[BOXRIGHT]=(MININT); bbox[BOXTOP]=(MININT); bbox[BOXLEFT]=(MAXINT); bbox[BOXBOTTOM]=(MAXINT); } public static void AddToBox(int[] box, int x, int y) { if (x < box[BOXLEFT]) box[BOXLEFT]=x; if (x > box[BOXRIGHT]) box[BOXRIGHT]=x; if (y < box[BOXBOTTOM]) box[BOXBOTTOM]=y; if (y > box[BOXTOP]) box[BOXTOP]=y; } } package m; import data.Defines; // Emacs style mode select -*- C++ -*- //----------------------------------------------------------------------------- // // $Id: fixed_t.java,v 1.14 2011/10/25 19:52:13 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: // Fixed point implementation. // //----------------------------------------------------------------------------- // // Fixed point, 32bit as 16.16. // // Most functionality of C-based ports is preserved, EXCEPT that there's // no typedef of ints into fixed_t, and that there's no actual object fixed_t // type that is actually instantiated in the current codebase, for performance reasons. // There are still remnants of a full OO implementation that still do work, // and the usual FixedMul/FixedDiv etc. methods are still used throughout the codebase, // but operate on int operants (signed, 32-bit integers). public class fixed_t implements Comparable<fixed_t>{ public static final int FRACBITS = 16; public static final int FRACUNIT = (1<<FRACBITS); public static final int MAPFRACUNIT = FRACUNIT/Defines.TIC_MUL; public int val; public fixed_t(){ this.set(0); } public int get(){ return val; } public void set(int val){ this.val=val; } public void copy(fixed_t a){ this.set(a.get()); } public boolean equals(fixed_t a){ return (this.get()==a.get())?true:false; } public static boolean equals(fixed_t a, fixed_t b){ return (a.get()==b.get())?true:false; } public fixed_t(int val){ this.val=val; } public fixed_t(fixed_t x) { this.val=x.val; } public static final String rcsid = "$Id: fixed_t.java,v 1.14 2011/10/25 19:52:13 velktron Exp $"; /** Creates a new fixed_t object for the result a*b * * @param a * @param b * @return */ public static int FixedMul ( fixed_t a, fixed_t b ) { return (int)(((long) a.val * (long ) b.val) >>> FRACBITS); } public static int FixedMul ( int a, fixed_t b ) { return (int)(((long) a * (long ) b.val) >>> FRACBITS); } public static final int FixedMul ( int a, int b ) { return (int)(((long) a * (long ) b) >>> FRACBITS); } /** Returns result straight as an int.. * * @param a * @param b * @return */ public static int FixedMulInt ( fixed_t a, fixed_t b ) { return (int)(((long) a.val * (long ) b.val) >> FRACBITS); } /** In-place c=a*b * * @param a * @param b * @param c */ public final static void FixedMul ( fixed_t a, fixed_t b, fixed_t c) { c.set((int)(((long) a.val * (long ) b.val) >> FRACBITS)); } /** In-place this=this*a * * @param a * @param b * @param c */ public final void FixedMul ( fixed_t a) { this.set((int)(((long) a.val * (long ) this.val) >> FRACBITS)); } public final static int FixedDiv ( int a, int b ) { if ((Math.abs(a) >> 14) >= Math.abs(b)) { return (a^b) < 0 ? Integer.MIN_VALUE : Integer.MAX_VALUE; } else { long result; result = ((long) a << 16) / b; return (int) result; } } public final static int FixedDiv2 ( int a, int b ) { int c; c = (int)(((long)a<<16) / (long)b); return c; /* double c; c = ((double)a) / ((double)b) * FRACUNIT; if (c >= 2147483648.0 || c < -2147483648.0) throw new ArithmeticException("FixedDiv: divide by zero"); return (int)c;*/ } @Override public int compareTo(fixed_t o) { if (o.getClass()!=fixed_t.class) return -1; if (this.val==((fixed_t)(o)).val) return 0; if (this.val>((fixed_t)(o)).val) return 1; else return -1; } public int compareTo(int o) { if (this.val==o) return 0; if (this.val>o) return 1; else return -1; } public void add(fixed_t a){ this.val+=a.val; } public void sub(fixed_t a){ this.val-=a.val; } public void add(int a){ this.val+=a; } public void sub(int a){ this.val-=a; } /** a+b * * @param a * @param b * @return */ public static int add(fixed_t a,fixed_t b){ return a.val+b.val; } /** a-b * * @param a * @param b * @return */ public static int sub(fixed_t a,fixed_t b){ return a.val-b.val; } /** c=a+b * * @param c * @param a * @param b */ public static void add(fixed_t c, fixed_t a,fixed_t b){ c.val= a.val+b.val; } /** c=a-b * * @param c * @param a * @param b */ public static void sub(fixed_t c,fixed_t a,fixed_t b){ c.val= a.val-b.val; } /** Equals Zero * * @return */ public boolean isEZ() { return (this.val==0); } /** Greater than Zero * * @return */ public boolean isGZ() { return (this.val>0); } /** Less than Zero * * @return */ public boolean isLZ() { return (this.val<0); } // These are here to make easier handling all those methods in R // that return "1" or "0" based on one result. public int oneEZ(){ return (this.val==0)?1:0; } public int oneGZ(){ return (this.val>0)?1:0; } public int oneLZ(){ return (this.val<0)?1:0; } } package m; // Emacs style mode select -*- C++ -*- // ----------------------------------------------------------------------------- // // $Id: cheatseq_t.java,v 1.8 2011/11/01 23:47:50 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. // // $Log: cheatseq_t.java,v $ // Revision 1.8 2011/11/01 23:47:50 velktron // Added constructor method to start from unscrambled strings. // // Revision 1.7 2011/05/06 14:00:54 velktron // More of _D_'s changes committed. // // Revision 1.6 2010/12/14 00:53:32 velktron // Some input sanitizing. Far from perfect but heh... // // Revision 1.5 2010/08/25 00:50:59 velktron // Some more work... // // Revision 1.4 2010/07/21 11:41:47 velktron // Work on menus... // // Revision 1.3 2010/07/20 15:52:56 velktron // LOTS of changes, Automap almost complete. Use of fixed_t inside methods // severely limited. // // Revision 1.2 2010/07/03 23:24:13 velktron // Added a LOT of stuff, like Status bar code & objects. Now we're cooking with // gas! // // Revision 1.1 2010/06/30 08:58:50 velktron // Let's see if this stuff will finally commit.... // // // Most stuff is still being worked on. For a good place to start and get an // idea of what is being done, I suggest checking out the "testers" package. // // Revision 1.1 2010/06/29 11:07:34 velktron // Release often, release early they say... // // Commiting ALL stuff done so far. A lot of stuff is still broken/incomplete, // and there's still mixed C code in there. I suggest you load everything up in // Eclpise and see what gives from there. // // A good place to start is the testers/ directory, where you can get an idea of // how a few of the implemented stuff works. // // // DESCRIPTION: // Cheat sequence checking. // // ----------------------------------------------------------------------------- /** * Cheat sequence checking. MAES: all of this stuff used to be in cheat.h and * cheat.c, but seeing how all manipulation is done on "cheatseq_t" objects, it * makes more sense to move this functionality in here, and go OO all the way. * So away with the fugly static methods!!! */ public class cheatseq_t { // This holds the actual data (was a char*). public char[] sequence; // This is used as a pointer inside sequence. // Was a char*, but in Java it makes more sense to have it as an int. public int p; public cheatseq_t(char[] sequence, int p) { this.sequence = sequence; this.p = p; } public cheatseq_t(char[] sequence) { this.sequence = sequence; this.p = 0; } public cheatseq_t(String sequence, boolean prescrambled){ if (prescrambled){ this.sequence=sequence.toCharArray(); p=0; } else { this.sequence=scrambleString(sequence); p=0; } } /** * This was in cheat.c, but makes more sense to be used as an * initializer/constructor. */ public void GetParam(char[] buffer) { // char[] p; char c; int ptr = 0; // p = this.sequence; // Increments pointer until the sequence reaches its first internal "1" // ??? while (this.sequence[ptr++] != 1) ; int bptr = 0; // Now it copies the contents of this cheatseq_t into buffer...and nils // it??? do { c = this.sequence[ptr]; buffer[bptr++] = c; this.sequence[ptr++] = 0; } while ((c != 0) && (this.sequence[ptr] != 0xff)); if (this.sequence[ptr] == 0xff) buffer[bptr] = 0; } /** * Called in st_stuff module, which handles the input. Returns true if the * cheat was successful, false if failed. MAES: Let's make this boolean. * * @param cht * @param key * @return */ public boolean CheckCheat(cheatseq_t cht, char key) { boolean rc = false; if (cht.p < 0) cht.p = 0; // initialize if first time if (cht.p == 0) // This actually points inside "sequence" // *(cht->p++) = key; cht.sequence[cht.p++] = key; else if (cheat_xlate_table[(char) key] == cht.sequence[cht.p]) cht.p++; else // Failure: back to the beginning. cht.p = 0; if (cht.sequence[cht.p] == 1) cht.p++; else if (cht.sequence[cht.p] == 0xff) // end of sequence character { cht.p = 0; rc = true; } return rc; } /** * Called in st_stuff module, which handles the input. Returns true if the * cheat was successful, false if failed. MAES: Let's make this boolean. * * @param key * @return */ public boolean CheckCheat(char key) { boolean rc = false; if (this.p < 0) this.p = 0; // initialize if first time if (sequence[p] == 0) // This actually points inside "sequence" // *(cht->p++) = key; sequence[p++] = key; //p++; //_D_: this fixed cheat with parm problem (IDCLIP) else if (cheat_xlate_table[(char) key] == sequence[p]) p++; else // Failure: back to the beginning. p = 0; if (sequence[p] == 1) p++; else if (sequence[p] == 0xff) // end of sequence character { p = 0; rc = true; } return rc; } /** * Scrambles a character. 7 -> 0 6 -> 1 5 -> 5 4 -> 3 3 -> 4 2 -> 2 1 -> 6 0 * -> 7 * * @param a * @return */ public static char SCRAMBLE(char a) { return (char) ((((a) & 1) << 7) + (((a) & 2) << 5) + ((a) & 4) + (((a) & 8) << 1) + (((a) & 16) >>> 1) + ((a) & 32) + (((a) & 64) >>> 5) + (((a) & 128) >>> 7)); } public static char[] scrambleString(String s){ char[] tmp=new char[s.length()+1]; for (int i=0;i<s.length();i++){ tmp[i]=SCRAMBLE(s.charAt(i)); } tmp[s.length()]=0xff; return tmp; } /** * These should be common among all instances, unless for soooome reason you * need multiple different such tables. */ public static boolean firsttime = true; public static char[] cheat_xlate_table = new char[256]; static { if (firsttime) { firsttime = false; for (char i = 0; i < 256; i++) cheat_xlate_table[i] = SCRAMBLE(i); } } } package m; import doom.DoomStatus; import doom.IDoomGame; import hu.HU; import i.DoomStatusAware; import i.IDoomSystem; import rr.RendererState; import s.IDoomSound; import timing.ITicker; import v.DoomVideoRenderer; import v.IVideoScaleAware; import w.IWadLoader; public abstract class AbstractDoomMenu implements IDoomMenu { ////////////////////// CONTEXT /////////////////// DoomStatus DM; IDoomGame DG; IWadLoader W; DoomVideoRenderer V; HU HU; RendererState R; IDoomSystem I; IDoomSound S; ITicker TICK; @Override public void updateStatus(DoomStatus DS) { this.DM=DS.DM; this.DG=DS.DG; this.V=DM.V; this.W=DM.W; this.HU=DM.HU; this.I=DM.I; this.S=DM.S; this.R=(RendererState) DM.R; this.TICK=DM.TICK; } } package m; import i.DoomSystem; import java.awt.image.BufferedImage; import java.awt.image.DataBufferByte; import java.awt.image.DataBufferInt; import java.awt.image.DataBufferUShort; import java.awt.image.IndexColorModel; import java.io.BufferedInputStream; import java.io.DataOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.nio.ByteBuffer; import java.util.Arrays; import javax.imageio.ImageIO; import w.IWritableDoomObject; // Emacs style mode select -*- C++ -*- //----------------------------------------------------------------------------- // // $Id: MenuMisc.java,v 1.29 2012/09/24 17:16:22 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: // Main loop menu stuff. // Default Config File. // PCX Screenshots. // //----------------------------------------------------------------------------- public abstract class MenuMisc{ public static final String rcsid = "$Id: MenuMisc.java,v 1.29 2012/09/24 17:16:22 velktron Exp $"; // // SCREEN SHOTS // public static boolean WriteFile(String name, byte[] source, int length) { OutputStream handle; try { handle = new FileOutputStream(name); handle.write(source, 0, length); handle.close(); } catch (Exception e) { DoomSystem.MiscError("Couldn't write file %s (%s)", name, e.getMessage()); return false; } return true; } public static boolean WriteFile(String name, IWritableDoomObject source) { DataOutputStream handle; try { handle = new DataOutputStream(new FileOutputStream(name)); source.write(handle); handle.close(); } catch (Exception e) { DoomSystem.MiscError("Couldn't write file %s (%s)", name, e.getMessage()); return false; } return true; } /** M_ReadFile * This version returns a variable-size ByteBuffer, so * we don't need to know a-priori how much stuff to read. * */ public static ByteBuffer ReadFile(String name) { BufferedInputStream handle; int length; // struct stat fileinfo; ByteBuffer buf; try { handle = new BufferedInputStream(new FileInputStream(name)); length = (int) handle.available(); buf = ByteBuffer.allocate(length); handle.read(buf.array()); handle.close(); } catch (Exception e) { DoomSystem.MiscError("Couldn't read file %s (%s)", name, e.getMessage()); return null; } return buf; } /** M_ReadFile */ public static int ReadFile(String name, byte[] buffer) { BufferedInputStream handle; int count, length; // struct stat fileinfo; byte[] buf; try { handle = new BufferedInputStream(new FileInputStream(name)); length = (int) handle.available(); buf = new byte[length]; count = handle.read(buf); handle.close(); if (count < length) throw new Exception("Read only " + count + " bytes out of " + length); } catch (Exception e) { DoomSystem.MiscError("Couldn't read file %s (%s)", name, e.getMessage()); return -1; } System.arraycopy(buf, 0, buffer, 0, Math.min(count,buffer.length)); return length; } // // WritePCXfile // public static void WritePCXfile ( String filename, byte[] data, int width, int height, byte[] palette ) { int length; pcx_t pcx; byte[] pack; pcx = new pcx_t(); pack=new byte[width*height*2]; // allocate that much data, just in case. pcx.manufacturer = 0x0a; // PCX id pcx.version = 5; // 256 color pcx.encoding = 1; // uncompressed pcx.bits_per_pixel = 8; // 256 color pcx.xmin = 0; pcx.ymin = 0; pcx.xmax = (char) (width-1); pcx.ymax = (char) (height-1); pcx.hres = (char) width; pcx.vres = (char) height; // memset (pcx->palette,0,sizeof(pcx->palette)); pcx.color_planes = 1; // chunky image pcx.bytes_per_line = (char) width; pcx.palette_type = 2; // not a grey scale //memset (pcx->filler,0,sizeof(pcx->filler)); // pack the image //pack = &pcx->data; int p_pack=0; for (int i=0 ; i<width*height ; i++) { if ( (data[i] & 0xc0) != 0xc0) pack[p_pack++] = data[i]; else { pack[p_pack++] = (byte) 0xc1; pack[p_pack++] = data[i]; } } // write the palette pack[p_pack++] = 0x0c; // palette ID byte for (int i=0 ; i<768 ; i++) pack[p_pack++] = palette[i]; // write output file length = p_pack; pcx.data=Arrays.copyOf(pack, length); DataOutputStream f=null; try { f = new DataOutputStream(new FileOutputStream(filename)); } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } try { //f.setLength(0); pcx.write(f); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } public abstract boolean getShowMessages(); public abstract void setShowMessages(boolean val); public static void WritePNGfile(String imagename, short[] linear, int width, int height) { BufferedImage buf=new BufferedImage(width,height,BufferedImage.TYPE_USHORT_555_RGB); DataBufferUShort sh=(DataBufferUShort) buf.getRaster().getDataBuffer(); short[] shd=sh.getData(); System.arraycopy(linear,0,shd,0,Math.min(linear.length,shd.length)); try { ImageIO.write(buf, "PNG",new File(imagename)); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } // TODO Auto-generated method stub } public static void WritePNGfile(String imagename, int[] linear, int width, int height) { BufferedImage buf=new BufferedImage(width,height,BufferedImage.TYPE_INT_ARGB); DataBufferInt sh=(DataBufferInt) buf.getRaster().getDataBuffer(); int[] shd=sh.getData(); System.arraycopy(linear,0,shd,0,Math.min(linear.length,shd.length)); try { ImageIO.write(buf, "PNG",new File(imagename)); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } // TODO Auto-generated method stub } public static void WritePNGfile(String imagename, byte[] linear, int width, int height,IndexColorModel icm) { BufferedImage buf=new BufferedImage(width,height,BufferedImage.TYPE_BYTE_INDEXED,icm); DataBufferByte sh=(DataBufferByte) buf.getRaster().getDataBuffer(); byte[] shd=sh.getData(); System.arraycopy(linear,0,shd,0,Math.min(linear.length,shd.length)); try { ImageIO.write(buf, "PNG",new File(imagename)); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } // TODO Auto-generated method stub } } // $Log: MenuMisc.java,v $ // Revision 1.29 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.28.2.4 2012/09/24 16:57:43 velktron // Addressed generics warnings. // // Revision 1.28.2.3 2012/09/17 15:58:58 velktron // Defaults loading & handling moved out to variables management subsystem // // Revision 1.28.2.2 2011/11/18 21:37:59 velktron // Saves PNGs now. // // Revision 1.28.2.1 2011/11/14 00:27:11 velktron // A barely functional HiColor branch. Most stuff broken. DO NOT USE // // Revision 1.28 2011/10/25 19:52:03 velktron // Using buffered I/O when possible // // Revision 1.27 2011/10/24 02:11:27 velktron // Stream compliancy // // Revision 1.26 2011/07/30 22:04:30 velktron // Removed unused imports (including one that would cause problems compiling with OpenJDK). // // Revision 1.25 2011/07/15 13:53:52 velktron // Implemented WritePCXFile, at last. // // Revision 1.24 2011/06/03 16:37:09 velktron // Readfile will only read at most as much as the buffer allows. // // Revision 1.23 2011/05/31 13:33:54 velktron // -verbosity // // Revision 1.22 2011/05/31 09:57:45 velktron // Fixed broken parsing of unspaced strings. // It's never fun having to come up with your own function for string manipulation! // // Revision 1.21 2011/05/30 15:46:50 velktron // AbstractDoomMenu implemented. // // Revision 1.20 2011/05/26 17:54:16 velktron // Removed some Menu verbosity, better defaults functionality. // // Revision 1.19 2011/05/26 13:39:15 velktron // Now using ICommandLineManager // // Revision 1.18 2011/05/24 17:46:03 velktron // Added vanilla default.cfg loading. // package m; import java.io.BufferedInputStream; import java.io.BufferedReader; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.PrintStream; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map.Entry; import java.util.Set; import java.util.StringTokenizer; import doom.ICommandLineManager; import utils.C2JUtils; /** Variables manager registers and retrieves variables (or "settings") * For the purposes of Doom, three types will suffice: * boolean, integer (maybe float?), and string. * * Variables can be registered at startup, through *.cfg files, * and through a (future) console. They can also be deleted * and updated. * * The variables manager just has the job of managing them. * Applying/making use of the variables themselves is the job * of the code that specifically uses them. * * Proposed way of proceeding: making an "IUseVariables" interface, * which is module-specific, and is called whenever new settings must * be applied. * * E.g. stuff is read from config.cfg, and the the updateSettings() * method of e.g. DoomMain is called, applying whatever changes need * to be done. Same thing with menus. * * TODO: a similar system was being planned for the CommandLine manager, * but never implemented, wtf.... fix now? * */ public class VarsManager implements IVariablesManager { private final HashMap<String,DoomSetting> settings; private final ICommandLineManager CLM; public VarsManager(ICommandLineManager CLM){ this.settings=new HashMap<String,DoomSetting>(); this.CLM=CLM; } @Override public DoomSetting getSetting(String name){ DoomSetting tmp=settings.get(name); if (tmp!=null) { return tmp; } // Good question...what to do here? return DoomSetting.NULL_SETTING; } @Override public DoomSetting getSetting(Settings name){ DoomSetting tmp=settings.get(name.name()); if (tmp!=null) { return tmp; } // Good question...what to do here? return DoomSetting.NULL_SETTING; } @Override public boolean isSettingLiteral(String name,String value){ return getSetting(name).getString().equalsIgnoreCase(value); } /** Creates a new setting, overriding any existing ones */ @Override public void putSetting(String name,Object value,boolean persist){ DoomSetting tmp=new DoomSetting(name,value.toString(),persist); settings.put(name,tmp); } @Override public void putSetting(Settings name,Object value){ DoomSetting tmp=new DoomSetting(name.name(),value.toString(),true); settings.put(name.name(),tmp); } @Override public void putSetting(String name,Object value){ DoomSetting tmp=new DoomSetting(name,value.toString(),true); settings.put(name,tmp); } // // M_LoadDefaults // @Override public void LoadDefaults (String defaultfile) { int i; BufferedReader in; // Set everything to base values. These are persistent. int numdefaults = Settings.values().length; for (i=0 ; i<numdefaults ; i++){ putSetting(Settings.values()[i].name(),Settings.values()[i].value,true); } try { // read the file in, overriding any set defaults in = new BufferedReader(new InputStreamReader(new BufferedInputStream(new FileInputStream(defaultfile)))); if (in!=null) { String name = null, value = null; String s; // Let's make this the sane way...read a string. while (( s=in.readLine())!=null) { StringTokenizer tk=new StringTokenizer(s); // They should be exactly two. int tokens=tk.countTokens(); if (tokens==2){ name=tk.nextToken(); value=tk.nextToken(); // TODO default value? if (value==null) value="0"; } else if (tokens>2){ // Not clear what to do in case of multiple values on // the same line. Either introduce multi-value vars (ugh...) // or "join" the tokens, assuming that they are unquoted strings. name=tk.nextToken(); value = C2JUtils.unquote(s,'"'); if (value==null) continue; } // All var names should be lower case. name=name.toLowerCase(); //System.out.printf("NAME: %s VALUE: %s\n",name,value); // Everything read from the file should be marked // as "persistent". There are no "unknown" settings. // Unusable, maybe. settings.put(name, new DoomSetting(name,value, true)); } // end-while in.close(); } // not null }catch (IOException e){ // This won't destroy successfully read values, though. System.err.printf("I just can't read the settings file %s, will use defaults.\n",defaultfile); } } @Override public void applySetting(IUseVariables setme, String name, String value) { // TODO Auto-generated method stub } // // M_SaveDefaults // @Override public void SaveDefaults (String defaultfile) { OutputStream f; PrintStream ps; try { f = new FileOutputStream(defaultfile); } catch (FileNotFoundException e) { // can't write the file, but don't complain return; } ps=new PrintStream(f); Set<Entry<String,DoomSetting>> stuff=settings.entrySet(); List<DoomSetting> save=new ArrayList<DoomSetting>(); for (Entry<String,DoomSetting> entry :stuff) { DoomSetting set=entry.getValue(); // Save only stuff marked as "persistent" if (set.getPersist()){ save.add(set); } } // Nicely sorted alphabetically :-) Collections.sort(save); for (DoomSetting var :save){ if (C2JUtils.flags(var.getTypeFlag(),DoomSetting.BOOLEAN)){ ps.printf("%s\t\t%s\n",var.getName(),var.getBoolean()); continue; } if (C2JUtils.flags(var.getTypeFlag(),DoomSetting.CHAR)){ ps.printf("%s\t\t\'%c\'\n",var.getName(),var.getChar()); continue; } if (C2JUtils.flags(var.getTypeFlag(),DoomSetting.INTEGER)){ ps.printf("%s\t\t%s\n",var.getName(),var.getString()); continue; } ps.printf("%s\t\t\"%s\"\n",var.getName(),var.getString()); } try { f.close();} catch (IOException e) { // Well duh.... return; } } public String getDefaultFile(){ // check for a custom default file int i = CLM.CheckParm("-config"); if ((i>0) && i<CLM.getArgc()-1) { return CLM.getArgv(i+1); //System.out.printf(" default file: %s\n",defaultfile); } else return Settings.basedefault; } } package m; import java.io.DataOutputStream; import java.io.IOException; import w.IWritableDoomObject; /** Yeah, this is actually a PCX header implementation, and Mocha Doom * saved PCX screenshots. Implemented it back just to shot that it can be * done (will switch to PNG ASAP though). * * @author Maes * */ public class pcx_t implements IWritableDoomObject{ // // SCREEN SHOTS // // char -> byte Bytes. /** manufacturer byte, must be 10 decimal */ public byte manufacturer; /** PCX version number */ byte version; /** run length encoding byte, must be 1 */ byte encoding; /** number of bits per pixel per bit plane */ byte bits_per_pixel; /** image limits in pixels: Xmin, Ymin, Xmax, Ymax */ public char xmin,ymin,xmax,ymax; /** horizontal dots per inch when printed (unreliable) */ char hres; /** vertical dots per inch when printed (unreliable) */ char vres; /** 16-color palette (16 RGB triples between 0-255) * UNUSED in Doom. */ byte[] palette=new byte[48]; /** reserved, must be zero */ byte reserved; /** number of bit planes */ byte color_planes; /** video memory bytes per image row */ char bytes_per_line; /** 16-color palette interpretation (unreliable) 0=color/b&w 1=grayscale */ char palette_type; // Seems off-spec. However it's left all zeroed out. byte[] filler=new byte[58]; //unsigned char data; byte[] data; @Override public void write(DataOutputStream f) throws IOException { // char -> byte Bytes. f.writeByte(manufacturer); f.writeByte(version); f.writeByte(encoding); f.writeByte(bits_per_pixel); // unsigned short -> char f.writeChar(Swap.SHORT(xmin)); f.writeChar(Swap.SHORT(ymin)); f.writeChar(Swap.SHORT(xmax)); f.writeChar(Swap.SHORT(ymax)); f.writeChar(Swap.SHORT(hres)); f.writeChar(Swap.SHORT(vres)); f.write(palette); f.writeByte(reserved); f.writeByte(color_planes); // unsigned short -> char f.writeChar(Swap.SHORT(bytes_per_line)); f.writeChar(Swap.SHORT(palette_type)); f.write(filler); //unsigned char data; // unbounded f.write(data); } } ; package m; /** Some utilities for switching between floating and signed 16.16 fixed-point at will. * They use direct bit manipulation with little -if any- looping. * * The methods can probably be generalized but not a priority for now. * They do not handle Infinities, NaNs and unnormalized numbers. * * @author Maes * */ public class FixedFloat { // Various bit masks for IEEE-754 floating point public static final int MANTISSA_32=0x007FFFFF; public static final int EXP_32=0x7F800000; public static final int IMPLICIT_32=0x00800000; public static final int SIGN_32=0x80000000; public static final int NONSIGN_32=0x7FFFFFFF; public static final long SIGN_64=0x8000000000000000L; public static final long EXP_64=0x7FF0000000000000L; public static final long IMPLICIT_64=0x0010000000000000L; public static final long MANTISSA_64=0x000fffffffffffffL; public static float toFloat(int fixed){ if (fixed==0) return (float)(0.0); // Remember sign. int sign=fixed&SIGN_32; if (fixed<0) fixed=-fixed; int exp=findShift(fixed); // First shift to left to "cancel" bits "above" the first. int mantissa=(fixed<<(exp+2))>>>9; int result=sign|(((14-exp)+127)<<23)|mantissa; /*if (fixed<0) System.out.println(Integer.toBinaryString(fixed) +"\n"+ Integer.toBinaryString(-fixed) +"\n"+ Integer.toBinaryString(result));*/ return Float.intBitsToFloat(result); } private static int findShift(int fixed){ // only non-sign bits. fixed&=NONSIGN_32; // We assume that the MSb after the sign is set. int shift=30; while((shift>=0)&&(fixed>>>shift)==0) // It's not, apparently shift--; // Positions 0-15 are fractional, anything above 15 is integer. // Return two's complement shift. return (30-shift); } public static double toDouble(int fixed){ // Remember sign. long fx=fixed; fx<<=32; long sign=(long)fx&SIGN_64; if (fixed<0) { fixed=-fixed; fx=-fx; } long exp=findShift(fixed); // First shift to left to "swallow" sign and implicit 1. long bits=(fx<<(exp+2))>>>12; long result=sign|(((14-exp)+1023)<<52)|bits; return Double.longBitsToDouble(result); } public static int toFixed(float fl){ // Get the raw bits. int flbits=Float.floatToRawIntBits(fl); // Remember sign. int sign=flbits&SIGN_32; // Join together: the implcit 1 and the mantissa bits. // We now have the "denormalized" value. int denorm=IMPLICIT_32|(flbits&MANTISSA_32); // Get exponent...acceptable values are (-15 ~ 15), else wrap around (use only sign and lowest 4 bits). int exp=(((flbits&EXP_32)>>23)-127)&0x8000000F; /* Remember, leftmost "1" will be at position 23. * So for an exponent of 0, we must shift to position 16. * For positive exponents in general, we must shift -7 + exp. * and for one of 15, to position 30, plus the sign. * While there is space for all bits, we can't keep them all, * as some (well, many)numbers can't be represented in fixed point. * */ int result; if ((exp-7)>=0) result=sign|(denorm<<(exp-7)); else result=sign|(denorm>>>(7-exp)); return result; } public static int toFixed(double fl){ // Get the raw bits. long flbits=Double.doubleToRawLongBits(fl); // Remember sign. int sign=(int)((flbits&SIGN_64)>>32); // Join together: the implcit 1 and the mantissa bits. // We now have the "denormalized" value. long denorm=IMPLICIT_64|(flbits&MANTISSA_64); //System.out.println("Denorm"+Integer.toBinaryString(denorm)); // Get exponent...acceptable values are (-15 ~ 15), else wrap around (use only sign and lowest 4 bits). int exp=(int)(((flbits&EXP_64)>>52)-1023)&0x8000000F; /* Remember, leftmost "1" will be at position 53. * So for an exponent of 0, we must shift to position 16. * For positive exponents in general, we must shift -37 + exp. * and for one of 15, to position 30, plus the sign. * While there is space for all bits, we can't keep them all, * as some (well, many)numbers can't be represented in fixed point. * */ int result; if ((exp-36)>=0) result=(int) (sign|(denorm<<(exp-36))); else result=(int) (sign|(denorm>>>(36-exp))); //int result=sign|(IMPLICIT_32|(mantissa<<(exp-127)))<<8; return result; } } package m; import static g.Keys.*; import static doom.englsh.*; /** An anumeration with the most basic default Doom settings their default * values, used if nothing else is available. They are applied first thing, * and then updated from the .cfg file. * */ public enum Settings { mouse_sensitivity("5"), sfx_volume("8"), music_volume("8"), show_messages("1"), alwaysrun("1"), key_right(KEY_RIGHTARROW), key_left(KEY_LEFTARROW), key_up('w'), key_down('s'), key_strafeleft('a'), key_straferight('d'), key_fire(KEY_CTRL), key_use(' '), key_strafe(KEY_ALT), key_speed(KEY_SHIFT), use_mouse(1), mouseb_fire(0), mouseb_strafe(1), mouseb_forward(2), use_joystick( 0), joyb_fire(0), joyb_strafe(1), joyb_use(3), joyb_speed(2), screenblocks(10), detaillevel(0), snd_channels(6), usegamma(0), mb_used(2), chatmacro0(HUSTR_CHATMACRO0 ), chatmacro1(HUSTR_CHATMACRO1 ), chatmacro2( HUSTR_CHATMACRO2 ), chatmacro3(HUSTR_CHATMACRO3 ), chatmacro4( HUSTR_CHATMACRO4 ), chatmacro5( HUSTR_CHATMACRO5 ), chatmacro6(HUSTR_CHATMACRO6 ), chatmacro7( HUSTR_CHATMACRO7 ), chatmacro8(HUSTR_CHATMACRO8 ), chatmacro9( HUSTR_CHATMACRO9 ); private Settings(String defaultval){ this.value=defaultval; } private Settings(int defaultval){ this.value=Integer.toString(defaultval); } public String value; /** Normally this is default.cfg, might be .doomrc on lunix??? */ public static String basedefault="default.cfg"; } package m; import java.util.Random; import data.mobjtype_t; import doom.think_t; // Emacs style mode select -*- C++ -*- //----------------------------------------------------------------------------- // // $Id: JavaRandom.java,v 1.3 2013/06/03 11:00:03 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: // Random number LUT using java.util.Random // Don't expect vanilla demo compatibility with THIS! // //----------------------------------------------------------------------------- public class JavaRandom implements IRandom{ protected int rndindex = 0; protected int prndindex = 0; // Which one is deterministic? public int P_Random () { rndindex++; return (0xFF&r.nextInt()); } public int M_Random () { prndindex++; return (0xFF&m.nextInt()); } public void ClearRandom () { rndindex=prndindex=0; r.setSeed(666); } public JavaRandom(){ r=new Random(666); m=new Random(666); this.ClearRandom(); } public int getIndex(){ return rndindex; } private Random r; private Random m; @Override public int P_Random(int caller) { // DUMMY return P_Random(); } @Override public int P_Random(String message) { // DUMMY return P_Random(); } @Override public int P_Random(think_t caller, int sequence) { // DUMMY return P_Random(); } @Override public int P_Random(think_t caller, mobjtype_t type, int sequence) { // DUMMY return P_Random(); } } //$Log: JavaRandom.java,v $ //Revision 1.3 2013/06/03 11:00:03 velktron //Implements interface without logging. // //Revision 1.2 2011/07/27 20:47:46 velktron //Proper commenting, cleanup. // //Revision 1.1 2011/05/29 22:15:32 velktron //Introduced IRandom interface. package m; public class menuitem_t { public menuitem_t(int status, String name, MenuRoutine routine, char alphaKey) { this.status=status; this.name=name; this.routine= routine; this.alphaKey=alphaKey; } public menuitem_t(int status, String name, MenuRoutine routine) { this.status=status; this.name=name; this.routine= routine; } /** 0 = no cursor here, 1 = ok, 2 = arrows ok */ int status; String name; // choice = menu item #. // if status = 2, // choice=0:leftarrow,1:rightarrow // MAES: OK... to probably we need some sort of "MenuRoutine" class for this one. // void (*routine)(int choice); MenuRoutine routine; /** hotkey in menu */ char alphaKey; } package m; /** menu_t required a function pointer to a (routine)() that drew stuff. * So any class implementing them will implement this interface, and * we can have a single class type for all of them. * * @author Maes * */ public interface DrawRoutine { public void invoke(); } package m; import v.IVideoScaleAware; import i.DoomStatusAware; import doom.DoomStatus; import doom.event_t; // Emacs style mode select -*- C++ -*- // ----------------------------------------------------------------------------- // // $Id: IDoomMenu.java,v 1.5 2011/09/29 15:16:23 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: // Menu widget stuff, episode selection and such. // // ----------------------------------------------------------------------------- /** * */ public interface IDoomMenu extends IVideoScaleAware, DoomStatusAware{ // // MENUS // /** * Called by main loop, saves config file and calls I_Quit when user exits. * Even when the menu is not displayed, this can resize the view and change * game parameters. Does all the real work of the menu interaction. */ public boolean Responder(event_t ev); /** * Called by main loop, only used for menu (skull cursor) animation. */ public void Ticker(); /** * Called by main loop, draws the menus directly into the screen buffer. */ public void Drawer(); /** * Called by D_DoomMain, loads the config file. */ public void Init(); /** * Called by intro code to force menu up upon a keypress, does nothing if * menu is already up. */ public void StartControlPanel(); public boolean getShowMessages(); public void setShowMessages(boolean val); public int getScreenBlocks(); public void setScreenBlocks(int val); public int getDetailLevel(); void ClearMenus(); } package m; /** General form for a classic, Doom-style menu with a bunch of * items and a drawing routine (menu_t's don't have action callbacks * proper, though). * * @author Maes * */ public class menu_t { public menu_t(int numitems, menu_t prev, menuitem_t[] items, DrawRoutine drawroutine, int x, int y, int lastOn) { this.numitems=numitems; this.prevMenu=prev; this.menuitems=items; this.routine=drawroutine; this.x=x; this.y=y; this.lastOn=lastOn; } /** # of menu items */ public int numitems; /** previous menu */ public menu_t prevMenu; /** menu items */ public menuitem_t[] menuitems; /** draw routine */ public DrawRoutine routine; /** x,y of menu */ public int x,y; /** last item user was on in menu */ public int lastOn; } package m; import data.mobjtype_t; import doom.think_t; // Emacs style mode select -*- C++ -*- //----------------------------------------------------------------------------- // // $Id: DoomRandom.java,v 1.4 2013/06/04 11:29:25 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. // // $Log: DoomRandom.java,v $ // Revision 1.4 2013/06/04 11:29:25 velktron // Dummy implementations // // Revision 1.3 2013/06/03 10:53:29 velktron // Implements the new IRandom. // // Revision 1.2.10.3 2013/01/09 19:38:26 velktron // Printing arbitrary messages // // Revision 1.2.10.2 2012/11/20 15:59:20 velktron // More tooling functions. // // Revision 1.2.10.1 2012/11/19 22:11:36 velktron // Added demo sync tooling. // // Revision 1.2 2011/05/30 02:24:30 velktron // *** empty log message *** // // Revision 1.1 2011/05/29 22:15:32 velktron // Introduced IRandom interface. // // Revision 1.4 2010/09/22 16:40:02 velktron // MASSIVE changes in the status passing model. // DoomMain and DoomGame unified. // Doomstat merged into DoomMain (now status and game functions are one). // // Most of DoomMain implemented. Possible to attempt a "classic type" start but will stop when reading sprites. // // Revision 1.3 2010/09/10 17:35:49 velktron // DoomGame, Menu, renderers // // Revision 1.2 2010/07/06 16:32:38 velktron // Threw some work in WI, now EndLevel. YEAH THERE'S GONNA BE A SEPARATE EndLevel OBJECT THAT'S HOW PIMP THE PROJECT IS!!!!11!!! // // Revision 1.1 2010/06/30 08:58:50 velktron // Let's see if this stuff will finally commit.... // // // Most stuff is still being worked on. For a good place to start and get an idea of what is being done, I suggest checking out the "testers" package. // // Revision 1.1 2010/06/29 11:07:34 velktron // Release often, release early they say... // // Commiting ALL stuff done so far. A lot of stuff is still broken/incomplete, and there's still mixed C code in there. I suggest you load everything up in Eclpise and see what gives from there. // // A good place to start is the testers/ directory, where you can get an idea of how a few of the implemented stuff works. // // // DESCRIPTION: // Random number LUT. // //----------------------------------------------------------------------------- public class DoomRandom implements IRandom{ // // M_Random // Returns a 0-255 number. Made into shorts for Java, because of their nature. // public static short rndtable[] = { 0, 8, 109, 220, 222, 241, 149, 107, 75, 248, 254, 140, 16, 66 , 74, 21, 211, 47, 80, 242, 154, 27, 205, 128, 161, 89, 77, 36 , 95, 110, 85, 48, 212, 140, 211, 249, 22, 79, 200, 50, 28, 188 , 52, 140, 202, 120, 68, 145, 62, 70, 184, 190, 91, 197, 152, 224 , 149, 104, 25, 178, 252, 182, 202, 182, 141, 197, 4, 81, 181, 242 , 145, 42, 39, 227, 156, 198, 225, 193, 219, 93, 122, 175, 249, 0 , 175, 143, 70, 239, 46, 246, 163, 53, 163, 109, 168, 135, 2, 235 , 25, 92, 20, 145, 138, 77, 69, 166, 78, 176, 173, 212, 166, 113 , 94, 161, 41, 50, 239, 49, 111, 164, 70, 60, 2, 37, 171, 75 , 136, 156, 11, 56, 42, 146, 138, 229, 73, 146, 77, 61, 98, 196 , 135, 106, 63, 197, 195, 86, 96, 203, 113, 101, 170, 247, 181, 113 , 80, 250, 108, 7, 255, 237, 129, 226, 79, 107, 112, 166, 103, 241 , 24, 223, 239, 120, 198, 58, 60, 82, 128, 3, 184, 66, 143, 224 , 145, 224, 81, 206, 163, 45, 63, 90, 168, 114, 59, 33, 159, 95 , 28, 139, 123, 98, 125, 196, 15, 70, 194, 253, 54, 14, 109, 226 , 71, 17, 161, 93, 186, 87, 244, 138, 20, 52, 123, 251, 26, 36 , 17, 46, 52, 231, 232, 76, 31, 221, 84, 37, 216, 165, 212, 106 , 197, 242, 98, 43, 39, 175, 254, 145, 190, 84, 118, 222, 187, 136 , 120, 163, 236, 249 }; protected int rndindex = 0; protected int prndindex = 0; // Which one is deterministic? public int P_Random () { prndindex = (prndindex+1)&0xff; return rndtable[prndindex]; } /** [Maes] I'd rather dispatch the call here, than making IRandom aware of * DoomStatus. Replace RND.P_Random calls with DM.P_Random(callerid) etc. * * Fixme: this could be made into a proper enum * * @param caller */ public int P_Random(int caller) { int value = P_Random(); SLY.sync("PR #%d [%d]=%d\n",caller,prndindex,value); return value; } public int P_Random(String message) { int value = P_Random(); SLY.sync("PR %s [%d]=%d\n", message, prndindex, value); return value; } public int P_Random(think_t caller, int sequence) { int value = P_Random(); /* SLY.sync("PR #%d %s_%d [%d]=%d\n", caller.ordinal(),caller,sequence, prndindex, value);*/ return value; } public int P_Random(think_t caller, mobjtype_t type,int sequence) { int value = P_Random(); /* SLY.sync("PR #%d %s_%d %s [%d]=%d\n", caller.ordinal(),caller,sequence, type, prndindex, value);*/ return value; } public int M_Random () { rndindex = (rndindex+1)&0xff; return rndtable[rndindex]; } public void ClearRandom () { rndindex = prndindex = 0; } public DoomRandom(){ SLY=null; } public int getIndex(){ return prndindex; } public DoomRandom(ISyncLogger SLY){ this.SLY=SLY; } private final ISyncLogger SLY; } package m; // Emacs style mode select -*- C++ -*- //----------------------------------------------------------------------------- // // $Id: Swap.java,v 1.2 2011/07/27 20:48:20 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: // Endianess handling, swapping 16bit and 32bit. // It's role is much less important than in C-based ports (because of stream // built-in endianness settings), but they are still used occasionally. // //----------------------------------------------------------------------------- public final class Swap{ // Swap 16bit, that is, MSB and LSB byte. public final static short SHORT(short x) { // No masking with 0xFF should be necessary. // MAES: necessary with java due to sign trailing. return (short) ((short) ((x>>>8)&0xFF) | (x<<8)); } //Swap 16bit, that is, MSB and LSB byte. public final static short SHORT(char x) { // No masking with 0xFF should be necessary. // MAES: necessary with java due to sign trailing. return (short) ((short) ((x>>>8)&0xFF) | (x<<8)); } //Swap 16bit, that is, MSB and LSB byte. public final static char USHORT(char x) { // No masking with 0xFF should be necessary. // MAES: necessary with java due to sign trailing. return (char) ((char) ((x>>>8)&0xFF) | (x<<8)); } // Swapping 32bit. // Maes: the "long" here is really 32-bit. public final static int LONG( int x) { return (x>>>24) | ((x>>>8) & 0xff00) | ((x<<8) & 0xff0000) | (x<<24); } } //$Log: Swap.java,v $ //Revision 1.2 2011/07/27 20:48:20 velktron //Proper commenting, cleanup. // //Revision 1.1 2010/06/30 08:58:50 velktron //Let's see if this stuff will finally commit.... package m; /** menuitem_t required a function pointer to a (routine)(int choice) * So any class implementing them will implement this interface, and * we can have a single class type for all of them. * * @author Velktron * */ public interface MenuRoutine { public void invoke(int choice); } package m; import utils.C2JUtils; /** A "Doom setting". Based on current experience, it could * represent an integer value, a string, or a boolean value. * * Therefore, every setting can be interpreted as any of the above, * based on some rules. Strings that can be interpreted as parseable * numbers are obvious, and numbers can also be interpreted as strings. * Strings that can't be interpreted as numbers will return "0" as a default * value. * * A numerical value of 1 means "true", any other value is "false". * A string representing the (case insensitive) value "true" will * be interpreted as a true boolean, false otherwise. * * @author velktron * * */ public class DoomSetting implements Comparable<DoomSetting> { public static final int BOOLEAN=1; public static final int CHAR=2; public static final int DOUBLE=4; public static final int INTEGER=8; public static final int STRING=16; private String name; private int typeflag; // Every setting can be readily interpreted as any of these private int int_val; private long long_val; private char char_val; private double double_val; private boolean boolean_val; private String string_val; /** Should be saved to file */ private boolean persist; public DoomSetting(String name, String value, boolean persist){ this.name=name; this.typeflag=STRING; this.updateValue(value); this.persist=persist; } public String getName(){ return name; } public int getInteger(){ return int_val; } public long getLong(){ return long_val; } public char getChar(){ return (char)int_val; } public String getString(){ return string_val; } public double getDouble(){ return double_val; } public boolean getBoolean(){ return boolean_val; } public boolean getPersist(){ return persist; } public int getTypeFlag(){ return typeflag; } /** All the gory disambiguation work should go here. * * @param value */ public void updateValue(String value){ boolean quoted=false; if (value.length()>2) if (quoted=C2JUtils.isQuoted(value,'"' )) value=C2JUtils.unquote(value, '"'); else if (quoted=C2JUtils.isQuoted(value,'\'' )) value=C2JUtils.unquote(value, '\''); // String value always available this.string_val=value; // If quoted and sensibly ranged, it gets priority as a "character" if (quoted && value.length()==1 && value.charAt(0)>=0 && value.charAt(0)<255){ char_val=Character.toLowerCase(value.charAt(0)); int_val=char_val; long_val=char_val; double_val=char_val; typeflag|=CHAR; return; } // Not a character, try all other stuff try { this.int_val=Integer.parseInt(value); typeflag|=INTEGER; } catch (NumberFormatException e) { // No nookie this.int_val=-1; } try { this.long_val=Long.parseLong(value); } catch (NumberFormatException e) { try { // Try decoding it as hex, octal, whatever. this.long_val=Long.decode(value); typeflag|=INTEGER; } catch (NumberFormatException h){ // If even THAT fails, then no nookie. this.long_val=-1; } } try { this.double_val=Double.parseDouble(value); typeflag|=DOUBLE; } catch (NumberFormatException e) { // No nookie this.double_val=Double.NaN; } // Use long value to "trump" smaller ones int_val=(int)long_val; char_val=(char)int_val; // Boolean has a few more options; // Only mark something explicitly as boolean if the string reads // actually "true" or "false". Numbers such as 0 and 1 might still get // interpreted as booleans, but that shouldn't trump the entire number, // otherwise everything and the cat is boolean this.boolean_val=(int_val==1); if (Boolean.parseBoolean(value) || (value.compareToIgnoreCase("false")==0)){ this.boolean_val=(int_val==1) || Boolean.parseBoolean(value); this.typeflag|=BOOLEAN; } } /** Answer definitively if a setting cannot ABSOLUTELY be * parsed into a number using simple Integer rules. * This excludes some special names like "+Inf" and "NaN". * * @return */ public boolean isIntegerNumeric(){ try { this.long_val=Long.parseLong(string_val); } catch (NumberFormatException e) { try { // Try decoding it as hex, octal, whatever. Long.decode(string_val); } catch (NumberFormatException h){ // If even THAT fails, then no nookie. return false; } } // Everything OK, I presume... return true; } /** Settings are "comparable" to each other by name, so we can save * nicely sorted setting files ;-) * * @param o * @return */ @Override public int compareTo(DoomSetting o) { return this.name.compareToIgnoreCase(o.getName()); } public String toString(){ return string_val; } /** A special setting that returns false, 0 and an empty string, if required. * Simplifies handling of nulls A LOT. So code that relies on specific settings * should be organized to work only on clear positivies (e.g. use a "fullscreen" setting * that must exist and be equal to 1 or true, instead of assuming that a zero/false * value enables it. */ public static DoomSetting NULL_SETTING=new DoomSetting("NULL","",false); static { // It's EVERYTHING NULL_SETTING.typeflag=0x1F; NULL_SETTING.string_val=""; NULL_SETTING.char_val=0; NULL_SETTING.double_val=0; NULL_SETTING.boolean_val=false; NULL_SETTING.int_val=0; NULL_SETTING.long_val=0; } } package m; import defines.*; import static data.Defines.HU_FONTSIZE; import static data.Defines.HU_FONTSTART; import static g.Keys.*; import static data.Defines.PU_CACHE; import static data.Defines.SAVESTRINGSIZE; import static data.dstrings.NUM_QUITMESSAGES; import static data.dstrings.SAVEGAMENAME; import static data.dstrings.endmsg; import static doom.englsh.DOSY; import static doom.englsh.EMPTYSTRING; import static doom.englsh.ENDGAME; import static doom.englsh.LOADNET; import static doom.englsh.MSGOFF; import static doom.englsh.MSGON; import static doom.englsh.NETEND; import static doom.englsh.NEWGAME; import static doom.englsh.NIGHTMARE; import static doom.englsh.QLOADNET; import static doom.englsh.QLPROMPT; import static doom.englsh.QSAVESPOT; import static doom.englsh.QSPROMPT; import static doom.englsh.SAVEDEAD; import static doom.englsh.SWSTRING; import java.io.BufferedInputStream; import java.io.DataInputStream; import java.io.FileInputStream; import java.io.IOException; import rr.patch_t; import utils.C2JUtils; import v.IVideoScale; import w.DoomIO; import data.sounds.sfxenum_t; import doom.DoomStatus; import doom.englsh; import doom.event_t; import doom.evtype_t; public class Menu extends AbstractDoomMenu { ////////////////// CONSTRUCTOR //////////////// public Menu(DoomStatus DS){ this.updateStatus(DS); } /** The fonts ... must "peg" them to those from HU */ patch_t[] hu_font = new patch_t[HU_FONTSIZE]; /** WTF?! */ boolean message_dontfuckwithme; // int mouseSensitivity; // has default /** Show messages has default, 0 = off, 1 = on */ private boolean showMessages=false; /** * showMessages can be read outside of Menu, but not modified. Menu has the * actual C definition (not declaration) */ @Override public boolean getShowMessages() { return showMessages; } @Override public void setShowMessages(boolean val) { this.showMessages=val; } /** Blocky mode, has default, 0 = high, 1 = normal */ int detailLevel; int screenblocks=10; // has default /** temp for screenblocks (0-9) */ int screenSize; /** -1 = no quicksave slot picked! */ int quickSaveSlot; /** 1 = message to be printed */ boolean messageToPrint; /** ...and here is the message string! */ String messageString; /** message x & y */ int messx, messy; boolean messageLastMenuActive; /** timed message = no input from user */ boolean messageNeedsInput; /** Probably I need some MessageRoutine interface at this point? */ public MenuRoutine messageRoutine; /** we are going to be entering a savegame string */ boolean saveStringEnter; int saveSlot; // which slot to save in int saveCharIndex; // which char we're editing /** old save description before edit */ char[] saveOldString = new char[SAVESTRINGSIZE]; boolean inhelpscreens; //int menuactive; protected static final int SKULLXOFF = -32; protected static final int LINEHEIGHT = 16; char[][] savegamestrings = new char[10][SAVESTRINGSIZE]; String endstring = new String(); // // MENU TYPEDEFS // /** menu item skull is on */ short itemOn; /** skull animation counter */ short skullAnimCounter; /** which skull to draw */ short whichSkull; /** * graphic name of skulls warning: initializer-string for array of chars is * too long */ private static String[] skullName = { "M_SKULL1", "M_SKULL2" }; /** current menudef */ // MAES: pointer? array? menu_t currentMenu; // // DOOM MENU // // MAES: was an enum called "main_e" used purely as numerals. No need for // strong typing. /** * MenuRoutine class definitions, replacing "function pointers". */ MenuRoutine ChangeDetail, ChangeMessages, ChangeSensitivity, ChooseSkill, EndGame, EndGameResponse, Episode, FinishReadThis, LoadGame, LoadSelect, MusicVol, NewGame, Options, VerifyNightmare, SaveSelect, SfxVol, SizeDisplay, SaveGame, Sound, QuitDOOM, QuitResponse, QuickLoadResponse, QuickSaveResponse, ReadThis, ReadThis2; /** DrawRoutine class definitions, replacing "function pointers". */ DrawRoutine DrawEpisode, DrawLoad,DrawMainMenu,DrawNewGame,DrawOptions, DrawReadThis1, DrawReadThis2, DrawSave, DrawSound; /** Initialize menu routines first */ private void initMenuRoutines() { ChangeMessages = new M_ChangeMessages(); ChangeDetail = new M_ChangeDetail(); ChangeSensitivity = new M_ChangeSensitivity(); ChooseSkill = new M_ChooseSkill(); EndGame = new M_EndGame(); EndGameResponse = new M_EndGameResponse(); Episode = new M_Episode(); FinishReadThis=new M_FinishReadThis(); LoadGame=new M_LoadGame(); LoadSelect=new M_LoadSelect(); MusicVol=new M_MusicVol(); NewGame = new M_NewGame(); Options = new M_Options(); QuitDOOM = new M_QuitDOOM(); QuickLoadResponse = new M_QuickLoadResponse(); QuickSaveResponse= new M_QuickSaveResponse(); QuitResponse = new M_QuitResponse(); ReadThis = new M_ReadThis(); ReadThis2 = new M_ReadThis2(); SaveGame=new M_SaveGame(); SaveSelect= new M_SaveSelect(); SfxVol=new M_SfxVol(); SizeDisplay = new M_SizeDisplay(); Sound = new M_Sound(); VerifyNightmare = new M_VerifyNightmare(); } /** Then drawroutines */ private void initDrawRoutines() { DrawEpisode = new M_DrawEpisode(); DrawNewGame = new M_DrawNewGame(); DrawReadThis1 = new M_DrawReadThis1(); DrawReadThis2 = new M_DrawReadThis2(); DrawOptions = new M_DrawOptions(); DrawLoad = new M_DrawLoad(); DrawSave = new M_DrawSave(); DrawSound=new M_DrawSound(); DrawMainMenu = new M_DrawMainMenu(); } /** Menuitem definitions. A "menu" can consist of multiple menuitems */ menuitem_t[] MainMenu,EpisodeMenu,NewGameMenu, OptionsMenu,ReadMenu1,ReadMenu2,SoundMenu,LoadMenu,SaveMenu; /** Actual menus. Each can point to an array of menuitems */ menu_t MainDef, EpiDef,NewDef,OptionsDef,ReadDef1, ReadDef2,SoundDef,LoadDef,SaveDef; /** First initialize those */ private void initMenuItems(){ MainMenu = new menuitem_t[] { new menuitem_t( 1, "M_NGAME", NewGame, 'n'), new menuitem_t(1, "M_OPTION", Options, 'o'), new menuitem_t(1, "M_LOADG", LoadGame, 'l'), new menuitem_t(1, "M_SAVEG", SaveGame, 's'), // Another hickup with Special edition. new menuitem_t(1, "M_RDTHIS", ReadThis, 'r'), new menuitem_t(1, "M_QUITG", QuitDOOM, 'q') }; MainDef = new menu_t(main_end, null, MainMenu, DrawMainMenu, 97, 64, 0); // // EPISODE SELECT // EpisodeMenu = new menuitem_t[] { new menuitem_t(1, "M_EPI1", Episode, 'k'), new menuitem_t(1, "M_EPI2", Episode, 't'), new menuitem_t(1, "M_EPI3", Episode, 'i'), new menuitem_t(1, "M_EPI4", Episode, 't') }; EpiDef = new menu_t(ep_end, // # of menu items MainDef, // previous menu EpisodeMenu, // menuitem_t -> DrawEpisode, // drawing routine -> 48, 63, // x,y ep1 // lastOn ); // // NEW GAME // NewGameMenu = new menuitem_t[] { new menuitem_t(1, "M_JKILL", ChooseSkill, 'i'), new menuitem_t(1, "M_ROUGH", ChooseSkill, 'h'), new menuitem_t(1, "M_HURT", ChooseSkill, 'h'), new menuitem_t(1, "M_ULTRA", ChooseSkill, 'u'), new menuitem_t(1, "M_NMARE", ChooseSkill, 'n') }; NewDef = new menu_t(newg_end, // # of menu items EpiDef, // previous menu NewGameMenu, // menuitem_t -> DrawNewGame, // drawing routine -> 48, 63, // x,y hurtme // lastOn ); // // OPTIONS MENU // OptionsMenu = new menuitem_t[] { new menuitem_t(1, "M_ENDGAM", EndGame, 'e'), new menuitem_t(1, "M_MESSG", ChangeMessages, 'm'), new menuitem_t(1, "M_DETAIL", ChangeDetail, 'g'), new menuitem_t(2, "M_SCRNSZ", SizeDisplay, 's'), new menuitem_t(-1, "", null), new menuitem_t(2, "M_MSENS", ChangeSensitivity, 'm'), new menuitem_t(-1, "", null), new menuitem_t(1, "M_SVOL", Sound, 's') }; OptionsDef = new menu_t(opt_end, this.MainDef, OptionsMenu, DrawOptions, 60, 37, 0); // Read This! MENU 1 ReadMenu1 = new menuitem_t[] { new menuitem_t(1, "", ReadThis2, (char) 0) }; ReadDef1 = new menu_t(read1_end, MainDef, ReadMenu1, DrawReadThis1, 280, 185, 0); // Read This! MENU 2 ReadMenu2 = new menuitem_t[] { new menuitem_t(1, "", FinishReadThis, (char) 0) }; ReadDef2 = new menu_t(read2_end, ReadDef1, ReadMenu2, DrawReadThis2, 330, 175, 0); // // SOUND VOLUME MENU // SoundMenu = new menuitem_t[] { new menuitem_t(2, "M_SFXVOL", SfxVol, 's'), new menuitem_t(-1, "", null), new menuitem_t(2, "M_MUSVOL", MusicVol, 'm'), new menuitem_t(-1, "", null) }; SoundDef = new menu_t(sound_end, OptionsDef, SoundMenu, DrawSound, 80, 64, 0); // // LOAD GAME MENU // LoadMenu =new menuitem_t[] { new menuitem_t(1, "", LoadSelect, '1'), new menuitem_t(1, "", LoadSelect, '2'), new menuitem_t(1, "", LoadSelect, '3'), new menuitem_t(1, "", LoadSelect, '4'), new menuitem_t(1, "", LoadSelect, '5'), new menuitem_t(1, "", LoadSelect, '6') }; LoadDef = new menu_t(load_end, MainDef, LoadMenu, DrawLoad, 80, 54, 0); // // SAVE GAME MENU // SaveMenu = new menuitem_t[] { new menuitem_t(1, "", SaveSelect, '1'), new menuitem_t(1, "", SaveSelect, '2'), new menuitem_t(1, "", SaveSelect, '3'), new menuitem_t(1, "", SaveSelect, '4'), new menuitem_t(1, "", SaveSelect, '5'), new menuitem_t(1, "", SaveSelect, '6') }; SaveDef = new menu_t(load_end, MainDef, SaveMenu, DrawSave, 80, 54, 0); } /** * M_ReadSaveStrings * read the strings from the savegame files */ public void ReadSaveStrings() { DataInputStream handle; int count; int i; String name; for (i = 0; i < load_end; i++) { if (DM.CM.CheckParm("-cdrom") != 0) name = "c:\\doomdata\\" + SAVEGAMENAME + (i) + ".dsg"; else name = SAVEGAMENAME + (i) + ".dsg"; try { handle = new DataInputStream(new BufferedInputStream(new FileInputStream(name))); savegamestrings[i] = DoomIO.readString(handle,SAVESTRINGSIZE).toCharArray(); handle.close(); LoadMenu[i].status = 1; } catch (IOException e) { savegamestrings[i][0] = 0x00; LoadMenu[i].status = 0; continue; } } } /** * Draw border for the savegame description. This is special in that it's * not "invokable" like the other drawroutines, but standalone. */ private void DrawSaveLoadBorder(int x, int y) { int i; V.DrawScaledPatch(x - 8, y + 7, 0, vs,W.CachePatchName("M_LSLEFT")); for (i = 0; i < 24; i++) { V.DrawScaledPatch(x, y + 7, 0, vs,W.CachePatchName("M_LSCNTR")); x += 8; } V.DrawScaledPatch(x, y + 7, 0, vs,W.CachePatchName("M_LSRGHT")); } /** Draws slider rail of a specified width (each notch is 8 base units wide) * and with a slider selector at position thermDot. * * @param x * @param y * @param thermWidth * @param thermDot */ public void DrawThermo(int x, int y, int thermWidth, int thermDot) { int xx; int i; xx = x; V.DrawScaledPatch(xx, y, 0, vs,W.CachePatchName("M_THERML")); xx += 8; for (i = 0; i < thermWidth; i++) { V.DrawScaledPatch(xx, y, 0, vs,W.CachePatchName("M_THERMM")); xx += 8; } V.DrawScaledPatch(xx, y, 0,vs, W.CachePatchName("M_THERMR")); V.DrawScaledPatch((x + 8) + thermDot * 8, y, 0,vs, W .CachePatchName("M_THERMO")); } public void DrawEmptyCell(menu_t menu, int item) { V.DrawScaledPatch(menu.x - 10, menu.y + item * LINEHEIGHT - 1, 0,vs, (patch_t) W.CacheLumpName("M_CELL1", PU_CACHE, patch_t.class)); } public void DrawSelCell(menu_t menu, int item) { V.DrawScaledPatch(menu.x - 10, menu.y + item * LINEHEIGHT - 1, 0,vs, (patch_t) W.CacheLumpName("M_CELL2", PU_CACHE, patch_t.class)); } // // M_SaveGame & Cie. // public class M_DrawSave implements DrawRoutine { @Override public void invoke(){ int i; V.DrawScaledPatch(72, 28, 0, vs,W.CachePatchName("M_SAVEG")); for (i = 0; i < load_end; i++) { DrawSaveLoadBorder(LoadDef.x, LoadDef.y + LINEHEIGHT * i); WriteText(LoadDef.x, LoadDef.y + LINEHEIGHT * i, savegamestrings[i]); } if (saveStringEnter) { i = StringWidth(savegamestrings[saveSlot]); WriteText(LoadDef.x + i, LoadDef.y + LINEHEIGHT * saveSlot, "_"); } } } /** * M_Responder calls this when user is finished * * @param slot */ public void DoSave(int slot) { DG.SaveGame(slot, new String(savegamestrings[slot])); ClearMenus(); // PICK QUICKSAVE SLOT YET? if (quickSaveSlot == -2) quickSaveSlot = slot; } /** * User wants to save. Start string input for M_Responder */ class M_SaveSelect implements MenuRoutine { @Override public void invoke(int choice) { // we are going to be intercepting all chars //System.out.println("ACCEPTING typing input"); saveStringEnter = true; saveSlot = choice; C2JUtils.strcpy(saveOldString, savegamestrings[choice]); if (C2JUtils.strcmp(savegamestrings[choice], EMPTYSTRING)) savegamestrings[choice][0] = 0; saveCharIndex = C2JUtils.strlen(savegamestrings[choice]); } } /** * Selected from DOOM menu */ class M_SaveGame implements MenuRoutine { @Override public void invoke(int choice) { if (!DM.usergame) { StartMessage(SAVEDEAD, null, false); return; } if (DM.gamestate != gamestate_t.GS_LEVEL) return; SetupNextMenu(SaveDef); ReadSaveStrings(); } } // // M_QuickSave // private String tempstring; class M_QuickSaveResponse implements MenuRoutine { @Override public void invoke(int ch) { if (ch == 'y') { DoSave(quickSaveSlot); S.StartSound(null, sfxenum_t.sfx_swtchx); } } } private void QuickSave() { if (!DM.usergame) { S.StartSound(null, sfxenum_t.sfx_oof); return; } if (DM.gamestate != gamestate_t.GS_LEVEL) return; if (quickSaveSlot < 0) { StartControlPanel(); ReadSaveStrings(); SetupNextMenu(SaveDef); quickSaveSlot = -2; // means to pick a slot now return; } tempstring = String.format(QSPROMPT,C2JUtils.nullTerminatedString(savegamestrings[quickSaveSlot])); StartMessage(tempstring,this.QuickSaveResponse,true); } // // M_QuickLoad // class M_QuickLoadResponse implements MenuRoutine { @Override public void invoke(int ch) { if (ch == 'y') { LoadSelect.invoke(quickSaveSlot); S.StartSound(null, sfxenum_t.sfx_swtchx); } } } class M_QuitResponse implements MenuRoutine { @Override public void invoke(int ch) { if (ch != 'y') return; if (!DM.netgame) { if (DM.isCommercial()) S.StartSound(null, quitsounds2[(DM.gametic >> 2) & 7]); else S.StartSound(null, quitsounds[(DM.gametic >> 2) & 7]); // TI.WaitVBL(105); } I.Quit(); } } public void QuickLoad() { if (DM.netgame) { StartMessage(QLOADNET, null, false); return; } if (quickSaveSlot < 0) { StartMessage(QSAVESPOT, null, false); return; } tempstring = String.format(QLPROMPT, C2JUtils.nullTerminatedString(savegamestrings[quickSaveSlot])); StartMessage(tempstring, QuickLoadResponse, true); } class M_Sound implements MenuRoutine { @Override public void invoke(int choice) { SetupNextMenu(SoundDef); } } class M_SfxVol implements MenuRoutine { @Override public void invoke(int choice) { switch (choice) { case 0: if (DM.snd_SfxVolume != 0) DM.snd_SfxVolume--; break; case 1: if (DM.snd_SfxVolume < 15) DM.snd_SfxVolume++; break; } S.SetSfxVolume(DM.snd_SfxVolume *8); } } class M_MusicVol implements MenuRoutine { @Override public void invoke(int choice) { switch (choice) { case 0: if (DM.snd_MusicVolume != 0) DM.snd_MusicVolume--; break; case 1: if (DM.snd_MusicVolume < 15) DM.snd_MusicVolume++; break; } S.SetMusicVolume(DM.snd_MusicVolume*8); } } // // M_Episode // private int epi; class M_VerifyNightmare implements MenuRoutine { @Override public void invoke(int ch) { if (ch != 'y') return; DG.DeferedInitNew(skill_t.sk_nightmare, epi + 1, 1); ClearMenus(); } } /** * M_ReadThis */ class M_ReadThis implements MenuRoutine { @Override public void invoke(int choice) { choice = 0; SetupNextMenu(ReadDef1); } } class M_ReadThis2 implements MenuRoutine { @Override public void invoke(int choice) { choice = 0; SetupNextMenu(ReadDef2); } } class M_FinishReadThis implements MenuRoutine { @Override public void invoke(int choice) { choice = 0; SetupNextMenu(MainDef); } } // // M_QuitDOOM // class M_QuitDOOM implements MenuRoutine { @Override public void invoke(int choice) { // We pick index 0 which is language sensitive, // or one at random, between 1 and maximum number. if (DM.language != Language_t.english) endstring = endmsg[0] + "\n\n" + DOSY; else endstring = endmsg[(DM.gametic % (NUM_QUITMESSAGES - 2)) + 1] + "\n\n" + DOSY; StartMessage(endstring, QuitResponse, true); } } class M_QuitGame implements MenuRoutine { @Override public void invoke(int ch) { if (ch != 'y') return; if (!DM.netgame) { if (DM.isCommercial()) S.StartSound(null,quitsounds2[(DM.gametic>>2)&7]); else S.StartSound(null,quitsounds[(DM.gametic>>2)&7]); I.WaitVBL(105); } I.Quit (); } } class M_SizeDisplay implements MenuRoutine { @Override public void invoke(int choice) { switch (choice) { case 0: if (screenSize > 0) { screenblocks--; screenSize--; } break; case 1: if (screenSize < 8) { screenblocks++; screenSize++; } break; } R.SetViewSize (screenblocks, detailLevel); } } class M_Options implements MenuRoutine { @Override public void invoke(int choice) { SetupNextMenu(OptionsDef); } } class M_NewGame implements MenuRoutine { @Override public void invoke(int choice) { if (DM.netgame && !DM.demoplayback) { StartMessage(NEWGAME, null, false); return; } if (DM.isCommercial()) SetupNextMenu(NewDef); else SetupNextMenu(EpiDef); } } public void StartMessage(String string, MenuRoutine routine, boolean input) { messageLastMenuActive = DM.menuactive; messageToPrint = true; messageString = new String(string); messageRoutine = routine; messageNeedsInput = input; DM.menuactive = true; // "true" return; } public void StopMessage() { DM.menuactive = messageLastMenuActive; messageToPrint = false; } /** * Find string width from hu_font chars */ public int StringWidth(char[] string) { int i; int w = 0; int c; for (i = 0; i < C2JUtils.strlen(string); i++) { c = Character.toUpperCase(string[i]) - HU_FONTSTART; if (c < 0 || c >= HU_FONTSIZE) w += 4; else w += hu_font[c].width; } return w; } /** * Find string height from hu_font chars. * * Actually it just counts occurences of 'n' and adds height to height. */ private int StringHeight(char[] string) { int i; int h; int height = hu_font[0].height; h = height; for (i = 0; i < string.length; i++) if (string[i] == '\n') h += height; return h; } /** * Find string height from hu_font chars */ private int StringHeight(String string) { return this.StringHeight(string.toCharArray()); } /** * Write a string using the hu_font */ private void WriteText(int x, int y, char[] string) { int w; char[] ch; int c; int cx; int cy; ch = string; int chptr = 0; cx = x; cy = y; while (chptr<ch.length) { c = ch[chptr]; chptr++; if (c == 0) break; if (c == '\n') { cx = x; cy += 12; continue; } c = Character.toUpperCase(c) - HU_FONTSTART; if (c < 0 || c >= HU_FONTSIZE) { cx += 4; continue; } w = hu_font[c].width; if (cx + w > SCREENWIDTH) break; V.DrawScaledPatch(cx, cy, 0,vs, hu_font[c]); cx += w; } } private void WriteText(int x, int y, String string) { if (string == null || string.length() == 0) return; int w; int cx; int cy; int chptr = 0; char c; cx = x; cy = y; while (chptr<string.length()) { c = string.charAt(chptr++); if (c == 0) break; if (c == '\n') { cx = x; cy += 12; continue; } c = (char) (Character.toUpperCase(c) - HU_FONTSTART); if (c < 0 || c >= HU_FONTSIZE) { cx += 4; continue; } w = hu_font[c].width; if (cx + w > SCREENWIDTH) break; V.DrawScaledPatch(cx, cy, 0,vs, hu_font[c]); cx += w; } } // These belong to the responder. private int joywait = 0; private int mousewait = 0; private int mousey = 0; private int lasty = 0; private int mousex = 0; private int lastx = 0; public boolean Responder(event_t ev) { char ch; int i; ch = 0xFFFF; //System.out.println("Processing keyevent:" +(ev.type==evtype_t.ev_keydown || ev.type==evtype_t.ev_keyup)+ " value = "+(char)ev.data1); // Joystick input if (ev.type == evtype_t.ev_joystick && joywait < TICK.GetTime()) { if (ev.data3 == -1) { ch = KEY_UPARROW; joywait = TICK.GetTime() + 5; } else if (ev.data3 == 1) { ch = KEY_DOWNARROW; joywait = TICK.GetTime() + 5; } if (ev.data2 == -1) { ch = KEY_LEFTARROW; joywait = TICK.GetTime() + 2; } else if (ev.data2 == 1) { ch = KEY_RIGHTARROW; joywait = TICK.GetTime() + 2; } if ((ev.data1 & 1) != 0) { ch = KEY_ENTER; joywait = TICK.GetTime() + 5; } if ((ev.data1 & 2) != 0) { ch = KEY_BACKSPACE; joywait = TICK.GetTime() + 5; } } else // Mouse input { if (ev.type == evtype_t.ev_mouse && mousewait < TICK.GetTime()) { mousey += ev.data3; if (mousey < lasty - 30) { ch = KEY_DOWNARROW; mousewait = TICK.GetTime() + 5; mousey = lasty -= 30; } else if (mousey > lasty + 30) { ch = KEY_UPARROW; mousewait = TICK.GetTime() + 5; mousey = lasty += 30; } mousex += ev.data2; if (mousex < lastx - 30) { ch = KEY_LEFTARROW; mousewait = TICK.GetTime() + 5; mousex = lastx -= 30; } else if (mousex > lastx + 30) { ch = KEY_RIGHTARROW; mousewait = TICK.GetTime() + 5; mousex = lastx += 30; } if ((ev.data1 & 1) != 0) { ch = KEY_ENTER; mousewait = TICK.GetTime() + 15; } if ((ev.data1 & 2) != 0) { ch = KEY_BACKSPACE; mousewait = TICK.GetTime() + 15; } } else if (ev.type == evtype_t.ev_keydown) { ch = (char) ev.data1; } } if (ch == 0xFFFF) return false; // Save Game string input if (saveStringEnter) { switch (ch) { case KEY_BACKSPACE: if (saveCharIndex > 0) { saveCharIndex--; savegamestrings[saveSlot][saveCharIndex] = 0; } break; case KEY_ESCAPE: saveStringEnter = false; C2JUtils.strcpy(savegamestrings[saveSlot], saveOldString); break; case KEY_ENTER: saveStringEnter = false; if (savegamestrings[saveSlot][0] != 0) DoSave(saveSlot); break; default: ch = Character.toUpperCase(ch); if (ch != 32) if (ch - HU_FONTSTART < 0 || ch - HU_FONTSTART >= HU_FONTSIZE) break; if (ch >= 32 && ch <= 127 && saveCharIndex < SAVESTRINGSIZE - 1 && StringWidth(savegamestrings[saveSlot]) < (SAVESTRINGSIZE - 2) * 8) { savegamestrings[saveSlot][saveCharIndex++] = ch; savegamestrings[saveSlot][saveCharIndex] = 0; } break; } return true; } // Take care of any messages that need input if (messageToPrint) { if (messageNeedsInput == true && !(ch == ' ' || ch == 'n' || ch == 'y' || ch == KEY_ESCAPE)) return false; DM.menuactive = messageLastMenuActive; messageToPrint = false; if (messageRoutine != null) messageRoutine.invoke(ch); DM.menuactive = false; // "false" S.StartSound(null, sfxenum_t.sfx_swtchx); return true; } if (DM.devparm && ch == KEY_F1) { DG.ScreenShot(); return true; } // F-Keys if (!DM.menuactive){ switch (ch) { case KEY_MINUS: // Screen size down if (DM.automapactive || HU.chat_on[0]) return false; SizeDisplay.invoke(0); S.StartSound(null, sfxenum_t.sfx_stnmov); return true; case KEY_EQUALS: // Screen size up if (DM.automapactive || HU.chat_on[0]) return false; SizeDisplay.invoke(1); S.StartSound(null, sfxenum_t.sfx_stnmov); return true; case KEY_F1: // Help key StartControlPanel(); if (DM.isRetail() || currentMenu==ReadDef1) currentMenu = ReadDef2; else currentMenu = ReadDef1; itemOn = 0; S.StartSound(null, sfxenum_t.sfx_swtchn); return true; case KEY_F2: // Save StartControlPanel(); S.StartSound(null, sfxenum_t.sfx_swtchn); SaveGame.invoke(0); return true; case KEY_F3: // Load StartControlPanel(); S.StartSound(null, sfxenum_t.sfx_swtchn); LoadGame.invoke(0); return true; case KEY_F4: // Sound Volume StartControlPanel(); currentMenu = SoundDef; itemOn = (short) sfx_vol; S.StartSound(null, sfxenum_t.sfx_swtchn); return true; case KEY_F5: // Detail toggle ChangeDetail.invoke(0); S.StartSound(null, sfxenum_t.sfx_swtchn); return true; case KEY_F6: // Quicksave S.StartSound(null, sfxenum_t.sfx_swtchn); QuickSave(); return true; case KEY_F7: // End game S.StartSound(null, sfxenum_t.sfx_swtchn); EndGame.invoke(0); return true; case KEY_F8: // Toggle messages ChangeMessages.invoke(0); S.StartSound(null, sfxenum_t.sfx_swtchn); return true; case KEY_F9: // Quickload S.StartSound(null, sfxenum_t.sfx_swtchn); QuickLoad(); return true; case KEY_F10: // Quit DOOM S.StartSound(null, sfxenum_t.sfx_swtchn); QuitDOOM.invoke(0); return true; case KEY_F11: // gamma toggle int usegamma = V.getUsegamma(); usegamma++; if (usegamma > 4) usegamma = 0; DM.players[DM.consoleplayer].message = gammamsg[usegamma]; // FIXME: it's pointless to reload the same palette. //I.SetPalette (W.CacheLumpName ("PLAYPAL",PU_CACHE)); DM.VI.SetPalette(0); DM.VI.SetGamma(usegamma); return true; } } // Pop-up menu? if (!DM.menuactive) { if (ch == KEY_ESCAPE) { StartControlPanel(); S.StartSound(null, sfxenum_t.sfx_swtchn); return true; } return false; } // Keys usable within menu switch (ch) { case KEY_DOWNARROW: do { if (itemOn + 1 > currentMenu.numitems - 1) itemOn = 0; else itemOn++; S.StartSound(null, sfxenum_t.sfx_pstop); } while (currentMenu.menuitems[itemOn].status == -1); return true; case KEY_UPARROW: do { if (itemOn == 0) itemOn = (short) (currentMenu.numitems - 1); else itemOn--; S.StartSound(null, sfxenum_t.sfx_pstop); } while (currentMenu.menuitems[itemOn].status == -1); return true; case KEY_LEFTARROW: if ((currentMenu.menuitems[itemOn].routine != null) && (currentMenu.menuitems[itemOn].status == 2)) { S.StartSound(null, sfxenum_t.sfx_stnmov); currentMenu.menuitems[itemOn].routine.invoke(0); } return true; case KEY_RIGHTARROW: if ((currentMenu.menuitems[itemOn].routine != null) && (currentMenu.menuitems[itemOn].status == 2)) { S.StartSound(null, sfxenum_t.sfx_stnmov); currentMenu.menuitems[itemOn].routine.invoke(1); } return true; case KEY_ENTER: { if ((currentMenu.menuitems[itemOn].routine != null) && currentMenu.menuitems[itemOn].status != 0) { currentMenu.lastOn = itemOn; if (currentMenu.menuitems[itemOn].status == 2) { currentMenu.menuitems[itemOn].routine.invoke(1); // right // arrow S.StartSound(null, sfxenum_t.sfx_stnmov); } else { currentMenu.menuitems[itemOn].routine.invoke(itemOn); S.StartSound(null, sfxenum_t.sfx_pistol); } } } return true; case KEY_ESCAPE: currentMenu.lastOn = itemOn; ClearMenus(); S.StartSound(null, sfxenum_t.sfx_swtchx); return true; case KEY_BACKSPACE: currentMenu.lastOn = itemOn; if (currentMenu.prevMenu != null) { currentMenu = currentMenu.prevMenu; itemOn = (short) currentMenu.lastOn; S.StartSound(null, sfxenum_t.sfx_swtchn); } return true; default: for (i = itemOn + 1; i < currentMenu.numitems; i++) if (currentMenu.menuitems[i].alphaKey == ch) { itemOn = (short) i; S.StartSound(null, sfxenum_t.sfx_pstop); return true; } for (i = 0; i <= itemOn; i++) if (currentMenu.menuitems[i].alphaKey == ch) { itemOn = (short) i; S.StartSound(null, sfxenum_t.sfx_pstop); return true; } break; } return false; } /** * M_StartControlPanel */ public void StartControlPanel() { // intro might call this repeatedly if (DM.menuactive) return; DM.menuactive = true; currentMenu = MainDef; // JDC itemOn = (short) currentMenu.lastOn; // JDC } /** * M_Drawer Called after the view has been rendered, but before it has been * blitted. */ public void Drawer() { int x; int y; int max; char[] string = new char[40]; char[] msstring; int start; inhelpscreens = false; // Horiz. & Vertically center string and print // it. if (messageToPrint) { start = 0; y = 100 - this.StringHeight(messageString) / 2; msstring = messageString.toCharArray(); while(start<messageString.length()) { int i=0; for ( i = 0; i < messageString.length() - start; i++) if (msstring[start + i] == '\n') { C2JUtils.memset(string, (char) 0, 40); C2JUtils.strcpy(string, msstring, start, i); start += i + 1; break; } if (i == (messageString.length() - start)) { C2JUtils.strcpy(string, msstring,start); start += i; } x = 160 - this.StringWidth(string) / 2; this.WriteText(x, y, string); y += hu_font[0].height; } return; } if (!DM.menuactive) return; if (currentMenu.routine != null){ currentMenu.routine.invoke(); // call Draw routine } // DRAW MENU x = currentMenu.x; y = currentMenu.y; max = currentMenu.numitems; for (int i = 0; i < max; i++) { if (currentMenu.menuitems[i].name != null && currentMenu.menuitems[i].name!="") V.DrawScaledPatch(x, y, 0,vs, W.CachePatchName( currentMenu.menuitems[i].name, PU_CACHE)); y += LINEHEIGHT; } // DRAW SKULL V.DrawScaledPatch(x + SKULLXOFF, currentMenu.y - 5 + itemOn * LINEHEIGHT, 0, vs,W.CachePatchName(skullName[whichSkull], PU_CACHE)); } // // M_ClearMenus // public void ClearMenus() { DM.menuactive = false; V.clearCaches(); // MAES: was commented out :-/ //if (!DM.netgame && DM.usergame && DM.paused) // DM.setPaused(true); } /** * M_SetupNextMenu */ public void SetupNextMenu(menu_t menudef) { currentMenu = menudef; itemOn = (short) currentMenu.lastOn; } /** * M_Ticker */ public void Ticker() { if (--skullAnimCounter <= 0) { whichSkull ^= 1; skullAnimCounter = 8; } } /** * M_Init */ public void Init() { // Init menus. this.initMenuRoutines(); this.initDrawRoutines(); this.initMenuItems(); this.hu_font=HU.getHUFonts(); currentMenu = MainDef; DM.menuactive = false; itemOn = (short) currentMenu.lastOn; whichSkull = 0; skullAnimCounter = 10; screenSize = screenblocks - 3; messageToPrint = false; messageString = null; messageLastMenuActive = DM.menuactive; quickSaveSlot = -1; // Here we could catch other version dependencies, // like HELP1/2, and four episodes. switch (DM.getGameMode()) { case commercial: case pack_plut: case pack_tnt: // This is used because DOOM 2 had only one HELP // page. I use CREDIT as second page now, but // kept this hack for educational purposes. MainMenu[readthis] = MainMenu[quitdoom]; MainDef.numitems--; MainDef.y += 8; NewDef.prevMenu = MainDef; ReadDef1.routine = DrawReadThis1; ReadDef1.x = 330; ReadDef1.y = 165; ReadMenu1[0].routine = FinishReadThis; break; case shareware: // Episode 2 and 3 are handled, // branching to an ad screen. case registered: // We need to remove the fourth episode. EpiDef.numitems--; break; case retail: // We are fine. default: break; } } /** * M_DrawText Returns the final X coordinate HU_Init must have been called * to init the font. Unused? * * @param x * @param y * @param direct * @param string * @return */ public int DrawText(int x, int y, boolean direct, String string) { int c; int w; int ptr = 0; while ((c=string.charAt(ptr)) > 0) { c = Character.toUpperCase(c) - HU_FONTSTART; ptr++; if (c < 0 || c > HU_FONTSIZE) { x += 4; continue; } w = hu_font[c].width; if (x + w > SCREENWIDTH) break; if (direct) V.DrawScaledPatch(x, y, 0,vs, hu_font[c]); else V.DrawScaledPatch(x, y, 0,vs, hu_font[c]); x += w; } return x; } // ////////////////////////// DRAWROUTINES // ////////////////////////////////// class M_DrawEpisode implements DrawRoutine { @Override public void invoke() { V.DrawScaledPatch(54, 38, 0,vs, W.CachePatchName("M_EPISOD")); } } /** * M_LoadGame & Cie. */ class M_DrawLoad implements DrawRoutine { @Override public void invoke() { int i; V.DrawScaledPatch(72, 28, 0,vs, W.CachePatchName("M_LOADG")); for (i = 0; i < load_end; i++) { DrawSaveLoadBorder(LoadDef.x, LoadDef.y + LINEHEIGHT * i); WriteText(LoadDef.x, LoadDef.y + LINEHEIGHT * i, savegamestrings[i]); } } } class M_DrawMainMenu implements DrawRoutine { @Override public void invoke() { V.DrawScaledPatch(94, 2, 0,vs, (patch_t) (W.CachePatchName("M_DOOM"))); } } class M_DrawNewGame implements DrawRoutine { @Override public void invoke() { V.DrawScaledPatch(96, 14, 0, vs,(patch_t) W.CachePatchName("M_NEWG")); V.DrawScaledPatch(54, 38, 0, vs,(patch_t) W.CachePatchName("M_SKILL")); } } class M_DrawOptions implements DrawRoutine { private String detailNames[] = { "M_GDHIGH", "M_GDLOW" }; private String msgNames[] = { "M_MSGOFF", "M_MSGON" }; @Override public void invoke() { V.DrawScaledPatch(108, 15, 0, vs,W.CachePatchName("M_OPTTTL")); V.DrawScaledPatch(OptionsDef.x + 175, OptionsDef.y + LINEHEIGHT * detail, 0,vs, W.CachePatchName(detailNames[detailLevel])); V.DrawScaledPatch(OptionsDef.x + 120, OptionsDef.y + LINEHEIGHT * messages, 0,vs, W.CachePatchName(msgNames[showMessages?1:0])); DrawThermo(OptionsDef.x, OptionsDef.y + LINEHEIGHT * (mousesens + 1), 10, DM.mouseSensitivity); DrawThermo(OptionsDef.x, OptionsDef.y + LINEHEIGHT * (scrnsize + 1), 9, screenSize); } } /** * Read This Menus Had a "quick hack to fix romero bug" */ class M_DrawReadThis1 implements DrawRoutine { @Override public void invoke() { inhelpscreens = true; switch (DM.getGameMode()) { case commercial: V.DrawPatchSolidScaled(0, 0, 0, vs,W.CachePatchName("HELP")); break; case shareware: case registered: case retail: V.DrawPatchSolidScaled(0, 0, 0,vs, W.CachePatchName("HELP1")); break; default: break; } // Maes: we need to do this here, otherwide the status bar appears "dirty" DM.ST.forceRefresh(); return; } } /** * Read This Menus - optional second page. */ class M_DrawReadThis2 implements DrawRoutine { @Override public void invoke() { inhelpscreens = true; switch (DM.getGameMode()) { case retail: case commercial: // This hack keeps us from having to change menus. V.DrawPatchSolidScaled(0, 0, 0, vs,W.CachePatchName("CREDIT")); break; case shareware: case registered: V.DrawPatchSolidScaled(0, 0, 0, vs,W.CachePatchName("HELP2")); break; default: break; } // Maes: we need to do this here, otherwide the status bar appears "dirty" DM.ST.forceRefresh(); return; } } /** * Change Sfx & Music volumes */ class M_DrawSound implements DrawRoutine { public void invoke() { V.DrawScaledPatch(60, 38, 0,vs, (patch_t) W.CacheLumpName("M_SVOL", PU_CACHE, patch_t.class)); DrawThermo(SoundDef.x, SoundDef.y + LINEHEIGHT * (sfx_vol + 1), 16, DM.snd_SfxVolume); DrawThermo(SoundDef.x, SoundDef.y + LINEHEIGHT * (music_vol + 1), 16, DM.snd_MusicVolume); } } // /////////////////////////// MENU ROUTINES // /////////////////////////////////// class M_ChangeDetail implements MenuRoutine { @Override public void invoke(int choice) { choice = 0; detailLevel = 1 - detailLevel; // FIXME - does not work. Remove anyway? //System.err.print("M_ChangeDetail: low detail mode n.a.\n"); //return; R.SetViewSize (screenblocks, detailLevel); if (detailLevel==0) DM.players[DM.consoleplayer].message = englsh.DETAILHI; else DM.players[DM.consoleplayer].message = englsh.DETAILLO; } } /** * Toggle messages on/off */ class M_ChangeMessages implements MenuRoutine { @Override public void invoke(int choice) { // warning: unused parameter `int choice' //choice = 0; showMessages = !showMessages; if (!showMessages) DM.players[DM.consoleplayer].message = MSGOFF; else DM.players[DM.consoleplayer].message = MSGON; message_dontfuckwithme = true; } } class M_ChangeSensitivity implements MenuRoutine { @Override public void invoke(int choice) { switch (choice) { case 0: if (DM.mouseSensitivity != 0) DM.mouseSensitivity--; break; case 1: if (DM.mouseSensitivity < 9) DM.mouseSensitivity++; break; } } } class M_ChooseSkill implements MenuRoutine { @Override public void invoke(int choice) { if (choice == nightmare) { StartMessage(NIGHTMARE, VerifyNightmare, true); return; } DG.DeferedInitNew(skill_t.values()[choice], epi + 1, 1); ClearMenus(); } } /** * M_EndGame */ class M_EndGame implements MenuRoutine { @Override public void invoke(int choice) { choice = 0; if (!DM.usergame) { S.StartSound(null, sfxenum_t.sfx_oof); return; } if (DM.netgame) { StartMessage(NETEND, null, false); return; } StartMessage(ENDGAME, EndGameResponse, true); } } class M_EndGameResponse implements MenuRoutine { @Override public void invoke(int ch) { if (ch != 'y') return; currentMenu.lastOn = itemOn; ClearMenus(); DG.StartTitle(); } } class M_Episode implements MenuRoutine { @Override public void invoke(int choice) { if (DM.isShareware() && (choice != 0)) { StartMessage(SWSTRING, null, false); SetupNextMenu(ReadDef2); return; } // Yet another hack... if (!DM.isRetail() && (choice > 2)) { System.err .print("M_Episode: 4th episode requires UltimateDOOM\n"); choice = 0; } epi = choice; SetupNextMenu(NewDef); } } /** * User wants to load this game */ class M_LoadSelect implements MenuRoutine { @Override public void invoke(int choice) { String name; if (DM.CM.CheckParm("-cdrom") != 0) name = ("c:\\doomdata\\" + SAVEGAMENAME + (choice) + ".dsg"); else name = (SAVEGAMENAME + (choice) + ".dsg"); DG.LoadGame(name); ClearMenus(); } } /** * Selected from DOOM menu */ class M_LoadGame implements MenuRoutine { @Override public void invoke(int choice) { if (DM.netgame) { StartMessage(LOADNET, null, false); return; } SetupNextMenu(LoadDef); ReadSaveStrings(); } } // ////////////////////// VARIOUS CONSTS ////////////////////// private static final sfxenum_t[] quitsounds = { sfxenum_t.sfx_pldeth, sfxenum_t.sfx_dmpain, sfxenum_t.sfx_popain, sfxenum_t.sfx_slop, sfxenum_t.sfx_telept, sfxenum_t.sfx_posit1, sfxenum_t.sfx_posit3, sfxenum_t.sfx_sgtatk }; private static final sfxenum_t[] quitsounds2 = { sfxenum_t.sfx_vilact, sfxenum_t.sfx_getpow, sfxenum_t.sfx_boscub, sfxenum_t.sfx_slop, sfxenum_t.sfx_skeswg, sfxenum_t.sfx_kntdth, sfxenum_t.sfx_bspact, sfxenum_t.sfx_sgtatk }; /** episodes_e enum */ private static final int ep1 = 0, ep2 = 1, ep3 = 2, ep4 = 3, ep_end = 4; /** load_e enum */ private static final int load1 = 0, load2 = 1, load3 = 2, load4 = 3, load5 = 4, load6 = 5, load_end = 6; /** options_e enum; */ private static final int endgame = 0, messages = 1, detail = 2, scrnsize = 3, option_empty1 = 4, mousesens = 5, option_empty2 = 6, soundvol = 7, opt_end = 8; /** main_e enum; */ private static final int newgame = 0, options = 1, loadgam = 2, savegame = 3, readthis = 4, quitdoom = 5, main_end = 6; /** read_e enum */ private static final int rdthsempty1 = 0, read1_end = 1; /** read_2 enum */ private static final int rdthsempty2 = 0, read2_end = 1; /** newgame_e enum;*/ public static final int killthings = 0, toorough = 1, hurtme = 2, violence = 3, nightmare = 4, newg_end = 5; private static final String[] gammamsg = { "GAMMALVL0", "GAMMALVL1", "GAMMALVL2", "GAMMALVL3", "GAMMALVL4" }; /** sound_e enum */ static final int sfx_vol = 0, sfx_empty1 = 1, music_vol = 2, sfx_empty2 = 3, sound_end = 4; @Override public void setScreenBlocks(int val) { this.screenblocks=val; } @Override public int getScreenBlocks() { return this.screenblocks; } @Override public int getDetailLevel() { return this.detailLevel; } ////////////////////////////VIDEO SCALE STUFF //////////////////////////////// protected int SCREENWIDTH; protected int SCREENHEIGHT; protected IVideoScale vs; @Override public void setVideoScale(IVideoScale vs) { this.vs=vs; } @Override public void initScaling() { this.SCREENHEIGHT=vs.getScreenHeight(); this.SCREENWIDTH=vs.getScreenWidth(); } } package m; public interface IUseVariables { /** Register a variable manager with this module. * * @param manager */ public void registerVariableManager(IVariablesManager manager); /** Apply listener-specific variables, asking the manager for them. * Every listener should concern itself with its own variables/settings. * * This method should be called by the manager on every registered * listener. Each listener then "knows" which settings it must update. * * Good for block updates, but maybe a more lightweight mechanism should * be provided, e.g. being able to update just one setting for a listener. * */ public void update(); /** If the variables user makes too many changes, it may be better to * communicate them back to the manager in-block. This shouldn't be needed, * if everywhere a certain setting has to be modified tis done through the * manager. */ public void commit(); } package m; public interface IVariablesManager { /** Does two things: sets a variable, and then calls setme. * This way, immediate updates of a single variables user * are possible, avoiding some of the overhead. * * Sadly, users with too many variables will still incur an * O(n) penalty. * * @param setme * @param name * @param value */ public void applySetting(IUseVariables setme,String name, String value); public DoomSetting getSetting(String name); public DoomSetting getSetting(Settings name); public boolean isSettingLiteral(String name,String value); /** Creates a new setting, overriding any existing ones. * * @param name * @param value * @param persist */ public void putSetting(String name, Object value, boolean persist); /** Puts a new setting or updates an existing one. In this case, * the value of the "persist" field is kept unaltered, so that persistent * settings are not lost during updates. * * @param name * @param value */ public void putSetting(String name, Object value); public void putSetting(Settings name, Object value); void LoadDefaults(String defaultfile); void SaveDefaults(String defaultfile); String getDefaultFile(); } package m; import v.IVideoScale; import doom.DoomStatus; import doom.event_t; /** A dummy menu, useful for testers that do need a defined * menu object. * * @author Maes * */ public class DummyMenu extends AbstractDoomMenu { @Override public void setVideoScale(IVideoScale vs) { // TODO Auto-generated method stub } @Override public void initScaling() { // TODO Auto-generated method stub } @Override public boolean Responder(event_t ev) { // TODO Auto-generated method stub return false; } @Override public void Ticker() { // TODO Auto-generated method stub } @Override public void Drawer() { // TODO Auto-generated method stub } @Override public void Init() { // TODO Auto-generated method stub } @Override public void StartControlPanel() { // TODO Auto-generated method stub } @Override public boolean getShowMessages() { // TODO Auto-generated method stub return false; } @Override public void setShowMessages(boolean val) { // TODO Auto-generated method stub } @Override public int getScreenBlocks() { // TODO Auto-generated method stub return 0; } @Override public void setScreenBlocks(int val) { // TODO Auto-generated method stub } @Override public int getDetailLevel() { // TODO Auto-generated method stub return 0; } @Override public void updateStatus(DoomStatus DS) { // TODO Auto-generated method stub } @Override public void ClearMenus() { // TODO Auto-generated method stub } } package m; public class default_t { public default_t(String name, int[] location, int defaultvalue) { this.name = name; this.location = location; this.defaultvalue = defaultvalue; } public String name; /** this is supposed to be a pointer */ public int[] location; public int defaultvalue; int scantranslate; // PC scan code hack int untranslated; // lousy hack }; package st; enum st_chatstateenum_t { StartChatState, WaitDestState, GetChatState } package st; // Emacs style mode select -*- C++ -*- // ----------------------------------------------------------------------------- // // $Id: StatusBar.java,v 1.47 2011/11/01 23:46:37 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: // Status bar code. // Does the face/direction indicator animatin. // Does palette indicators as well (red pain/berserk, bright pickup) // // ----------------------------------------------------------------------------- import defines.*; import static data.Limits.MAXPLAYERS; import static g.Keys.*; import static data.Defines.*; import static doom.englsh.*; import static automap.IAutoMap.*; import static doom.player_t.*; import static doom.items.*; import static data.Tables.*; import p.mobj_t; import m.cheatseq_t; import data.sounds.musicenum_t; import doom.DoomStatus; import doom.event_t; import doom.evtype_t; import doom.player_t; import doom.weapontype_t; import rr.patch_t; import v.IVideoScale; import static v.DoomVideoRenderer.*; public class StatusBar extends AbstractStatusBar { public static final String rcsid = "$Id: StatusBar.java,v 1.47 2011/11/01 23:46:37 velktron Exp $"; // Size of statusbar. // Now sensitive for scaling. // // STATUS BAR DATA // // Palette indices. // For damage/bonus red-/gold-shifts private static int STARTREDPALS = 1; private static int STARTBONUSPALS = 9; private static int NUMREDPALS = 8; private static int NUMBONUSPALS = 4; // Radiation suit, green shift. private static int RADIATIONPAL = 13; // N/256*100% probability // that the normal face state will change private static int ST_FACEPROBABILITY = 96; // For Responder private static int ST_TOGGLECHAT = KEY_ENTER; // Location of status bar private int ST_X = 0; private int ST_X2; private int ST_FX; private int ST_FY; // Should be set to patch width // for tall numbers later on // TODO: private static int ST_TALLNUMWIDTH = (tallnum[0].width); // Number of status faces. private static int ST_NUMPAINFACES = 5; private static int ST_NUMSTRAIGHTFACES = 3; private static int ST_NUMTURNFACES = 2; private static int ST_NUMSPECIALFACES = 3; private static int ST_FACESTRIDE = (ST_NUMSTRAIGHTFACES + ST_NUMTURNFACES + ST_NUMSPECIALFACES); private static int ST_NUMEXTRAFACES = 2; private static int ST_NUMFACES = (ST_FACESTRIDE * ST_NUMPAINFACES + ST_NUMEXTRAFACES); private static int ST_TURNOFFSET = (ST_NUMSTRAIGHTFACES); private static int ST_OUCHOFFSET = (ST_TURNOFFSET + ST_NUMTURNFACES); private static int ST_EVILGRINOFFSET = (ST_OUCHOFFSET + 1); private static int ST_RAMPAGEOFFSET = (ST_EVILGRINOFFSET + 1); private static int ST_GODFACE = (ST_NUMPAINFACES * ST_FACESTRIDE); private static int ST_DEADFACE = (ST_GODFACE + 1); private int ST_FACESX; private int ST_FACESY; private static int ST_EVILGRINCOUNT = (2 * TICRATE); private static int ST_STRAIGHTFACECOUNT = (TICRATE / 2); private static int ST_TURNCOUNT = (1 * TICRATE); private static int ST_OUCHCOUNT = (1 * TICRATE); private static int ST_RAMPAGEDELAY = (2 * TICRATE); private static int ST_MUCHPAIN = 20; // Location and size of statistics, // justified according to widget type. // Problem is, within which space? STbar? Screen? // Note: this could be read in by a lump. // Problem is, is the stuff rendered // into a buffer, // or into the frame buffer? // AMMO number pos. private int ST_AMMOWIDTH; private int ST_AMMOX; private int ST_AMMOY; // HEALTH number pos. private int ST_HEALTHWIDTH=3; private int ST_HEALTHX; private int ST_HEALTHY; // Weapon pos. private int ST_ARMSX; private int ST_ARMSY; private int ST_ARMSBGX; private int ST_ARMSBGY; private int ST_ARMSXSPACE; private int ST_ARMSYSPACE; // Frags pos. private int ST_FRAGSX; private int ST_FRAGSY; private int ST_FRAGSWIDTH; // ARMOR number pos. private int ST_ARMORWIDTH = 3; private int ST_ARMORX; private int ST_ARMORY; // Key icon positions. private int ST_KEY0WIDTH; private int ST_KEY0HEIGHT; private int ST_KEY0X; private int ST_KEY0Y; private int ST_KEY1WIDTH; private int ST_KEY1X; private int ST_KEY1Y; private int ST_KEY2WIDTH; private int ST_KEY2X; private int ST_KEY2Y; // Ammunition counter. private int ST_AMMO0WIDTH; private int ST_AMMO0HEIGHT; private int ST_AMMO0X; private int ST_AMMO0Y; private int ST_AMMO1WIDTH; private int ST_AMMO1X; private int ST_AMMO1Y; private int ST_AMMO2WIDTH; private int ST_AMMO2X; private int ST_AMMO2Y; private int ST_AMMO3WIDTH; private int ST_AMMO3X; private int ST_AMMO3Y; // Indicate maximum ammunition. // Only needed because backpack exists. private int ST_MAXAMMO0WIDTH; private int ST_MAXAMMO0HEIGHT; private int ST_MAXAMMO0X; private int ST_MAXAMMO0Y; private int ST_MAXAMMO1WIDTH; private int ST_MAXAMMO1X; private int ST_MAXAMMO1Y; private int ST_MAXAMMO2WIDTH; private int ST_MAXAMMO2X; private int ST_MAXAMMO2Y; private int ST_MAXAMMO3WIDTH; private int ST_MAXAMMO3X; private int ST_MAXAMMO3Y; // pistol private int ST_WEAPON0X; private int ST_WEAPON0Y; // shotgun private int ST_WEAPON1X; private int ST_WEAPON1Y; // chain gun private int ST_WEAPON2X; private int ST_WEAPON2Y; // missile launcher private int ST_WEAPON3X; private int ST_WEAPON3Y; // plasma gun private int ST_WEAPON4X; private int ST_WEAPON4Y; // bfg private int ST_WEAPON5X; private int ST_WEAPON5Y; // WPNS title private int ST_WPNSX; private int ST_WPNSY; // DETH title private int ST_DETHX; private int ST_DETHY; // Incoming messages window location // UNUSED // #define ST_MSGTEXTX (viewwindowx) // #define ST_MSGTEXTY (viewwindowy+viewheight-18) private static int ST_MSGTEXTX = 0; private static int ST_MSGTEXTY = 0; // Dimensions given in characters. private static int ST_MSGWIDTH = 52; // Or shall I say, in lines? private static int ST_MSGHEIGHT = 1; private static int ST_OUTTEXTX = 0; private static int ST_OUTTEXTY = 6; // Width, in characters again. private static int ST_OUTWIDTH = 52; // Height, in lines. private static int ST_OUTHEIGHT = 1; // TODO private static int ST_MAPWIDTH = // (mapnames[(gameepisode-1)*9+(gamemap-1)].length)); // TODO private static int ST_MAPTITLEX = (SCREENWIDTH - ST_MAPWIDTH * // ST_CHATFONTWIDTH); private static int ST_MAPTITLEY = 0; private static int ST_MAPHEIGHT = 1; // main player in game private player_t plyr; // ST_Start() has just been called, OR we want to force an redraw anyway. private boolean st_firsttime; @Override public void forceRefresh(){ st_firsttime=true; } // used to execute ST_Init() only once private int veryfirsttime = 1; // lump number for PLAYPAL private int lu_palette; // used for timing (unsigned int .. maybe long !) private long st_clock; // used for making messages go away int st_msgcounter = 0; // used when in chat private st_chatstateenum_t st_chatstate; // whether in automap or first-person private st_stateenum_t st_gamestate; /** whether left-side main status bar is active. This fugly hax * (and others like it) are necessary in order to have something * close to a pointer. */ private boolean[] st_statusbaron={false}; // whether status bar chat is active private boolean st_chat; // value of st_chat before message popped up private boolean st_oldchat; // whether chat window has the cursor on private boolean[] st_cursoron={false}; /** !deathmatch */ private boolean[] st_notdeathmatch={true}; /** !deathmatch && st_statusbaron */ private boolean[] st_armson={true}; /** !deathmatch */ private boolean[] st_fragson={false}; // main bar left private patch_t sbar; // 0-9, tall numbers private patch_t[] tallnum = new patch_t[10]; // tall % sign private patch_t tallpercent; // 0-9, short, yellow (,different!) numbers private patch_t[] shortnum = new patch_t[10]; // 3 key-cards, 3 skulls private patch_t[] keys = new patch_t[NUMCARDS]; // face status patches private patch_t[] faces = new patch_t[ST_NUMFACES]; // face background private patch_t faceback; // main bar right private patch_t armsbg; // weapon ownership patches private patch_t[][] arms = new patch_t[6][2]; // // WIDGETS ///// // ready-weapon widget private st_number_t w_ready; // in deathmatch only, summary of frags stats private st_number_t w_frags; // health widget private st_percent_t w_health; // arms background private st_binicon_t w_armsbg; // weapon ownership widgets private st_multicon_t[] w_arms = new st_multicon_t[6]; // face status widget private st_multicon_t w_faces; // keycard widgets private st_multicon_t[] w_keyboxes = new st_multicon_t[3]; // armor widget private st_percent_t w_armor; // ammo widgets private st_number_t[] w_ammo = new st_number_t[4]; // max ammo widgets private st_number_t[] w_maxammo = new st_number_t[4]; // / END WIDGETS //// // number of frags so far in deathmatch private int[] st_fragscount={0}; // used to use appopriately pained face private int st_oldhealth = -1; // used for evil grin private boolean[] oldweaponsowned = new boolean[NUMWEAPONS]; // count until face changes private int st_facecount = 0; // current face index, used by w_faces private int[] st_faceindex = new int[1]; // holds key-type for each key box on bar private int[] keyboxes = new int[3]; // a random number per tick private int st_randomnumber; // idmypos toggle mode private boolean st_idmypos=false; // Massive bunches of cheat shit // to keep it from being easy to figure them out. // Yeah, right... private char cheat_mus_seq[] = { 0xb2, 0x26, 0xb6, 0xae, 0xea, 1, 0, 0, 0xff }; private char cheat_choppers_seq[] = { 0xb2, 0x26, 0xe2, 0x32, 0xf6, 0x2a, 0x2a, 0xa6, 0x6a, 0xea, 0xff // id... }; private char cheat_god_seq[] = { 0xb2, 0x26, 0x26, 0xaa, 0x26, 0xff // iddqd }; private char cheat_ammo_seq[] = { 0xb2, 0x26, 0xf2, 0x66, 0xa2, 0xff // idkfa }; private char cheat_ammonokey_seq[] = { 0xb2, 0x26, 0x66, 0xa2, 0xff // idfa }; // Smashing Pumpkins Into Samml Piles Of Putried Debris. private char cheat_noclip_seq[] = { 0xb2, 0x26, 0xea, 0x2a, 0xb2, // idspispopd 0xea, 0x2a, 0xf6, 0x2a, 0x26, 0xff }; // private char cheat_commercial_noclip_seq[] = { 0xb2, 0x26, 0xe2, 0x36, 0xb2, 0x2a, 0xff // idclip }; private char cheat_powerup_seq[][] = { { 0xb2, 0x26, 0x62, 0xa6, 0x32, 0xf6, 0x36, 0x26, 0x6e, 0xff }, // beholdv { 0xb2, 0x26, 0x62, 0xa6, 0x32, 0xf6, 0x36, 0x26, 0xea, 0xff }, // beholds { 0xb2, 0x26, 0x62, 0xa6, 0x32, 0xf6, 0x36, 0x26, 0xb2, 0xff }, // beholdi { 0xb2, 0x26, 0x62, 0xa6, 0x32, 0xf6, 0x36, 0x26, 0x6a, 0xff }, // beholdr { 0xb2, 0x26, 0x62, 0xa6, 0x32, 0xf6, 0x36, 0x26, 0xa2, 0xff }, // beholda { 0xb2, 0x26, 0x62, 0xa6, 0x32, 0xf6, 0x36, 0x26, 0x36, 0xff }, // beholdl { 0xb2, 0x26, 0x62, 0xa6, 0x32, 0xf6, 0x36, 0x26, 0xff } // behold }; private char cheat_clev_seq[] = { 0xb2, 0x26, 0xe2, 0x36, 0xa6, 0x6e, 1, 0, 0, 0xff // idclev }; // my position cheat private char cheat_mypos_seq[] = { 0xb2, 0x26, 0xb6, 0xba, 0x2a, 0xf6, 0xea, 0xff // idmypos }; // Now what? cheatseq_t cheat_mus = new cheatseq_t(cheat_mus_seq, 0); cheatseq_t cheat_god = new cheatseq_t(cheat_god_seq, 0); cheatseq_t cheat_ammo = new cheatseq_t(cheat_ammo_seq, 0); cheatseq_t cheat_ammonokey = new cheatseq_t(cheat_ammonokey_seq, 0); cheatseq_t cheat_noclip = new cheatseq_t(cheat_noclip_seq, 0); cheatseq_t cheat_commercial_noclip = new cheatseq_t(cheat_commercial_noclip_seq, 0); cheatseq_t[] cheat_powerup = { new cheatseq_t(cheat_powerup_seq[0], 0), new cheatseq_t(cheat_powerup_seq[1], 0), new cheatseq_t(cheat_powerup_seq[2], 0), new cheatseq_t(cheat_powerup_seq[3], 0), new cheatseq_t(cheat_powerup_seq[4], 0), new cheatseq_t(cheat_powerup_seq[5], 0), new cheatseq_t(cheat_powerup_seq[6], 0) }; cheatseq_t cheat_choppers = new cheatseq_t(cheat_choppers_seq, 0); cheatseq_t cheat_clev = new cheatseq_t(cheat_clev_seq, 0); cheatseq_t cheat_mypos = new cheatseq_t(cheat_mypos_seq, 0); cheatseq_t cheat_tnthom= new cheatseq_t("tnthom",false); // String[] mapnames; // // STATUS BAR CODE // public StatusBar(DoomStatus DC) { this.updateStatus(DC); //this.plyr=DM.players[DM.] } public void refreshBackground() { if (st_statusbaron[0]) { V.DrawPatchSolidScaled(ST_X, 0, SAFE_SCALE, SAFE_SCALE, BG, sbar); //V.DrawPatch(ST_X, 0, BG, sbar); if (DM.netgame) V.DrawScaledPatch(ST_FX, 0, BG,vs, faceback); //V.DrawPatch(ST_FX, 0, BG, faceback); // Buffers the background. V.CopyRect(ST_X, 0, BG, ST_WIDTH, ST_HEIGHT, ST_X, ST_Y, FG); } } public void Init() { veryfirsttime = 0; loadData(); // MAES: screen(4) of the Video Renderer is actually reserved for the status bar. // The "clean" status bar is cached in there, and redrawn only as required. this.V.setScreen(BG,ST_WIDTH,ST_HEIGHT); } protected boolean st_stopped = true; public void Start() { if (!st_stopped) Stop(); initData(); createWidgets(); st_stopped = false; } public void Stop() { if (st_stopped) return; // Reset palette. VI.SetPalette (0); st_stopped = true; } public void loadData() { lu_palette = W.GetNumForName("PLAYPAL"); loadGraphics(); } // Respond to keyboard input events, // intercept cheats. public boolean Responder(event_t ev) { int i; // Filter automap on/off. if (ev.type == evtype_t.ev_keyup && ((ev.data1 & 0xffff0000) == AM_MSGHEADER)) { switch (ev.data1) { case AM_MSGENTERED: st_gamestate = st_stateenum_t.AutomapState; st_firsttime = true; break; case AM_MSGEXITED: // fprintf(stderr, "AM exited\n"); st_gamestate = st_stateenum_t.FirstPersonState; break; } } // if a user keypress... else if (ev.type == evtype_t.ev_keydown) { if (!DM.netgame) { // b. - enabled for more debug fun. // if (gameskill != sk_nightmare) { // 'dqd' cheat for toggleable god mode if (cheat_god.CheckCheat((char) ev.data1)) { plyr.cheats ^= CF_GODMODE; if ((plyr.cheats & CF_GODMODE) != 0) { if (plyr.mo != null) plyr.mo.health = 100; plyr.health[0] = 100; plyr.message = STSTR_DQDON; } else plyr.message = STSTR_DQDOFF; } // 'fa' cheat for killer fucking arsenal else if (cheat_ammonokey.CheckCheat((char) ev.data1)) { plyr.armorpoints[0] = 200; plyr.armortype = 2; for (i = 0; i < NUMWEAPONS; i++) plyr.weaponowned[i] = true; // true for (i = 0; i < NUMAMMO; i++) plyr.ammo[i] = plyr.maxammo[i]; plyr.message = STSTR_FAADDED; } // 'kfa' cheat for key full ammo else if (cheat_ammo.CheckCheat((char) ev.data1)) { plyr.armorpoints[0] = 200; plyr.armortype = 2; for (i = 0; i < NUMWEAPONS; i++) plyr.weaponowned[i] = true; // true for (i = 0; i < NUMAMMO; i++) plyr.ammo[i] = plyr.maxammo[i]; for (i = 0; i < NUMCARDS; i++) plyr.cards[i] = true; plyr.message = STSTR_KFAADDED; } // 'mus' cheat for changing music else if (cheat_mus.CheckCheat((char) ev.data1)) { char[] buf = new char[3]; int musnum; plyr.message = STSTR_MUS; cheat_mus.GetParam(buf); if (DM.isCommercial()) { musnum = musicenum_t.mus_runnin.ordinal() + (buf[0] - '0') * 10 + buf[1] - '0' - 1; if (((buf[0] - '0') * 10 + buf[1] - '0') > 35) plyr.message = STSTR_NOMUS; else S.ChangeMusic(musnum, true); } else { musnum = musicenum_t.mus_e1m1.ordinal() + (buf[0] - '1') * 9 + (buf[1] - '1'); if (((buf[0] - '1') * 9 + buf[1] - '1') > 31) plyr.message = STSTR_NOMUS; else S.ChangeMusic(musnum, true); } } // Simplified, accepting both "noclip" and "idspispopd". // no clipping mode cheat else if (cheat_noclip.CheckCheat((char) ev.data1) || cheat_commercial_noclip.CheckCheat((char) ev.data1)) { plyr.cheats ^= CF_NOCLIP; if ((plyr.cheats & CF_NOCLIP) != 0) plyr.message = STSTR_NCON; else plyr.message = STSTR_NCOFF; } // 'behold?' power-up cheats for (i = 0; i < 6; i++) { if (cheat_powerup[i].CheckCheat((char) ev.data1)) { if (plyr.powers[i] == 0) plyr.GivePower(i); else if (i != pw_strength) plyr.powers[i] = 1; else plyr.powers[i] = 0; plyr.message = STSTR_BEHOLDX; } } // 'behold' power-up menu if (cheat_powerup[6].CheckCheat((char) ev.data1)) { plyr.message = STSTR_BEHOLD; } // 'choppers' invulnerability & chainsaw else if (cheat_choppers.CheckCheat((char) ev.data1)) { plyr.weaponowned[weapontype_t.wp_chainsaw.ordinal()] = true; plyr.powers[pw_invulnerability] = 1; // true plyr.message = STSTR_CHOPPERS; } // 'mypos' for player position else if (cheat_mypos.CheckCheat((char) ev.data1)) { // MAES: made into a toggleable cheat. this.st_idmypos=!st_idmypos; } else if (cheat_tnthom.CheckCheat((char) ev.data1)) { // MAES: made into a toggleable cheat. plyr.message = (DM.flashing_hom = !DM.flashing_hom) ? "HOM Detection On" : "HOM Detection Off"; } } // 'clev' change-level cheat if (cheat_clev.CheckCheat((char) ev.data1)) { char[] buf = new char[3]; int epsd; int map; cheat_clev.GetParam(buf); // This applies to Doom II, Plutonia and TNT. if (DM.isCommercial()) { epsd = 0; map = (buf[0] - '0') * 10 + buf[1] - '0'; } else { epsd = buf[0] - '0'; map = buf[1] - '0'; } // Catch invalid maps. if (epsd < 1 && (!DM.isCommercial())) return false; if (map < 1) return false; // Ohmygod - this is not going to work. if (DM.isRetail() && ((epsd > 4) || (map > 9))) return false; // MAES: If it's doom.wad but not ultimate if (DM.isRegistered()&& !DM.isRetail() && ((epsd > 3) || (map > 9))) return false; if (DM.isShareware() && ((epsd > 1) || (map > 9))) return false; if (DM.isCommercial() && ((epsd > 1) || (map > 34))) return false; // So be it. plyr.message = STSTR_CLEV; DM.DeferedInitNew(DM.gameskill, epsd, map); } } return false; } protected int lastcalc; protected int oldhealth = -1; public int calcPainOffset() { int health = 0; health = plyr.health[0] > 100 ? 100 : plyr.health[0]; if (health != oldhealth) { lastcalc = ST_FACESTRIDE * (((100 - health) * ST_NUMPAINFACES) / 101); oldhealth = health; } return lastcalc; } protected int lastattackdown = -1; protected int priority = 0; /** * This is a not-very-pretty routine which handles the face states and their * timing. the precedence of expressions is: dead > evil grin > turned head * > straight ahead */ public void updateFaceWidget() { long badguyangle; // angle_t long diffang; boolean doevilgrin; if (priority < 10) { // dead if (plyr.health[0] == 0) { priority = 9; st_faceindex[0] = ST_DEADFACE; st_facecount = 1; } } if (priority < 9) { if (plyr.bonuscount != 0) { // picking up bonus doevilgrin = false; for (int i = 0; i < NUMWEAPONS; i++) { if (oldweaponsowned[i] != plyr.weaponowned[i]) { doevilgrin = true; oldweaponsowned[i] = plyr.weaponowned[i]; } } if (doevilgrin) { // evil grin if just picked up weapon priority = 8; st_facecount = ST_EVILGRINCOUNT; st_faceindex[0] = calcPainOffset() + ST_EVILGRINOFFSET; } } } if (priority < 8) { if ((plyr.damagecount != 0) && (plyr.attacker != null) && (plyr.attacker != plyr.mo)) { // being attacked priority = 7; if (plyr.health[0] - st_oldhealth > ST_MUCHPAIN) { st_facecount = ST_TURNCOUNT; st_faceindex[0] = calcPainOffset() + ST_OUCHOFFSET; } else { badguyangle = R.PointToAngle2(plyr.mo.x, plyr.mo.y, plyr.attacker.x, plyr.attacker.y); boolean obtuse; // that's another "i" if (badguyangle > plyr.mo.angle) { // whether right or left diffang = badguyangle - plyr.mo.angle; obtuse = diffang > ANG180; } else { // whether left or right diffang = plyr.mo.angle - badguyangle; obtuse = diffang <= ANG180; } // confusing, aint it? st_facecount = ST_TURNCOUNT; st_faceindex[0] = calcPainOffset(); if (diffang < ANG45) { // head-on st_faceindex[0] += ST_RAMPAGEOFFSET; } else if (obtuse) { // turn face right st_faceindex[0] += ST_TURNOFFSET; } else { // turn face left st_faceindex[0] += ST_TURNOFFSET + 1; } } } } if (priority < 7) { // getting hurt because of your own damn stupidity if (plyr.damagecount != 0) { if (plyr.health[0] - st_oldhealth > ST_MUCHPAIN) { priority = 7; st_facecount = ST_TURNCOUNT; st_faceindex[0] = calcPainOffset() + ST_OUCHOFFSET; } else { priority = 6; st_facecount = ST_TURNCOUNT; st_faceindex[0] = calcPainOffset() + ST_RAMPAGEOFFSET; } } } if (priority < 6) { // rapid firing if (plyr.attackdown) { if (lastattackdown == -1) lastattackdown = ST_RAMPAGEDELAY; else if (--lastattackdown == 0) { priority = 5; st_faceindex[0] = calcPainOffset() + ST_RAMPAGEOFFSET; st_facecount = 1; lastattackdown = 1; } } else lastattackdown = -1; } if (priority < 5) { // invulnerability if (((plyr.cheats & CF_GODMODE) != 0) || (plyr.powers[pw_invulnerability] != 0)) { priority = 4; st_faceindex[0] = ST_GODFACE; st_facecount = 1; } } // look left or look right if the facecount has timed out if (st_facecount == 0) { st_faceindex[0] = calcPainOffset() + (st_randomnumber % 3); st_facecount = ST_STRAIGHTFACECOUNT; priority = 0; } st_facecount--; } protected int largeammo = 1994; // means "n/a" /** * MAES: this code updated the widgets. Now, due to the way they are * constructed, they originally were "hooked" to actual variables using * pointers so that they could tap into them directly and self-update. * Clearly we can't do that in Java unless said variables are inside an * array and we provide both the array AND an index. For other cases, we * must simply build ad-hoc hacks. * * In any case, only "status" updates are performed here. Actual visual * updates are performed by the Drawer. * */ public void updateWidgets() { int i; // MAES: sticky idmypos cheat that is actually useful // TODO: this spams the player message queue at every tic. // A direct overlay with a widget would be more useful. if (this.st_idmypos){ mobj_t mo = DM.players[DM.consoleplayer].mo; plyr.message = String.format("ang= 0x%x; x,y= (%x, %x)", (int)mo.angle,mo.x,mo.y); } // must redirect the pointer if the ready weapon has changed. // if (w_ready.data != plyr.readyweapon) // { if (weaponinfo[plyr.readyweapon.ordinal()].ammo == ammotype_t.am_noammo) w_ready.numindex = largeammo; else w_ready.numindex = weaponinfo[plyr.readyweapon.ordinal()].ammo.ordinal(); w_ready.data = plyr.readyweapon.ordinal(); // if (*w_ready.on) // STlib_updateNum(&w_ready, true); // refresh weapon change // } // update keycard multiple widgets for (i = 0; i < 3; i++) { keyboxes[i] = plyr.cards[i] ? i : -1; if (plyr.cards[i + 3]) keyboxes[i] = i + 3; } // refresh everything if this is him coming back to life updateFaceWidget(); // used by the w_armsbg widget st_notdeathmatch[0] = !DM.deathmatch; // used by w_arms[] widgets st_armson[0] = st_statusbaron[0] && !(DM.altdeath||DM.deathmatch); // used by w_frags widget st_fragson[0] = (DM.altdeath||DM.deathmatch) && st_statusbaron[0]; st_fragscount[0] = 0; for (i = 0; i < MAXPLAYERS; i++) { if (i != DM.consoleplayer) st_fragscount[0] += plyr.frags[i]; else st_fragscount[0] -= plyr.frags[i]; } // get rid of chat window if up because of message if (--st_msgcounter == 0) st_chat = st_oldchat; } public void Ticker() { st_clock++; st_randomnumber = RND.M_Random(); updateWidgets(); st_oldhealth = plyr.health[0]; } static int st_palette = 0; public void doPaletteStuff() { int palette; //byte[] pal; int cnt; int bzc; cnt = plyr.damagecount; if (plyr.powers[pw_strength] != 0) { // slowly fade the berzerk out bzc = 12 - (plyr.powers[pw_strength] >> 6); if (bzc > cnt) cnt = bzc; } if (cnt != 0) { palette = (cnt + 7) >> 3; if (palette >= NUMREDPALS) palette = NUMREDPALS - 1; palette += STARTREDPALS; } else if (plyr.bonuscount != 0) { palette = (plyr.bonuscount + 7) >> 3; if (palette >= NUMBONUSPALS) palette = NUMBONUSPALS - 1; palette += STARTBONUSPALS; } else if (plyr.powers[pw_ironfeet] > 4 * 32 || (plyr.powers[pw_ironfeet] & 8) != 0) palette = RADIATIONPAL; else palette = 0; if (palette != st_palette) { st_palette = palette; VI.SetPalette (palette); } } public void drawWidgets(boolean refresh) { int i; // used by w_arms[] widgets st_armson[0] = st_statusbaron[0] && !(DM.altdeath||DM.deathmatch); // used by w_frags widget st_fragson[0] = DM.deathmatch && st_statusbaron[0]; w_ready.update(refresh); for (i = 0; i < 4; i++) { w_ammo[i].update(refresh); w_maxammo[i].update(refresh); } w_health.update(refresh); w_armor.update(refresh); w_armsbg.update(refresh); for (i = 0; i < 6; i++) w_arms[i].update(refresh); w_faces.update(refresh); for (i = 0; i < 3; i++) w_keyboxes[i].update(refresh); w_frags.update(refresh); } public void doRefresh() { st_firsttime = false; // draw status bar background to off-screen buff refreshBackground(); // and refresh all widgets drawWidgets(true); } public void diffDraw() { // update all widgets drawWidgets(false); } public void Drawer(boolean fullscreen, boolean refresh) { st_statusbaron[0] = (!fullscreen) || DM.automapactive; st_firsttime = st_firsttime || refresh; // Do red-/gold-shifts from damage/items doPaletteStuff(); // If just after ST_Start(), refresh all if (st_firsttime) doRefresh(); // Otherwise, update as little as possible else diffDraw(); } public void loadGraphics() { int i; int j; int facenum; String namebuf; // Load the numbers, tall and short for (i = 0; i < 10; i++) { namebuf = ("STTNUM" + i); tallnum[i] = W.CachePatchName(namebuf, PU_STATIC); namebuf = ("STYSNUM" + i); shortnum[i] = W.CachePatchName(namebuf, PU_STATIC); } // Load percent key. // Note: why not load STMINUS here, too? tallpercent = W.CachePatchName("STTPRCNT", PU_STATIC); // MAES: in fact, I do this for sanity. Fuck them. Seriously. sttminus= W.CachePatchName("STTMINUS"); // key cards for (i = 0; i < NUMCARDS; i++) { namebuf = ("STKEYS" + i); keys[i] = W.CachePatchName(namebuf, PU_STATIC); } // arms background armsbg = W.CachePatchName("STARMS", PU_STATIC); // arms ownership widgets for (i = 0; i < 6; i++) { namebuf = ("STGNUM" + (i + 2)); // gray # arms[i][0] = W.CachePatchName(namebuf, PU_STATIC); // yellow # arms[i][1] = shortnum[i + 2]; } // face backgrounds for different color players namebuf = ("STFB" + DM.consoleplayer); faceback = W.CachePatchName(namebuf, PU_STATIC); // status bar background bits sbar = W.CachePatchName("STBAR", PU_STATIC); // face states facenum = 0; for (i = 0; i < ST_NUMPAINFACES; i++) { for (j = 0; j < ST_NUMSTRAIGHTFACES; j++) { namebuf = ("STFST" + (i) + (j)); faces[facenum++] = W.CachePatchName(namebuf, PU_STATIC); } namebuf = "STFTR" + i + "0"; // turn right faces[facenum++] = W.CachePatchName(namebuf, PU_STATIC); namebuf = "STFTL" + i + "0"; // turn left faces[facenum++] = W.CachePatchName(namebuf, PU_STATIC); namebuf = "STFOUCH" + i; // ouch! faces[facenum++] = W.CachePatchName(namebuf, PU_STATIC); namebuf = "STFEVL" + i; // evil grin ;) faces[facenum++] = W.CachePatchName(namebuf, PU_STATIC); namebuf = "STFKILL" + i; // pissed off faces[facenum++] = W.CachePatchName(namebuf, PU_STATIC); } faces[facenum++] = W.CachePatchName("STFGOD0", PU_STATIC); faces[facenum++] = W.CachePatchName("STFDEAD0", PU_STATIC); } public void unloadGraphics() { int i; // unload the numbers, tall and short for (i=0;i<10;i++) { W.UnlockLumpNum(tallnum[i]); tallnum[i]=null; W.UnlockLumpNum(shortnum[i]); shortnum[i]=null; } // unload tall percent W.UnlockLumpNum(tallpercent); tallpercent=null; // unload arms background W.UnlockLumpNum(armsbg); armsbg=null; // unload gray #'s for (i=0;i<6;i++) { W.UnlockLumpNum(arms[i][0]); arms[i][0]=null; W.UnlockLumpNum(arms[i][1]); arms[i][1]=null; } // unload the key cards for (i=0;i<NUMCARDS;i++) for (i=0;i<6;i++) { W.UnlockLumpNum(keys[i]); keys[i]=null; } W.UnlockLumpNum(sbar); sbar=null; W.UnlockLumpNum(faceback); faceback=null; for (i=0;i<ST_NUMFACES;i++){ W.UnlockLumpNum(faces[i]); faces[i]=null; } // Note: nobody ain't seen no unloading // of stminus yet. Dude. } public void unloadData() { unloadGraphics(); } public void initData() { int i; st_firsttime = true; plyr = DM.players[DM.consoleplayer]; st_clock = 0; st_chatstate = st_chatstateenum_t.StartChatState; st_gamestate = st_stateenum_t.FirstPersonState; st_statusbaron[0] = true; st_oldchat = st_chat = false; st_cursoron[0] = false; st_faceindex[0] = 0; st_palette = -1; st_oldhealth = -1; for (i = 0; i < NUMWEAPONS; i++) oldweaponsowned[i] = plyr.weaponowned[i]; for (i = 0; i < 3; i++) keyboxes[i] = -1; Init(); } /** * Widgets are created here. Be careful, because their "constructors" used * reference to boolean or int variables so that they could be auto-updated * by the global refresh functions. We can only do this with some * limitations in Java (e.g. passing an array AND an index). */ public void createWidgets() { int i; // ready weapon ammo w_ready = new st_number_t(ST_AMMOX, ST_AMMOY, tallnum, plyr.ammo, weaponinfo[plyr.readyweapon.ordinal()].ammo.ordinal(), st_statusbaron, 0,ST_AMMOWIDTH); // the last weapon type w_ready.data = plyr.readyweapon.ordinal(); // health percentage w_health = new st_percent_t(ST_HEALTHX, ST_HEALTHY, tallnum, plyr.health, 0, st_statusbaron,0, tallpercent); // arms background w_armsbg = new st_binicon_t(ST_ARMSBGX, ST_ARMSBGY, armsbg, st_notdeathmatch,0, st_statusbaron,0); // weapons owned for (i = 0; i < 6; i++) { w_arms[i] = new st_multicon_t(ST_ARMSX + (i % 3) * ST_ARMSXSPACE, ST_ARMSY + (i / 3) * ST_ARMSYSPACE, arms[i], plyr.weaponowned, i + 1, st_armson,0); } // frags sum w_frags = new st_number_t(ST_FRAGSX, ST_FRAGSY, tallnum, st_fragscount, 0, // dummy, // we're // passing // an // integer. st_fragson,0, ST_FRAGSWIDTH); // faces w_faces = new st_multicon_t(ST_FACESX, ST_FACESY, faces, st_faceindex, 0, st_statusbaron,0); // armor percentage - should be colored later w_armor = new st_percent_t(ST_ARMORX, ST_ARMORY, tallnum, plyr.armorpoints, 0, st_statusbaron, 0,tallpercent); // keyboxes 0-2 w_keyboxes[0] = new st_multicon_t(ST_KEY0X, ST_KEY0Y, keys, keyboxes, 0, st_statusbaron,0); w_keyboxes[1] = new st_multicon_t(ST_KEY1X, ST_KEY1Y, keys, keyboxes, 1, st_statusbaron,0); w_keyboxes[2] = new st_multicon_t(ST_KEY2X, ST_KEY2Y, keys, keyboxes, 2, st_statusbaron,0); // ammo count (all four kinds) w_ammo[0] = new st_number_t(ST_AMMO0X, ST_AMMO0Y, shortnum, plyr.ammo, 0, st_statusbaron,0, ST_AMMO0WIDTH); w_ammo[1] = new st_number_t(ST_AMMO1X, ST_AMMO1Y, shortnum, plyr.ammo, 1, st_statusbaron,0, ST_AMMO1WIDTH); w_ammo[2] = new st_number_t(ST_AMMO2X, ST_AMMO2Y, shortnum, plyr.ammo, 2, st_statusbaron,0, ST_AMMO2WIDTH); w_ammo[3] = new st_number_t(ST_AMMO3X, ST_AMMO3Y, shortnum, plyr.ammo, 3, st_statusbaron,0, ST_AMMO3WIDTH); // max ammo count (all four kinds) w_maxammo[0] = new st_number_t(ST_MAXAMMO0X, ST_MAXAMMO0Y, shortnum, plyr.maxammo, 0, st_statusbaron,0, ST_MAXAMMO0WIDTH); w_maxammo[1] = new st_number_t(ST_MAXAMMO1X, ST_MAXAMMO1Y, shortnum, plyr.maxammo, 1, st_statusbaron,0, ST_MAXAMMO1WIDTH); w_maxammo[2] = new st_number_t(ST_MAXAMMO2X, ST_MAXAMMO2Y, shortnum, plyr.maxammo, 2, st_statusbaron,0, ST_MAXAMMO2WIDTH); w_maxammo[3] = new st_number_t(ST_MAXAMMO3X, ST_MAXAMMO3Y, shortnum, plyr.maxammo, 3, st_statusbaron,0, ST_MAXAMMO3WIDTH); } /** Binary Icon widget * This is used for stuff such as keys or weapons, which you either have * or you don't. * * */ class st_binicon_t implements StatusBarWidget { // center-justified location of icon int x; int y; // last icon value boolean oldval; /** pointer to current icon status */ boolean[] val; int valindex; /** pointer to boolean stating whether to update icon */ boolean[] on; int onindex; patch_t p; // icon int data; // user data // Binary Icon widget routines public st_binicon_t(int x, int y, patch_t i, boolean[] val, int valindex, boolean[] on, int onindex) { this.x = x; this.y = y; this.oldval = false; this.val = val; this.valindex=valindex; this.on = on; this.onindex=onindex; this.p = i; this.val[valindex]=false;; } @Override public void update(boolean refresh) { st_binicon_t bi = this; int x; int y; int w; int h; if (bi.on[onindex] && ((bi.oldval != bi.val[valindex]) || refresh)) { x = bi.x - bi.p.leftoffset; y = bi.y - bi.p.topoffset; w = bi.p.width; h = bi.p.height; if (y - ST_Y < 0) I.Error("updateBinIcon: y - ST_Y < 0"); if (bi.val[valindex]) V.DrawScaledPatch(bi.x, bi.y, V_PREDIVIDE|FG,vs, bi.p); else V.CopyRect(x/vs.getScalingX(), y/vs.getScalingY() - ST_Y, BG, w*BEST_X_SCALE, h*BEST_Y_SCALE, x, y, FG); bi.oldval = bi.val[valindex]; } } } /** Icon widget */ class st_multicon_t implements StatusBarWidget { // center-justified location of icons int x; int y; // last icon number int oldinum; /** pointer to current icon, if not an array type. */ int[] iarray; int inum; // pointer to boolean stating // whether to update icon boolean[] on; int onindex; // list of icons patch_t[] p; // user data int data; /** special status 0=boolean[] 1=integer[] -1= unspecified */ int status = -1; protected boolean[] asboolean; protected int[] asint; public st_multicon_t(int x, int y, patch_t[] il, Object iarray, int inum, boolean []on,int onindex) { this.x = x; this.y = y; this.oldinum = -1; this.inum = inum; this.on = on; this.p = il; if (iarray instanceof boolean[]) { status = 0; asboolean = (boolean[]) iarray; } else if (iarray instanceof int[]){ status = 1; asint = (int[]) iarray; } } @Override public void update(boolean refresh) { int w; int h; int x; int y; // Actual value to be considered. Poor man's generics! int thevalue = -1; switch (status) { case 0: thevalue = asboolean[inum] ? 1 : 0; break; case 1: thevalue = asint[inum]; break; } // Unified treatment of boolean and integer references // So the widget will update iff: // a) It's on AND // b) The new value is different than the old one // c) Neither of them is -1 // d) We actually asked for a refresh. if (this.on[onindex] && ((this.oldinum != thevalue) || refresh) && (thevalue != -1)) { // Previous value must not have been -1. if (this.oldinum != -1) { x = this.x - this.p[this.oldinum].leftoffset*BEST_X_SCALE; y = this.y - this.p[this.oldinum].topoffset*BEST_Y_SCALE; w = this.p[this.oldinum].width*BEST_X_SCALE; h = this.p[this.oldinum].height*BEST_Y_SCALE; if (y - ST_Y < 0) I.Error("updateMultIcon: y - ST_Y < 0"); //System.out.printf("Restoring at x y %d %d w h %d %d\n",x, y - ST_Y,w,h); V.CopyRect(x, y - ST_Y, BG, w, h, x, y, FG); //V.FillRect(x, y - ST_Y, w, h, FG); } //System.out.printf("Drawing at x y %d %d w h %d %d\n",this.x,this.y,p[thevalue].width,p[thevalue].height); V.DrawScaledPatch(this.x,this.y, V_SCALEOFFSET|V_NOSCALESTART|FG, vs,this.p[thevalue]); this.oldinum = thevalue; } } } protected patch_t sttminus; /** Number widget */ class st_number_t implements StatusBarWidget { /** upper right-hand corner of the number (right-justified) */ int x, y; /** max # of digits in number */ int width; /** last number value */ int oldnum; /** * Array in which to point with num. * * Fun fact: initially I tried to use Integer and Boolean, but those are * immutable -_-. Fuck that, Java. * */ int[] numarray; /** pointer to current value. Of course makes sense only for arrays. */ int numindex; /** pointer to boolean stating whether to update number */ boolean[] on; int onindex; /** list of patches for 0-9 */ patch_t[] p; /** user data */ int data; // Number widget routines public st_number_t(int x, int y, patch_t[] pl, int[] numarray, int numindex, boolean[] on,int onindex, int width) { init(x, y, pl, numarray, numindex, on,onindex, width); } public void init(int x, int y, patch_t[] pl, int[] numarray,int numindex, boolean[] on, int onindex, int width) { this.x = x; this.y = y; this.oldnum = 0; this.width = width; this.numarray = numarray; this.on = on; this.onindex=onindex; this.p = pl; this.numindex=numindex; // _D_ fixed this bug } // // A fairly efficient way to draw a number // based on differences from the old number. // Note: worth the trouble? // public void drawNum(boolean refresh) { if (this.numindex==largeammo) return; //st_number_t n = this; int numdigits = this.width; // HELL NO. This only worked while the width happened // to be 3. int num = ((int[]) this.numarray)[this.numindex]; int w = this.p[0].width*BEST_X_SCALE; int h = this.p[0].height*BEST_Y_SCALE; int x = this.x; boolean neg; // In this way, num and oldnum are exactly the same. Maybe this // should go in the end? this.oldnum = num; neg = num < 0; if (neg) { if (numdigits == 2 && num < -9) num = -9; else if (numdigits == 3 && num < -99) num = -99; num = -num; } // clear the area x = this.x - numdigits * w; if (this.y - ST_Y < 0) { I.Error("drawNum: n.y - ST_Y < 0"); } // Restore BG from buffer //V.FillRect(x+(numdigits-3) * w, y, w*3 , h, FG); V.CopyRect(x+(numdigits-3)*w, y- ST_Y, BG, w * 3, h, x+(numdigits-3)*w, y, FG); // if non-number, do not draw it if (num == 1994) return; x = this.x; // in the special case of 0, you draw 0 if (num == 0) //V.DrawPatch(x - w, n.y, FG, n.p[0]); V.DrawScaledPatch(x - w, this.y, FG|V_NOSCALESTART|V_TRANSLUCENTPATCH, vs,p[0]); // draw the new number while (((num != 0) && (numdigits-- != 0))) { x -= w; //V.DrawPatch(x, n.y, FG, n.p[num % 10]); V.DrawScaledPatch(x, this.y, FG|V_NOSCALESTART|V_TRANSLUCENTPATCH,vs, p[num % 10]); num /= 10; } // draw a minus sign if necessary if (neg) V.DrawScaledPatch/*DrawPatch*/(x - 8*BEST_X_SCALE, this.y, FG|V_NOSCALESTART|V_TRANSLUCENTPATCH,vs, sttminus); //V.DrawPatch(x - sttminus.width*vs.getScalingX(), n.y, FG, sttminus); } @Override public void update(boolean refresh) { if (this.on[onindex]) drawNum(refresh); } } class st_percent_t implements StatusBarWidget { // Percent widget ("child" of number widget, // or, more precisely, contains a number widget.) // number information st_number_t n; // percent sign graphic patch_t p; public st_percent_t(int x, int y, patch_t[] pl, int[] numarray, int numindex, boolean[] on, int onindex, patch_t percent) { n = new st_number_t(x, y, pl, numarray, numindex, on,onindex, 3); p = percent; } @Override public void update(boolean refresh) { if (refresh && this.n.on[0]) V.DrawScaledPatch(n.x, n.y, V_PREDIVIDE|FG,vs, p); n.update(refresh); } } interface StatusBarWidget { public void update(boolean refresh); } // Size of statusbar. // Now sensitive for scaling. public int ST_HEIGHT; public int ST_WIDTH; public int ST_Y; ////////////////////////////VIDEO SCALE STUFF //////////////////////////////// protected int SCREENWIDTH; protected int SCREENHEIGHT; protected int SAFE_SCALE; protected int BEST_X_SCALE; protected int BEST_Y_SCALE; protected IVideoScale vs; @Override public void setVideoScale(IVideoScale vs) { this.vs=vs; } @Override public void initScaling() { SCREENHEIGHT=vs.getScreenHeight(); SCREENWIDTH=vs.getScreenWidth(); SAFE_SCALE=vs.getSafeScaling(); BEST_X_SCALE=vs.getScalingX(); BEST_Y_SCALE=vs.getScalingY(); // Pre-scale stuff. ST_HEIGHT =32*SAFE_SCALE; ST_WIDTH =SCREENWIDTH; ST_Y =(SCREENHEIGHT - ST_HEIGHT); ST_X2 = (int) (104*SAFE_SCALE); ST_FX = (int) (143*SAFE_SCALE); ST_FY = (int) (169*SAFE_SCALE); ST_FACESX = (int) (143*SAFE_SCALE); ST_FACESY = (int) (168*SAFE_SCALE); // AMMO number pos. ST_AMMOWIDTH= 3; ST_AMMOX = (int) (44*SAFE_SCALE); ST_AMMOY = (int) (171*SAFE_SCALE); // HEALTH number pos ST_HEALTHWIDTH= 3; ST_HEALTHX = (int) (90*SAFE_SCALE); ST_HEALTHY = (int) (171*SAFE_SCALE); // Weapon pos. ST_ARMSX = (int) (111*SAFE_SCALE); ST_ARMSY = (int) (172*SAFE_SCALE); ST_ARMSBGX = (int) (104*SAFE_SCALE); ST_ARMSBGY = (int) (168*SAFE_SCALE); ST_ARMSXSPACE = 12*SAFE_SCALE;; ST_ARMSYSPACE = 10*SAFE_SCALE;; // Frags pos. ST_FRAGSX = (int) (138*SAFE_SCALE); ST_FRAGSY = (int) (171*SAFE_SCALE); ST_FRAGSWIDTH=2; // ST_ARMORX = (int) (221*SAFE_SCALE); ST_ARMORY = (int) (171*SAFE_SCALE); // Key icon positions. ST_KEY0WIDTH = 8*SAFE_SCALE;; ST_KEY0HEIGHT = 5*SAFE_SCALE;; ST_KEY0X = (int) (239*SAFE_SCALE); ST_KEY0Y = (int) (171*SAFE_SCALE); ST_KEY1WIDTH = ST_KEY0WIDTH; ST_KEY1X = (int) (239*SAFE_SCALE); ST_KEY1Y = (int) (181*SAFE_SCALE); ST_KEY2WIDTH = ST_KEY0WIDTH; ST_KEY2X = (int) (239*SAFE_SCALE); ST_KEY2Y = (int) (191*SAFE_SCALE); // Ammunition counter. ST_AMMO0WIDTH = 3*SAFE_SCALE; ST_AMMO0HEIGHT = 6*SAFE_SCALE; ST_AMMO0X = (int) (288*SAFE_SCALE); ST_AMMO0Y = (int) (173*SAFE_SCALE); ST_AMMO1WIDTH = ST_AMMO0WIDTH; ST_AMMO1X = (int) (288*SAFE_SCALE); ST_AMMO1Y = (int) (179*SAFE_SCALE); ST_AMMO2WIDTH = ST_AMMO0WIDTH; ST_AMMO2X = (int) (288*SAFE_SCALE); ST_AMMO2Y = (int) (191*SAFE_SCALE); ST_AMMO3WIDTH = ST_AMMO0WIDTH; ST_AMMO3X = (int) (288*SAFE_SCALE); ST_AMMO3Y = (int) (185*SAFE_SCALE); // Indicate maximum ammunition. // Only needed because backpack exists. ST_MAXAMMO0WIDTH = 3*SAFE_SCALE; ST_MAXAMMO0HEIGHT = 5*SAFE_SCALE; ST_MAXAMMO0X = (int) (314*SAFE_SCALE); ST_MAXAMMO0Y = (int) (173*SAFE_SCALE); ST_MAXAMMO1WIDTH = ST_MAXAMMO0WIDTH; ST_MAXAMMO1X = 314*SAFE_SCALE; ST_MAXAMMO1Y = (int) (179*SAFE_SCALE); ST_MAXAMMO2WIDTH = ST_MAXAMMO0WIDTH; ST_MAXAMMO2X = (int) (314*SAFE_SCALE); ST_MAXAMMO2Y = (int) (191*SAFE_SCALE); ST_MAXAMMO3WIDTH = ST_MAXAMMO0WIDTH; ST_MAXAMMO3X = (int) (314*SAFE_SCALE); ST_MAXAMMO3Y = (int) (185*SAFE_SCALE); // pistol ST_WEAPON0X = (int) (110*SAFE_SCALE); ST_WEAPON0Y = (int) (172*SAFE_SCALE); // shotgun ST_WEAPON1X = (int) (122*SAFE_SCALE); ST_WEAPON1Y = (int) (172*SAFE_SCALE); // chain gun ST_WEAPON2X = (int) (134*SAFE_SCALE); ST_WEAPON2Y = (int) (172*SAFE_SCALE); // missile launcher ST_WEAPON3X = (int) (110*SAFE_SCALE); ST_WEAPON3Y = (int) (181*SAFE_SCALE); // plasma gun ST_WEAPON4X = (int) (122*SAFE_SCALE); ST_WEAPON4Y = (int) (181*SAFE_SCALE); // bfg ST_WEAPON5X = (int) (134*SAFE_SCALE); ST_WEAPON5Y = (int) (181*SAFE_SCALE); // WPNS title ST_WPNSX = (int) (109*SAFE_SCALE); ST_WPNSY = (int) (191*SAFE_SCALE); // DETH title ST_DETHX = (int) (109*SAFE_SCALE); ST_DETHY = (int) (191*SAFE_SCALE); } @Override public int getHeight() { return this.ST_HEIGHT; } } //$Log: StatusBar.java,v $ //Revision 1.47 2011/11/01 23:46:37 velktron //Added TNTHOM cheat. // //Revision 1.46 2011/10/23 15:57:08 velktron //BG reference // //Revision 1.45 2011/10/07 16:07:14 velktron //Now using g.Keys for key input stuff. // //Revision 1.44 2011/08/23 16:15:30 velktron //Got rid of Z remnants. // //Revision 1.43 2011/07/28 10:29:46 velktron //Added hack for forcing full redraw of status bar after help screen drawing. // //Revision 1.42 2011/07/22 15:37:16 velktron //Sticky idmypos cheat // //Revision 1.41 2011/06/23 17:17:04 velktron //Using BG constant. // //Revision 1.40 2011/06/13 21:03:48 velktron //Fixed Ultimate Doom clev bug // //Revision 1.39 2011/06/02 14:20:45 velktron //Implemented unloading code....kind of pointless, really. // //Revision 1.38 2011/06/01 18:13:37 velktron //Fixed idmypos crash. // //Revision 1.37 2011/06/01 00:07:26 velktron //Moved status, fixed soundinterface. // //Revision 1.36 2011/05/31 13:32:03 velktron //Added V_SCALEOFFSET flag to doomguy's mug. // //Revision 1.35 2011/05/31 12:39:31 velktron //Face centering fixed. // //Revision 1.34 2011/05/30 15:47:34 velktron //AbstractStatusBar introduced. // //Revision 1.33 2011/05/30 10:34:20 velktron //Fixed binicon refresh bug... // //Revision 1.32 2011/05/30 02:21:08 velktron //Fixed number widget diffdraw // //Revision 1.31 2011/05/29 22:15:32 velktron //Introduced IRandom interface. // //Revision 1.30 2011/05/29 20:54:43 velktron //Fixed status bar scaling // //Revision 1.29 2011/05/24 13:42:22 velktron //Fidgeting around with the STBar refresh // //Revision 1.28 2011/05/24 11:31:23 velktron //Got rid of a whole bunch of useless interfaces. // //Revision 1.27 2011/05/23 16:59:02 velktron //Migrated to VideoScaleInfo. // //Revision 1.26 2011/05/21 15:00:14 velktron //Adapted to use new gamemode system. // //Revision 1.25 2011/05/18 16:57:21 velktron //Changed to DoomStatus package st; // States for the chat code. enum st_enumstatcodes_t { StartChatState, WaitDestState, GetChatState } package st; import m.IRandom; import doom.DoomMain; import doom.DoomStatus; import i.DoomVideoInterface; import i.IDoomSystem; import rr.Renderer; import s.IDoomSound; import v.DoomVideoRenderer; import v.IVideoScaleAware; import w.IWadLoader; public abstract class AbstractStatusBar implements IDoomStatusBar, IVideoScaleAware{ // /// STATUS ////////// protected DoomVideoRenderer<?,?> V; protected IWadLoader W; protected Renderer<?,?> R; protected DoomMain<?,?> DM; protected IRandom RND; protected IDoomSystem I; protected DoomVideoInterface<?> VI; protected IDoomSound S; @Override public void updateStatus(DoomStatus<?,?> DC) { this.DM=DC.DM; this.V=DC.V; this.W=DC.W; this.RND=DC.RND; this.R= DC.R; this.VI=DC.VI; this.I=DC.I; this.S=DC.S; } } package st; // Emacs style mode select -*- C++ -*- //----------------------------------------------------------------------------- // // $Id: IDoomStatusBar.java,v 1.4 2012/09/24 17:16:23 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: // Status bar code. // Does the face/direction indicator animatin. // Does palette indicators as well (red pain/berserk, bright pickup) // //----------------------------------------------------------------------------- import i.DoomStatusAware; import v.IVideoScaleAware; import doom.event_t; public interface IDoomStatusBar extends IVideoScaleAware,DoomStatusAware{ /** Points to "screen 4" which is treated as a buffer */ static final int BG =4; /** Points to "screen 0" which is what you actually see */ static final int FG =0; // // STATUS BAR // /** Called by main loop. */ public boolean Responder (event_t ev); /** Called by main loop. */ public void Ticker (); /** Called by main loop.*/ public void Drawer (boolean fullscreen, boolean refresh); /** Called when the console player is spawned on each level. */ public void Start (); /** Called by startup code. */ public void Init (); /** Used externally to determine window scaling. * This means that drawing transparent status bars is possible, but * it will look fugly because of the solid windowing (and possibly * HOMS). */ public int getHeight(); /** Forces a full refresh for reasons not handled otherwise, e.g. after full-page * draws of help screens, which normally don't trigger a complete redraw even if * they should, really. */ void forceRefresh(); } package st; //States for status bar code. public enum st_stateenum_t { AutomapState, FirstPersonState } package tools; import java.io.BufferedOutputStream; import java.io.DataInputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.util.Arrays; import java.util.HashMap; import rr.line_t; import data.maplinedef_t; import data.mapsector_t; import data.mapsidedef_t; import data.mapthing_t; import data.mapvertex_t; import doom64.MapLights; import doom64.MapLinedef; import doom64.MapSector; import doom64.MapSidedef; import doom64.MapThing; import doom64.MapVertex; import utils.C2JUtils; import w.*; public class D64Converter { static final String[] MAPS={"MAP01" ,"MAP02","MAP03","MAP04","MAP05", "MAP06","MAP07","MAP08","MAP09","MAP10", "MAP11","MAP12","MAP13","MAP14","MAP15", "MAP16","MAP17","MAP18","MAP19","MAP20", "MAP21","MAP22","MAP23","MAP24","MAP25", "MAP26","MAP27","MAP28","MAP29","MAP30", "MAP31","MAP32","MAP33"}; static final String DOOM64="d://doom//iwads//doom64.wad"; static final String[] COPY_LUMPS_1={"SEGS","SSECTORS","NODES"}; static final String[] COPY_LUMPS_2={"REJECT","BLOCKMAP"}; static final String VOID_TEXTURE="-"; WadLoader W,WAD64; String[] TEXTURES,FLATS; HashMap<Integer,String> TexHash,FlatHash; /* ResourceManager::getTextureHash * Returns the Doom64 hash of a given texture name, computed using * the same hash algorithm as Doom64 EX itself *******************************************************************/ public static int getTextureHash(String name) { char[] str=new char[8]; Arrays.fill(str, (char)0); for (int c = 0; c < name.length() && c < 8; c++) str[c] = name.charAt(c); long hash = 1315423911; for(int i = 0; i < 8 && str[i] != '\0'; ++i){ hash ^= ((hash << 5)+ Character.toUpperCase(str[i]) + (hash >> 2)); hash&=0x00FFFFFFFFL; } hash %= 65536; return (int) hash; } public mapsector_t[] ConvertSectors(int lump, MapLights[] lights) throws IOException { MapSector[] data; mapsector_t[] sectors; MapSector d64; mapsector_t doom; int numsectors = WAD64.LumpLength (lump) / MapSector.sizeOf(); // Read "mapsectors" data=WAD64.CacheLumpNumIntoArray(lump,numsectors,MapSector.class); sectors = C2JUtils.createArrayOfObjects(mapsector_t.class,numsectors); for (int i=0 ; i<numsectors ; i++) { d64 = data[i]; doom = sectors[i]; doom.floorheight =d64.floorheight; doom.ceilingheight = d64.ceilingheight; doom.floorpic=FlatHash.get(new Integer(d64.floorpic)); doom.ceilingpic=FlatHash.get(new Integer(d64.ceilingpic)); if (doom.floorpic==null) doom.floorpic=TexHash.get(new Integer(d64.floorpic)); if (doom.floorpic==null) System.err.printf("Warning: Sector %d floorpic hash %d has null mapping!\n",i,(int)d64.floorpic); if (doom.ceilingpic==null) doom.ceilingpic=TexHash.get(new Integer(d64.ceilingpic)); if (doom.ceilingpic==null) System.err.printf("Warning: Sector %d ceilingpic hash %d has null mapping!\n",i,(int)d64.ceilingpic); if(doom.floorpic.equals("?")) doom.floorpic=VOID_TEXTURE; if(doom.ceilingpic.equals("?")) doom.ceilingpic=VOID_TEXTURE; // System.out.printf("Sector %d mapped to %s %s\n",i,dooms.floorpic,dooms.ceilingpic); doom.lightlevel=lights[d64.thingcolor].greyscaleLightLevel(); //dooms.floorpic = FLATS[d64s.floorpic]; //dooms.ceilingpic = FLATS[d64s.ceilingpic]; //dooms.lightlevel = ms.lightlevel; doom.special = d64.special; doom.tag = d64.tag; } return sectors; } /** * P_LoadThings * @throws IOException */ public mapthing_t[] ConvertThings(int lump) throws IOException { MapThing[] data; mapthing_t[] things; MapThing d64thing; mapthing_t mt; int numthings; numthings = WAD64.LumpLength (lump) / MapThing.sizeOf(); data=WAD64.CacheLumpNumIntoArray(lump,numthings,MapThing.class); things = C2JUtils.createArrayOfObjects(mapthing_t.class,numthings); for (int i=0 ; i<numthings ; i++) { mt=things[i]; d64thing=data[i]; mt.x=d64thing.x; mt.y=d64thing.y; mt.angle=d64thing.angle; mt.options=d64thing.flags; mt.type=d64thing.type; } return things; } public maplinedef_t[] ConvertLinedefs(int lump) throws IOException { MapLinedef[] data; maplinedef_t[] lines; MapLinedef d64l; maplinedef_t mld; int numlines = WAD64.LumpLength (lump) / MapLinedef.sizeOf(); lines = C2JUtils.createArrayOfObjects(maplinedef_t.class,numlines); // read "maplinedefs" data = WAD64.CacheLumpNumIntoArray(lump,numlines,MapLinedef.class); for (int i=0 ; i<numlines ; i++) { d64l=data[i]; mld=lines[i]; mld.v1=(char) d64l.v1; mld.v2=(char) d64l.v2; // TODO: adapt flags mld.flags = (short) (d64l.flags&0xFFFF); mld.special = (short) (d64l.special&0xFF); mld.tag = d64l.tag; mld.sidenum[0]=(char) d64l.left; mld.sidenum[1]=(char) d64l.right; } return lines; } public mapsidedef_t[] ConvertSidedefs(int lump) throws IOException { MapSidedef[] data; mapsidedef_t[] sides; MapSidedef d64; mapsidedef_t doom; int numsides = WAD64.LumpLength (lump) / MapSidedef.sizeOf(); // Read "mapsectors" data=WAD64.CacheLumpNumIntoArray(lump,numsides,MapSidedef.class); sides = C2JUtils.createArrayOfObjects(mapsidedef_t.class,numsides); for (int i=0 ; i<numsides ; i++) { d64 = data[i]; doom = sides[i]; doom.textureoffset=d64.xoffset; doom.rowoffset=d64.yoffset; doom.sector=d64.sector; doom.bottomtexture=TexHash.get(new Integer(d64.bottomtexture)); doom.midtexture=TexHash.get(new Integer(d64.midtexture)); doom.toptexture=TexHash.get(new Integer(d64.toptexture)); if (doom.bottomtexture==null) doom.bottomtexture=FlatHash.get(new Integer(d64.bottomtexture)); if (doom.bottomtexture==null) System.err.printf("Warning: Sidedef %d bottomtexture hash %d has null mapping!\n",i,(int)d64.bottomtexture); if (doom.midtexture==null) doom.midtexture=FlatHash.get(new Integer(d64.midtexture)); if (doom.midtexture==null) System.err.printf("Warning: Sidedef %d midtexture hash %d has null mapping!\n",i,(int)d64.midtexture); if (doom.toptexture==null) doom.toptexture=FlatHash.get(new Integer(d64.toptexture)); if (doom.toptexture==null) System.err.printf("Warning: Sidedef %d toptexture hash %d has null mapping!\n",i,(int)d64.toptexture); if(doom.bottomtexture.equals("?")) doom.bottomtexture=VOID_TEXTURE; if(doom.midtexture.equals("?")) doom.midtexture=VOID_TEXTURE; if(doom.toptexture.equals("?")) doom.toptexture=VOID_TEXTURE; // System.out.printf("Sidedef %d mapped to %s %s %s\n",i,doom.bottomtexture,doom.midtexture,doom.toptexture); } return sides; } public mapvertex_t[] ConvertVertexes(int lump) throws IOException { MapVertex[] data; mapvertex_t[] vertexes; MapVertex d64; mapvertex_t doom; int numvertexes = WAD64.LumpLength (lump) / MapVertex.sizeOf(); data=WAD64.CacheLumpNumIntoArray(lump,numvertexes,MapVertex.class); vertexes = C2JUtils.createArrayOfObjects(mapvertex_t.class,numvertexes); for (int i=0 ; i<numvertexes ; i++) { d64 = data[i]; doom = vertexes[i]; doom.x= (short) (d64.x>>16); doom.y= (short) (d64.y>>16); } return vertexes; } private MapLights[] ConvertLights(int lump) { MapLights[] lights; MapLights[] data; int numlights = WAD64.LumpLength (lump) / MapLights.sizeOf(); // First 256 lights are auto-generated lights=new MapLights[256+numlights]; for (int i=0;i<256;i++){ lights[i]=new MapLights((short) i); } // Read extra lights data=WAD64.CacheLumpNumIntoArray(lump,numlights,MapLights.class); for (int i=0;i<numlights;i++) lights[256+i]=data[i]; return lights; } public static void main(String[] argv) throws Exception { D64Converter conv=new D64Converter(); conv.gogogo(); } public void gogogo() throws Exception{ DataInputStream dis=new DataInputStream(new FileInputStream(DOOM64)); W=new WadLoader(DOOM64); System.out.println(W.numlumps); int T_START=W.GetNumForName("T_START"); int T_END=W.GetNumForName("T_END"); // Find "?" textures. The first texture and the first flat will both be "?" int[] suck_markers=new int[2]; int mark=0; for (int lump=T_START+1;lump<T_END;lump++){ if (W.GetNameForNum(lump).equals("?")) suck_markers[mark++]=lump; if (mark>1) break; } // Can't separate between flats and textures, not enough "?" if (mark<2) return; TEXTURES=new String[suck_markers[1]-suck_markers[0]]; FLATS=new String[T_END-suck_markers[1]]; TexHash=new HashMap<Integer,String>(); FlatHash=new HashMap<Integer,String>(); int k=0; for (int lump=suck_markers[0];lump<suck_markers[1];lump++,k++){ TEXTURES[k]=W.GetNameForNum(lump); System.out.printf("Texture %d %s hash %d\n",k,TEXTURES[k],getTextureHash(TEXTURES[k])); TexHash.put(getTextureHash(TEXTURES[k]),TEXTURES[k]); } k=0; for (int lump=suck_markers[1];lump<T_END;lump++,k++){ FLATS[k]=W.GetNameForNum(lump); System.out.printf("Flat %d %s hash %d\n",k,FLATS[k],getTextureHash(FLATS[k])); FlatHash.put(getTextureHash(FLATS[k]),FLATS[k]); } WadBuilder WB=new WadBuilder("Doom64conv.wad", "PWAD"); WB.start(); for (String map: MAPS){ System.err.println(map); byte[] mapdata=W.CacheLumpNameAsRawBytes(map,0); File f=new File(String.format("%s.d64.wad",map)); BufferedOutputStream bos=new BufferedOutputStream(new FileOutputStream(f)); bos.write(mapdata); bos.close(); WAD64=new WadLoader(f.getName()); System.out.printf("%s %d\n",f.getName(),WAD64.numlumps); // We have no use for the LEAFS and MACROS nodes, but we do have some use for the LIGHTS node, // since it's the only way to deduct some lighting information. int lump=WAD64.GetNumForName("LIGHTS"); MapLights[] lights=ConvertLights(lump); lump=WAD64.GetNumForName("THINGS"); mapthing_t[] things=ConvertThings(lump); lump=WAD64.GetNumForName("LINEDEFS"); maplinedef_t[] lines=ConvertLinedefs(lump); lump=WAD64.GetNumForName("SIDEDEFS"); mapsidedef_t[] sides=ConvertSidedefs(lump); ProcessLinesSides(lines,sides); lump=WAD64.GetNumForName("SECTORS"); mapsector_t[] sectors=ConvertSectors(lump,lights); lump=WAD64.GetNumForName("VERTEXES"); mapvertex_t[] vertexes=ConvertVertexes(lump); WB.add(map); WB.add("THINGS", things); WB.add("LINEDEFS", lines); WB.add("SIDEDEFS", sides); WB.add("VERTEXES", vertexes); // SEGS, SSECTORS and NODES are the same as vanilla WB.add(WAD64,COPY_LUMPS_1); WB.add("SECTORS", sectors); // REJECT and BLOCKMAP are the same as vanilla... WB.add(WAD64,COPY_LUMPS_2); } WB.close(); } private void ProcessLinesSides(maplinedef_t[] lines, mapsidedef_t[] sides) { for (int k=0;k<lines.length;k++){ // Samuel: there's a flag in D64 maps that is "render mid-texture" // if that flag is not set but there is a midtexture present, it should be nullified to no texture // note that the nullification should only occur on two-sided lines, one-sided lines should be left alone in that regard // if (C2JUtils.flags(lines[k].flags,line_t.ML_TWOSIDED) && !C2JUtils.flags(lines[k].flags,MapLinedef.D64_ML_RENDERMIDDLE)) { int front=lines[k].sidenum[0]; int back=lines[k].sidenum[1]; if(front!=0xFFFF && !sides[front].midtexture.equals(VOID_TEXTURE)){ System.err.printf("Linedef %d Sidedef %d (front) Flags %x Mid %s =>",k,front,lines[k].flags,sides[front].midtexture); sides[front].midtexture=VOID_TEXTURE; System.err.printf(" %s\n",sides[front].midtexture); } if(back!=0xFFFF && !sides[back].midtexture.equals(VOID_TEXTURE)){ System.err.printf("Linedef %d Sidedef %d (front) Flags %x Mid %s =>",k,back,lines[k].flags,sides[back].midtexture); sides[back].midtexture=VOID_TEXTURE; System.err.printf(" %s\n",sides[back].midtexture); } } } } } package v; import java.awt.image.BufferedImage; import java.awt.image.DataBufferByte; import java.awt.image.DataBufferInt; import java.awt.image.DataBufferUShort; import m.BBox; public class BufferedRenderer32 extends SoftwareVideoRenderer32 { static final String rcsid = "$Id: BufferedRenderer32.java,v 1.4 2016/07/04 07:52:26 velktron Exp $"; /** Buffered Renderer has a bunch of images "pegged" to the underlying arrays */ public BufferedImage[] screenbuffer=new BufferedImage[5]; public BufferedRenderer32(int w, int h) { super(w,h); } @Override public final void Init () { int i; for (i=0 ; i<4 ; i++){ //screens[i] = new byte[this.getHeight()*this.getWidth()]; this.setScreen(i, this.width, this.height); } dirtybox=new BBox(); } /** This implementation will "tie" a bufferedimage to the underlying byte raster. * * NOTE: this relies on the ability to "tap" into a BufferedImage's backing array, * in order to have fast writes without setpixel/getpixel. If that is not possible, * then we'll need to use a special renderer. * */ @Override public final void setScreen(int index, int width, int height){ if (screens[index]==null){ screenbuffer[index]=new BufferedImage(width,height,BufferedImage.TYPE_INT_ARGB); screens[index]=((DataBufferInt)screenbuffer[index].getRaster().getDataBuffer()).getData(); } } public void setPalette(int palette){ this.usepalette=palette%maxpalettes; // Invalidate cached graphics, otherwise older colormaps // will persist. this.clearCaches(); // Tint the current set of colormaps. getCachedCmap(palette); //this.currentpal=palette%maxpalettes; this.currentscreen=this.screenbuffer[0]; } @Override public void setUsegamma(int gamma) { this.usegamma=gamma%maxgammas; // Invalidate palette cache. super.clearPalettes(); // Re-synthesize current palette. setPalette(usepalette); } public void setCurrentScreen(int screen){ super.setCurrentScreen(screen); this.currentscreen=this.screenbuffer[0]; } @Override protected final void specificPaletteCreation(byte[] paldata, short[][] gammadata, final int palettes, final int colors, final int stride, final int gammalevels){ System.out.printf("Enough data for %d palettes",maxpalettes); System.out.printf("Enough data for %d gamma levels",maxgammas); //this.palettes=new int[maxpalettes*maxgammas][]; this.palettes=new int[maxpalettes][]; // Apply gammas a-posteriori, not a-priori. // Initial palette can be neutral or based upon "gamma 0", // which is actually a bit biased and distorted for (int z=0;z<1;z++){ // For each palette for (int y=0;y<maxpalettes;y++){ this.palettes[z*maxpalettes+y]=new int[colors]; for (int x=0;x<colors;x++){ int r=gammadata[z][0xFF&paldata[y*colors*stride+stride*x]]; // R int g=gammadata[z][0xFF&paldata[1+y*colors*stride+stride*x]]; // G int b=gammadata[z][0xFF&paldata[2+y*colors*stride+stride*x]]; // B int color=0xFF000000|r<<16|g<<8|b; this.palettes[z*maxpalettes+y][x]=color; } } } // Set base colormap cmap_base=PaletteGenerator.RF_BuildLights24(this.palettes[0], NUMLIGHTS); cmap_work=PaletteGenerator.RF_BuildLights24(this.palettes[0], NUMLIGHTS); } public int getBaseColor(int color){ return cmap_work[0][color]; } } package v; import doom.ICommandLineManager; import static utils.C2JUtils.*; public class VisualSettings { /** Default video scale is "triple vanilla: 3 x (320 x 200) */ public final static IVideoScale vanilla=new VideoScaleInfo(1.0f); public final static IVideoScale double_vanilla=new VideoScaleInfo(2.0f); public final static IVideoScale triple_vanilla=new VideoScaleInfo(3.0f); public final static IVideoScale default_scale=triple_vanilla; /** Parses the command line for resolution-specific commands, and creates * an appropriate IVideoScale object. * * @param CM * @return */ public final static IVideoScale parse(ICommandLineManager CM){ int width=-1; int height=-1; // -multiply parameter defined from linux doom. // It gets priority over all others, if present. int p=CM.CheckParm("-multiply"); if (eval(p)) { try { width=Integer.parseInt(CM.getArgv(p+1)); } catch (NumberFormatException e){ // We failed. Mark the occasion. width=-1; } } // If -multiply was successful, trump any others. // Implied to be a solid multiple of the vanilla resolution. if (width>0 && width<=5) return new VideoScaleInfo(width); // Width defined? p=CM.CheckParm("-width"); if (eval(p)) { try { width=Integer.parseInt(CM.getArgv(p+1)); } catch (NumberFormatException e){ // We failed. Mark the occasion. width=-1; } } p=CM.CheckParm("-height"); if (eval(p)) { try { height=Integer.parseInt(CM.getArgv(p+1)); } catch (NumberFormatException e){ // We failed. Mark the occasion. height=-1; } } // Nothing to do? if (height==-1 && width==-1) return default_scale; // At least one of them is not a dud. int mulx, muly,mulf; // Break them down to the nearest multiple of the base width or height. mulx=Math.round((float)width/IVideoScale.BASE_WIDTH); muly=Math.round((float)height/IVideoScale.BASE_HEIGHT); // Do not accept zero or sub-vanilla resolutions if (mulx>0 || muly>0){ // Use the maximum multiplier. We don't support skewed // aspect ratios yet. mulf=Math.max(mulx,muly); if (mulf>=1 && mulf<=5) return new VideoScaleInfo(mulf); } // In all other cases... return default_scale; } } package v; import java.awt.Image; import java.awt.image.IndexColorModel; import java.io.IOException; import rr.patch_t; /** DoomVideoSystem is now an interface, that all "video drivers" (wheter do screen, disk, etc.) * must implement. * * 23/10/2011: Made into a generic type, which affects the underlying raw screen data * type. This should make -in theory- true colour or super-indexed (>8 bits) video modes * possible. The catch is that everything directly meddling with the renderer must also * be aware of the underlying implementation. E.g. the various screen arrays will not be * necessarily byte[]. * * @author Maes * */ public interface DoomVideoRenderer<T,V> extends IVideoScaleAware { //flags hacked in scrn (not supported by all functions (see src)) // Added by _D_. Unsure if I should use VSI objects instead, as they // already carry scaling information which doesn't need to be repacked... public static final int V_NOSCALESTART = 0x010000; // dont scale x,y, start coords public static final int V_SCALESTART = 0x020000; // scale x,y, start coords public static final int V_SCALEPATCH = 0x040000; // scale patch public static final int V_NOSCALEPATCH = 0x080000; // don't scale patch public static final int V_WHITEMAP = 0x100000; // draw white (for v_drawstring) public static final int V_FLIPPEDPATCH = 0x200000; // flipped in y public static final int V_TRANSLUCENTPATCH = 0x400000; // draw patch translucent public static final int V_PREDIVIDE = 0x800000; // pre-divide by best x/y scale. public static final int V_SCALEOFFSET = 0x1000000; // Scale the patch offset public static final int SCREEN_FG = 0; // Foreground screen public static final int SCREEN_BG= 1; // Used for endlevel/finale BG public static final int SCREEN_WS = 2; // Wipe start screen, also used for screenshots public static final int SCREEN_WE = 3; // Wipe end screen public static final int SCREEN_SB = 4; // Used for status bar // Allocates buffer screens, call before R_Init. public void Init(); public void CopyRect(int srcx, int srcy, int srcscrn, int width, int height, int destx, int desty, int destscrn); public void FillRect(int srcx, int srcy, int width, int height,int destscrn); public void DrawPatch(int x, int y, int scrn, patch_t patch); public void DrawPatchFlipped ( int x, int y, int scrn, patch_t patch ); public void DrawPatchDirect(int x, int y, int scrn, patch_t patch); /** V_DrawPatch * Draws a SOLID (non-masked) patch to the screen with integer scaling * m and n. * Useful for stuff such as help screens, titlepic and status bar. Not * very useful for menus, though. * desttop, dest and source were byte */ public void DrawPatchSolidScaled ( int x, int y,int m, int n, int scrn, patch_t patch ); public void DrawPatchSolidScaled ( int x, int y, int scrn, IVideoScale vs, patch_t patch ); // Draw a linear block of pixels into the view buffer. public void DrawBlock(int x, int y, int scrn, int width, int height, T src); // Draw a linear block of pixels into the view buffer. public void DrawBlock(int x, int y, int scrn, int width, int height, T src,int offset); // Reads a linear block of pixels into the view buffer. public void GetBlock(int x, int y, int scrn, int width, int height, V dest); public void MarkRect(int x, int y, int width, int height); public V getScreen(int index); public void setScreen(int index, int width, int height); public int getUsegamma(); public void takeScreenShot(int screen, String imagefile, IndexColorModel icm) throws IOException; public int getWidth(); public int getHeight(); /** Shamelessly ripped from Doom Legacy (for menus, etc) by _D_ ;-) * It uses FLAGS (see above) hacked into the scrn parameter, to be * parsed afterwards. */ public void DrawScaledPatch(int x, int y, int scrn, IVideoScale VSI, // hacked flags in it... patch_t patch); void DrawPatchColScaled(int x, patch_t patch,int col, IVideoScale vs, int screen); /** Perform any action necessary so that palettes get modified according to specified gamma. * Consider this a TIME CONSUMING operation, so don't call it unless really necessary. * * @param gammalevel * */ void setUsegamma(int gammalevel); /** Perform any action necessary so that the screen output uses the specified palette * Consider this a TIME CONSUMING operation, so don't call it unless really necessary. * * @param palette */ void setPalette(int palette); /** Perform any action necessary so that palettes and gamma tables are created, e.g. by reading * from on-disk resources or from somewhere else. * */ void createPalettes(byte[] paldata, short[][] gammadata, final int palettes, final int colors, final int stride,final int gammalevels); /** No matter how complex/weird/arcane palette manipulations you do internally, the AWT module * must always be able to "tap" into what's the current, "correct" screen after all manipulation and * color juju was applied. Call after a palette/gamma change. * */ Image getCurrentScreen(); /** Final call before updating a particular screen. * In case that e.g. manual palette application or additonal * rendering must be performed on a screen. * */ void update(); /** Which of the internal screens you want to display next time you call getCurrentScreen * * @param screen */ void setCurrentScreen(int screen); void FillRect(int color, int screen, int x, int y, int width, int height); V[] getColorMaps(); /** Get the value corresponding to a base color (0-255). * Depending on the implementation this might be indexed, * RGB etc. Use whenever you need "absolute" colors. * * @return */ int getBaseColor(int color); /** Clear any byte-to-short or byte-to-int post or flat caches generated * during e.g. extended color blits or OpenGL acceleration. * * Good moments to call this function include: * *After starting a new level. * *After Menu, Finale, Endlevel screens or Wipers have been deactivated. * *In general, after anything that might use fixed graphics has completed. * * This is necessary because the cache keeps references to */ void clearCaches(); /** Return current palette. Only works for 8-bit renderer, others return null */ IndexColorModel getPalette(); } package v; import static data.Defines.RANGECHECK; import i.DoomStatusAware; import i.IDoomSystem; import java.awt.Image; import java.awt.image.BufferedImage; import java.awt.image.DataBufferByte; import java.awt.image.IndexColorModel; import doom.DoomStatus; import m.BBox; import rr.LightsAndColors; import rr.patch_t; public abstract class SoftwareVideoRenderer<T,V> implements DoomVideoRenderer<T,V>, IVideoScaleAware, DoomStatusAware{ /** Light levels. Binded to the colormap subsystem */ public static final int NUMLIGHTS=LightsAndColors.LIGHTLEVELS; /** Colormap used for static graphics (menu etc.) */ public static final int CMAP_FIXED=0; protected IDoomSystem I; protected Image currentscreen; public Image getCurrentScreen(){ return currentscreen; } public final void updateStatus(DoomStatus<?,?> DM){ this.I=DM.I; } protected int width; protected int height; /** Each screen is [SCREENWIDTH*SCREENHEIGHT]; * This is what the various modules (menu, automap, * renderer etc.) get to manipulate at the pixel * level. To go beyond 8 bit displays, these must be extended */ protected V[] screens; //MAES: maybe this should be a bbox? public BBox dirtybox=new BBox(); /** Colormaps are now part of the base software renderer. This * allows some flexibility over manipulating them. * * Use base as immutable, use work for applying effects. * */ protected V[] cmap_base,cmap_work; /** PLAYPAL-read palettes, used to build dynamic color maps * Use [z*maxpalettes+y] form, where z=gamme, y=palette * */ protected int[][] palettes; public SoftwareVideoRenderer(){ // Defaults width=SCREENWIDTH; height=SCREENHEIGHT; } public SoftwareVideoRenderer(int w,int h){ // Defaults width=w; height=h; } protected int usegamma=0; protected int usepalette=0; protected int maxpalettes; protected int maxgammas; protected int currentpal; protected int currentgamma; protected int usescreen=0; public final int getUsegamma() { return usegamma; } /** V_Markrect: * Apparently, video.c had its own "dirtybox" bbox, and this was a crude method for * expanding it. * */ public final void MarkRect ( int x, int y, int width, int height ) { dirtybox.AddToBox(x, y); dirtybox.AddToBox(x+width-1, y+height-1); } /** * V_CopyRect */ public final void CopyRect (int srcx, int srcy, int srcscrn, int width, int height, int destx, int desty, int destscrn ) { // These are pointers inside an array. final V src=screens[srcscrn]; final V dest=screens[destscrn]; if (RANGECHECK) { if (srcx<0 ||srcx+width >this.width || srcy<0 || srcy+height>SCREENHEIGHT ||destx<0||destx+width >this.width || desty<0 || desty+height>SCREENHEIGHT || srcscrn>4 || destscrn>4) { I.Error ("Bad V_CopyRect"); } } this.MarkRect (destx, desty, width, height); // MAES: these were pointers to a specific position inside the screen. int srcPos = this.width*srcy+srcx; int destPos = this.width*desty+destx; for ( ; height>0 ; height--) { System.arraycopy(src,srcPos, dest, destPos, width); //memcpy (dest, src, width); srcPos += this.width; destPos += this.width; } } protected final boolean doRangeCheck(int x, int y,patch_t patch, int scrn){ return (x<0 ||x+patch.width >this.width || y<0 || y+patch.height>this.height || scrn>4); } protected final boolean doRangeCheck(int x, int y, int scrn){ return (x<0 ||x>this.width || y<0 || y>this.height || scrn>4); } public void DrawPatchSolidScaled ( int x, int y, int scrn, IVideoScale vs, patch_t patch ){ this.DrawPatchSolidScaled(x, y, vs.getScalingX(), vs.getScalingY(),scrn, patch); } public final int getHeight() { return this.height; } public final int getWidth() { return this.width; } public final void DrawPatchDirect(int x, int y, int scrn, patch_t patch) { this.DrawPatch(x, y, scrn, patch); } public final V getScreen(int index) { return screens[index]; } /* public final boolean isRasterNull(int screen){ for (int i=0;i<screens[screen].length;i++){ if (screens[screen][i]!=0) return false; } return true; } */ public void setCurrentScreen(int screen){ this.usescreen=screen; } public void update(){ // Override only if there's something else to be done, e.g. map palette to truecolor buffer } public void report(BufferedImage[] b){ System.out.println("Main video buffer "+screens[0]); for (int i=0;i<b.length;i++){ System.out.println(((Object)b[i].getRaster()).toString()+" "+b[i].getRaster().hashCode()+" "+((DataBufferByte)(b[i].getRaster().getDataBuffer())).getData()); } } ////////////////////////////VIDEO SCALE STUFF //////////////////////////////// protected int SCREENWIDTH=320; protected int SCREENHEIGHT=200; protected IVideoScale vs; @Override public void setVideoScale(IVideoScale vs) { this.vs=vs; } @Override public void initScaling() { this.SCREENHEIGHT=vs.getScreenHeight(); this.SCREENWIDTH=vs.getScreenWidth(); } /** Built-in method for recovering from palette disasters. * Uses PaletteGenerator class to generate Doom's palettes with only the data of * the first palette. * */ protected final void paletteRecovery() { createPalettes(PaletteGenerator.generatePalette(PaletteGenerator.playpal, 256,ColorTint.tints), GammaTables.gammatables, 14, 256, 3, 5); } /** Internal method for setting up palettes (and gamma tables) * */ public void createPalettes(byte[] paldata, short[][] gammadata, final int palettes, final int colors, final int stride,final int gammalevels){ // Sanity check on supplied data length. If there is not enough data to create the specified palettes, // their number will be limited. if (paldata!=null) // As many as are likely contained maxpalettes=paldata.length/(colors*stride); else maxpalettes=0; // Do some default action on null palette. if (gammadata!=null) // As many as are likely contained maxgammas=gammadata.length; else maxgammas=0; // Do some default action on null gamma tables. if (maxgammas==0){ gammadata=GammaTables.gammatables; maxgammas=GammaTables.gammatables.length; } // Enough data for all palettes. // Enough data for all palettes. if (maxpalettes>0 && maxgammas>0) specificPaletteCreation(paldata,gammadata,palettes,colors,stride,gammalevels); else paletteRecovery(); } /** Override this in extending classes to perform specific actions depending on the * type of renderer. It's better not to assign a default action, nor make assumptions * on the underlying types of actual palettes * * @param paldata * @param gammadata * @param palettes * @param colors * @param stride * @param gammalevels */ protected abstract void specificPaletteCreation(byte[] paldata, short[][] gammadata, final int palettes, final int colors, final int stride, final int gammalevels); protected int lastcolor=-1; protected byte[] scanline; public int getBaseColor(int color){ return color; } /** Override if there's something special to do for colormap caches, e.g. during * palette or gamma changes. Not required for indexed. */ public void clearCaches(){ // Does nothing for indexed. } /** Should return colormaps, if you ever move their management in here. * */ public V[] getColorMaps(){ return null; } public void setColorMaps(V[] colormaps,int num){ // Dummy } public IndexColorModel getPalette(){ return null; } } package v; import static data.Defines.RANGECHECK; import i.DoomStatusAware; import i.IDoomSystem; import java.awt.Image; import java.awt.image.BufferedImage; import java.awt.image.IndexColorModel; import java.io.File; import java.io.IOException; import java.util.HashMap; import javax.imageio.ImageIO; import doom.DoomStatus; import m.BBox; import rr.column_t; import rr.patch_t; import utils.C2JUtils; public abstract class SoftwareVideoRenderer16 extends SoftwareVideoRenderer<byte[],short[]> { // MAES: maybe this should be a bbox? public BBox dirtybox = new BBox(); public SoftwareVideoRenderer16() { // Defaults super(); screens = new short[5][]; } public SoftwareVideoRenderer16(int w, int h) { super(w,h); screens = new short[5][]; } /** * V_Fillrect */ @Override public void FillRect(int srcx, int srcy, int width, int height, int destscrn) { // These are pointers inside an array. final short[] dest = screens[destscrn]; if (RANGECHECK) { if (srcx < 0 || srcx + width > this.width || srcy < 0 || srcy + height > SCREENHEIGHT || destscrn > 4) { I.Error("Bad V_FillRect"); } } this.MarkRect(srcx, srcy, width, height); // MAES: these were pointers to a specific position inside the screen. int srcPos = this.width * srcy + srcx; for (; height > 0; height--) { for (int i = 0; i < width; i++) { dest[srcPos + i] = 0; } // memcpy (dest, src, width); srcPos += this.width; } } /** * V_DrawPatch Masks a column based masked pic to the screen. desttop, dest * and source were byte* */ public final void DrawPatch(int x, int y, int scrn, patch_t patch) { column_t column; int desttop; final short[] dest = screens[scrn]; int w; y -= patch.topoffset; x -= patch.leftoffset; if (RANGECHECK) if (doRangeCheck(x, y, patch, scrn)) { System.err.print("Patch at " + x + "," + y + " exceeds LFB\n"); // No I_Error abort - what is up with TNT.WAD? System.err.print("V_DrawPatch: bad patch (ignored)\n"); return; } if (scrn == 0) this.MarkRect(x, y, patch.width, patch.height); w = patch.width; desttop = x + this.width * y; // For each column.. int destPos; int ptr = 0; for (int col = 0; col < w; desttop++, col++, x++) { // This points at a "column" object. // column = (column_t *)((byte *)patch + // LONG(patch->columnofs[col])); column = patch.columns[col]; final short[] data=getShortVersion(column.data); // For each post... for (int i = 0; i < column.posts; i++) { // Get pointer to post offset. ptr = column.postofs[i]; // Get post delta short delta = column.postdeltas[i]; // We skip delta, len and padding. // ptr+=3; NOT NEEDED< pre-skipped at parsing. // Skip transparent rows... if (delta == 0xFF) break; destPos = desttop + delta * this.width; // These lengths are already correct. for (int j = 0; j < column.postlen[i]; j++) { dest[destPos] = data[ptr++]; destPos += this.width; } } } } /** * V_DrawPatchSolidScaled Draws a SOLID (non-masked) patch to the screen * with integer scaling m and n. Useful for stuff such as help screens, * titlepic and status bar. Not very useful for menus, though. desttop, dest * and source were byte */ public final void DrawPatchSolidScaled(int x, int y, int m, int n, int scrn, patch_t patch) { if ((m == 1) && (n == 1)) { DrawPatch(x, y, scrn, patch); return; } column_t column; short[] data; int desttop; final short[] dest = screens[scrn]; int w; y = y * n - patch.topoffset; x = x * m - patch.leftoffset; if (RANGECHECK) if (doRangeCheck(x, y, patch, scrn)) { System.err.print("Patch at " + x + "," + y + " exceeds LFB\n"); // No I_Error abort - what is up with TNT.WAD? System.err.print("V_DrawPatch: bad patch (ignored)\n"); return; } if (scrn == 0) this.MarkRect(x, y, patch.width, patch.height); w = patch.width; desttop = m * x + this.width * y; // For each column.. int destPos; int ptr = 0; // x increases by m. // Some unrolling... if (m == 2) { // desttop=2*desttop; for (int col = 0; col < w; desttop += 2, col++) { // This points at a "column" object. // column = (column_t *)((byte *)patch + // LONG(patch->columnofs[col])); column = patch.columns[col]; data=this.getShortVersion(column.data); // For each post... for (int i = 0; i < column.posts; i++) { // Get pointer to post offset. ptr = column.postofs[i]; // Get post delta short delta = column.postdeltas[i]; // We skip delta, len and padding. // ptr+=3; // Skip transparent rows... if (delta == 0xFF) break; destPos = desttop + n * delta * this.width; // These lengths are already correct. for (int j = 0; j < column.postlen[i]; j++) { dest[destPos] = data[ptr++]; dest[destPos + 1] = dest[destPos]; destPos += n * this.width; } } } } else if (m == 3) { // desttop=3*desttop; for (int col = 0; col < w; desttop += 3, col++) { column = patch.columns[col]; data=this.getShortVersion(column.data); // For each post... for (int i = 0; i < column.posts; i++) { // Get pointer to post offset. ptr = column.postofs[i]; // Get post delta short delta = column.postdeltas[i]; // We skip delta, len and padding. // ptr+=3; // Skip transparent rows... if (delta == 0xFF) break; destPos = desttop + n * delta * this.width; // These lengths are already correct. for (int j = 0; j < column.postlen[i]; j++) { dest[destPos] = data[ptr++]; dest[destPos + 1] = dest[destPos]; dest[destPos + 2] = dest[destPos]; destPos += n * this.width; } } } } else if (m == 4) { // desttop=4*desttop; for (int col = 0; col < w; desttop += 4, col++) { column = patch.columns[col]; data=this.getShortVersion(column.data); // For each post... for (int i = 0; i < column.posts; i++) { // Get pointer to post offset. ptr = column.postofs[i]; // Get post delta short delta = column.postdeltas[i]; // We skip delta, len and padding. // ptr+=3; // Skip transparent rows... if (delta == 0xFF) break; destPos = desttop + n * delta * this.width; // These lengths are already correct. for (int j = 0; j < column.postlen[i]; j++) { dest[destPos] = data[ptr++]; dest[destPos + 1] = dest[destPos]; dest[destPos + 2] = dest[destPos]; dest[destPos + 3] = dest[destPos]; destPos += n * this.width; } } } } else { // desttop=m*desttop; for (int col = 0; col < w; desttop += m, col++) { // This points at a "column" object. // column = (column_t *)((byte *)patch + // LONG(patch->columnofs[col])); column = patch.columns[col]; // For each post... for (int i = 0; i < column.posts; i++) { // Get pointer to post offset. ptr = column.postofs[i]; // Get post delta short delta = column.postdeltas[i]; // We skip delta, len and padding. // ptr+=3; // Skip transparent rows... if (delta == 0xFF) break; destPos = desttop + n * delta * this.width; // These lengths are already correct. for (int j = 0; j < column.postlen[i]; j++) { for (int k = 0; k < m; k++) dest[destPos + k] = column.data[ptr]; ptr++; destPos += n * this.width; } } } } scaleSolid(m, n, scrn, m * patch.width); } /** * Pretty crude in-place scaling. It's fast, but only works full-screen * Width needs to be specific, height is implied. * */ protected final void scaleSolid(int m, int n, int screen, int width) { int height = screens[screen].length / width; for (int i = 0; i < height; i += n) { for (int j = 0; j < n - 1; j++) { System.arraycopy(screens[screen], (i + j) * width, screens[screen], (i + j + 1) * width, width); } } } /** * V_DrawPatchFlipped Masks a column based masked pic to the screen. Flips * horizontally, e.g. to mirror face. * * Currently UNUSED, as any occurence to it is also scaled and best served * by another function. * * */ public final void DrawPatchFlipped(int x, int y, int scrn, patch_t patch) { column_t column; int desttop; short[] dest = screens[scrn]; int w; y -= patch.topoffset; x -= patch.leftoffset; if (RANGECHECK) if (doRangeCheck(x, y, patch, scrn)) { { System.err.print("Patch origin " + x + "," + y + " exceeds LFB\n"); // No I_Error abort - what is up with TNT.WAD? I.Error("Bad V_DrawPatch in V_DrawPatchFlipped"); } } if (scrn == 0) this.MarkRect(x, y, patch.width, patch.height); // Set x and y coords inside dest array. w = patch.width; desttop = y * this.width + x; // For each column.. for (int col = 0; col < w; desttop++, col++) { // This points at a "column" object. // Notice the flipping on this one. // column = (column_t *)((byte *)patch + // LONG(patch->columnofs[w-1-col])); column = patch.columns[w - 1 - col]; // For each post... // System.out.println("Column"+(w-1-col)); for (int i = 0; i < column.posts; i++) { // Get pointer to post offset. int ptr = column.postofs[i]; // Get post delta int delta = 0xFF & column.data[ptr]; // We skip delta, len and padding. // ptr+=3; if (delta == 0xFF) break; int destPos = desttop + delta * this.width; // count = column.length; // These lengths are already correct. for (int j = 0; j < column.postlen[i]; j++, destPos += this.width) { dest[destPos] = column.data[ptr + j]; // next line } } } } /** * V_DrawScaledPatch like V_DrawPatch, but scaled 2,3,4 times the original * size and position this is used for menu and title screens, with high * resolutions * * added:05-02-98: default params : scale patch and scale start * * Iniially implemented for Mocha Doom by _D_ (shamelessly ripped from * Eternity Engine ;-), adapted to scale based on a scaling info object * (VSI). * * Unless overriden by flags, starting x and y are automatically scaled * (implied V_SCALESTART) * */ @Override public void DrawScaledPatch(int x, int y, int scrn, IVideoScale VSI, patch_t patch) { int col; column_t column; int desttop; final short[] dest = screens[scrn & 0xFF]; // byte[] source; int dupx, dupy; int colfrac, rowfrac; // System.out.printf("V_DrawScaledPatch %d %d \n",x,y); // draw an hardware converted patch /* * #ifdef HWRENDER if (rendermode != render_soft) { * HWR_DrawPatch((GlidePatch_t *) patch, x, y, scrn); return; } #endif */ // A very common operation, eliminates the need to pre-divide. if (C2JUtils.flags(scrn, V_PREDIVIDE)) { x /= vs.getScalingX(); y /= vs.getScalingY(); } if (C2JUtils.flags(scrn, V_NOSCALEPATCH)) dupx = dupy = 1; else { dupx = VSI.getScalingX(); dupy = VSI.getScalingY(); } // Eliminates. // MAES: added this fix so that non-zero patch offsets can be // taken into account, regardless of whether we use pre-scaled // coords or not. Only Doomguy's face needs this hack for now. if (C2JUtils.flags(scrn, V_SCALEOFFSET)) { y -= patch.topoffset * dupx; x -= patch.leftoffset * dupy; } else { y -= patch.topoffset; x -= patch.leftoffset; } colfrac = dupx; rowfrac = dupy; // desttop = screens[scrn & 0xFF]; if (C2JUtils.flags(scrn, V_NOSCALESTART)) desttop = (y * this.width) + x; else desttop = (y * dupy * this.width) + (x * dupx) /* + scaledofs */; // destend = desttop + /*SHORT(*/patch.width/*)*/ * dupx; int w = patch.width * dupx; int colInc = 1; col = 0; if (C2JUtils.flags(scrn, V_FLIPPEDPATCH)) { colInc = -1; col = w - 1;// (/*SHORT(*/patch.width/*)*/ << fixed_t.FRACBITS) + // colfrac; } for (; col >= 0 && col < w/* ; desttop < destend */; col += colInc, desttop++) { // column = (column_t *) ((byte *) patch + LONG(patch.columnofs[col // >> FRACBITS])); column = patch.columns[col / colfrac]; final short[] data=getShortVersion(column.data); int destPos; int ptr = 0; int ptrOfs; // while (column.topdelta != 0xff) for (int i = 0; i < column.posts; i++) { { ptrOfs = column.postofs[i];// +3; ptr = 0; short delta = column.postdeltas[i]; // Skip transparent rows... if (delta == 0xFF) break; destPos = desttop + delta * dupy * this.width; // dest = desttop + column.topdelta * dupy * this.width; // ofs = 0; // while (count-- > 0) for (int j = 0; j < column.postlen[i] * dupy; j++) { dest[destPos] = data[ptrOfs + ptr / rowfrac]; destPos += this.width; ptr++; // ofs += rowfrac; } // column = (column_t *) ((byte *) column + column.length + // 4); } } } } @Override public final void DrawBlock(int x, int y, int scrn, int width, int height, byte[] src) { DrawBlock(x,y,scrn,width,height,src,0); } @Override public final void DrawBlock(int x, int y, int scrn, int width, int height, byte[] src,int offset) { // This is "screens[scrn]" final short[] dest = screens[scrn]; final short[] data=getShortVersion(src); if (doRangeCheck(x, y, scrn)) { I.Error("Bad V_DrawBlock"); } this.MarkRect(x, y, width, height); int destPos = /* screens[scrn] + */y * this.width + x; // MAES: making an assumption here. A BIIIIG one. int srcPos = offset; while ((height--) > 0) { // A real dog. It's good that this ain't used // so often. for (int xx=0;xx<width;xx++) dest[destPos+xx]=data[srcPos+xx]; //System.arraycopy(src, srcPos, dest, destPos, width); srcPos += width; destPos += this.width; } } /** * Replaces DrawPatchCol for bunny scrolled in Finale. * * */ @Override public final void DrawPatchColScaled(int x, patch_t patch, int col, IVideoScale vs, int screen) { column_t column; int source; final short[] dest; final short[] data; int desttop; final int scale = vs.getScalingX(); column = patch.columns[col]; data=this.getShortVersion(column.data); desttop = x * scale; // Scale X position. dest = screens[screen]; // step through the posts in a column for (int i = 0; i < column.posts; i++) { // Get pointer to post offset. source = column.postofs[i]; // Get post delta short delta = column.postdeltas[i]; // We skip delta, len and padding. // source+=3; NOT NEEDED ANYMORE, skipped by parsing. // Skip transparent rows... if (delta == 0xFF) break; // Replicate each column scale times vertically, // with spaced pixels. final int startsource = source; for (int kl = 0; kl < scale; kl++) { int destPos = desttop + (delta + kl) * SCREENWIDTH; for (int j = 0; j < column.postlen[i]; j++) { final short datap = data[source++]; // replicate each column's pixel horizontally and // vertically. for (int k = 0; k < scale; k++) dest[destPos + k] = datap; destPos += scale * SCREENWIDTH; } source = startsource; } } } public void setScreen(int index, int width, int height) { this.screens[index] = new short[width * height]; } public final void takeScreenShot(int screen, String imagefile, IndexColorModel icm) throws IOException { BufferedImage b = new BufferedImage(this.getWidth(), this.getHeight(), BufferedImage.TYPE_BYTE_INDEXED, icm); int[] tmp = new int[this.screens[screen].length]; for (int i = 0; i < this.screens[screen].length; i++) { tmp[i] = this.screens[screen][i]; } b.getRaster().setPixels(0, 0, this.getWidth(), this.getHeight(), tmp); File outputFile = new File(imagefile + ".png"); ImageIO.write(b, "PNG", outputFile); } /** Internal method for setting up palettes (and gamma tables) * */ public void createPalettes(byte[] paldata, short[][] gammadata, final int palettes, final int colors, final int stride,final int gammalevels){ // Sanity check on supplied data length. If there is not enough data to create the specified palettes, // their number will be limited. if (paldata!=null) // As many as are likely contained maxpalettes=paldata.length/(colors*stride); else maxpalettes=0; // Do some default action on null palette. if (gammadata!=null) // As many as are likely contained maxgammas=gammadata.length; else maxgammas=0; // Do some default action on null gamma tables. if (maxgammas==0){ gammadata=GammaTables.gammatables; maxgammas=GammaTables.gammatables.length; } // Enough data for all palettes. // Enough data for all palettes. if (maxpalettes>0 && maxgammas>0) specificPaletteCreation(paldata,gammadata,palettes,colors,stride,gammalevels); else paletteRecovery(); } /** Override this in extending classes to perform specific actions depending on the * type of renderer. It's better not to assign a default action, nor make assumptions * on the underlying types of actual palettes * * @param paldata * @param gammadata * @param palettes * @param colors * @param stride * @param gammalevels */ protected abstract void specificPaletteCreation(byte[] paldata, short[][] gammadata, final int palettes, final int colors, final int stride, final int gammalevels); /** * Clear automap frame buffer or fill solid color * MAES: optimized for efficiency, seen the lack of a proper "memset" in Java. * */ @Override public final void FillRect(int color, int screen, int x, int y, int width, int height) { short[] arr = screens[screen]; // Do a "per scanline" copy. int fromIndex = x + y * SCREENWIDTH; int toIndex = x + (y + height - 1) * SCREENWIDTH; final short c=(short) getBaseColor(color); // First scanline. for (int i = 0; i < width; i++) arr[fromIndex + i] = c; for (; fromIndex < toIndex; fromIndex += SCREENWIDTH) { System.arraycopy(arr, fromIndex, arr, fromIndex + SCREENWIDTH, width); } } public final void GetBlock(int x, int y, int scrn, int width, int height, short[] dest) { final short[] src = screens[scrn]; if (RANGECHECK) { if (doRangeCheck(x, y, scrn)) { I.Error("Bad V_DrawBlock"); } } int srcPos = y * this.width + x; int destPos = 0; while ((height--) > 0) { System.arraycopy(src, srcPos, dest, destPos, width); // memcpy (dest, src, width); srcPos += width; destPos += this.width; } } public final void setColorMaps(short[] stuff, int num){ // For HiCOlor, load COLORS15 lump this.cmap_base=new short[num][256]; this.cmap_work=new short[num][256]; for (int i = 0; i < cmap_base.length; i++) { System.arraycopy(stuff, i * 256, cmap_base[i], 0, 256); System.arraycopy(stuff, i * 256, cmap_work[i], 0, 256); } } public final void setColorMaps(int[] stuff, int num){ // For HiCOlor, load COLORS15 lump this.cmap_base=new short[num][256]; this.cmap_work=new short[num][256]; for (int i = 0; i < cmap_base.length; i++) { for (int j=0;j<256;j++){ cmap_base[i][j]=PaletteGenerator.rgb888to555(stuff[i*256+j]); cmap_work[i][j]=cmap_base[i][j]; } } } public short[][] getColorMaps(){ return cmap_work; } ///// MEGA HACK FOR SUPER-8BIT MODES private final HashMap<Integer,short[]> colcache=new HashMap<Integer,short[]>(); private final short[] getShortVersion(byte[] data){ if (!colcache.containsKey(data.hashCode())){ //System.out.printf("Generated cache for %d\n",data.hashCode()); short[] stuff=new short[data.length]; for (int i=0;i<stuff.length;i++){ stuff[i]=cmap_work[CMAP_FIXED][0xFF&data[i]]; } colcache.put(data.hashCode(),stuff); } return colcache.get(data.hashCode()); } @Override public final void clearCaches(){ this.colcache.clear(); } ///// MODIFIED COLORMAP CACHING ////////// private final HashMap<Integer,short[][]> cmapcache=new HashMap<Integer,short[][]>(); /** Cache colormaps in order to avoid expensive re-computations * Invalidate if gamma is changed. Changes are applied on-the-fly * to working_colormap. * * */ protected final void getCachedCmap(int palette){ short[][] stuff=cmapcache.get(palette); if (stuff==null){ // Generate the full range of colormap for a given palette effect //stuff= //PaletteGenerator.RF_BuildLights15(palettes[usegamma*maxpalettes+usepalette], NUMLIGHTS); // Tinting more faithful to the original, thanks to Sodaholic's input. stuff=new short[cmap_base.length][cmap_base[0].length]; for (int i = 0; i < stuff.length; i++) { PaletteGenerator.tintColormap(cmap_base[i], stuff[i], 256, ColorTint.tints[palette%14],GammaTables.gammatables[usegamma]); } cmapcache.put(palette,stuff); } // Use fast arraycopy to copy data to working colormap. for (int i = 0; i < stuff.length; i++) { System.arraycopy(stuff[i], 0, cmap_work[i], 0, 256); } } protected final void clearPalettes(){ this.cmapcache.clear(); } } package v; import static data.Defines.RANGECHECK; import i.DoomStatusAware; import i.IDoomSystem; import java.awt.Image; import java.awt.image.BufferedImage; import java.awt.image.IndexColorModel; import java.io.File; import java.io.IOException; import java.util.HashMap; import javax.imageio.ImageIO; import doom.DoomStatus; import m.BBox; import rr.column_t; import rr.patch_t; import utils.C2JUtils; public abstract class SoftwareVideoRenderer32 extends SoftwareVideoRenderer<byte[],int[]> { // MAES: maybe this should be a bbox? public BBox dirtybox = new BBox(); public SoftwareVideoRenderer32() { // Defaults super(); screens = new int[5][]; } public SoftwareVideoRenderer32(int w, int h) { super(w,h); screens = new int[5][]; } /** * V_Fillrect */ @Override public void FillRect(int srcx, int srcy, int width, int height, int destscrn) { // These are pointers inside an array. final int[] dest = screens[destscrn]; if (RANGECHECK) { if (srcx < 0 || srcx + width > this.width || srcy < 0 || srcy + height > SCREENHEIGHT || destscrn > 4) { I.Error("Bad V_FillRect"); } } this.MarkRect(srcx, srcy, width, height); // MAES: these were pointers to a specific position inside the screen. int srcPos = this.width * srcy + srcx; for (; height > 0; height--) { for (int i = 0; i < width; i++) { dest[srcPos + i] = 0; } // memcpy (dest, src, width); srcPos += this.width; } } /** * V_DrawPatch Masks a column based masked pic to the screen. desttop, dest * and source were byte* */ public final void DrawPatch(int x, int y, int scrn, patch_t patch) { column_t column; int desttop; final int[] dest = screens[scrn]; int w; y -= patch.topoffset; x -= patch.leftoffset; if (RANGECHECK) if (doRangeCheck(x, y, patch, scrn)) { System.err.print("Patch at " + x + "," + y + " exceeds LFB\n"); // No I_Error abort - what is up with TNT.WAD? System.err.print("V_DrawPatch: bad patch (ignored)\n"); return; } if (scrn == 0) this.MarkRect(x, y, patch.width, patch.height); w = patch.width; desttop = x + this.width * y; // For each column.. int destPos; int ptr = 0; for (int col = 0; col < w; desttop++, col++, x++) { // This points at a "column" object. // column = (column_t *)((byte *)patch + // LONG(patch->columnofs[col])); column = patch.columns[col]; final int[] data=getShortVersion(column.data); // For each post... for (int i = 0; i < column.posts; i++) { // Get pointer to post offset. ptr = column.postofs[i]; // Get post delta short delta = column.postdeltas[i]; // We skip delta, len and padding. // ptr+=3; NOT NEEDED< pre-skipped at parsing. // Skip transparent rows... if (delta == 0xFF) break; destPos = desttop + delta * this.width; // These lengths are already correct. for (int j = 0; j < column.postlen[i]; j++) { dest[destPos] = data[ptr++]; destPos += this.width; } } } } /** * V_DrawPatchSolidScaled Draws a SOLID (non-masked) patch to the screen * with integer scaling m and n. Useful for stuff such as help screens, * titlepic and status bar. Not very useful for menus, though. desttop, dest * and source were byte */ public final void DrawPatchSolidScaled(int x, int y, int m, int n, int scrn, patch_t patch) { if ((m == 1) && (n == 1)) { DrawPatch(x, y, scrn, patch); return; } column_t column; int[] data; int desttop; final int[] dest = screens[scrn]; int w; y = y * n - patch.topoffset; x = x * m - patch.leftoffset; if (RANGECHECK) if (doRangeCheck(x, y, patch, scrn)) { System.err.print("Patch at " + x + "," + y + " exceeds LFB\n"); // No I_Error abort - what is up with TNT.WAD? System.err.print("V_DrawPatch: bad patch (ignored)\n"); return; } if (scrn == 0) this.MarkRect(x, y, patch.width, patch.height); w = patch.width; desttop = m * x + this.width * y; // For each column.. int destPos; int ptr = 0; // x increases by m. // Some unrolling... if (m == 2) { // desttop=2*desttop; for (int col = 0; col < w; desttop += 2, col++) { // This points at a "column" object. // column = (column_t *)((byte *)patch + // LONG(patch->columnofs[col])); column = patch.columns[col]; data=this.getShortVersion(column.data); // For each post... for (int i = 0; i < column.posts; i++) { // Get pointer to post offset. ptr = column.postofs[i]; // Get post delta short delta = column.postdeltas[i]; // We skip delta, len and padding. // ptr+=3; // Skip transparent rows... if (delta == 0xFF) break; destPos = desttop + n * delta * this.width; // These lengths are already correct. for (int j = 0; j < column.postlen[i]; j++) { dest[destPos] = data[ptr++]; dest[destPos + 1] = dest[destPos]; destPos += n * this.width; } } } } else if (m == 3) { // desttop=3*desttop; for (int col = 0; col < w; desttop += 3, col++) { column = patch.columns[col]; data=this.getShortVersion(column.data); // For each post... for (int i = 0; i < column.posts; i++) { // Get pointer to post offset. ptr = column.postofs[i]; // Get post delta short delta = column.postdeltas[i]; // We skip delta, len and padding. // ptr+=3; // Skip transparent rows... if (delta == 0xFF) break; destPos = desttop + n * delta * this.width; // These lengths are already correct. for (int j = 0; j < column.postlen[i]; j++) { dest[destPos] = data[ptr++]; dest[destPos + 1] = dest[destPos]; dest[destPos + 2] = dest[destPos]; destPos += n * this.width; } } } } else if (m == 4) { // desttop=4*desttop; for (int col = 0; col < w; desttop += 4, col++) { column = patch.columns[col]; data=this.getShortVersion(column.data); // For each post... for (int i = 0; i < column.posts; i++) { // Get pointer to post offset. ptr = column.postofs[i]; // Get post delta short delta = column.postdeltas[i]; // We skip delta, len and padding. // ptr+=3; // Skip transparent rows... if (delta == 0xFF) break; destPos = desttop + n * delta * this.width; // These lengths are already correct. for (int j = 0; j < column.postlen[i]; j++) { dest[destPos] = data[ptr++]; dest[destPos + 1] = dest[destPos]; dest[destPos + 2] = dest[destPos]; dest[destPos + 3] = dest[destPos]; destPos += n * this.width; } } } } else { // desttop=m*desttop; for (int col = 0; col < w; desttop += m, col++) { // This points at a "column" object. // column = (column_t *)((byte *)patch + // LONG(patch->columnofs[col])); column = patch.columns[col]; // For each post... for (int i = 0; i < column.posts; i++) { // Get pointer to post offset. ptr = column.postofs[i]; // Get post delta short delta = column.postdeltas[i]; // We skip delta, len and padding. // ptr+=3; // Skip transparent rows... if (delta == 0xFF) break; destPos = desttop + n * delta * this.width; // These lengths are already correct. for (int j = 0; j < column.postlen[i]; j++) { for (int k = 0; k < m; k++) dest[destPos + k] = column.data[ptr]; ptr++; destPos += n * this.width; } } } } scaleSolid(m, n, scrn, m * patch.width); } /** * Pretty crude in-place scaling. It's fast, but only works full-screen * Width needs to be specific, height is implied. * */ protected final void scaleSolid(int m, int n, int screen, int width) { int height = screens[screen].length / width; for (int i = 0; i < height; i += n) { for (int j = 0; j < n - 1; j++) { System.arraycopy(screens[screen], (i + j) * width, screens[screen], (i + j + 1) * width, width); } } } /** * V_DrawPatchFlipped Masks a column based masked pic to the screen. Flips * horizontally, e.g. to mirror face. * * Currently UNUSED, as any occurence to it is also scaled and best served * by another function. * * */ public final void DrawPatchFlipped(int x, int y, int scrn, patch_t patch) { column_t column; int desttop; int[] dest = screens[scrn]; int w; y -= patch.topoffset; x -= patch.leftoffset; if (RANGECHECK) if (doRangeCheck(x, y, patch, scrn)) { { System.err.print("Patch origin " + x + "," + y + " exceeds LFB\n"); // No I_Error abort - what is up with TNT.WAD? I.Error("Bad V_DrawPatch in V_DrawPatchFlipped"); } } if (scrn == 0) this.MarkRect(x, y, patch.width, patch.height); // Set x and y coords inside dest array. w = patch.width; desttop = y * this.width + x; // For each column.. for (int col = 0; col < w; desttop++, col++) { // This points at a "column" object. // Notice the flipping on this one. // column = (column_t *)((byte *)patch + // LONG(patch->columnofs[w-1-col])); column = patch.columns[w - 1 - col]; // For each post... // System.out.println("Column"+(w-1-col)); for (int i = 0; i < column.posts; i++) { // Get pointer to post offset. int ptr = column.postofs[i]; // Get post delta int delta = 0xFF & column.data[ptr]; // We skip delta, len and padding. // ptr+=3; if (delta == 0xFF) break; int destPos = desttop + delta * this.width; // count = column.length; // These lengths are already correct. for (int j = 0; j < column.postlen[i]; j++, destPos += this.width) { dest[destPos] = column.data[ptr + j]; // next line } } } } /** * V_DrawScaledPatch like V_DrawPatch, but scaled 2,3,4 times the original * size and position this is used for menu and title screens, with high * resolutions * * added:05-02-98: default params : scale patch and scale start * * Iniially implemented for Mocha Doom by _D_ (shamelessly ripped from * Eternity Engine ;-), adapted to scale based on a scaling info object * (VSI). * * Unless overriden by flags, starting x and y are automatically scaled * (implied V_SCALESTART) * */ @Override public void DrawScaledPatch(int x, int y, int scrn, IVideoScale VSI, patch_t patch) { int col; column_t column; int desttop; final int[] dest = screens[scrn & 0xFF]; // byte[] source; int dupx, dupy; int colfrac, rowfrac; // System.out.printf("V_DrawScaledPatch %d %d \n",x,y); // draw an hardware converted patch /* * #ifdef HWRENDER if (rendermode != render_soft) { * HWR_DrawPatch((GlidePatch_t *) patch, x, y, scrn); return; } #endif */ // A very common operation, eliminates the need to pre-divide. if (C2JUtils.flags(scrn, V_PREDIVIDE)) { x /= vs.getScalingX(); y /= vs.getScalingY(); } if (C2JUtils.flags(scrn, V_NOSCALEPATCH)) dupx = dupy = 1; else { dupx = VSI.getScalingX(); dupy = VSI.getScalingY(); } // Eliminates. // MAES: added this fix so that non-zero patch offsets can be // taken into account, regardless of whether we use pre-scaled // coords or not. Only Doomguy's face needs this hack for now. if (C2JUtils.flags(scrn, V_SCALEOFFSET)) { y -= patch.topoffset * dupx; x -= patch.leftoffset * dupy; } else { y -= patch.topoffset; x -= patch.leftoffset; } colfrac = dupx; rowfrac = dupy; // desttop = screens[scrn & 0xFF]; if (C2JUtils.flags(scrn, V_NOSCALESTART)) desttop = (y * this.width) + x; else desttop = (y * dupy * this.width) + (x * dupx) /* + scaledofs */; // destend = desttop + /*SHORT(*/patch.width/*)*/ * dupx; int w = patch.width * dupx; int colInc = 1; col = 0; if (C2JUtils.flags(scrn, V_FLIPPEDPATCH)) { colInc = -1; col = w - 1;// (/*SHORT(*/patch.width/*)*/ << fixed_t.FRACBITS) + // colfrac; } for (; col >= 0 && col < w/* ; desttop < destend */; col += colInc, desttop++) { // column = (column_t *) ((byte *) patch + LONG(patch.columnofs[col // >> FRACBITS])); column = patch.columns[col / colfrac]; final int[] data=getShortVersion(column.data); int destPos; int ptr = 0; int ptrOfs; // while (column.topdelta != 0xff) for (int i = 0; i < column.posts; i++) { { ptrOfs = column.postofs[i];// +3; ptr = 0; short delta = column.postdeltas[i]; // Skip transparent rows... if (delta == 0xFF) break; destPos = desttop + delta * dupy * this.width; // dest = desttop + column.topdelta * dupy * this.width; // ofs = 0; // while (count-- > 0) for (int j = 0; j < column.postlen[i] * dupy; j++) { dest[destPos] = data[ptrOfs + ptr / rowfrac]; destPos += this.width; ptr++; // ofs += rowfrac; } // column = (column_t *) ((byte *) column + column.length + // 4); } } } } @Override public final void DrawBlock(int x, int y, int scrn, int width, int height, byte[] src) { DrawBlock(x,y,scrn,width,height,src,0); } @Override public final void DrawBlock(int x, int y, int scrn, int width, int height, byte[] src,int offset) { // This is "screens[scrn]" final int[] dest = screens[scrn]; final int[] data=getShortVersion(src); if (doRangeCheck(x, y, scrn)) { I.Error("Bad V_DrawBlock"); } this.MarkRect(x, y, width, height); int destPos = /* screens[scrn] + */y * this.width + x; // MAES: making an assumption here. A BIIIIG one. int srcPos = offset; while ((height--) > 0) { // A real dog. It's good that this ain't used // so often. for (int xx=0;xx<width;xx++) dest[destPos+xx]=data[srcPos+xx]; //System.arraycopy(src, srcPos, dest, destPos, width); srcPos += width; destPos += this.width; } } /** * Replaces DrawPatchCol for bunny scrolled in Finale. * * */ @Override public final void DrawPatchColScaled(int x, patch_t patch, int col, IVideoScale vs, int screen) { column_t column; int source; final int[] dest; final int[] data; int desttop; final int scale = vs.getScalingX(); column = patch.columns[col]; data=this.getShortVersion(column.data); desttop = x * scale; // Scale X position. dest = screens[screen]; // step through the posts in a column for (int i = 0; i < column.posts; i++) { // Get pointer to post offset. source = column.postofs[i]; // Get post delta short delta = column.postdeltas[i]; // We skip delta, len and padding. // source+=3; NOT NEEDED ANYMORE, skipped by parsing. // Skip transparent rows... if (delta == 0xFF) break; // Replicate each column scale times vertically, // with spaced pixels. final int startsource = source; for (int kl = 0; kl < scale; kl++) { int destPos = desttop + (delta + kl) * SCREENWIDTH; for (int j = 0; j < column.postlen[i]; j++) { final int datap = data[source++]; // replicate each column's pixel horizontally and // vertically. for (int k = 0; k < scale; k++) dest[destPos + k] = datap; destPos += scale * SCREENWIDTH; } source = startsource; } } } public void setScreen(int index, int width, int height) { this.screens[index] = new int[width * height]; } public final void takeScreenShot(int screen, String imagefile, IndexColorModel icm) throws IOException { BufferedImage b = new BufferedImage(this.getWidth(), this.getHeight(), BufferedImage.TYPE_BYTE_INDEXED, icm); int[] tmp = new int[this.screens[screen].length]; for (int i = 0; i < this.screens[screen].length; i++) { tmp[i] = this.screens[screen][i]; } b.getRaster().setPixels(0, 0, this.getWidth(), this.getHeight(), tmp); File outputFile = new File(imagefile + ".png"); ImageIO.write(b, "PNG", outputFile); } /** Internal method for setting up palettes (and gamma tables) * */ public void createPalettes(byte[] paldata, short[][] gammadata, final int palettes, final int colors, final int stride,final int gammalevels){ // Sanity check on supplied data length. If there is not enough data to create the specified palettes, // their number will be limited. if (paldata!=null) // As many as are likely contained maxpalettes=paldata.length/(colors*stride); else maxpalettes=0; // Do some default action on null palette. if (gammadata!=null) // As many as are likely contained maxgammas=gammadata.length; else maxgammas=0; // Do some default action on null gamma tables. if (maxgammas==0){ gammadata=GammaTables.gammatables; maxgammas=GammaTables.gammatables.length; } // Enough data for all palettes. // Enough data for all palettes. if (maxpalettes>0 && maxgammas>0) specificPaletteCreation(paldata,gammadata,palettes,colors,stride,gammalevels); else paletteRecovery(); } /** Override this in extending classes to perform specific actions depending on the * type of renderer. It's better not to assign a default action, nor make assumptions * on the underlying types of actual palettes * * @param paldata * @param gammadata * @param palettes * @param colors * @param stride * @param gammalevels */ protected abstract void specificPaletteCreation(byte[] paldata, short[][] gammadata, final int palettes, final int colors, final int stride, final int gammalevels); /** * Clear automap frame buffer or fill solid color * MAES: optimized for efficiency, seen the lack of a proper "memset" in Java. * */ @Override public final void FillRect(int color, int screen, int x, int y, int width, int height) { int[] arr = screens[screen]; final int c=getBaseColor(color); // Do a "per scanline" copy. int fromIndex = x + y * SCREENWIDTH; int toIndex = x + (y + height - 1) * SCREENWIDTH; // First scanline. for (int i = 0; i < width; i++) arr[fromIndex + i] = c; for (; fromIndex < toIndex; fromIndex += SCREENWIDTH) { System.arraycopy(arr, fromIndex, arr, fromIndex + SCREENWIDTH, width); } } public final void GetBlock(int x, int y, int scrn, int width, int height, int[] dest) { final int[] src = screens[scrn]; if (RANGECHECK) { if (doRangeCheck(x, y, scrn)) { I.Error("Bad V_DrawBlock"); } } int srcPos = y * this.width + x; int destPos = 0; while ((height--) > 0) { System.arraycopy(src, srcPos, dest, destPos, width); // memcpy (dest, src, width); srcPos += width; destPos += this.width; } } public final void setColorMaps(short[] stuff, int num){ // For HiCOlor, load COLORS15 lump this.cmap_base=new int[num][256]; this.cmap_work=new int[num][256]; for (int i = 0; i < cmap_base.length; i++) { System.arraycopy(stuff, i * 256, cmap_base[i], 0, 256); System.arraycopy(stuff, i * 256, cmap_work[i], 0, 256); } } public final void setColorMaps(int[] stuff, int num){ // For HiCOlor, load COLORS15 lump this.cmap_base=new int[num][256]; this.cmap_work=new int[num][256]; for (int i = 0; i < cmap_base.length; i++) { for (int j=0;j<256;j++){ cmap_base[i][j]=PaletteGenerator.rgb888to555(stuff[i*256+j]); cmap_work[i][j]=cmap_base[i][j]; } } } public int[][] getColorMaps(){ return cmap_work; } ///// MEGA HACK FOR SUPER-8BIT MODES private final HashMap<Integer,int[]> colcache=new HashMap<Integer,int[]>(); private final int[] getShortVersion(byte[] data){ if (!colcache.containsKey(data.hashCode())){ //System.out.printf("Generated cache for %d\n",data.hashCode()); int[] stuff=new int[data.length]; for (int i=0;i<stuff.length;i++){ stuff[i]=cmap_work[CMAP_FIXED][0xFF&data[i]]; } colcache.put(data.hashCode(),stuff); } return colcache.get(data.hashCode()); } @Override public final void clearCaches(){ this.colcache.clear(); } ///// MODIFIED COLORMAP CACHING ////////// private final HashMap<Integer,int[][]> cmapcache=new HashMap<Integer,int[][]>(); /** Cache colormaps in order to avoid expensive re-computations * Invalidate if gamma is changed. Changes are applied on-the-fly * to working_colormap. * * */ protected final void getCachedCmap(int palette){ int[][] stuff=cmapcache.get(palette); if (stuff==null){ // Generate the full range of colormap for a given palette effect // Results in different tinting behavior than vanilla. //stuff= //PaletteGenerator.RF_BuildLights24(palettes[usegamma*maxpalettes+usepalette], NUMLIGHTS); // Tinting more faithful to the original, thanks to Sodaholic's input. stuff=new int[cmap_base.length][cmap_base[0].length]; for (int i = 0; i < stuff.length; i++) PaletteGenerator.tintColormap(cmap_base[i], stuff[i], 256, ColorTint.tints[palette%14],GammaTables.gammatables[usegamma]); cmapcache.put(palette,stuff); } // Use fast arraycopy to copy data to working colormap. for (int i = 0; i < stuff.length; i++) { System.arraycopy(stuff[i], 0, cmap_work[i], 0, 256); } } protected final void clearPalettes(){ this.cmapcache.clear(); } } package v; // Emacs style mode select -*- C++ -*- //----------------------------------------------------------------------------- // //$Id: GammaTables.java,v 1.2 2016/07/04 07:52:26 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: // Gamma correction LUT stuff. // public class GammaTables { // Now where did these came from? /*[5][256]*/ public static final short[][] gammatables = { {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, 17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32, 33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48, 49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64, 65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80, 81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96, 97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112, 113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128, 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159, 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175, 176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191, 192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207, 208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223, 224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239, 240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255}, {2,4,5,7,8,10,11,12,14,15,16,18,19,20,21,23,24,25,26,27,29,30,31, 32,33,34,36,37,38,39,40,41,42,44,45,46,47,48,49,50,51,52,54,55, 56,57,58,59,60,61,62,63,64,65,66,67,69,70,71,72,73,74,75,76,77, 78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98, 99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114, 115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,129, 130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145, 146,147,148,148,149,150,151,152,153,154,155,156,157,158,159,160, 161,162,163,163,164,165,166,167,168,169,170,171,172,173,174,175, 175,176,177,178,179,180,181,182,183,184,185,186,186,187,188,189, 190,191,192,193,194,195,196,196,197,198,199,200,201,202,203,204, 205,205,206,207,208,209,210,211,212,213,214,214,215,216,217,218, 219,220,221,222,222,223,224,225,226,227,228,229,230,230,231,232, 233,234,235,236,237,237,238,239,240,241,242,243,244,245,245,246, 247,248,249,250,251,252,252,253,254,255}, {4,7,9,11,13,15,17,19,21,22,24,26,27,29,30,32,33,35,36,38,39,40,42, 43,45,46,47,48,50,51,52,54,55,56,57,59,60,61,62,63,65,66,67,68,69, 70,72,73,74,75,76,77,78,79,80,82,83,84,85,86,87,88,89,90,91,92,93, 94,95,96,97,98,100,101,102,103,104,105,106,107,108,109,110,111,112, 113,114,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128, 129,130,131,132,133,133,134,135,136,137,138,139,140,141,142,143,144, 144,145,146,147,148,149,150,151,152,153,153,154,155,156,157,158,159, 160,160,161,162,163,164,165,166,166,167,168,169,170,171,172,172,173, 174,175,176,177,178,178,179,180,181,182,183,183,184,185,186,187,188, 188,189,190,191,192,193,193,194,195,196,197,197,198,199,200,201,201, 202,203,204,205,206,206,207,208,209,210,210,211,212,213,213,214,215, 216,217,217,218,219,220,221,221,222,223,224,224,225,226,227,228,228, 229,230,231,231,232,233,234,235,235,236,237,238,238,239,240,241,241, 242,243,244,244,245,246,247,247,248,249,250,251,251,252,253,254,254, 255}, {8,12,16,19,22,24,27,29,31,34,36,38,40,41,43,45,47,49,50,52,53,55, 57,58,60,61,63,64,65,67,68,70,71,72,74,75,76,77,79,80,81,82,84,85, 86,87,88,90,91,92,93,94,95,96,98,99,100,101,102,103,104,105,106,107, 108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124, 125,126,127,128,129,130,131,132,133,134,135,135,136,137,138,139,140, 141,142,143,143,144,145,146,147,148,149,150,150,151,152,153,154,155, 155,156,157,158,159,160,160,161,162,163,164,165,165,166,167,168,169, 169,170,171,172,173,173,174,175,176,176,177,178,179,180,180,181,182, 183,183,184,185,186,186,187,188,189,189,190,191,192,192,193,194,195, 195,196,197,197,198,199,200,200,201,202,202,203,204,205,205,206,207, 207,208,209,210,210,211,212,212,213,214,214,215,216,216,217,218,219, 219,220,221,221,222,223,223,224,225,225,226,227,227,228,229,229,230, 231,231,232,233,233,234,235,235,236,237,237,238,238,239,240,240,241, 242,242,243,244,244,245,246,246,247,247,248,249,249,250,251,251,252, 253,253,254,254,255}, {16,23,28,32,36,39,42,45,48,50,53,55,57,60,62,64,66,68,69,71,73,75,76, 78,80,81,83,84,86,87,89,90,92,93,94,96,97,98,100,101,102,103,105,106, 107,108,109,110,112,113,114,115,116,117,118,119,120,121,122,123,124, 125,126,128,128,129,130,131,132,133,134,135,136,137,138,139,140,141, 142,143,143,144,145,146,147,148,149,150,150,151,152,153,154,155,155, 156,157,158,159,159,160,161,162,163,163,164,165,166,166,167,168,169, 169,170,171,172,172,173,174,175,175,176,177,177,178,179,180,180,181, 182,182,183,184,184,185,186,187,187,188,189,189,190,191,191,192,193, 193,194,195,195,196,196,197,198,198,199,200,200,201,202,202,203,203, 204,205,205,206,207,207,208,208,209,210,210,211,211,212,213,213,214, 214,215,216,216,217,217,218,219,219,220,220,221,221,222,223,223,224, 224,225,225,226,227,227,228,228,229,229,230,230,231,232,232,233,233, 234,234,235,235,236,236,237,237,238,239,239,240,240,241,241,242,242, 243,243,244,244,245,245,246,246,247,247,248,248,249,249,250,250,251, 251,252,252,253,254,254,255,255} }; } package v; import java.awt.Color; /** * Palette generation failsafe. Uses only data from the first palette, and * generates the rest by tinting according to the Doom wiki specs. Uses info * from: http://doom.wikia.com/wiki/PLAYPAL * * Now extended to contain many extended color synthesizing tools, used * in super-indexed and truecolor software rendered displays. * * @author Maes */ public class PaletteGenerator { public final static int playpal[] = { 0x00, 0x00, 0x00, 0x1F, 0x17, 0x0B, 0x17, 0x0F, 0x07, 0x4B, 0x4B, 0x4B, 0xFF, 0xFF, 0xFF, 0x1B, 0x1B, 0x1B, 0x13, 0x13, 0x13, 0x0B, 0x0B, 0x0B, 0x07, 0x07, 0x07, 0x2F, 0x37, 0x1F, 0x23, 0x2B, 0x0F, 0x17, 0x1F, 0x07, 0x0F, 0x17, 0x00, 0x4F, 0x3B, 0x2B, 0x47, 0x33, 0x23, 0x3F, 0x2B, 0x1B, 0xFF, 0xB7, 0xB7, 0xF7, 0xAB, 0xAB, 0xF3, 0xA3, 0xA3, 0xEB, 0x97, 0x97, 0xE7, 0x8F, 0x8F, 0xDF, 0x87, 0x87, 0xDB, 0x7B, 0x7B, 0xD3, 0x73, 0x73, 0xCB, 0x6B, 0x6B, 0xC7, 0x63, 0x63, 0xBF, 0x5B, 0x5B, 0xBB, 0x57, 0x57, 0xB3, 0x4F, 0x4F, 0xAF, 0x47, 0x47, 0xA7, 0x3F, 0x3F, 0xA3, 0x3B, 0x3B, 0x9B, 0x33, 0x33, 0x97, 0x2F, 0x2F, 0x8F, 0x2B, 0x2B, 0x8B, 0x23, 0x23, 0x83, 0x1F, 0x1F, 0x7F, 0x1B, 0x1B, 0x77, 0x17, 0x17, 0x73, 0x13, 0x13, 0x6B, 0x0F, 0x0F, 0x67, 0x0B, 0x0B, 0x5F, 0x07, 0x07, 0x5B, 0x07, 0x07, 0x53, 0x07, 0x07, 0x4F, 0x00, 0x00, 0x47, 0x00, 0x00, 0x43, 0x00, 0x00, 0xFF, 0xEB, 0xDF, 0xFF, 0xE3, 0xD3, 0xFF, 0xDB, 0xC7, 0xFF, 0xD3, 0xBB, 0xFF, 0xCF, 0xB3, 0xFF, 0xC7, 0xA7, 0xFF, 0xBF, 0x9B, 0xFF, 0xBB, 0x93, 0xFF, 0xB3, 0x83, 0xF7, 0xAB, 0x7B, 0xEF, 0xA3, 0x73, 0xE7, 0x9B, 0x6B, 0xDF, 0x93, 0x63, 0xD7, 0x8B, 0x5B, 0xCF, 0x83, 0x53, 0xCB, 0x7F, 0x4F, 0xBF, 0x7B, 0x4B, 0xB3, 0x73, 0x47, 0xAB, 0x6F, 0x43, 0xA3, 0x6B, 0x3F, 0x9B, 0x63, 0x3B, 0x8F, 0x5F, 0x37, 0x87, 0x57, 0x33, 0x7F, 0x53, 0x2F, 0x77, 0x4F, 0x2B, 0x6B, 0x47, 0x27, 0x5F, 0x43, 0x23, 0x53, 0x3F, 0x1F, 0x4B, 0x37, 0x1B, 0x3F, 0x2F, 0x17, 0x33, 0x2B, 0x13, 0x2B, 0x23, 0x0F, 0xEF, 0xEF, 0xEF, 0xE7, 0xE7, 0xE7, 0xDF, 0xDF, 0xDF, 0xDB, 0xDB, 0xDB, 0xD3, 0xD3, 0xD3, 0xCB, 0xCB, 0xCB, 0xC7, 0xC7, 0xC7, 0xBF, 0xBF, 0xBF, 0xB7, 0xB7, 0xB7, 0xB3, 0xB3, 0xB3, 0xAB, 0xAB, 0xAB, 0xA7, 0xA7, 0xA7, 0x9F, 0x9F, 0x9F, 0x97, 0x97, 0x97, 0x93, 0x93, 0x93, 0x8B, 0x8B, 0x8B, 0x83, 0x83, 0x83, 0x7F, 0x7F, 0x7F, 0x77, 0x77, 0x77, 0x6F, 0x6F, 0x6F, 0x6B, 0x6B, 0x6B, 0x63, 0x63, 0x63, 0x5B, 0x5B, 0x5B, 0x57, 0x57, 0x57, 0x4F, 0x4F, 0x4F, 0x47, 0x47, 0x47, 0x43, 0x43, 0x43, 0x3B, 0x3B, 0x3B, 0x37, 0x37, 0x37, 0x2F, 0x2F, 0x2F, 0x27, 0x27, 0x27, 0x23, 0x23, 0x23, 0x77, 0xFF, 0x6F, 0x6F, 0xEF, 0x67, 0x67, 0xDF, 0x5F, 0x5F, 0xCF, 0x57, 0x5B, 0xBF, 0x4F, 0x53, 0xAF, 0x47, 0x4B, 0x9F, 0x3F, 0x43, 0x93, 0x37, 0x3F, 0x83, 0x2F, 0x37, 0x73, 0x2B, 0x2F, 0x63, 0x23, 0x27, 0x53, 0x1B, 0x1F, 0x43, 0x17, 0x17, 0x33, 0x0F, 0x13, 0x23, 0x0B, 0x0B, 0x17, 0x07, 0xBF, 0xA7, 0x8F, 0xB7, 0x9F, 0x87, 0xAF, 0x97, 0x7F, 0xA7, 0x8F, 0x77, 0x9F, 0x87, 0x6F, 0x9B, 0x7F, 0x6B, 0x93, 0x7B, 0x63, 0x8B, 0x73, 0x5B, 0x83, 0x6B, 0x57, 0x7B, 0x63, 0x4F, 0x77, 0x5F, 0x4B, 0x6F, 0x57, 0x43, 0x67, 0x53, 0x3F, 0x5F, 0x4B, 0x37, 0x57, 0x43, 0x33, 0x53, 0x3F, 0x2F, 0x9F, 0x83, 0x63, 0x8F, 0x77, 0x53, 0x83, 0x6B, 0x4B, 0x77, 0x5F, 0x3F, 0x67, 0x53, 0x33, 0x5B, 0x47, 0x2B, 0x4F, 0x3B, 0x23, 0x43, 0x33, 0x1B, 0x7B, 0x7F, 0x63, 0x6F, 0x73, 0x57, 0x67, 0x6B, 0x4F, 0x5B, 0x63, 0x47, 0x53, 0x57, 0x3B, 0x47, 0x4F, 0x33, 0x3F, 0x47, 0x2B, 0x37, 0x3F, 0x27, 0xFF, 0xFF, 0x73, 0xEB, 0xDB, 0x57, 0xD7, 0xBB, 0x43, 0xC3, 0x9B, 0x2F, 0xAF, 0x7B, 0x1F, 0x9B, 0x5B, 0x13, 0x87, 0x43, 0x07, 0x73, 0x2B, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xDB, 0xDB, 0xFF, 0xBB, 0xBB, 0xFF, 0x9B, 0x9B, 0xFF, 0x7B, 0x7B, 0xFF, 0x5F, 0x5F, 0xFF, 0x3F, 0x3F, 0xFF, 0x1F, 0x1F, 0xFF, 0x00, 0x00, 0xEF, 0x00, 0x00, 0xE3, 0x00, 0x00, 0xD7, 0x00, 0x00, 0xCB, 0x00, 0x00, 0xBF, 0x00, 0x00, 0xB3, 0x00, 0x00, 0xA7, 0x00, 0x00, 0x9B, 0x00, 0x00, 0x8B, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x73, 0x00, 0x00, 0x67, 0x00, 0x00, 0x5B, 0x00, 0x00, 0x4F, 0x00, 0x00, 0x43, 0x00, 0x00, 0xE7, 0xE7, 0xFF, 0xC7, 0xC7, 0xFF, 0xAB, 0xAB, 0xFF, 0x8F, 0x8F, 0xFF, 0x73, 0x73, 0xFF, 0x53, 0x53, 0xFF, 0x37, 0x37, 0xFF, 0x1B, 0x1B, 0xFF, 0x00, 0x00, 0xFF, 0x00, 0x00, 0xE3, 0x00, 0x00, 0xCB, 0x00, 0x00, 0xB3, 0x00, 0x00, 0x9B, 0x00, 0x00, 0x83, 0x00, 0x00, 0x6B, 0x00, 0x00, 0x53, 0xFF, 0xFF, 0xFF, 0xFF, 0xEB, 0xDB, 0xFF, 0xD7, 0xBB, 0xFF, 0xC7, 0x9B, 0xFF, 0xB3, 0x7B, 0xFF, 0xA3, 0x5B, 0xFF, 0x8F, 0x3B, 0xFF, 0x7F, 0x1B, 0xF3, 0x73, 0x17, 0xEB, 0x6F, 0x0F, 0xDF, 0x67, 0x0F, 0xD7, 0x5F, 0x0B, 0xCB, 0x57, 0x07, 0xC3, 0x4F, 0x00, 0xB7, 0x47, 0x00, 0xAF, 0x43, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xD7, 0xFF, 0xFF, 0xB3, 0xFF, 0xFF, 0x8F, 0xFF, 0xFF, 0x6B, 0xFF, 0xFF, 0x47, 0xFF, 0xFF, 0x23, 0xFF, 0xFF, 0x00, 0xA7, 0x3F, 0x00, 0x9F, 0x37, 0x00, 0x93, 0x2F, 0x00, 0x87, 0x23, 0x00, 0x4F, 0x3B, 0x27, 0x43, 0x2F, 0x1B, 0x37, 0x23, 0x13, 0x2F, 0x1B, 0x0B, 0x00, 0x00, 0x53, 0x00, 0x00, 0x47, 0x00, 0x00, 0x3B, 0x00, 0x00, 0x2F, 0x00, 0x00, 0x23, 0x00, 0x00, 0x17, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0xFF, 0x9F, 0x43, 0xFF, 0xE7, 0x4B, 0xFF, 0x7B, 0xFF, 0xFF, 0x00, 0xFF, 0xCF, 0x00, 0xCF, 0x9F, 0x00, 0x9B, 0x6F, 0x00, 0x6B, 0xA7, 0x6B, 0x6B }; public final static int greypal[] = new int[768]; public static byte[] generatePalette(int[] data, int colors, ColorTint[] tints) { final int palstride = colors * 3; final byte[] tmp = new byte[palstride * tints.length]; final int[] rgb = new int[3]; final int[] rgb2 = new int[3]; for (int i = 0; i < colors; i++) { rgb[0] = data[3 * i]; rgb[1] = data[1 + 3 * i]; rgb[2] = data[2 + 3 * i]; for (int t = 0; t < tints.length; t++) { tintRGB(tints[t], rgb, rgb2); tmp[palstride * t + 3 * i] = (byte) rgb2[0]; tmp[palstride * t + 3 * i + 1] = (byte) rgb2[1]; tmp[palstride * t + 3 * i + 2] = (byte) rgb2[2]; } } return tmp; } /** * Tint a part of a RGB_888 colormap. * * @param original * The original reference colormap. DO NOT modify! * @param modified * @param colors * Usually 256. * @param tint * @param gamma */ public static void tintColormap(final int[] original, int[] modified, int colors, ColorTint tint,short[] gamma) { final int[] rgb = new int[4]; final int[] rgb2 = new int[4]; for (int i = 0; i < colors; i++) { final int rgba=original[i]; rgb[0] = getRed(rgba); rgb[1] = getGreen(rgba); rgb[2] = getBlue(rgba); tintRGB(tint, rgb, rgb2); // Apply gamma correction. rgb2[0]=gamma[rgb2[0]]; rgb2[1]=gamma[rgb2[1]]; rgb2[2]=gamma[rgb2[2]]; modified[i] = getARGB(rgb2[0],rgb2[1],rgb2[2]); } } public static void tintColormap(final short[] original, short[] modified, int colors, ColorTint tint,short[] gamma) { final int[] rgb = new int[3]; final int[] rgb2 = new int[3]; for (int i = 0; i < colors; i++) { final int rgba = rgb555to888(original[i]); rgb[0] = getRed(rgba); rgb[1] = getGreen(rgba); rgb[2] = getBlue(rgba); tintRGB(tint, rgb, rgb2); // Apply gamma correction. rgb2[0]=gamma[rgb2[0]]; rgb2[1]=gamma[rgb2[1]]; rgb2[2]=gamma[rgb2[2]]; modified[i] = rgb888to555(rgb2[0],rgb2[1],rgb2[2]); } } public static final void tintRGB(final ColorTint tint, final int[] rgb, int[] rgb2) { rgb2[0] = (int) (rgb[0] * (1 - tint.tint) + tint.r * tint.tint); rgb2[1] = (int) (rgb[1] * (1 - tint.tint) + tint.g * tint.tint); rgb2[2] = (int) (rgb[2] * (1 - tint.tint) + tint.b * tint.tint); if (rgb2[0] > 255) rgb2[0] = 255; if (rgb2[1] > 255) rgb2[1] = 255; if (rgb2[2] > 255) rgb2[2] = 255; } /** * ColorShiftPalette - lifted from dcolors.c Operates on RGB888 palettes in * separate bytes. at shift = 0, the colors are normal at shift = steps, the * colors are all the given rgb */ public static final void ColorShiftPalette(byte[] inpal, byte[] outpal, int r, int g, int b, int shift, int steps) { int i; int dr, dg, db; int in_p, out_p; in_p = 0; out_p = 0; for (i = 0; i < 256; i++) { dr = r - inpal[in_p + 0]; dg = g - inpal[in_p + 1]; db = b - inpal[in_p + 2]; outpal[out_p + 0] = (byte) (inpal[in_p + 0] + dr * shift / steps); outpal[out_p + 1] = (byte) (inpal[in_p + 1] + dg * shift / steps); outpal[out_p + 2] = (byte) (inpal[in_p + 2] + db * shift / steps); in_p += 3; out_p += 3; } } /** Get ARGB_8888 from RGB_555, with proper higher-bit * replication. * * @param rgb * @return */ protected static final int rgb555to888(short rgb) { int ri, gi, bi; int bits; // .... .... .... .... // 111 11 = 7C00 // 11 111 = 03E0 // 1F= 1 1111 ri = (0x7C00 & rgb) >> 7; gi = (0x03E0 & rgb) >> 2; bi = (0x001F & rgb) << 3; // replicate 3 higher bits bits = (ri & 0xE0) >> 5; ri = ri + bits; bits = (gi & 0xE0) >> 5; gi = gi + bits; bits = (bi & 0xE0) >> 5; bi = bi + bits; // ARGB 8888 packed return 0xFF000000+(ri << 16) + (gi << 8) + (bi); } protected static final short rgb4444to555(short rgb) { int ri, gi, bi; int bits; // .... .... .... .... // 1111 ri = (0xF000 & rgb) >> 11; gi = (0x0F00 & rgb) >> 7; bi = (0x00F0 & rgb) >> 3; bits = (ri & 0x10) >> 4; ri = ri + bits; bits = (gi & 0x10) >> 4; gi = gi + bits; bits = (bi & 0x10) >> 4; bi = bi + bits; // RGBA 555 packed for NeXT return (short) ((ri << 10) + (gi << 5) + (bi)); } /** Get RGB_555 from packed ARGB_8888. * * @param argb * @return */ protected static final short rgb888to555(int rgb) { int ri, gi, bi; ri = (0xFF0000 & rgb) >> 19; gi = (0x00FF00 & rgb) >> 11; bi = (0x0000FF & rgb) >> 3; return (short) ((ri << 10) + (gi << 5) + (bi)); } /** Get packed RGB_555 word from individual 8-bit RGB components. * * WARNING: there's no sanity/overflow check for performance reasons. * * @param r * @param g * @param b * @return */ protected static final short rgb888to555(int r,int g,int b) { return (short) (((r>>3) << 10) + ((g>>3) << 5) + (b>>3)); } /** Get red from packed argb long word. * * @param argb * @return */ protected final static int getRed(int argb) { return (0xFF0000 & argb) >> 16; } /** Get green from packed argb long word. * * @param argb * @return */ protected final static int getGreen(int argb) { return (0x00FF00 & argb) >> 8; } /** Get blue from packed argb long word. * * @param argb * @return */ protected final static int getBlue(int argb) { return (0x0000FF & argb); } protected final static int getARGB(int r,int g, int b){ return 0xFF000000+(r << 16) + (g << 8) + (b); } protected static final short getRGB555(int red,int green,int blue){ int ri,gi,bi; ri = (((red+4)>255?255:red+4))>>3; ri = ri > 31 ? 31 : ri; gi = (((green+4)>255?255:green+4))>>3; gi = gi > 31 ? 31 : gi; bi = (((blue+4)>255?255:blue+4))>>3; bi = bi > 31 ? 31 : bi; // RGB555 for HiColor return (short) ((ri<<10) + (gi<<5) + bi); } protected static final short getRGB555(int rgb){ return getRGB555(getRed(rgb),getGreen(rgb),getBlue(rgb)); } /**RF_BuildLights lifted from dcolors.c * * Used to compute extended-color colormaps even in absence of the * COLORS15 lump. Must be recomputed if gamma levels change, since * they actually modify the RGB envelopes. * * @author John Carmack * @author Velktron * @param palette A packed ARGB 256-entry int palette, eventually tinted. * @param NUMLIGHTS Number of light levels to synth. Usually 32. */ public static final short[][] RF_BuildLights15 (int[] palette,int NUMLIGHTS) { int l,c; int red,green,blue; short[][] stuff=new short[NUMLIGHTS+1][256]; for (l=0;l<NUMLIGHTS;l++) { for (c=0;c<256;c++) { red = getRed(palette[c]); green = getGreen(palette[c]); blue = getBlue(palette[c]); red = (red*(NUMLIGHTS-l)+NUMLIGHTS/2)/NUMLIGHTS; green = (green*(NUMLIGHTS-l)+NUMLIGHTS/2)/NUMLIGHTS; blue = (blue*(NUMLIGHTS-l)+NUMLIGHTS/2)/NUMLIGHTS; // RGB555 for HiColor stuff[l][c] = getRGB555(red,green,blue); } } // Build special map for invulnerability BuildSpecials15(stuff[NUMLIGHTS],palette); return stuff; } private static final void BuildSpecials15 (short[] stuff, int[] palette) { int c,gray,best; int red, green, blue;; for (c=0;c<256;c++) { red = getRed(palette[c]); green = getGreen(palette[c]); blue = getBlue(palette[c]); gray = (int) (255*(1.0-((float)red*0.299/256.0 + (float)green*0.587/256.0 + (float)blue*0.114/256.0))); // We are not done. Because of the grayscaling, the all-white cmap best=palette[BestColor(gray,gray,gray,palette,0,255)]; stuff[c] = getRGB555(best); } // will lack tinting. } private static final void BuildSpecials24 (int[] stuff, int[] palette) { int c,gray,best; int red, green, blue;; for (c=0;c<256;c++) { red = getRed(palette[c]); green = getGreen(palette[c]); blue = getBlue(palette[c]); gray = (int) (255*(1.0-((float)red*0.299/256.0 + (float)green*0.587/256.0 + (float)blue*0.114/256.0))); // We are not done. Because of the grayscaling, the all-white cmap //best=palette[BestColor(gray,gray,gray,palette,0,255)]; stuff[c] = new Color(gray,gray,gray).getRGB(); } // will lack tinting. } public static final int BestColor (int r, int g, int b, int[] palette, int rangel, int rangeh) { int i; long dr, dg, db; long bestdistortion, distortion; int bestcolor; int pal; // // let any color go to 0 as a last resort // bestdistortion = ( (long)r*r + (long)g*g + (long)b*b )*2; bestcolor = 0; for (i=rangel ; i<= rangeh ; i++) { dr = r - getRed(palette[i]); dg = g - getGreen(palette[i]); db = b - getBlue(palette[i]); distortion = dr*dr + dg*dg + db*db; if (distortion < bestdistortion) { if (distortion==0) return i; // perfect match bestdistortion = distortion; bestcolor = i; } } return bestcolor; } /** Variation that produces true-color lightmaps * * @author John Carmack * @param palette A packed ARGB 256-entry int palette, eventually tinted. * @param NUMLIGHTS Number of light levels to synth. Usually 32. */ public static final int[][] RF_BuildLights24 (int[] palette,int NUMLIGHTS) { int l,c; int red,green,blue; int[][] stuff=new int[NUMLIGHTS+1][256]; for (l=0;l<NUMLIGHTS;l++) { for (c=0;c<256;c++) { red = getRed(palette[c]); green = getGreen(palette[c]); blue = getBlue(palette[c]); red = (red*(NUMLIGHTS-l)+NUMLIGHTS/2)/NUMLIGHTS; green = (green*(NUMLIGHTS-l)+NUMLIGHTS/2)/NUMLIGHTS; blue = (blue*(NUMLIGHTS-l)+NUMLIGHTS/2)/NUMLIGHTS; // Full-quality truecolor. stuff[l][c] = new Color(red,green,blue).getRGB(); } } BuildSpecials24(stuff[NUMLIGHTS],palette); return stuff; } public static final int[][] BuildLights24 (int[] palette,int NUMLIGHTS) { int l,c; int red,green,blue; int[][] stuff=new int[NUMLIGHTS+1][256]; for (l=0;l<NUMLIGHTS;l++) { for (c=0;c<256;c++) { red = getRed(palette[c]); green = getGreen(palette[c]); blue = getBlue(palette[c]); red = (red*(NUMLIGHTS-l)+NUMLIGHTS/2)/NUMLIGHTS; green = (green*(NUMLIGHTS-l)+NUMLIGHTS/2)/NUMLIGHTS; blue = (blue*(NUMLIGHTS-l)+NUMLIGHTS/2)/NUMLIGHTS; // Full-quality truecolor. stuff[l][c] = new Color(red,green,blue).getRGB(); } } BuildSpecials24(stuff[NUMLIGHTS],palette); return stuff; } static { for (int i = 0; i < 256; i++) { greypal[3 * i] = i; greypal[3 * i + 1] = i; greypal[3 * i + 2] = i; } } } package v; public interface IVideoScaleAware { /** Set the video scale for a certain object. Setting * does NOT (re)initialize an object yet. This is only done * by calling the init() method at a safe moment. * * @param vs */ public void setVideoScale(IVideoScale vs); /** Initialize an object according to the current video scale * settings. This should adapt multipliers, static constants, * etc. and should be set before the object is first used * or after a dynamic (if ever implemented) resolution change. * * The proposed method is to initialize everything en-bloc * before entering the display loop, and after initializing * */ public void initScaling(); } package v; public class ColorTint { public ColorTint(int r, int g, int b, float tint) { super(); this.r = r; this.g = g; this.b = b; this.tint = tint; } public int r, g, b; public float tint; public static final ColorTint[] tints = { new ColorTint(0, 0, 0, .0f), // 0 // Normal new ColorTint(255, 2, 3, 0.11f), // 1 Unused. 11% red tint of // RGB(252, 2, 3). new ColorTint(255, 0, 0, 0.22f), // 2 new ColorTint(255, 0, 0, 0.33f), // 3 new ColorTint(255, 0, 0, 0.44f), // 4 new ColorTint(255, 0, 0, 0.55f), // 5 new ColorTint(255, 0, 0, 0.66f), // 6 new ColorTint(255, 0, 0, 0.77f), // 7 new ColorTint(255, 0, 0, 0.88f), // 8 new ColorTint(215, 185, 68, 0.12f), // 9 new ColorTint(215, 185, 68, 0.25f), // 10 new ColorTint(215, 185, 68, 0.375f), // 11 new ColorTint(215, 185, 68, 0.50f), // 12 new ColorTint(3, 253, 3, 0.125f) // 13 }; } package v; import static data.Defines.RANGECHECK; import i.DoomStatusAware; import i.IDoomSystem; import java.awt.Image; import java.awt.image.BufferedImage; import java.awt.image.DataBufferByte; import java.awt.image.IndexColorModel; import java.io.File; import java.io.IOException; import javax.imageio.ImageIO; import doom.DoomStatus; import m.BBox; import rr.column_t; import rr.patch_t; import utils.C2JUtils; public abstract class SoftwareVideoRenderer8 extends SoftwareVideoRenderer<byte[],byte[]> { public Image getCurrentScreen(){ return currentscreen; } public SoftwareVideoRenderer8(){ super(); screens=new byte[5][]; } public SoftwareVideoRenderer8(int w,int h){ super(w,h); screens=new byte[5][]; } /** V_DrawPatch * Masks a column based masked pic to the screen. * desttop, dest and source were byte* */ public final void DrawPatch ( int x, int y, int scrn, patch_t patch ) { column_t column; int desttop; final byte[] dest=screens[scrn]; int w; y -= patch.topoffset; x -= patch.leftoffset; if (RANGECHECK) if (doRangeCheck(x,y,patch,scrn)) { System.err.print("Patch at "+x+","+y+" exceeds LFB\n"); // No I_Error abort - what is up with TNT.WAD? System.err.print("V_DrawPatch: bad patch (ignored)\n"); return; } if (scrn==0) this.MarkRect (x, y, patch.width, patch.height); w = patch.width; desttop = x+this.width*y; // For each column.. int destPos; int ptr=0; for (int col=0 ; col<w ; desttop++, col++,x++) { // This points at a "column" object. //column = (column_t *)((byte *)patch + LONG(patch->columnofs[col])); column=patch.columns[col]; // For each post... for (int i=0;i<column.posts;i++){ // Get pointer to post offset. ptr=column.postofs[i]; // Get post delta short delta=column.postdeltas[i]; // We skip delta, len and padding. // ptr+=3; NOT NEEDED< pre-skipped at parsing. // Skip transparent rows... if (delta==0xFF) break; destPos = desttop + delta*this.width; // These lengths are already correct. for (int j=0;j<column.postlen[i];j++){ dest[destPos] = column.data[ptr++]; destPos += this.width; } } } } /** V_DrawPatchSolidScaled * Draws a SOLID (non-masked) patch to the screen with integer scaling * m and n. * Useful for stuff such as help screens, titlepic and status bar. Not * very useful for menus, though. * desttop, dest and source were byte */ public final void DrawPatchSolidScaled ( int x, int y,int m, int n, int scrn, patch_t patch ) { if ((m==1)&&(n==1)){ DrawPatch(x,y,scrn,patch); return; } column_t column; int desttop; final byte[] dest=screens[scrn]; int w; y =y*n- patch.topoffset; x =x*m- patch.leftoffset; if (RANGECHECK) if (doRangeCheck(x,y,patch,scrn)) { System.err.print("Patch at "+x+","+y+" exceeds LFB\n"); // No I_Error abort - what is up with TNT.WAD? System.err.print("V_DrawPatch: bad patch (ignored)\n"); return; } if (scrn==0) this.MarkRect (x, y, patch.width, patch.height); w = patch.width; desttop = m*x+this.width*y; // For each column.. int destPos; int ptr=0; // x increases by m. // Some unrolling... if (m==2) { //desttop=2*desttop; for (int col=0 ; col<w ; desttop+=2, col++) { // This points at a "column" object. //column = (column_t *)((byte *)patch + LONG(patch->columnofs[col])); column=patch.columns[col]; // For each post... for (int i=0;i<column.posts;i++){ // Get pointer to post offset. ptr=column.postofs[i]; // Get post delta short delta=column.postdeltas[i]; // We skip delta, len and padding. //ptr+=3; // Skip transparent rows... if (delta==0xFF) break; destPos = desttop+ n*delta*this.width; // These lengths are already correct. for (int j=0;j<column.postlen[i];j++){ dest[destPos] = column.data[ptr++]; dest[destPos+1] = dest[destPos]; destPos += n*this.width; } } } } else if (m==3) { // desttop=3*desttop; for (int col=0 ; col<w ; desttop+=3, col++) { // This points at a "column" object. //column = (column_t *)((byte *)patch + LONG(patch->columnofs[col])); column=patch.columns[col]; // For each post... for (int i=0;i<column.posts;i++){ // Get pointer to post offset. ptr=column.postofs[i]; // Get post delta short delta=column.postdeltas[i]; // We skip delta, len and padding. //ptr+=3; // Skip transparent rows... if (delta==0xFF) break; destPos = desttop + n*delta*this.width; // These lengths are already correct. for (int j=0;j<column.postlen[i];j++){ dest[destPos] = column.data[ptr++]; dest[destPos+1] = dest[destPos]; dest[destPos+2] = dest[destPos]; destPos += n*this.width; } } } }else if (m==4) { //desttop=4*desttop; for (int col=0 ; col<w ; desttop+=4, col++) { // This points at a "column" object. //column = (column_t *)((byte *)patch + LONG(patch->columnofs[col])); column=patch.columns[col]; // For each post... for (int i=0;i<column.posts;i++){ // Get pointer to post offset. ptr=column.postofs[i]; // Get post delta short delta=column.postdeltas[i]; // We skip delta, len and padding. //ptr+=3; // Skip transparent rows... if (delta==0xFF) break; destPos = desttop + n*delta*this.width; // These lengths are already correct. for (int j=0;j<column.postlen[i];j++){ dest[destPos] = column.data[ptr++]; dest[destPos+1] = dest[destPos]; dest[destPos+2] = dest[destPos]; dest[destPos+3] = dest[destPos]; destPos += n*this.width; } } } } else { // desttop=m*desttop; for (int col=0 ; col<w ; desttop+=m, col++) { // This points at a "column" object. //column = (column_t *)((byte *)patch + LONG(patch->columnofs[col])); column=patch.columns[col]; // For each post... for (int i=0;i<column.posts;i++){ // Get pointer to post offset. ptr=column.postofs[i]; // Get post delta short delta=column.postdeltas[i]; // We skip delta, len and padding. //ptr+=3; // Skip transparent rows... if (delta==0xFF) break; destPos = desttop + n*delta*this.width; // These lengths are already correct. for (int j=0;j<column.postlen[i];j++){ for (int k=0;k<m;k++) dest[destPos+k] = column.data[ptr]; ptr++; destPos += n*this.width; } } } } scaleSolid(m,n, scrn,m*patch.width); } /** Pretty crude in-place scaling. It's fast, but only works full-screen * Width needs to be specific, height is implied. * */ protected final void scaleSolid(int m, int n, int screen,int width){ int height=screens[screen].length/width; for (int i=0;i<height;i+=n){ for (int j=0;j<n-1;j++){ System.arraycopy(screens[screen], (i+j)*width, screens[screen],(i+j+1)*width,width); } } } public void DrawPatchSolidScaled ( int x, int y, int scrn, IVideoScale vs, patch_t patch ){ this.DrawPatchSolidScaled(x, y, vs.getScalingX(), vs.getScalingY(),scrn, patch); } /** * V_DrawPatchFlipped * Masks a column based masked pic to the screen. * Flips horizontally, e.g. to mirror face. * * Currently UNUSED, as any occurence to it is also scaled and * best served by another function. * * */ public final void DrawPatchFlipped ( int x, int y, int scrn, patch_t patch ) { column_t column; int desttop; final byte[] dest=screens[scrn]; int w; y -= patch.topoffset; x -= patch.leftoffset; if (RANGECHECK) if (doRangeCheck(x,y,patch,scrn)) { { System.err.print("Patch origin "+x+","+y +" exceeds LFB\n" ); // No I_Error abort - what is up with TNT.WAD? I.Error("Bad V_DrawPatch in V_DrawPatchFlipped"); } } if (scrn==0) this.MarkRect (x, y, patch.width, patch.height); // Set x and y coords inside dest array. w = patch.width; desttop = y*this.width+x; // For each column.. for (int col=0 ; col<w ; desttop++,col++) { // This points at a "column" object. // Notice the flipping on this one. // column = (column_t *)((byte *)patch + LONG(patch->columnofs[w-1-col])); column=patch.columns[w-1-col]; // For each post... //System.out.println("Column"+(w-1-col)); for (int i=0;i<column.posts;i++){ // Get pointer to post offset. int ptr=column.postofs[i]; // Get post delta int delta=0xFF&column.data[ptr]; // We skip delta, len and padding. //ptr+=3; if (delta==0xFF) break; int destPos = desttop+delta*this.width; //count = column.length; // These lengths are already correct. for (int j=0;j<column.postlen[i];j++, destPos += this.width){ dest[destPos] = column.data[ptr+j]; // next line } } } } /** * V_DrawScaledPatch * like V_DrawPatch, but scaled 2,3,4 times the original size and position * this is used for menu and title screens, with high resolutions * *added:05-02-98: *default params : scale patch and scale start * * Iniially implemented for Mocha Doom by _D_ (shamelessly ripped from * Eternity Engine ;-), adapted to scale based on a scaling info object (VSI). * * Unless overriden by flags, starting x and y are automatically * scaled (implied V_SCALESTART) * */ @Override public void DrawScaledPatch(int x, int y, int scrn, IVideoScale VSI, patch_t patch) { int col; column_t column; int desttop; final byte[] dest = screens[scrn&0xFF]; //byte[] source; int dupx, dupy; int colfrac, rowfrac; // System.out.printf("V_DrawScaledPatch %d %d \n",x,y); // draw an hardware converted patch /*#ifdef HWRENDER if (rendermode != render_soft) { HWR_DrawPatch((GlidePatch_t *) patch, x, y, scrn); return; } #endif*/ // A very common operation, eliminates the need to pre-divide. if (C2JUtils.flags(scrn,V_PREDIVIDE)){ x/=vs.getScalingX(); y/=vs.getScalingY(); } if (C2JUtils.flags(scrn, V_NOSCALEPATCH)) dupx = dupy = 1; else { dupx = VSI.getScalingX(); dupy = VSI.getScalingY(); } // Eliminates. // MAES: added this fix so that non-zero patch offsets can be // taken into account, regardless of whether we use pre-scaled // coords or not. Only Doomguy's face needs this hack for now. if (C2JUtils.flags(scrn, V_SCALEOFFSET)) { y -= patch.topoffset*dupx; x -= patch.leftoffset*dupy; } else { y -= patch.topoffset; x -= patch.leftoffset; } colfrac = dupx; rowfrac = dupy; //desttop = screens[scrn & 0xFF]; if (C2JUtils.flags(scrn, V_NOSCALESTART)) desttop = (y * this.width) + x; else desttop = (y * dupy * this.width) + (x * dupx) /*+ scaledofs*/; //destend = desttop + /*SHORT(*/patch.width/*)*/ * dupx; int w = patch.width*dupx; int colInc = 1; col = 0; if (C2JUtils.flags(scrn, V_FLIPPEDPATCH)) { colInc = -1; col = w-1;//(/*SHORT(*/patch.width/*)*/ << fixed_t.FRACBITS) + colfrac; } for (; col >= 0 && col<w/*; desttop < destend*/; col += colInc, desttop++) { //column = (column_t *) ((byte *) patch + LONG(patch.columnofs[col >> FRACBITS])); column=patch.columns[col/colfrac]; int destPos; int ptr = 0; int ptrOfs; //while (column.topdelta != 0xff) for (int i=0;i<column.posts;i++){ { ptrOfs = column.postofs[i];//+3; ptr = 0; short delta = column.postdeltas[i]; // Skip transparent rows... if (delta==0xFF) break; destPos = desttop + delta*dupy*this.width; //dest = desttop + column.topdelta * dupy * this.width; //ofs = 0; //while (count-- > 0) for (int j=0;j<column.postlen[i]*dupy;j++) { dest[destPos] = column.data[ptrOfs+ptr/rowfrac]; destPos += this.width; ptr++; //ofs += rowfrac; } //column = (column_t *) ((byte *) column + column.length + 4); } } } } @Override public final void DrawBlock ( int x, int y, int scrn, int width, int height, byte[] src ) { // This is "screens[scrn]" final byte[] dest=screens[scrn]; if (doRangeCheck(x, y, scrn)) { I.Error("Bad V_DrawBlock"); } this.MarkRect(x, y, width, height); int destPos = /*screens[scrn] +*/ y*this.width +x; // MAES: making an assumption here. A BIIIIG one. int srcPos=0; while ((height--)>0) { // memcpy (dest, src, width); System.arraycopy(src, srcPos, dest, destPos, width); srcPos += width; destPos += this.width; } } @Override public final void DrawBlock(int x, int y, int scrn, int width, int height, byte[] src,int offset) { // This is "screens[scrn]" final byte[] dest = screens[scrn]; if (doRangeCheck(x, y, scrn)) { I.Error("Bad V_DrawBlock"); } this.MarkRect(x, y, width, height); int destPos = /* screens[scrn] + */y * this.width + x; // MAES: making an assumption here. A BIIIIG one. int srcPos = offset; while ((height--) > 0) { System.arraycopy(src, srcPos, dest, destPos, width); srcPos += width; destPos += this.width; } } /** * V_GetBlock * Gets a linear block of pixels from the view buffer. */ public final void GetBlock ( int x, int y, int scrn, int width, int height, byte[] dest ) { final byte[] src=screens[scrn]; if (RANGECHECK){ if (doRangeCheck(x,y,scrn)){ I.Error ("Bad V_DrawBlock"); } } int srcPos = y*this.width+x; int destPos=0; while ((height--)>0) { System.arraycopy(src, srcPos, dest, destPos, width); //memcpy (dest, src, width); srcPos += width; destPos += this.width; } } /** Replaces DrawPatchCol for bunny scrolled in Finale. * * */ @Override public final void DrawPatchColScaled ( int x, patch_t patch,int col, IVideoScale vs, int screen ) { column_t column; int source; final byte[] dest; int desttop; final int scale=vs.getScalingX(); column = patch.columns[col]; desttop = x*scale; // Scale X position. dest=screens[screen]; // step through the posts in a column for (int i=0;i<column.posts;i++){ // Get pointer to post offset. source=column.postofs[i]; // Get post delta short delta=column.postdeltas[i]; // We skip delta, len and padding. //source+=3; NOT NEEDED ANYMORE, skipped by parsing. // Skip transparent rows... if (delta==0xFF) break; // Replicate each column scale times vertically, // with spaced pixels. final int startsource=source; for (int kl=0;kl<scale;kl++){ int destPos = desttop + (delta+kl)*SCREENWIDTH; for (int j=0;j<column.postlen[i];j++){ final byte data=column.data[source++]; // replicate each column's pixel horizontally and vertically. for (int k=0;k<scale;k++) dest[destPos+k] =data; destPos += scale*SCREENWIDTH; } source=startsource; } } } public void setScreen(int index, int width, int height){ this.screens[index]=new byte[width*height]; } public final void takeScreenShot(int screen, String imagefile, IndexColorModel icm) throws IOException { BufferedImage b=new BufferedImage(this.getWidth(),this.getHeight(), BufferedImage.TYPE_BYTE_INDEXED, icm); int[] tmp=new int[this.screens[screen].length]; for (int i=0;i<this.screens[screen].length;i++){ tmp[i]=this.screens[screen][i]; } b.getRaster().setPixels(0, 0, this.getWidth(),this.getHeight(), tmp); File outputFile = new File( imagefile+".png"); ImageIO.write(b, "PNG", outputFile); } /* public final boolean isRasterNull(int screen){ for (int i=0;i<screens[screen].length;i++){ if (screens[screen][i]!=0) return false; } return true; } */ public void setCurrentScreen(int screen){ this.usescreen=screen; } public void update(){ // Override only if there's something else to be done, e.g. map palette to truecolor buffer } public void report(BufferedImage[] b){ System.out.println("Main video buffer "+screens[0]); for (int i=0;i<b.length;i++){ System.out.println(((Object)b[i].getRaster()).toString()+" "+b[i].getRaster().hashCode()+" "+((DataBufferByte)(b[i].getRaster().getDataBuffer())).getData()); } } /** Internal method for setting up palettes (and gamma tables) * */ public void createPalettes(byte[] paldata, short[][] gammadata, final int palettes, final int colors, final int stride,final int gammalevels){ // Sanity check on supplied data length. If there is not enough data to create the specified palettes, // their number will be limited. if (paldata!=null) // As many as are likely contained maxpalettes=paldata.length/(colors*stride); else maxpalettes=0; // Do some default action on null palette. if (gammadata!=null) // As many as are likely contained maxgammas=gammadata.length; else maxgammas=0; // Do some default action on null gamma tables. if (maxgammas==0){ gammadata=GammaTables.gammatables; maxgammas=GammaTables.gammatables.length; } // Enough data for all palettes. // Enough data for all palettes. if (maxpalettes>0 && maxgammas>0) specificPaletteCreation(paldata,gammadata,palettes,colors,stride,gammalevels); else paletteRecovery(); } /** Override this in extending classes to perform specific actions depending on the * type of renderer. It's better not to assign a default action, nor make assumptions * on the underlying types of actual palettes * * @param paldata * @param gammadata * @param palettes * @param colors * @param stride * @param gammalevels */ protected abstract void specificPaletteCreation(byte[] paldata, short[][] gammadata, final int palettes, final int colors, final int stride, final int gammalevels); protected int lastcolor=-1; protected byte[] scanline; /** * Clear automap frame buffer or fi * MAES: optimized for efficiency, seen the lack of a proper "memset" in Java. * */ @Override public final void FillRect(int color,int screen, int x,int y,int width, int height) { if (RANGECHECK) { if (x<0 ||x+width >this.width || y<0 || y+height>SCREENHEIGHT || screen>4) { I.Error ("Bad V_FillRect"); } } byte[] arr=screens[screen]; // Do a "per scanline" copy. int fromIndex=x+y*SCREENWIDTH; int toIndex=x+(y+height-1)*SCREENWIDTH; // First scanline. for (int i=0;i<width;i++) arr[fromIndex+i]=(byte) color; for (;fromIndex<toIndex;fromIndex+=SCREENWIDTH){ System.arraycopy(arr,fromIndex,arr,fromIndex+SCREENWIDTH,width); } } /** * V_Fillrect */ @Override public void FillRect(int srcx, int srcy, int width, int height, int destscrn) { // These are pointers inside an array. final byte[] dest = screens[destscrn]; if (RANGECHECK) { if (srcx < 0 || srcx + width > this.width || srcy < 0 || srcy + height > SCREENHEIGHT || destscrn > 4) { I.Error("Bad V_FillRect"); } } this.MarkRect(srcx, srcy, width, height); // MAES: these were pointers to a specific position inside the screen. int srcPos = this.width * srcy + srcx; for (; height > 0; height--) { for (int i = 0; i < width; i++) { dest[srcPos + i] = 0; } // memcpy (dest, src, width); srcPos += this.width; } } /** Should return colormaps, if you ever move their management in here. * */ public final byte[][] getColorMaps(){ return null; } } package v; import java.awt.image.BufferedImage; import java.awt.image.DataBufferByte; import java.awt.image.DataBufferUShort; import static rr.LightsAndColors.LIGHTLEVELS; import m.BBox; public class BufferedRenderer16 extends SoftwareVideoRenderer16 { static final String rcsid = "$Id: BufferedRenderer16.java,v 1.5 2016/07/04 07:52:26 velktron Exp $"; /** Buffered Renderer has a bunch of images "pegged" to the underlying arrays */ public BufferedImage[] screenbuffer=new BufferedImage[5]; public BufferedRenderer16(int w, int h) { super(w,h); } @Override public final void Init () { int i; for (i=0 ; i<4 ; i++){ //screens[i] = new byte[this.getHeight()*this.getWidth()]; this.setScreen(i, this.width, this.height); } dirtybox=new BBox(); } /** This implementation will "tie" a bufferedimage to the underlying byte raster. * * NOTE: this relies on the ability to "tap" into a BufferedImage's backing array, * in order to have fast writes without setpixel/getpixel. If that is not possible, * then we'll need to use a special renderer. * */ @Override public final void setScreen(int index, int width, int height){ if (screens[index]==null){ screenbuffer[index]=new BufferedImage(width,height,BufferedImage.TYPE_USHORT_555_RGB); screens[index]=((DataBufferUShort)screenbuffer[index].getRaster().getDataBuffer()).getData(); } } public void setPalette(int palette){ this.usepalette=palette%maxpalettes; // Invalidate cached graphics, otherwise older colormaps // will persist. this.clearCaches(); // Tint the current set of colormaps. getCachedCmap(palette); //this.currentpal=palette%maxpalettes; this.currentscreen=this.screenbuffer[0]; } @Override public void setUsegamma(int gamma) { this.usegamma=gamma%maxgammas; // Invalidate palette cache. super.clearPalettes(); // Re-synthesize current palette. setPalette(usepalette); } public void setCurrentScreen(int screen){ super.setCurrentScreen(screen); this.currentscreen=this.screenbuffer[0]; } @Override protected final void specificPaletteCreation(byte[] paldata, short[][] gammadata, final int palettes, final int colors, final int stride, final int gammalevels){ System.out.printf("Enough data for %d palettes",maxpalettes); System.out.printf("Enough data for %d gamma levels",maxgammas); //this.palettes=new int[maxpalettes*maxgammas][]; this.palettes=new int[maxpalettes][]; // Apply gammas a-posteriori, not a-priori. // Initial palette can be neutral or based upon "gamma 0", // which is actually a bit biased and distorted for (int z=0;z<1;z++){ // For each palette for (int y=0;y<maxpalettes;y++){ this.palettes[z*maxpalettes+y]=new int[colors]; for (int x=0;x<colors;x++){ int r=gammadata[z][0xFF&paldata[y*colors*stride+stride*x]]; // R int g=gammadata[z][0xFF&paldata[1+y*colors*stride+stride*x]]; // G int b=gammadata[z][0xFF&paldata[2+y*colors*stride+stride*x]]; // B int color=0xFF000000|r<<16|g<<8|b; this.palettes[z*maxpalettes+y][x]=color; } } } // Set base colormap cmap_base=PaletteGenerator.RF_BuildLights15(this.palettes[0], NUMLIGHTS); cmap_work=PaletteGenerator.RF_BuildLights15(this.palettes[0], NUMLIGHTS); } public int getBaseColor(int color){ return cmap_work[0][color]; } } package v; import static data.Defines.RANGECHECK; import java.awt.image.BufferedImage; import java.awt.image.DataBufferInt; import java.util.concurrent.BrokenBarrierException; import java.util.concurrent.CyclicBarrier; import java.util.concurrent.Executor; import java.util.concurrent.Executors; import rr.column_t; import rr.patch_t; import m.BBox; /** N.B. this renderer just uses 8-bit resources and 256 color, but renders directly onto a * 24-bit canvas, so technically it should be a byte[],byte[] renderer. It's not a "true color" * renderer in the sense of using extended colormaps etc. * * @author velktron * */ public class ParallelTrueColorRenderer extends SoftwareVideoRenderer<byte[],byte[]> { static final String rcsid = "$Id: ParallelTrueColorRenderer.java,v 1.3 2016/06/06 14:27:55 velktron Exp $"; /* With a truecolour raster, some things are indeed easier */ protected int[][] palettes; protected int[] raster; protected final int nrOfProcessors; protected Runnable[] paletteThreads; protected final Executor executor; public ParallelTrueColorRenderer(){ super(); Runtime runtime = Runtime.getRuntime(); nrOfProcessors = runtime.availableProcessors(); updateBarrier=new CyclicBarrier(nrOfProcessors+1); paletteThreads=new PaletteThread[nrOfProcessors]; int len=this.getHeight()*this.getWidth(); int chunk=len/nrOfProcessors; for (int i=0;i<nrOfProcessors;i++){ paletteThreads[i]=new PaletteThread(i*chunk,(i+1)*chunk); } this.executor=Executors.newFixedThreadPool(nrOfProcessors); } public ParallelTrueColorRenderer(int w,int h){ // Defaults super(w,h); Runtime runtime = Runtime.getRuntime(); nrOfProcessors = runtime.availableProcessors(); updateBarrier=new CyclicBarrier(nrOfProcessors+1); paletteThreads=new PaletteThread[nrOfProcessors]; int len=w*h; int chunk=len/nrOfProcessors; for (int i=0;i<nrOfProcessors;i++){ paletteThreads[i]=new PaletteThread(i*chunk,(i+1)*chunk); } this.executor=Executors.newFixedThreadPool(nrOfProcessors); } public void Init () { int i; for (i=0 ; i<screens.length ; i++){ screens[i] = new byte[this.getHeight()*this.getWidth()]; } dirtybox=new BBox(); // Using ARGB is half the speed, WTF? While RGB is almost as fast as indexed. Go figure. this.currentscreen=new BufferedImage(width,height, BufferedImage.TYPE_INT_RGB); this.mapInternalRasterToBufferedImage((BufferedImage) currentscreen); } @Override public void setUsegamma(int gammalevel) { this.usegamma=gammalevel%maxgammas; } @Override public void setPalette(int palette) { this.usepalette=palette%maxpalettes; } @Override protected final void specificPaletteCreation(byte[] paldata, short[][] gammadata, final int palettes, final int colors, final int stride, final int gammalevels){ System.out.printf("Enough data for %d palettes",maxpalettes); System.out.printf("Enough data for %d gamma levels",maxgammas); this.palettes=new int[maxgammas*maxpalettes][]; for (int z=0;z<maxgammas;z++){ // For each palette for (int y=0;y<maxpalettes;y++){ this.palettes[z*maxpalettes+y]=new int[colors]; for (int x=0;x<colors;x++){ int r=gammadata[z][0xFF&paldata[y*colors*stride+stride*x]]; // R int g=gammadata[z][0xFF&paldata[1+y*colors*stride+stride*x]]; // G int b=gammadata[z][0xFF&paldata[2+y*colors*stride+stride*x]]; // B int color=0xFF000000|r<<16|g<<8|b; this.palettes[z*maxpalettes+y][x]=color; } } } } /** Hotlinks a 32-bit "canvas" (the raster int[] array) to an externally supplied * buffered image. Now whatever we write into raster, will appear in the image as well, * without using drawing primitives. Necessary for fast rendering. * * @param b */ private void mapInternalRasterToBufferedImage(BufferedImage b){ raster=((DataBufferInt)(b.getRaster().getDataBuffer())).getData(); } /** Update "canvas" to one of the internal screens. * * @param screen * @param b */ @Override public final void update() { for (int i=0;i<this.nrOfProcessors;i++){ executor.execute(paletteThreads[i]); } try { updateBarrier.await(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (BrokenBarrierException e) { // TODO Auto-generated catch block e.printStackTrace(); } /* final byte[] scr=this.screens[usescreen]; final int length=scr.length; final int[] pal=this.palettes[usegamma*maxpalettes+usepalette]; for (int i=0;i<length;i+=16){ raster[i]=pal[0xFF&scr[i]]; raster[i+1]=pal[0xFF&scr[i+1]]; raster[i+2]=pal[0xFF&scr[i+2]]; raster[i+3]=pal[0xFF&scr[i+3]]; raster[i+4]=pal[0xFF&scr[i+4]]; raster[i+5]=pal[0xFF&scr[i+5]]; raster[i+6]=pal[0xFF&scr[i+6]]; raster[i+7]=pal[0xFF&scr[i+7]]; raster[i+8]=pal[0xFF&scr[i+8]]; raster[i+9]=pal[0xFF&scr[i+9]]; raster[i+10]=pal[0xFF&scr[i+10]]; raster[i+11]=pal[0xFF&scr[i+11]]; raster[i+12]=pal[0xFF&scr[i+12]]; raster[i+13]=pal[0xFF&scr[i+13]]; raster[i+14]=pal[0xFF&scr[i+14]]; raster[i+15]=pal[0xFF&scr[i+15]]; } */ } protected final CyclicBarrier updateBarrier; private class PaletteThread implements Runnable{ private final int start; private final int stop; public PaletteThread(int start, int stop){ this.start=start; this.stop=stop; } @Override public void run() { final byte[] scr=screens[usescreen]; final int[] pal=palettes[usegamma*maxpalettes+usepalette]; for (int i=start;i<stop;i+=16){ raster[i]=pal[0xFF&scr[i]]; raster[i+1]=pal[0xFF&scr[i+1]]; raster[i+2]=pal[0xFF&scr[i+2]]; raster[i+3]=pal[0xFF&scr[i+3]]; raster[i+4]=pal[0xFF&scr[i+4]]; raster[i+5]=pal[0xFF&scr[i+5]]; raster[i+6]=pal[0xFF&scr[i+6]]; raster[i+7]=pal[0xFF&scr[i+7]]; raster[i+8]=pal[0xFF&scr[i+8]]; raster[i+9]=pal[0xFF&scr[i+9]]; raster[i+10]=pal[0xFF&scr[i+10]]; raster[i+11]=pal[0xFF&scr[i+11]]; raster[i+12]=pal[0xFF&scr[i+12]]; raster[i+13]=pal[0xFF&scr[i+13]]; raster[i+14]=pal[0xFF&scr[i+14]]; raster[i+15]=pal[0xFF&scr[i+15]]; } try { updateBarrier.await(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (BrokenBarrierException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } public final void DrawPatch ( int x, int y, int scrn, patch_t patch ) { column_t column; int desttop; final byte[] dest=screens[scrn]; int w; y -= patch.topoffset; x -= patch.leftoffset; if (RANGECHECK) if (doRangeCheck(x,y,patch,scrn)) { System.err.print("Patch at "+x+","+y+" exceeds LFB\n"); // No I_Error abort - what is up with TNT.WAD? System.err.print("V_DrawPatch: bad patch (ignored)\n"); return; } if (scrn==0) this.MarkRect (x, y, patch.width, patch.height); w = patch.width; desttop = x+this.width*y; // For each column.. int destPos; int ptr=0; for (int col=0 ; col<w ; desttop++, col++,x++) { // This points at a "column" object. //column = (column_t *)((byte *)patch + LONG(patch->columnofs[col])); column=patch.columns[col]; // For each post... for (int i=0;i<column.posts;i++){ // Get pointer to post offset. ptr=column.postofs[i]; // Get post delta short delta=column.postdeltas[i]; // We skip delta, len and padding. // ptr+=3; NOT NEEDED< pre-skipped at parsing. // Skip transparent rows... if (delta==0xFF) break; destPos = desttop + delta*this.width; // These lengths are already correct. for (int j=0;j<column.postlen[i];j++){ dest[destPos] = column.data[ptr++]; destPos += this.width; } } } } } package v; import java.awt.image.BufferedImage; import java.awt.image.DataBufferByte; import java.awt.image.IndexColorModel; import java.awt.image.RasterOp; import java.awt.image.WritableRaster; import m.BBox; public class BufferedRenderer extends SoftwareVideoRenderer8 { static final String rcsid = "$Id: BufferedRenderer.java,v 1.18 2012/09/24 17:16:23 velktron Exp $"; /** Buffered Renderer has a bunch of images "pegged" to the underlying arrays */ private BufferedImage[] screenbuffer; /** Indexed renderers keep separate color models for each colormap (intended as gamma levels) and * palette levels */ private IndexColorModel[][] cmaps; public BufferedRenderer(int w, int h, IndexColorModel icm) { super(w,h); this.setIcm(icm); } /** Normally, you only have the palettes available ONLY after you read the palette from disk. * So use the super contructor, and then this when the palettes are available. * * @param icm2 */ public void setIcm(IndexColorModel icm2) { this.icm=icm2; } public BufferedRenderer(int w, int h) { super(w,h); } // Used only for internal status. private IndexColorModel icm; @Override public final void Init () { int i; for (i=0 ; i<4 ; i++){ //screens[i] = new byte[this.getHeight()*this.getWidth()]; this.setScreen(i, this.width, this.height); } dirtybox=new BBox(); } /** This actually creates a raster with a fixed underlying array, but NOT the images * themselves. So it's possible to have "imageless" rasters (unless you specifically * request to make them visible, of course). * */ @Override public final void setScreen(int index, int width, int height){ // We must FIRST initialize the image, so that the (immutable) color model will be set. if (this.icm==null){ final byte[] dummy=new byte[256]; for (int i=0;i<dummy.length;i++) dummy[i]=(byte) i; icm=new IndexColorModel(8,256,dummy,dummy,dummy); } r[index]=icm.createCompatibleWritableRaster(width,height); // Only create non-visible data, pegged to the raster. Create visible images // only on-demand. screens[index]=((DataBufferByte)r[index].getDataBuffer()).getData(); } /** We only call this once we have a stable WritableRaster, and we only want * a different colormodel (e.g. after changing gamma). It's slower than keepings * severerl BufferedImages ready, so it's only used when changing gamma. The * backing screen, array etc. should not have changed at this moment. * * @param index * @param r */ private final BufferedImage createScreen(int index,IndexColorModel icm, WritableRaster r){ return new BufferedImage(icm,r,false,null); } /* public BufferedImage mapBufferedImageToScreen(int screen, IndexColorModel icm){ // Map databuffer to one of the screens. DataBufferByte dbb=new DataBufferByte(screens[screen],screens[screen].length); BufferedImage b=new BufferedImage(this.getWidth(),this.getHeight(),BufferedImage.TYPE_BYTE_INDEXED,icm); WritableRaster r=WritableRaster.createPackedRaster(dbb,b.getWidth(), b.getHeight(), 8, new Point(0,0)); b.setData(r); return b; } */ /* public BufferedImage cloneScreen(int screen, IndexColorModel icm){ BufferedImage b=new BufferedImage(this.getWidth(),this.getHeight(),BufferedImage.TYPE_BYTE_INDEXED,icm); b.setData(screenbuffer[0].getRaster()); return b; } */ public final void changePalette(int pal){ this.usepalette=(pal<<8);//+0x00FF; //this.usepalette=/*(pal<<8)+*/0xFF; } /** Get a bunch of BufferedImages "pegged" on the same output screen of this * Doom Video Renderer, but with different palettes, defined in icms[]. * This is VERY speed efficient assuming that an IndexedColorModel will be used, * rather than a 32-bit canvas, and memory overhead is minimal. Call this ONLY * ONCE when initializing the video renderer, else it will invalidate pretty much * everything in an ongoing game. * * NOTE: this will actually CREATE a new byte array for the screen, so it's important * that this is called BEFORE anything else taps into it. * * @param screen * @param icms * @return */ private BufferedImage[] createScreenForPalettes(int screen,IndexColorModel[] icms) { // These screens represent a complete range of palettes for a specific gamma // and specific screen BufferedImage[] b=new BufferedImage[icms.length]; // MEGA hack: all images share the same raster data as screenbuffer[screen] // If this is the first time we called this method, the actually backing array // will be actually created. If not... // Create the first of the screens. this.icm=icms[0]; if (r[screen]==null){ // This will create the first buffered image (and its data array)/ // as screenbuffer[0] setScreen(screen,this.getWidth(),this.getHeight()); } // This is the base image for this set of palettes (usually index 0). // Create the rest of the screens (with different palettes) on the same raster. for (int i=0;i<icms.length;i++){ b[i]=createScreen(screen,icms[i],r[screen]); } return b; } protected final void specificPaletteCreation(byte[] paldata, short[][] gammadata, final int palettes, final int colors, final int stride, final int gammalevels){ System.out.printf("Enough data for %d palettes",maxpalettes); System.out.printf("Enough data for %d gamma levels",maxgammas); // Create as gamma levels as specified. cmaps=new IndexColorModel[maxgammas][]; // First set of palettes, normal gamma. cmaps[0]=new IndexColorModel[maxpalettes]; // Now we have our palettes. for (int i=0;i<maxpalettes;i++){ cmaps[0][i]=new IndexColorModel(8, colors,paldata, i*stride*colors, false); } // Wire the others according to the gamma table. byte[] tmpcmap=new byte[colors*stride]; // For each gamma value... for (int j=1;j<maxgammas;j++){ cmaps[j]=new IndexColorModel[maxpalettes]; // For each palette for (int i=0;i<maxpalettes;i++){ for (int k=1;k<256;k++){ tmpcmap[3*k]=(byte) gammadata[j][0x00FF&paldata[i*colors*stride+stride*k]]; // R tmpcmap[3*k+1]=(byte) gammadata[j][0x00FF&paldata[1+i*colors*stride+stride*k]]; // G tmpcmap[3*k+2]=(byte) gammadata[j][0x00FF&paldata[2+i*colors*stride+stride*k]]; // B } cmaps[j][i]=new IndexColorModel(8, 256,tmpcmap, 0, false); } } } private WritableRaster[] r=new WritableRaster[5]; public void setPalette(int palette){ this.currentpal=palette%maxpalettes; this.currentscreen=this.screenbuffer[currentpal]; } @Override public void setUsegamma(int gamma) { this.usegamma=gamma%maxgammas; // Changing gamma also "fixes" the screens! this.setCurrentScreen(0); } public void setCurrentScreen(int screen){ super.setCurrentScreen(screen); this.screenbuffer=this.createScreenForPalettes(usescreen, cmaps[usegamma]); this.currentscreen=this.screenbuffer[currentpal]; } public IndexColorModel getPalette(){ return cmaps[0][this.usepalette]; } } //$Log: BufferedRenderer.java,v $ //Revision 1.18 2012/09/24 17:16:23 velktron //Massive merge between HiColor and HEAD. There's no difference from now on, and development continues on HEAD. // //Revision 1.17.2.3 2012/09/24 16:56:06 velktron //New hierarchy, less code repetition. // package v; /** Interface for an object that conveys screen resolution/scaling * information, meant to replace the static declarations in Defines. * * Classes that rely on resolution changes should implement an interface * called "IVideoScaleAware", which should support * * @author admin * */ public interface IVideoScale { //It is educational but futile to change this //scaling e.g. to 2. Drawing of status bar, //menues etc. is tied to the scale implied //by the graphics. public static double INV_ASPECT_RATIO = 0.625; // 0.75, ideally // // For resize of screen, at start of game. // It will not work dynamically, see visplanes. // public static final int BASE_WIDTH = 320; public static final int BASE_HEIGHT= (int) (INV_ASPECT_RATIO*320); // 200 int getScreenWidth(); int getScreenHeight(); int getScalingX(); int getScalingY(); /** Safest global scaling for fixed stuff like menus, titlepic etc */ int getSafeScaling(); /** Get floating point screen multiplier. Not recommended, as it causes * visual glitches. Replace with safe scale, whenever possible */ float getScreenMul(); /** Future, should signal aware objects that they should * refresh their resolution-dependent state, structures, variables etc. * * @return */ boolean changed(); } package v; public class VideoScaleInfo implements IVideoScale { protected float scale; protected int width; protected int height; protected int bestScaleX; protected int bestScaleY; protected int bestSafeScale; /** Scale is intended as a multiple of the base resolution, 320 x 200. * If changing the ratio is also desired, then keep in mind that * the base width is always considered fixed, while the base height * is not. * * @param scale */ public VideoScaleInfo(float scale){ this.scale=scale; width=(int) (BASE_WIDTH*scale); height=(int) (scale*BASE_WIDTH*INV_ASPECT_RATIO); bestScaleX= (int) Math.floor((float)width/(float)BASE_WIDTH); bestScaleY= (int) Math.floor((float)height/(float)BASE_HEIGHT); bestSafeScale= Math.min(bestScaleX, bestScaleY); } /** It's possible to specify other aspect ratios, too, keeping in mind * that there are maximum width and height limits to take into account, * and that scaling of graphics etc. will be rather problematic. Default * ratio is 0.625, 0.75 will give a nice 4:3 ratio. * * TODO: pretty lame... * * @param scale * @param ratio */ public VideoScaleInfo(float scale, float ratio){ this.scale=scale; width=(int) (BASE_WIDTH*scale); height=(int) (scale*BASE_WIDTH*ratio); bestScaleX= (int) Math.floor((float)width/(float)BASE_WIDTH); bestScaleY= (int) Math.floor((float)height/(float)BASE_HEIGHT); bestSafeScale= Math.min(bestScaleX, bestScaleY); } @Override public int getScreenWidth() { return width; } @Override public int getScreenHeight() { return height; } @Override public int getScalingX() { return bestScaleX; } @Override public int getScalingY() { return bestScaleY; } @Override public int getSafeScaling() { return bestSafeScale; } @Override public boolean changed() { return false; } @Override public float getScreenMul() { return scale; } } package doom64; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import w.CacheableDoomObject; public class MapThing implements CacheableDoomObject{ public short x; public short y; public short z; public short angle; public short type; public short flags; public short id; public MapThing() { // TODO Auto-generated constructor stub } @Override public void unpack(ByteBuffer buf) throws IOException { buf.order(ByteOrder.LITTLE_ENDIAN); this.x = buf.getShort(); this.y = buf.getShort(); this.z = buf.getShort(); this.angle = buf.getShort(); this.type = buf.getShort(); this.flags = buf.getShort(); this.id = buf.getShort(); } public static int sizeOf() { return 14; } } package doom64; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import w.CacheableDoomObject; public class MapLinedef implements CacheableDoomObject{ public short v1; public short v2; public int flags; public short special; public short tag; public short left; public short right; public MapLinedef() { } public void unpack(ByteBuffer buf) throws IOException { buf.order(ByteOrder.LITTLE_ENDIAN); this.v1 = buf.getShort(); this.v2 = buf.getShort(); this.flags = buf.getInt(); this.special = buf.getShort(); this.tag = buf.getShort(); this.left= buf.getShort(); this.right= buf.getShort(); } public static int sizeOf() { return 16; } /* These are Doom 64-specific linedef flags (not all of them) */ public static final int D64_ML_RENDERMIDDLE= 0x200; public static final int D64_ML_NOT_CLIPPED= 0x400; public static final int D64_ML_DONTPEGMIDDLE= 0x800; public static final int D64_ML_KILLTRIGGER= 0x1000; public static final int D64_ML_SWMASK1= 0x2000; public static final int D64_ML_SWMASK2= 0x4000; public static final int D64_ML_SWMASK3= 0x8000; } package doom64; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import w.CacheableDoomObject; import w.DoomBuffer; public class MapLights implements CacheableDoomObject { /** N.B.: read those as unsigned bytes */ public short red,green,blue; public byte pad; public short tag; public MapLights() { // TODO Auto-generated constructor stub } public MapLights(short i) { this.red=this.green=this.blue= i; } @Override public void unpack(ByteBuffer buf) throws IOException { buf.order(ByteOrder.LITTLE_ENDIAN); this.red=(short) (0x00FF&buf.get()); this.green=(short) (0x00FF&buf.get()); this.blue=(short) (0x00FF&buf.get()); this.pad=buf.get(); this.tag=buf.getShort(); } public final static int sizeOf(){ return 6; } public short greyscaleLightLevel(){ return (short) (0.2989 * red + 0.5870 * green + 0.1140 * blue); } } package doom64; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import w.CacheableDoomObject; public class MapSidedef implements CacheableDoomObject{ public short xoffset; public short yoffset; /* unsigned short hashes for DOOM64EX */ public char toptexture; public char bottomtexture; public char midtexture; public short sector; public MapSidedef() { // TODO Auto-generated constructor stub } @Override public void unpack(ByteBuffer buf) throws IOException { buf.order(ByteOrder.LITTLE_ENDIAN); this.xoffset= buf.getShort(); this.yoffset= buf.getShort(); this.toptexture= buf.getChar(); this.bottomtexture= buf.getChar(); this.midtexture= buf.getChar(); this.sector = buf.getShort(); } public static int sizeOf() { return 12; } } package doom64; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import w.CacheableDoomObject; public class MapVertex implements CacheableDoomObject{ public int x; public int y; public MapVertex() { } @Override public void unpack(ByteBuffer buf) throws IOException { buf.order(ByteOrder.LITTLE_ENDIAN); x=buf.getInt(); y=buf.getInt(); } public static final int sizeOf() { return 8; } } package doom64; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import w.CacheableDoomObject; public class MapSector implements CacheableDoomObject{ public short floorheight; public short ceilingheight; public char floorpic; public char ceilingpic; public short floorcolor; public short ceilingcolor; public short thingcolor; public short walltopcolor; public short wallbottomcolor; public short special; public short tag; public short flags; public MapSector() { // TODO Auto-generated constructor stub } @Override public void unpack(ByteBuffer buf) throws IOException { // ACHTUNG: the only situation where we'd // like to read memory-format sector_t's is from // savegames, and in vanilla savegames, not all info // is saved (or read) from disk. buf.order(ByteOrder.LITTLE_ENDIAN); this.floorheight = buf.getShort(); this.ceilingheight = buf.getShort(); this.floorpic = (char) buf.getShort(); this.ceilingpic = (char) buf.getShort(); this.floorcolor= buf.getShort(); this.ceilingcolor= buf.getShort(); this.thingcolor= buf.getShort(); this.walltopcolor= buf.getShort(); this.wallbottomcolor= buf.getShort(); this.special = buf.getShort(); this.tag = buf.getShort(); this.flags= buf.getShort(); } public static int sizeOf() { return 24; } private static final ByteBuffer iobuffer=ByteBuffer.allocate(MapSector.sizeOf()); } package savegame; import static data.Limits.*; import i.DoomStatusAware; import i.IDoomSystem; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.ArrayList; import java.util.Hashtable; import java.util.List; import data.info; import doom.DoomStatus; import doom.player_t; import doom.think_t; import doom.thinker_t; import p.AbstractLevelLoader; import p.Actions; import p.ThinkerList; import p.ceiling_t; import p.floormove_t; import p.glow_t; import p.lightflash_t; import p.mobj_t; import p.plat_t; import p.strobe_t; import p.vldoor_t; import rr.line_t; import rr.sector_t; import rr.side_t; import utils.C2JUtils; public class VanillaDSG implements IDoomSaveGame, DoomStatusAware { VanillaDSGHeader header; DoomStatus<?,?> DS; AbstractLevelLoader LL; Actions A; IDoomSystem I; public VanillaDSG(){ } @Override public void setThinkerList(ThinkerList li) { // TODO Auto-generated method stub } @Override public IDoomSaveGameHeader getHeader() { return header; } @Override public void setHeader(IDoomSaveGameHeader header) { this.header=(VanillaDSGHeader) header; } private DataInputStream f; private DataOutputStream fo; private int maxsize; @Override public boolean doLoad(DataInputStream f) { try { this.f=f; maxsize=f.available(); System.out.println("Max size "+maxsize); this.header=new VanillaDSGHeader(); header.read(f); UnArchivePlayers(); UnArchiveWorld(); UnArchiveThinkers(); UnArchiveSpecials(); byte terminator=f.readByte(); if (terminator != 0x1D) return false; else return true; } catch (Exception e){ e.printStackTrace(); System.err.printf("Error while loading savegame! Cause: %s",e.getMessage()); return false; // Needed to shut up compiler. } } /** * P_UnArchivePlayers * @throws IOException */ protected void UnArchivePlayers () throws IOException { int i; int j; for (i=0 ; i<MAXPLAYERS ; i++) { // Multiplayer savegames are different! if (!DS.playeringame[i]) continue; PADSAVEP(f,maxsize); // this will move us on the 52th byte, instead of 50th. DS.players[i].read(f); //memcpy (&players[i],save_p, sizeof(player_t)); //save_p += sizeof(player_t); // will be set when unarc thinker DS.players[i].mo = null; DS.players[i].message = null; DS.players[i].attacker = null; for (j=0 ; j<player_t.NUMPSPRITES ; j++) { if (C2JUtils.eval(DS.players[i].psprites[j].state)) { // MAES HACK to accomoadate state_t type punning a-posteriori DS.players[i].psprites[j].state = info.states[DS.players[i].psprites[j].readstate]; } } } } /** * P_UnArchivePlayers * @throws IOException */ protected void ArchivePlayers () throws IOException { for (int i=0 ; i<MAXPLAYERS ; i++) { // Multiplayer savegames are different! if (!DS.playeringame[i]) continue; PADSAVEP(fo); // this will move us on the 52th byte, instead of 50th. // State will have to be serialized when saving. DS.players[i].write(fo); //System.out.printf("Player %d has mobj hashcode %d",(1+i),DS.players[i].mo.hashCode()); } } // //P_ArchiveWorld // protected void ArchiveWorld () throws IOException { int i; int j; sector_t sec; line_t li; side_t si; // do sectors (allocate 14 bytes per sector) ByteBuffer buffer=ByteBuffer.allocate(LL.numsectors*14); buffer.order(ByteOrder.LITTLE_ENDIAN); deAdaptSectors(); for (i=0; i<LL.numsectors ; i++) { sec=LL.sectors[i]; // MAES: sectors are actually carefully // marshalled, so we don't just read/write // their entire memory footprint to disk. sec.pack(buffer); } adaptSectors(); fo.write(buffer.array(),0,buffer.position()); // do lines // Allocate for the worst-case scenario (6+20 per line) buffer=ByteBuffer.allocate(LL.numlines*(6+20)); buffer.order(ByteOrder.LITTLE_ENDIAN); buffer.position(0); //final side_t test1=new side_t(0x11111111,0x11111111,(short) 0x1111,(short)0x1111,(short)0x1111,null); //final side_t test2=new side_t(0x22222222,0x22222222,(short) 0x2222,(short)0x2222,(short)0x2222,null); for (i=0; i<LL.numlines ; i++) { li=LL.lines[i]; li.pack(buffer); for (j=0 ; j<2 ; j++) { if (li.sidenum[j] == line_t.NO_INDEX) continue; si = LL.sides[li.sidenum[j]]; si.pack(buffer); //if (j==0) test1.pack(buffer); //else test2.pack(buffer); } } int write=buffer.position(); fo.write(buffer.array(),0,write); } // //P_UnArchiveWorld // protected final void UnArchiveWorld () throws IOException { int i; int j; sector_t sec; line_t li; side_t si; // short get; //get = (short *)save_p; //List<sector_t> sectors=new ArrayList<sector_t>(); // do sectors for (i=0; i<LL.numsectors ; i++) { sec=LL.sectors[i]; // MAES: sectors were actually carefully // unmarshalled, so we don't just read/write // their entire memory footprint to disk. sec.read(f); sec.specialdata = null; sec.soundtarget = null; } adaptSectors(); // do lines for (i=0 ; i<LL.numlines ; i++) { li=LL.lines[i]; // MAES: something similar occurs with lines, too. li.read(f); //System.out.println("Line "+i+": "+li); //System.out.print(i+ " {"); for (j=0 ; j<2 ; j++) { // System.out.print(li.sidenum[j]); // if (j<2) System.out.print(","); // System.out.printf("Skipped sidenum %d for line %d\n",j,i); if (li.sidenum[j] == line_t.NO_INDEX){ // System.out.printf("Skipped sidenum %d for line %d\n",j,i); continue; } // Similarly, sides also get a careful unmarshalling even // in vanilla. No "dumb" block reads here. si = LL.sides[li.sidenum[j]]; si.read(f); } //System.out.printf("Position at end of WORLD: %d\n",f.getFilePointer()); } } /** Convert loaded sectors from vanilla savegames into the internal, * continuous index progression, by intercepting breaks corresponding to markers. */ protected void adaptSectors(){ sector_t sec; switch(DS.getGameMode()){ case registered: case shareware: for (int i=0;i<LL.numsectors;i++){ sec=LL.sectors[i]; // Between the F1_START and F1_END mark (in vanilla) if (sec.floorpic<=54){ sec.floorpic-=1; } else { // Between the F2_START and F2_END mark (in vanilla) sec.floorpic-=3; } if (sec.ceilingpic<=54){ sec.ceilingpic-=1; } else { // Between the F2_START and F2_END mark (in vanilla) sec.ceilingpic-=3; } } break; case commercial: case pack_plut: case pack_tnt: for (int i=0;i<LL.numsectors;i++){ sec=LL.sectors[i]; // Between the F1_START and F1_END mark (in vanilla) if (sec.floorpic<=54){ sec.floorpic-=1; } else if (sec.floorpic<=99){ // Between the F2_START and F2_END mark (in vanilla) sec.floorpic-=3; } else { sec.floorpic-=5; } if (sec.ceilingpic<=54){ sec.ceilingpic-=1; } else if (sec.ceilingpic<=99){ // Between the F2_START and F2_END mark (in vanilla) sec.ceilingpic-=3; } else { sec.ceilingpic-=5; } } } } /** De-convert sectors from an absolute to a vanilla-like index * progression, by adding proper skips */ protected void deAdaptSectors(){ sector_t sec; switch(DS.getGameMode()){ case registered: case shareware: for (int i=0;i<LL.numsectors;i++){ sec=LL.sectors[i]; // Between the F1_START and F1_END mark (in vanilla) if (sec.floorpic<54){ sec.floorpic+=1; } else { // Between the F2_START and F2_END mark (in vanilla) sec.floorpic+=3; } if (sec.ceilingpic<54){ sec.ceilingpic+=1; } else { // Between the F2_START and F2_END mark (in vanilla) sec.ceilingpic+=3; } } break; case commercial: case pack_plut: case pack_tnt: for (int i=0;i<LL.numsectors;i++){ sec=LL.sectors[i]; // Between the F1_START and F1_END mark (in vanilla) if (sec.floorpic<54){ sec.floorpic+=1; } else if (sec.floorpic<99){ // Between the F2_START and F2_END mark (in vanilla) sec.floorpic+=3; } else { sec.floorpic+=5; } if (sec.ceilingpic<54){ sec.ceilingpic+=1; } else if (sec.ceilingpic<99){ // Between the F2_START and F2_END mark (in vanilla) sec.ceilingpic+=3; } else { sec.ceilingpic+=5; } } } } // //Thinkers // protected enum thinkerclass_t { tc_end, tc_mobj; } List<mobj_t> TL=new ArrayList<mobj_t>(); // //P_UnArchiveThinkers // protected void ArchiveThinkers () throws IOException { thinker_t th; mobj_t mobj; // save off the current thinkers for (th = A.getThinkerCap().next ; th != A.getThinkerCap(); th=th.next) { if (th.function!=null && th.function==think_t.P_MobjThinker) { // Indicate valid thinker fo.writeByte(thinkerclass_t.tc_mobj.ordinal()); // Pad... PADSAVEP(fo); mobj=(mobj_t)th; mobj.write(fo); // MAES: state is explicit in state.id // save_p += sizeof(*mobj); // mobj->state = (state_t *)(mobj->state - states); // MAES: player is automatically generated at runtime and handled by the writer. //if (mobj->player) //mobj->player = (player_t *)((mobj->player-players) + 1); continue; } // I_Error ("P_ArchiveThinkers: Unknown thinker function"); } // add a terminating marker fo.writeByte(thinkerclass_t.tc_end.ordinal()); } // //P_UnArchiveThinkers // protected void UnArchiveThinkers () throws IOException { thinkerclass_t tclass; // was "byte", therefore unsigned thinker_t currentthinker; thinker_t next; mobj_t mobj; int id=0; // remove all the current thinkers currentthinker = A.getThinkerCap().next; while (currentthinker!=null && currentthinker != A.getThinkerCap()) { next = currentthinker.next; if (currentthinker.function == think_t.P_MobjThinker) A.RemoveMobj ((mobj_t)currentthinker); else{ //currentthinker.next.prev=currentthinker.prev; //currentthinker.prev.next=currentthinker.next; currentthinker=null; } currentthinker = next; } A.InitThinkers (); // read in saved thinkers boolean end=false; while (!end) { int tmp=f.readUnsignedByte(); tclass=thinkerclass_t.values()[tmp]; switch (tclass) { case tc_end: // That's how we know when to stop. end=true; break; // end of list case tc_mobj: PADSAVEP(f,maxsize); mobj=new mobj_t(A); mobj.read(f); mobj.id=++id; TL.add(mobj); mobj.state = info.states[mobj.stateid]; mobj.target = null; if (mobj.playerid!=0) { mobj.player = DS.players[mobj.playerid-1]; mobj.player.mo = mobj; } LL.SetThingPosition (mobj); mobj.info = info.mobjinfo[mobj.type.ordinal()]; mobj.floorz = mobj.subsector.sector.floorheight; mobj.ceilingz = mobj.subsector.sector.ceilingheight; mobj.function = think_t.P_MobjThinker; A.AddThinker (mobj); break; default: I.Error ("Unknown tclass %d in savegame",tclass); } } reconstructPointers(); rewirePointers(); } Hashtable<Integer,mobj_t> pointindex=new Hashtable<Integer,mobj_t> (); /** Allows reconstructing infighting targets from stored pointers/indices. * Works even with vanilla savegames as long as whatever it is that you * store is unique. A good choice would be progressive indices or hash values. * */ protected void reconstructPointers(){ int player=0; for(mobj_t th: TL){ if (th.player!=null){ player=th.id; // Player found, so that's our first key. pointindex.put(th.player.p_mobj,th); } } if (player==0) { System.err.println("Player not found, cannot reconstruct pointers!"); return; } int curr; // next or prev index // We start from the player's index, if found. // We subtract -1 so it matches that inside the thinkers list. for (int i=(player-1);i<TL.size()-1;i++){ // Get "next" pointer. curr=TL.get(i).nextid; pointindex.put(curr, TL.get(i+1)); } // We also search backwards, in case player wasn't first object // (can this even happen, in vanilla?) // -1 so it matches that of the TL list. for (int i=(player-1);i>0;i--){ // Get "prev" pointer. curr=TL.get(i).previd; pointindex.put(curr,TL.get(i-1)); } } /** Allows reconstructing infighting targets from stored pointers/indices from * the hashtable created by reconstructPointers. * */ protected void rewirePointers(){ for(mobj_t th: TL){ if (th.p_target!=0){ th.target=pointindex.get(th.p_target); th.tracer=pointindex.get(th.p_tracer); // System.out.printf("Object %s has target %s\n",th.type.toString(),th.target.type.toString()); } } } protected enum specials_e { tc_ceiling, tc_door, tc_floor, tc_plat, tc_flash, tc_strobe, tc_glow, tc_endspecials } ; // //P_UrchiveSpecials // protected void ArchiveSpecials () throws IOException { ceiling_t ceiling; vldoor_t door; floormove_t floor; plat_t plat; lightflash_t flash; strobe_t strobe; glow_t glow; int i; // Most of these objects are quite hefty, but estimating 128 bytes tops // for each should do (largest one is 56); ByteBuffer buffer=ByteBuffer.allocate(128); buffer.order(ByteOrder.LITTLE_ENDIAN); // save off the current thinkers for (thinker_t th = A.getThinkerCap().next ; th != A.getThinkerCap() ; th=th.next){ // Write out any pending objects. if (buffer.position()>0){ fo.write(buffer.array(),0,buffer.position()); //System.out.println("Wrote out "+buffer.position()+" bytes"); } // Back to the beginning. buffer.position(0); // So ceilings don't think? if (th.function== null) { // i maintains status between iterations for (i = 0; i < A.getMaxCeilings();i++) if ((th instanceof ceiling_t) && (A.getActiveCeilings()[i] == (ceiling_t)th)) break; if (i<MAXCEILINGS) { fo.writeByte(specials_e.tc_ceiling.ordinal()); PADSAVEP(fo); // Set id for saving ceiling=(ceiling_t)th; ceiling.sectorid=ceiling.sector.id; ceiling.pack(buffer); } continue; } // Well, apparently some do. if (th.function== think_t.T_MoveCeiling) { fo.writeByte(specials_e.tc_ceiling.ordinal()); PADSAVEP(fo); ceiling=(ceiling_t)th; ceiling.sectorid=ceiling.sector.id; ceiling.pack(buffer); continue; } // Well, apparently some do. if (th.function== think_t.T_VerticalDoor) { fo.writeByte(specials_e.tc_door.ordinal()); PADSAVEP(fo); door=(vldoor_t)th; door.sectorid=door.sector.id; door.pack(buffer); continue; } // Well, apparently some do. if (th.function== think_t.T_MoveFloor) { fo.writeByte(specials_e.tc_floor.ordinal()); PADSAVEP(fo); floor=(floormove_t)th; floor.sectorid=floor.sector.id; floor.pack(buffer); continue; } // Well, apparently some do. if (th.function== think_t.T_PlatRaise) { fo.writeByte(specials_e.tc_plat.ordinal()); PADSAVEP(fo); plat=(plat_t)th; plat.sectorid=plat.sector.id; plat.pack(buffer); continue; } // Well, apparently some do. if (th.function== think_t.T_LightFlash) { fo.writeByte(specials_e.tc_flash.ordinal()); PADSAVEP(fo); flash=(lightflash_t)th; flash.sectorid=flash.sector.id; flash.pack(buffer); continue; } // Well, apparently some do. if (th.function== think_t.T_StrobeFlash) { fo.writeByte(specials_e.tc_strobe.ordinal()); PADSAVEP(fo); strobe=(strobe_t)th; strobe.sectorid=strobe.sector.id; strobe.pack(buffer); continue; } // Well, apparently some do. if (th.function== think_t.T_Glow) { fo.writeByte(specials_e.tc_glow.ordinal()); PADSAVEP(fo); glow=(glow_t)th; glow.sectorid=glow.sector.id; glow.pack(buffer); continue; } } if (buffer.position()>0){ fo.write(buffer.array(),0,buffer.position()); } // Finito! fo.writeByte((byte) specials_e.tc_endspecials.ordinal()); } // //P_UnArchiveSpecials // protected void UnArchiveSpecials () throws IOException { specials_e tclass; ceiling_t ceiling; vldoor_t door; floormove_t floor; plat_t plat; lightflash_t flash; strobe_t strobe; glow_t glow; //List<thinker_t> A=new ArrayList<thinker_t>(); A.ClearPlatsBeforeLoading(); A.ClearCeilingsBeforeLoading(); // read in saved thinkers while (true) { int tmp=f.readUnsignedByte(); //tmp&=0x00ff; // To "unsigned byte" tclass=specials_e.values()[tmp]; switch (tclass) { case tc_endspecials: return; // end of list case tc_ceiling: PADSAVEP(f,maxsize); ceiling = new ceiling_t(); ceiling.read(f); ceiling.sector = LL.sectors[ceiling.sectorid]; ceiling.sector.specialdata = ceiling; if (ceiling.functionid!=0) ceiling.function = think_t.T_MoveCeiling; A.AddThinker (ceiling); A.AddActiveCeiling(ceiling); break; case tc_door: PADSAVEP(f,maxsize); door=new vldoor_t(); door.read(f); door.sector = LL.sectors[door.sectorid]; door.sector.specialdata = door; door.function = think_t.T_VerticalDoor; A.AddThinker (door); break; case tc_floor: PADSAVEP(f,maxsize); floor=new floormove_t(); floor.read(f); floor.sector = LL.sectors[floor.sectorid]; floor.sector.specialdata = floor; floor.function = think_t.T_MoveFloor; A.AddThinker (floor); break; case tc_plat: PADSAVEP(f,maxsize); plat=new plat_t(); plat.read(f); plat.sector = LL.sectors[plat.sectorid]; plat.sector.specialdata = plat; if (plat.functionid!=0) plat.function = think_t.T_PlatRaise; A.AddThinker (plat); A.AddActivePlat(plat); break; case tc_flash: PADSAVEP(f,maxsize); flash=new lightflash_t(this.DS.RND); flash.read(f); flash.sector =LL.sectors[flash.sectorid]; flash.function = think_t.T_LightFlash; A.AddThinker (flash); break; case tc_strobe: PADSAVEP(f,maxsize); strobe = new strobe_t(); strobe.read(f); strobe.sector = LL.sectors[strobe.sectorid]; strobe.function = think_t.T_StrobeFlash; A.AddThinker (strobe); break; case tc_glow: PADSAVEP(f,maxsize); glow = new glow_t(); glow.read(f); glow.sector = LL.sectors[glow.sectorid]; glow.function = think_t.T_Glow; A.AddThinker (glow); break; default: I.Error ("P_UnarchiveSpecials:Unknown tclass %d in savegame",tmp); } } } /** * Pads save_p to a 4-byte boundary * so that the load/save works on SGI&Gecko. * * @param save_p */ protected final int PADSAVEP(int save_p){ return (save_p += (4 - ((int) save_p & 3)) & 3); } //protected final int PADSAVEP(ByteBuffer b, int save_p){ // ByteBuffer // return (save_p += (4 - ((int) save_p & 3)) & 3); //} protected final long PADSAVEP(DataInputStream f, int maxsize) throws IOException{ long save_p=maxsize-f.available(); int padding =(4 - ((int) save_p & 3)) & 3; // System.out.printf("Current position %d Padding by %d bytes %d\n",save_p,padding,maxsize); f.skip(padding); return padding; } protected final long PADSAVEP(DataOutputStream f) throws IOException{ long save_p=f.size(); int padding =(4 - ((int) save_p & 3)) & 3; // System.out.printf("Current position %d Padding by %d bytes\n",save_p,padding); for (int i=0;i<padding;i++) f.write(0); return padding; } @Override public void updateStatus(DoomStatus<?,?> DS) { this.DS=DS; this.LL=DS.LL; this.A=DS.P; } @Override public boolean doSave(DataOutputStream f) { try { // The header must have been set, at this point. this.fo=f; //f.setLength(0); // Kill old info. header.write(f); //header.read(f); ArchivePlayers (); ArchiveWorld(); ArchiveThinkers(); ArchiveSpecials(); // TODO: the rest... f.write(0x1D); } catch (Exception e){ e.printStackTrace(); System.err.printf("Error while saving savegame! Cause: %s",e.getMessage()); return false; // Needed to shut up compiler. } return true; } } package savegame; import p.ThinkerList; public interface ILoadSaveGame { void setThinkerList(ThinkerList li); void doSave(ThinkerList li); void doLoad(ThinkerList li); } package savegame; import java.io.DataInputStream; import java.io.DataOutputStream; import i.DoomStatusAware; import p.ThinkerList; public interface IDoomSaveGame extends DoomStatusAware{ void setThinkerList(ThinkerList li); boolean doLoad(DataInputStream f); IDoomSaveGameHeader getHeader(); void setHeader(IDoomSaveGameHeader header); boolean doSave(DataOutputStream f); } package savegame; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.nio.ByteBuffer; import utils.C2JUtils; import w.CacheableDoomObject; import w.DoomBuffer; import w.DoomIO; import w.IReadableDoomObject; import w.IWritableDoomObject; import defines.skill_t; import static data.Defines.VERSION; import static data.Limits.MAXPLAYERS; import static data.Limits.SAVESTRINGSIZE; import static data.Limits.VERSIONSIZE; /** The header of a vanilla savegame. * * It contains a fixed-length, null-terminated string of 24 bytes max, in any case. * Then a 16-byte "version string", which normally reads "version 109". * Then bytes that record: * skill +1 * episode +1 * map +1 * players in game +4 * gametime +3 (as 24-bit big-endian) * * So the header has an total size of *drum roll* 50 bytes. * * * @author admin * */ public class VanillaDSGHeader implements IDoomSaveGameHeader,IReadableDoomObject, IWritableDoomObject, CacheableDoomObject{ public String name; // max size SAVEGAMENAME public String vcheck; // These are for DS public skill_t gameskill; public int gameepisode; public int gamemap; public boolean[] playeringame; /** what bullshit, stored as 24-bit integer?! */ public int leveltime; // These help checking shit. public boolean wrongversion; public boolean properend; public VanillaDSGHeader(){ this.playeringame=new boolean[MAXPLAYERS]; } @Override public void unpack(ByteBuffer buf) throws IOException { name=DoomBuffer.getNullTerminatedString(buf, SAVESTRINGSIZE); vcheck=DoomBuffer.getNullTerminatedString(buf, VERSIONSIZE); String vcheckb= ("version "+VERSION); // no more unpacking, and report it. if (wrongversion = !(vcheckb.equalsIgnoreCase(vcheck))) return; gameskill = skill_t.values()[buf.get()]; gameepisode = buf.get(); gamemap = buf.get(); for (int i=0 ; i<MAXPLAYERS ; i++) playeringame[i] = buf.get()!=0; // load a base level (this doesn't advance the pointer?) //G_InitNew (gameskill, gameepisode, gamemap); // get the times int a = C2JUtils.toUnsignedByte(buf.get()); int b = C2JUtils.toUnsignedByte(buf.get()); int c = C2JUtils.toUnsignedByte(buf.get()); // Quite anomalous, leveltime is stored as a BIG ENDIAN, 24-bit unsigned integer :-S leveltime = (a<<16) | (b<<8) | c; // Mark this position... buf.mark(); buf.position(buf.limit()-1); if (buf.get() != 0x1d) properend=false; else properend=true; buf.reset(); // We've loaded whatever consistutes "header" info, the rest must be unpacked by proper // methods in the game engine itself. } @Override public void write(DataOutputStream f) throws IOException { DoomIO.writeString(f,name,SAVESTRINGSIZE); DoomIO.writeString(f,vcheck,VERSIONSIZE); f.writeByte(gameskill.ordinal()); f.writeByte(gameepisode); f.writeByte(gamemap); for (int i=0 ; i<MAXPLAYERS ; i++) f.writeBoolean(playeringame[i]); // load a base level (this doesn't advance the pointer?) //G_InitNew (gameskill, gameepisode, gamemap); // get the times byte a = (byte) (0x0000FF&(leveltime>>16)); byte b = (byte) (0x00FF&(leveltime>>8)); byte c = (byte) (0x00FF&(leveltime)); // Quite anomalous, leveltime is stored as a BIG ENDIAN, 24-bit unsigned integer :-S f.writeByte(a); f.writeByte(b); f.writeByte(c); // The end. This is actually just the header, so we don't "end" here just yet. // f.writeByte(0x1d); } @Override public void read(DataInputStream f) throws IOException { name= DoomIO.readNullTerminatedString(f,SAVESTRINGSIZE); vcheck=DoomIO.readNullTerminatedString(f,VERSIONSIZE); gameskill=skill_t.values()[f.readUnsignedByte()]; gameepisode=f.readByte(); gamemap=f.readByte(); for (int i=0 ; i<MAXPLAYERS ; i++) playeringame[i]=f.readBoolean(); // get the times int a = f.readUnsignedByte(); int b = f.readUnsignedByte(); int c = f.readUnsignedByte(); // Quite anomalous, leveltime is stored as a BIG ENDIAN, 24-bit unsigned integer :-S leveltime = (a<<16) | (b<<8) | c; } ////////////////////////// NASTY GETTERS ////////////////////////////// @Override public String getName() { return name; } @Override public void setName(String name) { this.name = name; } @Override public String getVersion() { return vcheck; } @Override public void setVersion(String vcheck) { this.vcheck = vcheck; } @Override public skill_t getGameskill() { return gameskill; } @Override public void setGameskill(skill_t gameskill) { this.gameskill = gameskill; } @Override public int getGameepisode() { return gameepisode; } @Override public void setGameepisode(int gameepisode) { this.gameepisode = gameepisode; } @Override public int getGamemap() { return gamemap; } @Override public void setGamemap(int gamemap) { this.gamemap = gamemap; } @Override public boolean[] getPlayeringame() { return playeringame; } @Override public void setPlayeringame(boolean[] playeringame) { this.playeringame = playeringame; } @Override public int getLeveltime() { return leveltime; } @Override public void setLeveltime(int leveltime) { this.leveltime = leveltime; } @Override public boolean isWrongversion() { return wrongversion; } @Override public void setWrongversion(boolean wrongversion) { this.wrongversion = wrongversion; } @Override public boolean isProperend() { return properend; } } package savegame; import defines.skill_t; /** A Save Game Header should be able to be loaded quickly and return * some basic info about it (name, version, game time, etc.) in an unified * manner, no matter what actual format you use for saving. * * @author admin * */ public interface IDoomSaveGameHeader { String getName(); void setName(String name); skill_t getGameskill(); void setGameskill(skill_t gameskill); String getVersion(); void setVersion(String vcheck); int getGameepisode(); void setGameepisode(int gameepisode); boolean isProperend(); void setWrongversion(boolean wrongversion); boolean isWrongversion(); void setLeveltime(int leveltime); int getLeveltime(); void setPlayeringame(boolean[] playeringame); boolean[] getPlayeringame(); void setGamemap(int gamemap); int getGamemap(); } package timing; import static data.Defines.TICRATE; public class NanoTicker implements ITicker { /** * I_GetTime * returns time in 1/70th second tics */ @Override public int GetTime () { long tp; //struct timezone tzp; int newtics; // Attention: System.nanoTime() might not be consistent across multicore CPUs. // To avoid the core getting back to the past, tp=System.nanoTime(); if (basetime==0) basetime = tp; newtics = (int) (((tp-basetime)*TICRATE)/1000000000);// + tp.tv_usec*TICRATE/1000000; if (newtics<oldtics) { System.err.printf("Timer discrepancies detected : %d",(++discrepancies)); return oldtics; } return (oldtics=newtics); } protected volatile long basetime=0; protected volatile int oldtics=0; protected volatile int discrepancies; } package timing; public interface ITicker { public int GetTime(); } package timing; import static data.Defines.TICRATE; public class MilliTicker implements ITicker { /** * I_GetTime * returns time in 1/70th second tics */ @Override public int GetTime () { long tp; //struct timezone tzp; int newtics; tp=System.currentTimeMillis(); if (basetime==0) basetime = tp; newtics = (int) (((tp-basetime)*TICRATE)/1000); return newtics; } protected volatile long basetime=0; protected volatile int oldtics=0; protected volatile int discrepancies; } package timing; public class FastTicker implements ITicker { /** * I_GetTime * returns time in 1/70th second tics */ @Override public int GetTime () { return fasttic++; } protected volatile int fasttic=0; } package s; import static data.sounds.S_sfx; import java.util.HashMap; import java.util.Timer; import java.util.TimerTask; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.Semaphore; import javax.sound.sampled.AudioFormat; import javax.sound.sampled.AudioSystem; import javax.sound.sampled.DataLine; import javax.sound.sampled.SourceDataLine; import pooling.AudioChunkPool; import data.sounds.sfxenum_t; import doom.DoomStatus; /** * A spiffy new sound system, based on the Classic sound driver. * It is entirely asynchronous (runs in its own thread) and even has its own timer. * This allows it to continue mixing even when the main loop is not responding * (something which, arguably, could be achieved just with a timer calling * UpdateSound and SubmitSound). Uses message passing to deliver channel status * info, and mixed audio directly without using an intermediate buffer, * saving memory bandwidth. * * PROS: * a) All those of ClassicSoundDriver plus: * b) Continues normal playback even under heavy CPU load, works smoother * even on lower powered CPUs. * c) More efficient due to less copying of audio blocks. * c) Fewer audio glitches compared to ClassicSoundDriver. * * CONS: * a) All those of ClassicSoundDriver plus regarding timing accuracy. * * @author Maes */ public class SuperDoomSoundDriver extends AbstractSoundDriver { protected final Semaphore produce; protected final Semaphore consume; protected final Semaphore update_mixer; protected int chunk = 0; //protected FileOutputStream fos; //protected DataOutputStream dao; // The one and only line protected SourceDataLine line = null; protected HashMap<Integer, byte[]> cachedSounds = new HashMap<Integer, byte[]>(); protected final Timer MIXTIMER; public SuperDoomSoundDriver(DoomStatus DS, int numChannels) { super(DS,numChannels); channels = new boolean[numChannels]; produce = new Semaphore(1); consume = new Semaphore(1); update_mixer = new Semaphore(1); produce.drainPermits(); update_mixer.drainPermits(); this.MIXSRV=new MixServer(numChannels); MIXTIMER= new Timer(true); // Sound tics every 1/35th of a second. Grossly // inaccurate under Windows though, will get rounded // down to the closest multiple of 15 or 16 ms. MIXTIMER.schedule(new SoundTimer(), 0,SOUND_PERIOD); } /** These are still defined here to decouple them from the mixer's * ones, however they serve more as placeholders/status indicators; */ protected volatile boolean[] channels; protected volatile boolean mixed = false; /** * This function loops all active (internal) sound channels, retrieves a * given number of samples from the raw sound data, modifies it according to * the current (internal) channel parameters, mixes the per channel samples * into the global mixbuffer, clamping it to the allowed range, and sets up * everything for transferring the contents of the mixbuffer to the (two) * hardware channels (left and right, that is). This function currently * supports only 16bit. */ public void UpdateSound() { // This is pretty much a dummy. // The mixing thread goes on by itself, guaranteeing that it will // carry out at least currently enqueued mixing messages, regardless // of how badly the engine lags. } /** * SFX API Note: this was called by S_Init. However, whatever they did in * the old DPMS based DOS version, this were simply dummies in the Linux * version. See soundserver initdata(). */ @Override public void SetChannels(int numChannels) { // Init internal lookups (raw data, mixing buffer, channels). // This function sets up internal lookups used during // the mixing process. int steptablemid = 128; // Okay, reset internal mixing channels to zero. for (int i = 0; i < this.numChannels; i++) { channels[i] = false; } generateStepTable(steptablemid); generateVolumeLUT(); } protected PlaybackServer SOUNDSRV; protected final MixServer MIXSRV; protected Thread MIXTHREAD; protected Thread SOUNDTHREAD; @Override public boolean InitSound() { // Secure and configure sound device first. System.err.println("I_InitSound: "); // We only need a single data line. // PCM, signed, 16-bit, stereo, 22025 KHz, 2048 bytes per "frame", // maximum of 44100/2048 "fps" AudioFormat format = new AudioFormat(SAMPLERATE, 16, 2, true, true); DataLine.Info info = new DataLine.Info(SourceDataLine.class, format); if (AudioSystem.isLineSupported(info)) try { line = (SourceDataLine) AudioSystem.getSourceDataLine(format); line.open(format, AUDIOLINE_BUFFER); } catch (Exception e) { e.printStackTrace(); System.err.print("Could not play signed 16 data\n"); return false; } if (line != null) { System.err.print(" configured audio device\n"); line.start(); } else { System.err.print(" could not configure audio device\n"); return false; } SOUNDSRV = new PlaybackServer(line); SOUNDTHREAD = new Thread(SOUNDSRV); SOUNDTHREAD.setDaemon(true); SOUNDTHREAD.start(); // Vroom! MIXTHREAD= new Thread(MIXSRV); MIXTHREAD.setDaemon(true); MIXTHREAD.start(); // Initialize external data (all sounds) at start, keep static. System.err.print("I_InitSound: "); super.initSound8(); System.err.print(" pre-cached all sound data\n"); // Finished initialization. System.err.print("I_InitSound: sound module ready\n"); return true; } @Override protected int addsfx(int sfxid, int volume, int step, int seperation) { int i; int rc = -1; int oldest = DS.gametic; int oldestnum = 0; int slot; int rightvol; int leftvol; int broken=-1; // Chainsaw troubles. // Play these sound effects only one at a time. if (sfxid == sfxenum_t.sfx_sawup.ordinal() || sfxid == sfxenum_t.sfx_sawidl.ordinal() || sfxid == sfxenum_t.sfx_sawful.ordinal() || sfxid == sfxenum_t.sfx_sawhit.ordinal() || sfxid == sfxenum_t.sfx_stnmov.ordinal() || sfxid == sfxenum_t.sfx_pistol.ordinal()) { // Loop all channels, check. for (i = 0; i < numChannels; i++) { // Active, and using the same SFX? if (channels[i] && (channelids[i] == sfxid)) { // Reset. MixMessage m=new MixMessage(); m.stop=true; // We are sure that iff, // there will only be one. broken=i; break; } } } // Loop all channels to find oldest SFX. if (broken>=0) { i=broken; oldestnum=broken; } else for (i = 0; (i < numChannels) && channels[i]; i++) { if (channelstart[i] < oldest) { oldestnum = i; } } oldest = channelstart[oldestnum]; // Tales from the cryptic. // If we found a channel, fine. // If not, we simply overwrite the first one, 0. // Probably only happens at startup. if (i == numChannels) slot = oldestnum; else slot = i; MixMessage m=new MixMessage(); // Okay, in the less recent channel, // we will handle the new SFX. // Set pointer to raw data. channels[slot]=true; m.channel=slot; m.data=S_sfx[sfxid].data; // MAES: if you don't zero-out the channel pointer here, it gets ugly m.pointer= 0; // Set pointer to end of raw data. m.end = lengths[sfxid]; // Reset current handle number, limited to 0..100. if (handlenums == 0) // was !handlenums, so it's actually 1...100? handlenums = 100; // Assign current handle number. // Preserved so sounds could be stopped (unused). // Maes: this should really be decreasing, otherwide handles // should start at 0 and go towards 100. Just saying. channelhandles[slot] = rc = handlenums--; // Set stepping??? // Kinda getting the impression this is never used. // MAES: you're wrong amigo. m.step= step; // ??? m.remainder = 0; // Should be gametic, I presume. channelstart[slot] = DS.gametic; // Separation, that is, orientation/stereo. // range is: 1 - 256 seperation += 1; // Per left/right channel. // x^2 seperation, // adjust volume properly. leftvol = volume - ((volume * seperation * seperation) >> 16); // /(256*256); seperation = seperation - 257; rightvol = volume - ((volume * seperation * seperation) >> 16); // Sanity check, clamp volume. if (rightvol < 0 || rightvol > 127) DS.I.Error("rightvol out of bounds"); if (leftvol < 0 || leftvol > 127) DS.I.Error("leftvol out of bounds"); // Get the proper lookup table piece // for this volume level??? m.leftvol_lookup = vol_lookup[leftvol]; m.rightvol_lookup = vol_lookup[rightvol]; // Preserve sound SFX id, // e.g. for avoiding duplicates of chainsaw. channelids[slot] = sfxid; if (D) System.err.println(channelStatus()); if (D) System.err.printf( "Playing sfxid %d handle %d length %d vol %d on channel %d\n", sfxid, rc, S_sfx[sfxid].data.length, volume, slot); MIXSRV.submitMixMessage(m); // You tell me. return rc; } @Override public void ShutdownSound() { boolean done; // Unlock sound thread if it's waiting. produce.release(); update_mixer.release(); int i=0; do { done=true; for (i=0; i < numChannels; i++) { // If even one channel is playing, loop again. done&=!channels[i]; } //System.out.println(done+" "+this.channelStatus()); } while (!done); this.line.flush(); SOUNDSRV.terminate = true; MIXSRV.terminate = true; produce.release(); update_mixer.release(); try { SOUNDTHREAD.join(); MIXTHREAD.join(); } catch (InterruptedException e) { // Well, I don't care. } System.err.printf("3\n"); line.close(); System.err.printf("4\n"); } protected class PlaybackServer implements Runnable { public boolean terminate = false; public PlaybackServer(SourceDataLine line) { this.auline = line; } private SourceDataLine auline; private ArrayBlockingQueue<AudioChunk> audiochunks = new ArrayBlockingQueue<AudioChunk>(BUFFER_CHUNKS * 2); public void addChunk(AudioChunk chunk) { audiochunks.offer(chunk); } public volatile int currstate = 0; public void run() { while (!terminate) { // while (timing[mixstate]<=mytime){ // Try acquiring a produce permit before going on. try { //System.err.print("Waiting for a permit..."); produce.acquire(); //System.err.print("...got it\n"); } catch (InterruptedException e) { // Well, ouch. e.printStackTrace(); } int chunks = 0; // System.err.printf("Audio queue has %d chunks\n",audiochunks.size()); // Play back only at most a given number of chunks once you reach // this spot. int atMost=Math.min(ISoundDriver.BUFFER_CHUNKS,audiochunks.size()); while (atMost-->0){ AudioChunk chunk = null; try { chunk = audiochunks.take(); } catch (InterruptedException e1) { // Should not block } // Play back all chunks present in a buffer ASAP auline.write(chunk.buffer, 0, MIXBUFFERSIZE); chunks++; // No matter what, give the chunk back! chunk.free = true; audiochunkpool.checkIn(chunk); } //System.err.println(">>>>>>>>>>>>>>>>> CHUNKS " +chunks); // Signal that we consumed a whole buffer and we are ready for // another one. consume.release(); } } } /** A single channel does carry a lot of crap, figuratively speaking. * Instead of making updates to ALL channel parameters, it makes more * sense having a "mixing queue" with instructions that tell the * mixer routine to do so-and-so with a certain channel. The mixer * will then "empty" the queue when it has completed a complete servicing * of all messages and mapped them to its internal status. * */ protected class MixMessage { /** If this is set, the mixer considers that channel "muted" */ public boolean stop; /** This signals an update of a currently active channel. * Therefore pointer, remainder and data should remain untouched. * However volume and step of a particular channel can change. */ public boolean update; public int remainder; public int end; public int channel; public byte[] data; public int step; public int stepremainder; public int[] leftvol_lookup; public int[] rightvol_lookup; public int pointer; } /** Mixing thread. Mixing and submission must still go on even if * the engine lags behind due to excessive CPU load. * * @author Maes * */ protected class MixServer implements Runnable { private final ArrayBlockingQueue<MixMessage> mixmessages; /** * MAES: we'll have to use this for actual pointing. channels[] holds just * the data. */ protected int[] p_channels; /** * The second one is supposed to point at "the end", so I'll make it an int. */ protected int[] channelsend; private final byte[][] channels; /** The channel step amount... */ protected final int[] channelstep; /** ... and a 0.16 bit remainder of last step. */ protected final int[] channelstepremainder; protected final int[][] channelrightvol_lookup; protected final int[][] channelleftvol_lookup; private volatile boolean update=false; public MixServer(int numChannels){ // We can put only so many messages "on hold" mixmessages=new ArrayBlockingQueue<MixMessage>(35*numChannels); this.p_channels=new int[numChannels]; this.channels=new byte[numChannels][]; this.channelstepremainder=new int[numChannels]; this.channelsend=new int[numChannels]; this.channelstep=new int[numChannels]; this.channelleftvol_lookup=new int[numChannels][]; this.channelrightvol_lookup=new int[numChannels][]; } /** Adds a channel mixing message to the queue */ public void submitMixMessage(MixMessage m){ try{ this.mixmessages.add(m); } catch (IllegalStateException e){ // Queue full. Force clear (VERY rare). mixmessages.clear(); mixmessages.add(m); } } public boolean terminate=false; @Override public void run() { // Mix current sound data. // Data, from raw sound, for right and left. int sample = 0; int dl; int dr; // Pointers in global mixbuffer, left, right, end. // Maes: those were explicitly signed short pointers... int leftout; int rightout; // Step in mixbuffer, left and right, thus two. final int step=4; // Mixing channel index. int chan; // Determine end, for left channel only // (right channel is implicit). // MAES: this implies that the buffer will only mix // that many samples at a time, and that the size is just right. // Thus, it must be flushed (p_mixbuffer=0) before reusing it. final int leftend = SAMPLECOUNT * step; // Mix the next chunk, regardless of what the rest of the game is doing. while (!terminate) { // POINTERS to Left and right channel // which are in global mixbuffer, alternating. leftout = 0; rightout = 2; // Wait on interrupt semaphore anyway before draining queue. // This allows continuing mixing even if the main game loop // is stalled. This will result in continuous sounds, // rather than choppy interruptions. try { //System.err.print("Waiting on semaphore..."); update_mixer.acquire(); //System.err.print("...broke free\n"); } catch (InterruptedException e) { // Nothing to do. Suck it down. } // Get current number of element in queue. // At worse, there will be none. int messages=mixmessages.size(); // Drain the queue, applying changes to currently // looping channels, if applicable. This may result in new channels, // older ones being stopped, or current ones being altered. Changes // will be applied with priority either way. if (messages>0) drainAndApply(messages); // This may have changed in the mean. mixed=activeChannels(); if (mixed) {// Avoid mixing entirely if no active channel. // Get audio chunk NOW gunk= audiochunkpool.checkOut(); // Ha ha you're ass is mine! gunk.free = false; mixbuffer=gunk.buffer; while (leftout < leftend) { // Reset left/right value. dl = 0; dr = 0; // Love thy L2 chache - made this a loop. // Now more channels could be set at compile time // as well. Thus loop those channels. for (chan = 0; chan < numChannels; chan++) { // Check channel, if active. // MAES: this means that we must point to raw data here. if (channels[chan] != null) { int channel_pointer = p_channels[chan]; // Get the raw data from the channel. // Maes: this is supposed to be an 8-bit unsigned value. sample = 0x00FF & channels[chan][channel_pointer]; // Add left and right part for this channel (sound) // to the current data. Adjust volume accordingly. // Q: could this be optimized by converting samples to 16-bit // at load time, while also allowing for stereo samples? // A: Only for the stereo part. You would still look a lookup // for the CURRENT volume level. dl += channelleftvol_lookup[chan][sample]; dr += channelrightvol_lookup[chan][sample]; // This should increment the index inside a channel, but is // expressed in 16.16 fixed point arithmetic. channelstepremainder[chan] += channelstep[chan]; // The actual channel pointer is increased here. // The above trickery allows playing back different pitches. // The shifting retains only the integer part. channel_pointer += channelstepremainder[chan] >> 16; // This limits it to the "decimal" part in order to // avoid undue accumulation. channelstepremainder[chan] &= 0xFFFF; // Check whether we are done. Also to avoid overflows. if (channel_pointer >= channelsend[chan]) { // Reset pointer for a channel. if (D) System.err .printf( "Channel %d handle %d pointer %d thus done, stopping\n", chan, channelhandles[chan], channel_pointer); channels[chan] = null; // Communicate back to driver. SuperDoomSoundDriver.this.channels[chan]=false; channel_pointer = 0; } // Write pointer back, so we know where a certain channel // is the next time UpdateSounds is called. p_channels[chan] = channel_pointer; } } // for all channels. // MAES: at this point, the actual values for a single sample // (YIKES!) are in d1 and d2. We must use the leftout/rightout // pointers to write them back into the mixbuffer. // Clamp to range. Left hardware channel. // Remnant of 8-bit mixing code? That must have raped ears // and made them bleed. // if (dl > 127) *leftout = 127; // else if (dl < -128) *leftout = -128; // else *leftout = dl; if (dl > 0x7fff) dl = 0x7fff; else if (dl < -0x8000) dl = -0x8000; // Write left channel mixbuffer[leftout] = (byte) ((dl & 0xFF00) >>> 8); mixbuffer[leftout + 1] = (byte) (dl & 0x00FF); // Same for right hardware channel. if (dr > 0x7fff) dr = 0x7fff; else if (dr < -0x8000) dr = -0x8000; // Write right channel. mixbuffer[rightout] = (byte) ((dr & 0xFF00) >>> 8); mixbuffer[rightout + 1] = (byte) (dr & 0x00FF); // Increment current pointers in mixbuffer. leftout += step; rightout += step; } // End leftend/leftout while // for (chan = 0; chan < numChannels; chan++) { // if (channels[chan]!=null){ // System.err.printf("Channel %d pointer %d\n",chan,this.p_channels[chan]); // } // } } // if-mixed // After an entire buffer has been mixed, we can apply any updates. // This includes silent updates. submitSound(); } // terminate loop } private AudioChunk gunk; private final void submitSound(){ // It's possible to stay entirely silent and give the audio // queue a chance to get drained. without sending any data. // Saves BW and CPU cycles. if (mixed) { silence=0; // System.err.printf("Submitted sound chunk %d to buffer %d \n",chunk,mixstate); // Copy the currently mixed chunk into its position inside the // master buffer. // System.arraycopy(mixbuffer, 0, gunk.buffer, 0, MIXBUFFERSIZE); SOUNDSRV.addChunk(gunk); // System.err.println(chunk++); chunk++; // System.err.println(chunk); if (consume.tryAcquire()) produce.release(); } else { silence++; // MAES: attempt to fix lingering noise error if (silence >ISoundDriver.BUFFER_CHUNKS){ line.flush(); silence=0; } } } /** Drains message queue and applies to individual channels. * More recently enqueued messages will trump older ones. This method * only changes the STATUS of channels, and actual message submissions * can occur at most every sound frame. * * @param messages */ private void drainAndApply(int messages ) { MixMessage m; for (int i=0;i<messages;i++){ // There should be no problems, in theory. m=this.mixmessages.remove(); if (m.stop){ stopChannel(m.channel); } else if (m.update){ updateChannel(m); } else insertChannel(m); } } private final void stopChannel(int channel){ //System.err.printf("Stopping channel %d\n",channel); this.channels[channel]=null; this.p_channels[channel]=0; } private final void updateChannel(MixMessage m){ //System.err.printf("Updating channel %d\n",m.channel); this.channelleftvol_lookup[m.channel]=m.leftvol_lookup; this.channelrightvol_lookup[m.channel]=m.rightvol_lookup; this.channelstep[m.channel]=m.step; this.channelsend[m.channel]=m.end; } private final void insertChannel(MixMessage m){ int ch=m.channel; //System.err.printf("Inserting channel %d\n",ch); this.channels[ch]=m.data; this.p_channels[ch]=m.pointer; this.channelsend[ch]=m.end; this.channelstepremainder[ch]=m.remainder; this.channelleftvol_lookup[ch]=m.leftvol_lookup; this.channelrightvol_lookup[ch]=m.rightvol_lookup; this.channelstep[ch]=m.step; } private final boolean activeChannels(){ for (int chan = 0; chan < numChannels; chan++) { if (channels[chan] != null) // SOME mixing has taken place. return true; } return false; } public final boolean channelIsPlaying(int num){ return (channels[num]!=null); } } @Override public boolean SoundIsPlaying(int handle) { int c = getChannelFromHandle(handle); return (c != -2 && channels[c]); } /** * Internal use. * * @param handle * @return the channel that has the handle, or -2 if none has it. */ protected int getChannelFromHandle(int handle) { // Which channel has it? for (int i = 0; i < numChannels; i++) { if (channelhandles[i] == handle) return i; } return BUSY_HANDLE; } @Override public void StopSound(int handle) { // Which channel has it? int hnd = getChannelFromHandle(handle); if (hnd >= 0) { channels[hnd] = false; this.channelhandles[hnd] = IDLE_HANDLE; MixMessage m=new MixMessage(); m.channel=hnd; m.stop=true; // We can only "ask" the mixer to stop at the next //chunk. MIXSRV.submitMixMessage(m); } } @Override public void SubmitSound() { // Also a dummy. The mixing thread is in a better position to // judge when sound should be submitted. } private int silence=0; @Override public void UpdateSoundParams(int handle, int vol, int sep, int pitch) { int chan = this.getChannelFromHandle(handle); // Per left/right channel. // x^2 seperation, // adjust volume properly. int leftvol = vol - ((vol * sep * sep) >> 16); // /(256*256); sep = sep - 257; int rightvol = vol - ((vol * sep * sep) >> 16); // Sanity check, clamp volume. if (rightvol < 0 || rightvol > 127) DS.I.Error("rightvol out of bounds"); if (leftvol < 0 || leftvol > 127) DS.I.Error("leftvol out of bounds"); MixMessage m=new MixMessage(); // We are updating a currently active channel m.update=true; m.channel=chan; // Get the proper lookup table piece // for this volume level??? m.leftvol_lookup = vol_lookup[leftvol]; m.rightvol_lookup = vol_lookup[rightvol]; // Well, if you can get pitch to change too... m.step = steptable[pitch]; // Oddly enough, we could be picking a different channel here? :-S m.end = lengths[channelids[chan]]; MIXSRV.submitMixMessage(m); } protected StringBuilder sb = new StringBuilder(); public String channelStatus() { sb.setLength(0); for (int i = 0; i < numChannels; i++) { if (MIXSRV.channelIsPlaying(i)) sb.append(i); else sb.append('-'); } return sb.toString(); } // Schedule this to release the sound thread at regular intervals // so that it doesn't outrun the audioline's buffer and game updates. protected class SoundTimer extends TimerTask { public void run() { update_mixer.release(); } } protected final AudioChunk SILENT_CHUNK = new AudioChunk(); protected final AudioChunkPool audiochunkpool = new AudioChunkPool(); } package s; import data.sfxinfo_t; import data.sounds.musicenum_t; import data.sounds.sfxenum_t; import p.mobj_t; // Emacs style mode select -*- C++ -*- //----------------------------------------------------------------------------- // // $Id: IDoomSound.java,v 1.5 2011/08/24 15:55:12 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: // The not so system specific sound interface (s_sound.*) // Anything high-level like e.g. handling of panning, sound origin, // sound multiplicity etc. should be handled here, but not e.g. actual // sound playback, sound threads, etc. // That's the job of ISound and IMusic (roughly equivelnt to i_sound.*, but // with separate concerns for SFX and MUSIC. // //----------------------------------------------------------------------------- public interface IDoomSound { class channel_t{ // sound information (if null, channel avail.) sfxinfo_t sfxinfo; // origin of sound ISoundOrigin origin; // handle of the sound being played int handle; } /** Convenience hack */ public static final int NUMSFX=sfxenum_t.NUMSFX.ordinal(); // Purpose? public static final char snd_prefixen[] = { 'P', 'P', 'A', 'S', 'S', 'S', 'M', 'M', 'M', 'S', 'S', 'S' }; public static final int S_MAX_VOLUME=127; // when to clip out sounds // Does not fit the large outdoor areas. public static final int S_CLIPPING_DIST = (1200*0x10000); // Distance tp origin when sounds should be maxed out. // This should relate to movement clipping resolution // (see BLOCKMAP handling). // Originally: (200*0x10000). public static final int S_CLOSE_DIST =(160*0x10000); public static final int S_ATTENUATOR =((S_CLIPPING_DIST-S_CLOSE_DIST)>>m.fixed_t.FRACBITS); // Adjustable by menu. //protected final int NORM_VOLUME snd_MaxVolume public static final int NORM_PITCH = 128; public final static int NORM_PRIORITY = 64; public final static int NORM_SEP =128; public final static int S_PITCH_PERTURB = 1; public final static int S_STEREO_SWING = (96*0x10000); // percent attenuation from front to back public final static int S_IFRACVOL = 30; public final static int NA = 0; public final static int S_NUMCHANNELS= 2; /** * Initializes sound stuff, including volume Sets channels, SFX and music * volume, allocates channel buffer, sets S_sfx lookup. */ void Init(int sfxVolume, int musicVolume); /** * Per level startup code. Kills playing sounds at start of level, * determines music if any, changes music. */ public void Start(); /** * Start sound for thing at <origin> using <sound_id> from sounds.h */ public void StartSound(ISoundOrigin origin, int sound_id); /** * Start sound for thing at <origin> using <sound_id> from sounds.h * Convenience method using sfxenum_t instead. Delegated to int version. * */ public void StartSound(ISoundOrigin origin, sfxenum_t sound_id); /** Will start a sound at a given volume. */ public void StartSoundAtVolume(ISoundOrigin origin, int sound_id, int volume); /** Stop sound for thing at <origin> */ public void StopSound(ISoundOrigin origin); /** * Start music using <music_id> from sounds.h, and set whether looping * * @param musicnum * @param looping */ public void ChangeMusic(int musicnum, boolean looping); public void ChangeMusic(musicenum_t musicnum, boolean looping); /** Stops the music fer sure. */ public void StopMusic(); /** Stop and resume music, during game PAUSE. */ public void PauseSound(); public void ResumeSound(); /** * Updates music & sounds * * @param listener */ public void UpdateSounds(mobj_t listener); public void SetMusicVolume(int volume); public void SetSfxVolume(int volume); /** Start music using <music_id> from sounds.h */ public void StartMusic(int music_id); /** Start music using <music_id> from sounds.h * Convenience method using musicenum_t. */ public void StartMusic(musicenum_t music_id); // // Internals. // // MAES: these appear to be only of value for internal implementation, // and are never called externally. Thus, they might as well // not be part of the interface, even though it's convenient to reuse them. // /* int S_getChannel ( mobj_t origin, sfxinfo_t sfxinfo ); int S_AdjustSoundParams ( mobj_t listener, mobj_t source, int vol, int sep, int pitch ); void S_StopChannel(int cnum); */ } package s; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.concurrent.CancellationException; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executors; import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import javax.sound.midi.InvalidMidiDataException; import javax.sound.midi.MidiDevice; import javax.sound.midi.MidiEvent; import javax.sound.midi.MidiMessage; import javax.sound.midi.MidiSystem; import javax.sound.midi.MidiUnavailableException; import javax.sound.midi.Receiver; import javax.sound.midi.Sequence; import javax.sound.midi.ShortMessage; import javax.sound.midi.SysexMessage; import javax.sound.midi.Track; import javax.sound.midi.Transmitter; /** A music driver that bypasses Sequences and sends events from a MUS lump * directly to a MIDI device. * * Some songs (e.g. D_E1M8) vary individual channel volumes dynamically. This * driver multiplies the dynamic volume by the music volume set in the menu. * This does not work well with a {@link Sequence} because changes to events * (e.g. channel volume change events) do not take effect while the sequencer * is running. * * Disadvantages of this driver: * <ul><li>Supports MUS lumps only (no MID, OGG etc.)</li> * <li>Creates its own thread</li> * <li>Pausing is not implemented yet</li></ul> * * @author finnw * */ public class FinnwMusicModule implements IMusic { public FinnwMusicModule() { this.lock = new ReentrantLock(); this.channels = new ArrayList<Channel>(15); this.songs = new ArrayList<Song>(1); for (int midiChan = 0; midiChan < 16; ++ midiChan) { if (midiChan != 9) { channels.add(new Channel(midiChan)); } } channels.add(new Channel(9)); } @Override public void InitMusic() { try { receiver = getReceiver(); EventGroup genMidiEG = new EventGroup(1f); genMidiEG.generalMidi(1); genMidiEG.sendTo(receiver); sleepUninterruptibly(100, TimeUnit.MILLISECONDS); } catch (MidiUnavailableException ex) { System.err.println(ex); receiver = null; } exec = Executors.newSingleThreadScheduledExecutor(new ThreadFactoryImpl()); } /** Not yet implemented */ @Override public void PauseSong(int handle) { } @Override public void PlaySong(int handle, boolean looping) { lock.lock(); try { if (currentTransmitter != null) { currentTransmitter.stop(); } currentTransmitter = null; if (0 <= handle && handle < songs.size()) { prepare(receiver); Song song = songs.get(handle); currentTransmitter = new ScheduledTransmitter(song.getScoreBuffer(), looping); currentTransmitter.setReceiver(receiver); } } finally { lock.unlock(); } } @Override public int RegisterSong(byte[] data) { return RegisterSong(ByteBuffer.wrap(data)); } public int RegisterSong(ByteBuffer data) { Song song = new Song(data); lock.lock(); try { int result = songs.indexOf(null); if (result >= 0) { songs.set(result, song); } else { result = songs.size(); songs.add(song); } return result; } finally { lock.unlock(); } } @Override public void ResumeSong(int handle) { } @Override public void SetMusicVolume(int volume) { float fVol = volume * (1/127f); fVol = Math.max(0f, Math.min(fVol, 1f)); lock.lock(); try { this.volume = fVol; if (currentTransmitter != null) { currentTransmitter.volumeChanged(); } } finally { lock.unlock(); } } @Override public void ShutdownMusic() { exec.shutdown(); } @Override public void StopSong(int handle) { lock.lock(); try { if (currentTransmitter != null) { currentTransmitter.stop(); currentTransmitter = null; } } finally { lock.unlock(); } } @Override public void UnRegisterSong(int handle) { lock.lock(); try { if (0 <= handle && handle < songs.size()) { songs.set(handle, null); } } finally { lock.unlock(); } } static boolean hasMusMagic(ByteBuffer magicBuf) { return magicBuf.get(0) == 'M' && magicBuf.get(1) == 'U' && magicBuf.get(2) == 'S' && magicBuf.get(3) == 0x1a; } EventGroup nextEventGroup(ByteBuffer scoreBuffer, boolean looping) { EventGroup result = new EventGroup(volume); boolean last; do { if (! scoreBuffer.hasRemaining()) { if (looping) { scoreBuffer.flip(); } else { return result.emptyToNull(); } } int descriptor = scoreBuffer.get() & 0xff; last = (descriptor & 0x80) != 0; int eventType = (descriptor >> 4) & 7; int chanIndex = descriptor & 15; Channel channel = channels.get(chanIndex); switch (eventType) { case 0: { int note = scoreBuffer.get() & 0xff; if ((note & 0x80) != 0) { throw new IllegalArgumentException("Invalid note byte"); } checkChannelExists("note off", channel).noteOff(note, result); } break; case 1: { int note = scoreBuffer.get() & 0xff; boolean hasVelocity = (note & 0x80) != 0; if (hasVelocity) { int velocity = scoreBuffer.get() & 0xff; if ((velocity & 0x80) != 0) { throw new IllegalArgumentException("Invalid velocity byte"); } checkChannelExists("note on", channel).noteOn(note & 127, velocity, result); } else { checkChannelExists("note on", channel).noteOn(note, result); } } break; case 2: { int wheelVal = scoreBuffer.get() & 0xff; checkChannelExists("pitch bend", channel).pitchBend(wheelVal, result); } break; case 3: { int sysEvt = scoreBuffer.get() & 0xff; switch (sysEvt) { case 10: checkChannelExists("all sounds off", channel).allSoundsOff(result); break; case 11: checkChannelExists("all notes off", channel).allNotesOff(result); break; case 14: checkChannelExists("reset all controllers", channel).resetAll(result); break; default: String msg = String.format("Invalid system event (%d)", sysEvt); throw new IllegalArgumentException(msg); } } break; case 4: int cNum = scoreBuffer.get() & 0xff; if ((cNum & 0x80) != 0) { throw new IllegalArgumentException("Invalid controller number "); } int cVal = scoreBuffer.get() & 0xff; if (cNum == 3 && 133 <= cVal && cVal <= 135) { // workaround for some TNT.WAD tracks cVal = 127; } if ((cVal & 0x80) != 0) { String msg = String.format("Invalid controller value (%d; cNum=%d)", cVal, cNum); throw new IllegalArgumentException(msg); } switch (cNum) { case 0: checkChannelExists("patch change", channel).patchChange(cVal, result); break; case 1: // Don't forward this to the MIDI device. Some devices // react badly to banks that are undefined in GM Level 1 checkChannelExists("bank switch", channel); break; case 2: checkChannelExists("vibrato change", channel).vibratoChange(cVal, result); break; case 3: checkChannelExists("volume", channel).volume(cVal, result); break; case 4: checkChannelExists("pan", channel).pan(cVal, result); break; case 5: checkChannelExists("expression", channel).expression(cVal, result); break; case 6: checkChannelExists("reverb depth", channel).reverbDepth(cVal, result); break; case 7: checkChannelExists("chorus depth", channel).chorusDepth(cVal, result); break; default: throw new AssertionError("Controller number " + cNum + ": not yet implemented"); } break; case 6: if (looping) { scoreBuffer.flip(); } else { return result.emptyToNull(); } break; default: String msg = String.format("Unknown event type: last=%5s eventType=%d chanIndex=%d%n", last, eventType, chanIndex); throw new IllegalArgumentException(msg); } } while (! last); int qTics = readTime(scoreBuffer); result.addDelay(qTics); return result; } static class EventGroup { EventGroup(float volScale) { this.messages = new ArrayList<MidiMessage>(); this.volScale = volScale; } void addDelay(int tics) { delay += tics; } private static final int CHM_ALL_NOTES_OFF = 123; private static final int CHM_ALL_SOUND_OFF = 120; private static final int CTRL_CHORUS_DEPTH = 93; private static final int CTRL_EXPRESSION_POT = 11; private static final int CTRL_PAN = 10; private static final int RPM_PITCH_BEND_SENSITIVITY = 0; private static final int RPL_PITCH_BEND_SENSITIVITY = 0; private static final int CHM_RESET_ALL = 121; private static final int CTRL_REVERB_DEPTH = 91; private static final int CTRL_MODULATION_POT = 1; private static final int CTRL_VOLUME = 7; void allNotesOff(int midiChan) { addControlChange(midiChan, CHM_ALL_NOTES_OFF, 0); } void allSoundsOff(int midiChan) { addControlChange(midiChan, CHM_ALL_SOUND_OFF, 0); } long appendTo(Sequence sequence, int trackNum, long pos) { Track track = sequence.getTracks()[trackNum]; for (MidiMessage msg: messages) { track.add(new MidiEvent(msg, pos)); } return pos + delay * 3; } long appendTo(Track track, long pos, int scale) { for (MidiMessage msg: messages) { track.add(new MidiEvent(msg, pos)); } return pos + delay * scale; } void chorusDepth(int midiChan, int depth) { addControlChange(midiChan, CTRL_CHORUS_DEPTH, depth); } void generalMidi(int mode) { addSysExMessage(0xf0, (byte)0x7e, (byte)0x7f, (byte)9, (byte)mode, (byte)0xf7); } EventGroup emptyToNull() { if (messages.isEmpty()) { return null; } else { return this; } } void expression(int midiChan, int expr) { addControlChange(midiChan, CTRL_EXPRESSION_POT, expr); } int getDelay() { return delay; } void noteOn(int midiChan, int note, int velocity) { addShortMessage(midiChan, ShortMessage.NOTE_ON, note, velocity); } void noteOff(int midiChan, int note) { addShortMessage(midiChan, ShortMessage.NOTE_OFF, note, 0); } void pan(int midiChan, int pan) { addControlChange(midiChan, CTRL_PAN, pan); } void patchChange(int midiChan, int patchId) { addShortMessage(midiChan, ShortMessage.PROGRAM_CHANGE, patchId, 0); } void pitchBend(int midiChan, int wheelVal) { int pb14 = wheelVal * 64; addShortMessage(midiChan, ShortMessage.PITCH_BEND, pb14 % 128, pb14 / 128); } void pitchBendSensitivity(int midiChan, int semitones) { addRegParamChange(midiChan, RPM_PITCH_BEND_SENSITIVITY, RPL_PITCH_BEND_SENSITIVITY, semitones); } void resetAllControllern(int midiChan) { addControlChange(midiChan, CHM_RESET_ALL, 0); } void reverbDepth(int midiChan, int depth) { addControlChange(midiChan, CTRL_REVERB_DEPTH, depth); } void sendTo(Receiver receiver) { for (MidiMessage msg: messages) { receiver.send(msg, -1); } } void vibratoChange(int midiChan, int depth) { addControlChange(midiChan, CTRL_MODULATION_POT, depth); } void volume(int midiChan, int vol) { vol = (int) Math.round(vol * volScale); addControlChange(midiChan, CTRL_VOLUME, vol); } private void addControlChange(int midiChan, int ctrlId, int ctrlVal) { addShortMessage(midiChan, ShortMessage.CONTROL_CHANGE, ctrlId, ctrlVal); } private void addRegParamChange(int midiChan, int paramMsb, int paramLsb, int valMsb) { addControlChange(midiChan, 101, paramMsb); addControlChange(midiChan, 100, paramLsb); addControlChange(midiChan, 6, valMsb); } private void addShortMessage(int midiChan, int cmd, int data1, int data2) { try { ShortMessage msg = new ShortMessage(); msg.setMessage(cmd, midiChan, data1, data2); messages.add(msg); } catch (InvalidMidiDataException ex) { throw new RuntimeException(ex); } } private void addSysExMessage(int status, byte... data) { try { SysexMessage msg = new SysexMessage(); msg.setMessage(status, data, data.length); messages.add(msg); } catch (InvalidMidiDataException ex) { throw new RuntimeException(ex); } } private int delay; private final List<MidiMessage> messages; private final float volScale; } /** A collection of kludges to pick a MIDI output device until cvars are implemented */ static class MidiDeviceComparator implements Comparator<MidiDevice.Info> { @Override public int compare(MidiDevice.Info o1, MidiDevice.Info o2) { float score1 = score(o1), score2 = score(o2); if (score1 < score2) { return 1; } else if (score1 > score2) { return -1; } else { return 0; } } private float score(MidiDevice.Info info) { String lcName = info.getName().toLowerCase(Locale.ENGLISH); float result = 0f; if (lcName.contains("mapper")) { // "Midi Mapper" is ideal, because the user can select the default output device in the control panel result += 100; } else { if (lcName.contains("synth")) { // A synthesizer is usually better than a sequencer or USB MIDI port result += 50; if (lcName.contains("java")) { // "Java Sound Synthesizer" has a low sample rate; Prefer another software synth result -= 20; } if (lcName.contains("microsoft")) { // "Microsoft GS Wavetable Synth" is notoriously unpopular, but sometimes it's the only one // with a decent sample rate. result -= 7; } } } return result; } } static class ThreadFactoryImpl implements ThreadFactory { @Override public Thread newThread(final Runnable r) { Thread thread = new Thread(r, String.format("FinnwMusicModule-%d", NEXT_ID.getAndIncrement())); thread.setPriority(Thread.MAX_PRIORITY - 1); return thread; } private static final AtomicInteger NEXT_ID = new AtomicInteger(1); } final Lock lock; static final long nanosPerTick = 1000000000 / 140; /** Channels in MUS order (0-14 = instruments, 15 = percussion) */ final List<Channel> channels; ScheduledExecutorService exec; float volume; private static Receiver getReceiver() throws MidiUnavailableException { List<MidiDevice.Info> dInfos = new ArrayList<MidiDevice.Info>(Arrays.asList(MidiSystem.getMidiDeviceInfo())); for (Iterator<MidiDevice.Info> it = dInfos.iterator(); it.hasNext(); ) { MidiDevice.Info dInfo = it.next(); MidiDevice dev = MidiSystem.getMidiDevice(dInfo); if (dev.getMaxReceivers() == 0) { // We cannot use input-only devices it.remove(); } } if (dInfos.isEmpty()) return null; Collections.sort(dInfos, new MidiDeviceComparator()); MidiDevice.Info dInfo = dInfos.get(0); MidiDevice dev = MidiSystem.getMidiDevice((MidiDevice.Info) dInfo); dev.open(); return dev.getReceiver(); } private void prepare(Receiver receiver) { EventGroup setupEG = new EventGroup(volume); for (Channel chan: channels) { chan.allSoundsOff(setupEG); chan.resetAll(setupEG); chan.pitchBendSensitivity(2, setupEG); chan.volume(127, setupEG); } setupEG.sendTo(receiver); } private static void sleepUninterruptibly(int timeout, TimeUnit timeUnit) { boolean interrupted = false; long now = System.nanoTime(); final long expiry = now + timeUnit.toNanos(timeout); long remaining; while ((remaining = expiry - now) > 0L) { try { TimeUnit.NANOSECONDS.sleep(remaining); } catch (InterruptedException ex) { interrupted = true; } finally { now = System.nanoTime(); } } if (interrupted) { Thread.currentThread().interrupt(); } } private static Channel checkChannelExists(String type, Channel channel) throws IllegalArgumentException { if (channel == null) { String msg = String.format("Invalid channel for %s message", type); throw new IllegalArgumentException(msg); } else { return channel; } } private int readTime(ByteBuffer scoreBuffer) { int result = 0; boolean last; do { int digit = scoreBuffer.get() & 0xff; last = (digit & 0x80) == 0; result <<= 7; result |= digit & 127; } while (! last); return result; } private static class Channel { Channel(int midiChan) { this.midiChan = midiChan; } void allNotesOff(EventGroup eventGroup) { eventGroup.allNotesOff(midiChan); } void allSoundsOff(EventGroup eventGroup) { eventGroup.allSoundsOff(midiChan); } void chorusDepth(int depth, EventGroup eventGroup) { eventGroup.chorusDepth(midiChan, depth); } void expression(int expr, EventGroup eventGroup) { eventGroup.expression(midiChan, expr); } void noteOff(int note, EventGroup eventGroup) { eventGroup.noteOff(midiChan, note); } void noteOn(int note, EventGroup eventGroup) { eventGroup.noteOn(midiChan, note, lastVelocity); } void noteOn(int note, int velocity, EventGroup eventGroup) { lastVelocity = velocity; noteOn(note, eventGroup); } void pan(int pan, EventGroup eventGroup) { eventGroup.pan(midiChan, pan); } void patchChange(int patchId, EventGroup eventGroup) { eventGroup.patchChange(midiChan, patchId); } void pitchBend(int wheelVal, EventGroup eventGroup) { eventGroup.pitchBend(midiChan, wheelVal); } void pitchBendSensitivity(int semitones, EventGroup eventGroup) { eventGroup.pitchBendSensitivity(midiChan, semitones); } void resetAll(EventGroup eventGroup) { eventGroup.resetAllControllern(midiChan); } void reverbDepth(int depth, EventGroup eventGroup) { eventGroup.reverbDepth(midiChan, depth); } void vibratoChange(int depth, EventGroup eventGroup) { eventGroup.vibratoChange(midiChan, depth); } void volume(int vol, EventGroup eventGroup) { eventGroup.volume(midiChan, vol); lastVolume = vol; } void volumeChanged(EventGroup eventGroup) { eventGroup.volume(midiChan, lastVolume); } private int lastVelocity; private int lastVolume; private final int midiChan; } private class ScheduledTransmitter implements Transmitter { @Override public void close() { lock.lock(); try { if (autoShutdown && exec != null) { exec.shutdown(); } autoShutdown = false; exec = null; } finally { lock.unlock(); } } @Override public Receiver getReceiver() { return receiver; } @Override public void setReceiver(Receiver receiver) { EventGroup currentGroup = null; lock.lock(); try { if (this.receiver != null) { if (this.future.cancel(false)) { currentGroup = triggerTask.eventGroup; } } else { nextGroupTime = System.nanoTime(); } this.receiver = receiver; scheduleIfRequired(receiver, currentGroup); } finally { lock.unlock(); } } ScheduledTransmitter(ByteBuffer scoreBuffer, boolean looping) { this.exec = FinnwMusicModule.this.exec; this.looping = looping; this.scoreBuffer = scoreBuffer; } void scheduleIfRequired(Receiver receiver, EventGroup currentGroup) { assert (((ReentrantLock) lock).isHeldByCurrentThread()); if (currentGroup == null) { try { currentGroup = nextEventGroup(scoreBuffer, looping); if (currentGroup != null) { triggerTask = new TriggerTask(currentGroup, receiver); long delay = Math.max(0, nextGroupTime - System.nanoTime()); future = exec.schedule(triggerTask, delay, TimeUnit.NANOSECONDS); nextGroupTime += currentGroup.getDelay() * nanosPerTick; } else { triggerTask = null; future = null; } } catch (RejectedExecutionException ex) { // This is normal when shutting down } catch (Exception ex) { System.err.println(ex); } } } void stop() { assert (((ReentrantLock) lock).isHeldByCurrentThread()); if (future != null) { future.cancel(false); try { future.get(); } catch (InterruptedException ex) { } catch (ExecutionException ex) { } catch (CancellationException ex) { } future = null; } EventGroup cleanup = new EventGroup(0f); for (Channel chan: channels) { chan.allNotesOff(cleanup); } cleanup.sendTo(receiver); } void volumeChanged() { assert (((ReentrantLock) lock).isHeldByCurrentThread()); EventGroup adjust = new EventGroup(volume); for (Channel chan: channels) { chan.volumeChanged(adjust); } adjust.sendTo(receiver); } TriggerTask triggerTask; private class TriggerTask implements Runnable { @Override public void run() { boolean shouldSend = false; lock.lock(); try { if (triggerTask == this) { shouldSend = true; scheduleIfRequired(receiver, null); } } finally { lock.unlock(); } if (shouldSend) { eventGroup.sendTo(receiver); } } TriggerTask(EventGroup eventGroup, Receiver receiver) { this.eventGroup = eventGroup; this.receiver = receiver; } final EventGroup eventGroup; final Receiver receiver; } private boolean autoShutdown; private ScheduledExecutorService exec; private ScheduledFuture<?> future; private final boolean looping; private long nextGroupTime; private Receiver receiver; private final ByteBuffer scoreBuffer; } /** Contains unfiltered MUS data */ private class Song { Song(ByteBuffer data) { this.data = data.asReadOnlyBuffer(); this.data.order(ByteOrder.LITTLE_ENDIAN); byte[] magic = new byte[4]; this.data.get(magic); ByteBuffer magicBuf = ByteBuffer.wrap(magic); if (! hasMusMagic(magicBuf)) { throw new IllegalArgumentException("Expected magic string \"MUS\\x1a\" but found " + Arrays.toString(magic)); } this.scoreLen = this.data.getShort() & 0xffff; this.scoreStart = this.data.getShort() & 0xffff; } /** Get only the score part of the data (skipping the header) */ ByteBuffer getScoreBuffer() { ByteBuffer scoreBuffer = this.data.duplicate(); scoreBuffer.position(scoreStart); scoreBuffer.limit(scoreStart + scoreLen); ByteBuffer slice = scoreBuffer.slice(); return slice; } private final ByteBuffer data; private final int scoreLen; private final int scoreStart; } private ScheduledTransmitter currentTransmitter; private Receiver receiver; /** Songs indexed by handle */ private final List<Song> songs; } package s; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.Iterator; import java.util.List; import java.util.Locale; import javax.sound.midi.InvalidMidiDataException; import javax.sound.midi.MidiDevice; import javax.sound.midi.MidiMessage; import javax.sound.midi.MidiSystem; import javax.sound.midi.MidiUnavailableException; import javax.sound.midi.Receiver; import javax.sound.midi.Sequencer; import javax.sound.midi.ShortMessage; import javax.sound.midi.Synthesizer; /** A {@link Receiver} that scales channel volumes. * * Works by recognising channel volume change events and scaling the new volume * by the global music volume setting before forwarding the event to the * synthesizer. * * @author finnw * */ public class VolumeScalingReceiver implements Receiver { /** Guess which is the "best" available synthesizer & create a * VolumeScalingReceiver that forwards to it. * * @return a <code>VolumeScalingReceiver</code> connected to a semi- * intelligently-chosen synthesizer. * */ public static VolumeScalingReceiver getInstance() { try { List<MidiDevice.Info> dInfos = new ArrayList<MidiDevice.Info>(Arrays.asList(MidiSystem.getMidiDeviceInfo())); for (Iterator<MidiDevice.Info> it = dInfos.iterator(); it.hasNext(); ) { MidiDevice.Info dInfo = it.next(); MidiDevice dev = MidiSystem.getMidiDevice(dInfo); if (dev.getMaxReceivers() == 0) { // We cannot use input-only devices it.remove(); } } if (dInfos.isEmpty()) return null; Collections.sort(dInfos, new MidiDeviceComparator()); MidiDevice.Info dInfo = dInfos.get(0); MidiDevice dev = MidiSystem.getMidiDevice((MidiDevice.Info) dInfo); dev.open(); return new VolumeScalingReceiver(dev.getReceiver()); } catch (MidiUnavailableException ex) { return null; } } /** Create a VolumeScalingReceiver connected to a specific receiver. */ public VolumeScalingReceiver(Receiver delegate) { this.channelVolume = new int[16]; this.synthReceiver = delegate; Arrays.fill(this.channelVolume, 127); } @Override public void close() { synthReceiver.close(); } /** Set the scaling factor to be applied to all channel volumes */ public synchronized void setGlobalVolume(float globalVolume) { this.globalVolume = globalVolume; for (int chan = 0; chan < 16; ++ chan) { int volScaled = (int) Math.round(channelVolume[chan] * globalVolume); sendVolumeChange(chan, volScaled, -1); } } /** A collection of kludges to pick a synthesizer until cvars are implemented */ static class MidiDeviceComparator implements Comparator<MidiDevice.Info> { @Override public int compare(MidiDevice.Info o1, MidiDevice.Info o2) { float score1 = score(o1), score2 = score(o2); if (score1 < score2) { return 1; } else if (score1 > score2) { return -1; } else { return 0; } } /** Guess how suitable a MidiDevice is for music output. */ private float score(MidiDevice.Info info) { String lcName = info.getName().toLowerCase(Locale.ENGLISH); float result = 0f; try { MidiDevice dev = MidiSystem.getMidiDevice(info); dev.open(); try { if (dev instanceof Sequencer) { // The sequencer cannot be the same device as the synthesizer - that would create an infinite loop. return Float.NEGATIVE_INFINITY; } else if (lcName.contains("mapper")) { // "Midi Mapper" is ideal, because the user can select the default output device in the control panel result += 100; } else { if (dev instanceof Synthesizer) { // A synthesizer is usually better than a sequencer or USB MIDI port result += 50; if (lcName.contains("java")) { // "Java Sound Synthesizer" often has a low sample rate or no default soundbank; Prefer another software synth if (((Synthesizer) dev).getDefaultSoundbank() != null) { result -= 10; } else { // Probably won't be audible result -= 500; } } if (lcName.contains("microsoft")) { // "Microsoft GS Wavetable Synth" is notoriously unpopular, but sometimes it's the only one // with a decent sample rate. result -= 7; } } } return result; } finally { dev.close(); } } catch (MidiUnavailableException ex) { // Cannot use this one return Float.NEGATIVE_INFINITY; } } } /** Forward a message to the synthesizer. * * If <code>message</code> is a volume change message, the volume is * first multiplied by the global volume. Otherwise, the message is * passed unmodified to the synthesizer. */ @Override public synchronized void send(MidiMessage message, long timeStamp) { int chan = getVolumeChangeChannel(message); if (chan < 0) { synthReceiver.send(message, timeStamp); } else { int newVolUnscaled = message.getMessage()[2]; channelVolume[chan] = newVolUnscaled; int newVolScaled = (int) Math.round(newVolUnscaled * globalVolume); sendVolumeChange(chan, newVolScaled, timeStamp); } } /** Send a volume update to a specific channel. * * This is used for both local & global volume changes. */ private void sendVolumeChange(int chan, int newVolScaled, long timeStamp) { newVolScaled = Math.max(0, Math.min(newVolScaled, 127)); ShortMessage message = new ShortMessage(); try { message.setMessage(0xb0 | (chan & 15), 7, newVolScaled); synthReceiver.send(message, timeStamp); } catch (InvalidMidiDataException ex) { System.err.println(ex); } } /** Determine if the given message is a channel volume change. * * @return Channel number for which volume is being changed, or -1 if not a * channel volume change command. */ private int getVolumeChangeChannel(MidiMessage message) { if (message.getLength() >= 3) { byte[] mBytes = message.getMessage(); if ((byte) 0xb0 <= mBytes[0] && mBytes[0] < (byte) 0xc0 && mBytes[1] == 7) { return mBytes[0] & 15; } } return -1; } private final int[] channelVolume; private float globalVolume; private final Receiver synthReceiver; } package s; // // MUSIC I/O // public interface IMusic { void InitMusic(); void ShutdownMusic(); // Volume. void SetMusicVolume(int volume); /** PAUSE game handling. */ void PauseSong(int handle); void ResumeSong(int handle); /** Registers a song handle to song data. * This should handle any conversions from MUS/MIDI/OPL/etc. * * */ int RegisterSong(byte[] data); /** Called by anything that wishes to start music. plays a song, and when the song is done, starts playing it again in an endless loop. Horrible thing to do, considering. */ void PlaySong ( int handle, boolean looping ); /** Stops a song over 3 seconds. */ void StopSong(int handle); /** See above (register), then think backwards */ void UnRegisterSong(int handle); } package s; import java.io.DataInputStream; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import javax.sound.midi.InvalidMidiDataException; import javax.sound.midi.MetaMessage; import javax.sound.midi.MidiEvent; import javax.sound.midi.MidiMessage; import javax.sound.midi.MidiSystem; import javax.sound.midi.Sequence; import javax.sound.midi.ShortMessage; import javax.sound.midi.Track; import m.Swap; /** * A MUS lump reader that loads directly to a Sequence. * * Unlike QMusToMid, does not keep the MIDI version in a temporary file. * * @author finnw * */ public class MusReader { /** Create a sequence from an InputStream. * This is the counterpart of {@link MidiSystem#getSequence(InputStream)} * for MUS format. * * @param is MUS data (this method does not try to auto-detect the format.) */ public static Sequence getSequence(InputStream is) throws IOException, InvalidMidiDataException { DataInputStream dis = new DataInputStream(is); dis.skip(6); int rus = dis.readUnsignedShort(); short scoreStart = Swap.SHORT((char) rus); dis.skip(scoreStart - 8); Sequence sequence = new Sequence(Sequence.SMPTE_30, 14, 1); Track track = sequence.getTracks()[0]; int[] chanVelocity = new int[16]; Arrays.fill(chanVelocity, 100); EventGroup eg; long tick = 0; while ((eg = nextEventGroup(dis, chanVelocity)) != null) { tick = eg.appendTo(track, tick); } MetaMessage endOfSequence = new MetaMessage(); endOfSequence.setMessage(47, new byte[] {0}, 1); track.add(new MidiEvent(endOfSequence, tick)); return sequence; } private static EventGroup nextEventGroup(InputStream is, int[] channelVelocity) throws IOException { EventGroup result = new EventGroup(); boolean last; do { int b = is.read(); if (b < 0) { return result.emptyToNull(); } int descriptor = b & 0xff; last = (descriptor & 0x80) != 0; int eventType = (descriptor >> 4) & 7; int chanIndex = descriptor & 15; final int midiChan; if (chanIndex < 9) { midiChan = chanIndex; } else if (chanIndex < 15) { midiChan = chanIndex + 1; } else { midiChan = 9; } switch (eventType) { case 0: { int note = is.read() & 0xff; if ((note & 0x80) != 0) { throw new IllegalArgumentException("Invalid note byte"); } result.noteOff(midiChan, note); } break; case 1: { int note = is.read() & 0xff; boolean hasVelocity = (note & 0x80) != 0; final int velocity; if (hasVelocity) { velocity = is.read() & 0xff; if ((velocity & 0x80) != 0) { throw new IllegalArgumentException("Invalid velocity byte"); } channelVelocity[midiChan] = velocity; } else { velocity = channelVelocity[midiChan]; } result.noteOn(midiChan, note & 0x7f, velocity); } break; case 2: { int wheelVal = is.read() & 0xff; result.pitchBend(midiChan, wheelVal); } break; case 3: { int sysEvt = is.read() & 0xff; switch (sysEvt) { case 10: result.allSoundsOff(midiChan); break; case 11: result.allNotesOff(midiChan); break; case 14: result.resetAllControllers(midiChan); break; default: String msg = String.format("Invalid system event (%d)", sysEvt); throw new IllegalArgumentException(msg); } } break; case 4: int cNum = is.read() & 0xff; if ((cNum & 0x80) != 0) { throw new IllegalArgumentException("Invalid controller number "); } int cVal = is.read() & 0xff; if (cNum == 3 && 133 <= cVal && cVal <= 135) { // workaround for some TNT.WAD tracks cVal = 127; } if ((cVal & 0x80) != 0) { String msg = String.format("Invalid controller value (%d; cNum=%d)", cVal, cNum); throw new IllegalArgumentException(msg); } switch (cNum) { case 0: result.patchChange(midiChan, cVal); break; case 1: // Don't forward this to the MIDI device. Some synths if // in GM level 1 mode will react badly to banks that are // undefined in GM Level 1 break; case 2: result.vibratoChange(midiChan, cVal); break; case 3: result.volume(midiChan, cVal); break; case 4: result.pan(midiChan, cVal); break; case 5: result.expression(midiChan, cVal); break; case 6: result.reverbDepth(midiChan, cVal); break; case 7: result.chorusDepth(midiChan, cVal); break; case 8: result.sustain(midiChan, cVal); break; default: throw new AssertionError("Unknown controller number: " + cNum + "(value: " + cVal + ")"); } break; case 6: return result.emptyToNull(); default: String msg = String.format("Unknown event type: %d", eventType); throw new IllegalArgumentException(msg); } } while (! last); int qTics = readVLV(is); result.addDelay(qTics); return result; } private static int readVLV(InputStream is) throws IOException { int result = 0; boolean last; do { int digit = is.read() & 0xff; last = (digit & 0x80) == 0; result <<= 7; result |= digit & 127; } while (! last); return result; } private static class EventGroup { EventGroup() { this.messages = new ArrayList<MidiMessage>(); } void addDelay(long ticks) { delay += ticks; } void allNotesOff(int midiChan) { addControlChange(midiChan, CHM_ALL_NOTES_OFF, 0); } void allSoundsOff(int midiChan) { addControlChange(midiChan, CHM_ALL_SOUND_OFF, 0); } long appendTo(Track track, long tick) { for (MidiMessage msg: messages) { track.add(new MidiEvent(msg, tick)); } return tick + delay * 3; } void chorusDepth(int midiChan, int depth) { addControlChange(midiChan, CTRL_CHORUS_DEPTH, depth); } EventGroup emptyToNull() { if (messages.isEmpty()) { return null; } else { return this; } } void expression(int midiChan, int expr) { addControlChange(midiChan, CTRL_EXPRESSION_POT, expr); } void noteOn(int midiChan, int note, int velocity) { addShortMessage(midiChan, ShortMessage.NOTE_ON, note, velocity); } void noteOff(int midiChan, int note) { addShortMessage(midiChan, ShortMessage.NOTE_OFF, note, 0); } void pan(int midiChan, int pan) { addControlChange(midiChan, CTRL_PAN, pan); } void patchChange(int midiChan, int patchId) { addShortMessage(midiChan, ShortMessage.PROGRAM_CHANGE, patchId, 0); } void pitchBend(int midiChan, int wheelVal) { int pb14 = wheelVal * 64; addShortMessage(midiChan, ShortMessage.PITCH_BEND, pb14 % 128, pb14 / 128); } void resetAllControllers(int midiChan) { addControlChange(midiChan, CHM_RESET_ALL, 0); } void reverbDepth(int midiChan, int depth) { addControlChange(midiChan, CTRL_REVERB_DEPTH, depth); } void sustain(int midiChan, int on) { addControlChange(midiChan, CTRL_SUSTAIN, on); } void vibratoChange(int midiChan, int depth) { addControlChange(midiChan, CTRL_MODULATION_POT, depth); } void volume(int midiChan, int vol) { addControlChange(midiChan, CTRL_VOLUME, vol); } private void addControlChange(int midiChan, int ctrlId, int ctrlVal) { addShortMessage(midiChan, ShortMessage.CONTROL_CHANGE, ctrlId, ctrlVal); } private void addShortMessage(int midiChan, int cmd, int data1, int data2) { try { ShortMessage msg = new ShortMessage(); msg.setMessage(cmd, midiChan, data1, data2); messages.add(msg); } catch (InvalidMidiDataException ex) { throw new RuntimeException(ex); } } private static final int CHM_ALL_NOTES_OFF = 123; private static final int CHM_ALL_SOUND_OFF = 120; private static final int CTRL_CHORUS_DEPTH = 93; private static final int CTRL_EXPRESSION_POT = 11; private static final int CTRL_PAN = 10; private static final int CTRL_SUSTAIN = 64; private static final int CHM_RESET_ALL = 121; private static final int CTRL_REVERB_DEPTH = 91; private static final int CTRL_MODULATION_POT = 1; private static final int CTRL_VOLUME = 7; private long delay; private final List<MidiMessage> messages; } } package s; import javax.sound.sampled.AudioFormat; import javax.sound.sampled.SourceDataLine; import p.mobj_t; import data.sfxinfo_t; public class channel_t { public channel_t(){ sfxinfo=new sfxinfo_t(); } /** Currently playing sound. If null, then it's free */ DoomSound currentSound = null; sfxinfo_t sfxinfo; // origin of sound (usually a mobj_t). mobj_t origin; // handle of the sound being played int handle; AudioFormat format; public int sfxVolume; SourceDataLine auline = null; } package s; import static data.sounds.S_sfx; import m.FixedFloat; import data.sfxinfo_t; import data.sounds; import doom.DoomStatus; /** * Functionality and fields that are common among the various "sound drivers" * should go here. * * @author Maes */ public abstract class AbstractSoundDriver implements ISoundDriver { protected final static boolean D = false; // debug protected final DoomStatus<?,?> DS; /** * The global mixing buffer. Basically, samples from all active internal * channels are modifed and added, and stored in the buffer that is * submitted to the audio device. This is a 16-bit stereo signed PCM * mixbuffer. Memory order is LSB (?) and channel order is L-R-L-R... * * Not all i * */ protected byte[] mixbuffer;// = new byte[MIXBUFFERSIZE]; protected final int numChannels; /** The actual lengths of all sound effects. */ protected final int[] lengths = new int[NUMSFX]; /** * The sound in channel handles, determined on registration, might be used * to unregister/stop/modify, currently unused. */ protected final int[] channelhandles; /** * SFX id of the playing sound effect. Used to catch duplicates (like * chainsaw). */ protected final int[] channelids; /** * Pitch to stepping lookup, used in ClassicSoundDriver It's actually rigged * to have a -/+ 400% pitch variation! */ protected final int[] steptable = new int[256]; /** Volume lookups. 128 levels */ protected final int[][] vol_lookup = new int[128][256]; /** * Time/gametic that the channel started playing, used to determine oldest, * which automatically has lowest priority. In case number of active sounds * exceeds available channels. */ protected final int[] channelstart; // protected final static DataLine.Info info = new DataLine.Info(Clip.class, // format); public AbstractSoundDriver(DoomStatus<?,?> DS, int numChannels) { this.DS = DS; this.numChannels = numChannels; channelids = new int[numChannels]; channelhandles = new int[numChannels]; channelstart = new int[numChannels]; } /** * Generates volume lookup tables which also turn the unsigned samples into * signed samples. */ protected final void generateVolumeLUT() { for (int i = 0; i < 128; i++) for (int j = 0; j < 256; j++) vol_lookup[i][j] = (i * (j - 128) * 256) / 127; } /** * This table provides step widths for pitch parameters. Values go from 16K * to 256K roughly, with the middle of the table being 64K, and presumably * representing unitary pitch. So the pitch variation can be quite extreme, * allowing -/+ 400% stepping :-S * * @param steptablemid * @return */ protected void generateStepTable(int steptablemid) { for (int i = -128; i < 128; i++) { steptable[steptablemid + i] = (int) (Math.pow(2.0, (i / 64.0)) * 65536.0); //System.out.printf("Pitch %d %d %f\n",i,steptable[steptablemid + i],FixedFloat.toFloat(steptable[steptablemid + i])); } } /** Read a Doom-format sound effect from disk, leaving it in 8-bit mono format but * upsampling it to the target sample rate. * * @param sfxname * @param len * @param index * @return */ protected byte[] getsfx(String sfxname, int[] len, int index) { byte[] sfx; byte[] paddedsfx; int i; int size; int paddedsize; String name; int sfxlump; // Get the sound data from the WAD, allocate lump // in zone memory. name = String.format("ds%s", sfxname).toUpperCase(); // Now, there is a severe problem with the // sound handling, in it is not (yet/anymore) // gamemode aware. That means, sounds from // DOOM II will be requested even with DOOM // shareware. // The sound list is wired into sounds.c, // which sets the external variable. // I do not do runtime patches to that // variable. Instead, we will use a // default sound for replacement. if (DS.W.CheckNumForName(name) == -1) sfxlump = DS.W.GetNumForName("dspistol"); else sfxlump = DS.W.GetNumForName(name); DMXSound dmx= DS.W.CacheLumpNum(sfxlump, 0, DMXSound.class); // KRUDE if (dmx.speed==SAMPLERATE/2){ // Plain linear interpolation. dmx.data=DSP.crudeResample(dmx.data,2); //DSP.filter(dmx.data,SAMPLERATE, SAMPLERATE/4); dmx.datasize=dmx.data.length; } sfx = dmx.data; // MAES: A-ha! So that's how they do it. // SOund effects are padded to the highest multiple integer of // the mixing buffer's size (with silence) paddedsize = ((dmx.datasize + (SAMPLECOUNT - 1)) / SAMPLECOUNT) * SAMPLECOUNT; // Allocate from zone memory. paddedsfx = new byte[paddedsize]; // Now copy and pad. The first 8 bytes are header info, so we discard // them. System.arraycopy(sfx, 0, paddedsfx, 0, dmx.datasize); // Pad with silence (unsigned) for (i = dmx.datasize; i < paddedsize; i++) paddedsfx[i] = (byte) 127; // Remove the cached lump. DS.W.UnlockLumpNum(sfxlump); if (D) System.out.printf("SFX %d name %s size %d speed %d padded to %d\n", index, S_sfx[index].name, dmx.datasize,dmx.speed,paddedsize); // Preserve padded length. len[index] = paddedsize; // Return allocated padded data. // So the first 8 bytes are useless? return paddedsfx; } /** * Modified getsfx, which transforms samples into 16-bit, signed, stereo * beforehand, before being "fed" to the audio clips. * * @param sfxname * @param index * @return */ protected final byte[] getsfx16(String sfxname, int[] len, int index) { byte[] sfx; byte[] paddedsfx; int i; int size; int paddedsize; String name; int sfxlump; // Get the sound data from the WAD, allocate lump // in zone memory. name = String.format("ds%s", sfxname).toUpperCase(); // Now, there is a severe problem with the // sound handling, in it is not (yet/anymore) // gamemode aware. That means, sounds from // DOOM II will be requested even with DOOM // shareware. // The sound list is wired into sounds.c, // which sets the external variable. // I do not do runtime patches to that // variable. Instead, we will use a // default sound for replacement. if (DS.W.CheckNumForName(name) == -1) sfxlump = DS.W.GetNumForName("dspistol"); else sfxlump = DS.W.GetNumForName(name); size = DS.W.LumpLength(sfxlump); sfx = DS.W.CacheLumpNumAsRawBytes(sfxlump, 0); // Size blown up to accommodate two channels and 16 bits. // Sampling rate stays the same. paddedsize = (size - 8) * 2 * 2; // Allocate from zone memory. paddedsfx = new byte[paddedsize]; // Skip first 8 bytes (header), blow up the data // to stereo, BIG ENDIAN, SIGNED, 16 bit. Don't expect any fancy DSP // here! int sample = 0; for (i = 8; i < size; i++) { // final short sam=(short) vol_lookup[127][0xFF&sfx[i]]; final short sam = (short) ((0xFF & sfx[i] - 128) << 8); paddedsfx[sample++] = (byte) (0xFF & (sam >> 8)); paddedsfx[sample++] = (byte) (0xFF & sam); paddedsfx[sample++] = (byte) (0xFF & (sam >> 8)); paddedsfx[sample++] = (byte) (0xFF & sam); } // Remove the cached lump. DS.W.UnlockLumpNum(sfxlump); // Preserve padded length. len[index] = paddedsize; // Return allocated padded data. // So the first 8 bytes are useless? return paddedsfx; } /** * Starting a sound means adding it to the current list of active sounds in * the internal channels. As the SFX info struct contains e.g. a pointer to * the raw data it is ignored. As our sound handling does not handle * priority, it is ignored. Pitching (that is, increased speed of playback) * is set, but whether it's used or not depends on the final implementation * (e.g. classic mixer uses it, but AudioLine-based implementations are not * guaranteed. */ @Override public int StartSound(int id, int vol, int sep, int pitch, int priority) { if (id < 1 || id > S_sfx.length - 1) return BUSY_HANDLE; // Find a free channel and get a timestamp/handle for the new sound. int handle = this.addsfx(id, vol, steptable[pitch], sep); return handle; } /** * This function adds a sound to the list of currently active sounds, which * is maintained as a given number (eight, usually) of internal channels. * Returns a handle. * * @param sfxid * @param volume * @param step * @param seperation * @return */ protected abstract int addsfx(int sfxid, int volume, int step, int seperation); protected short handlenums = 0; // // Retrieve the raw data lump index // for a given SFX name. // public final int GetSfxLumpNum(sfxinfo_t sfx) { String namebuf; namebuf = String.format("ds%s", sfx.name).toUpperCase(); if (namebuf.equals("DSNONE")) return -1; int lump; try { lump = DS.W.GetNumForName(namebuf); } catch (Exception e) { e.printStackTrace(); return -1; } return lump; } /** * Initialize * * @return */ protected final void initMixBuffer() { for (int i = 0; i < MIXBUFFERSIZE; i += 4) { mixbuffer[i] = (byte) (((int) (0x7FFF * Math.sin(1.5 * Math.PI * (double) i / MIXBUFFERSIZE)) & 0xff00) >>> 8); mixbuffer[i + 1] = (byte) ((int) (0x7FFF * Math.sin(1.5 * Math.PI * (double) i / MIXBUFFERSIZE)) & 0xff); mixbuffer[i + 2] = (byte) (((int) (0x7FFF * Math.sin(1.5 * Math.PI * (double) i / MIXBUFFERSIZE)) & 0xff00) >>> 8); mixbuffer[i + 3] = (byte) ((int) (0x7FFF * Math.sin(1.5 * Math.PI * (double) i / MIXBUFFERSIZE)) & 0xff); } } /** * Loads samples in 8-bit format, forcibly converts them to the common sampling rate. * Used by. */ protected final void initSound8() { int i; // Initialize external data (all sounds) at start, keep static. for (i = 1; i < NUMSFX; i++) { // Alias? Example is the chaingun sound linked to pistol. if (sounds.S_sfx[i].link == null) { // Load data from WAD file. S_sfx[i].data = getsfx(S_sfx[i].name, lengths, i); } else { // Previously loaded already? S_sfx[i].data = S_sfx[i].link.data; } } } /** * This is only the common part of InitSound that caches sound data in * 16-bit, stereo format (used by Audiolines). INTO sfxenum_t. * * Only used by the Clip and David "drivers". * */ protected final void initSound16() { int i; // Initialize external data (all sounds) at start, keep static. for (i = 1; i < NUMSFX; i++) { // Alias? Example is the chaingun sound linked to pistol. if (sounds.S_sfx[i].link == null) { // Load data from WAD file. S_sfx[i].data = getsfx16(S_sfx[i].name, lengths, i); } else { // Previously loaded already? S_sfx[i].data = S_sfx[i].link.data; } } } } package s; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.Hashtable; import w.CacheableDoomObject; /** Blatantly ripping off Chocolate Doom */ public class SpeakerSound implements CacheableDoomObject{ public short header; public short length; public byte[] data; public static int[] timer_values=new int[]{0, 6818, 6628, 6449, 6279, 6087, 5906, 5736, 5575, 5423, 5279, 5120, 4971, 4830, 4697, 4554, 4435, 4307, 4186, 4058, 3950, 3836, 3728, 3615, 3519, 3418, 3323, 3224, 3131, 3043, 2960, 2875, 2794, 2711, 2633, 2560, 2485, 2415, 2348, 2281, 2213, 2153, 2089, 2032, 1975, 1918, 1864, 1810, 1757, 1709, 1659, 1612, 1565, 1521, 1478, 1435, 1395, 1355, 1316, 1280, 1242, 1207, 1173, 1140, 1107, 1075, 1045, 1015, 986, 959, 931, 905, 879, 854, 829, 806, 783, 760, 739, 718, 697, 677, 658, 640, 621, 604, 586, 570, 553, 538, 522, 507, 493, 479, 465, 452}; /* From analysis of fraggle's PC Speaker timings, it was found * that their natural logarithm had the following intercept * (starting at x=1) and slope. Therefore, it's possible * to go beyong the original 95 hardcoded values. */ public static final double INTERCEPT=8.827321453; public static final double SLOPE=-0.028890647; public static final int CIA_8543_FREQ=1193182; public static float[] f=new float[256]; static { f[0]=0; for (int x=1;x<f.length;x++){ //f[x] = CIA_8543_FREQ/timer_values[x]; f[x] = (float) (CIA_8543_FREQ/Math.exp(INTERCEPT+SLOPE*(x-1))); } } /** Will return a very basic, 8-bit 11.025 KHz rendition of the sound * This ain't no CuBase or MatLab, so if you were expecting perfect * sound and solid DSP, go elsewhere. * */ public byte[] toRawSample(){ // Length is in 1/140th's of a second byte[] chunk=new byte[this.length*11025/140]; int counter=0; for (int i=0;i<this.length;i++){ byte[] tmp=getPhoneme(this.data[i]); System.arraycopy(tmp, 0,chunk,counter,tmp.length); counter+=tmp.length; } return chunk; } private static Hashtable<Integer,byte[]> phonemes=new Hashtable<Integer,byte[]>(); public static byte[] getPhoneme(int phoneme){ if (!phonemes.containsKey(phoneme)){ // Generate a square wave with a duration of 1/140th of a second int samples=11025/140; byte[] tmp=new byte[samples]; float frequency=f[phoneme]; for (int i=0;i<samples;i++){ tmp[i]=(byte) (127+127*Math.signum(Math.sin(frequency*Math.PI*2*(i/11025f)))); } phonemes.put(phoneme, tmp); } return phonemes.get(phoneme); } @Override public void unpack(ByteBuffer buf) throws IOException { buf.order(ByteOrder.LITTLE_ENDIAN); header=buf.getShort(); length=buf.getShort(); data=new byte[length]; buf.get(data); } } package s; public class DummyMusic implements IMusic { @Override public void InitMusic() { // TODO Auto-generated method stub } @Override public void ShutdownMusic() { // TODO Auto-generated method stub } @Override public void SetMusicVolume(int volume) { // TODO Auto-generated method stub } @Override public void PauseSong(int handle) { // TODO Auto-generated method stub } @Override public void ResumeSong(int handle) { // TODO Auto-generated method stub } @Override public int RegisterSong(byte[] data) { // TODO Auto-generated method stub return 0; } @Override public void PlaySong(int handle, boolean looping) { // TODO Auto-generated method stub } @Override public void StopSong(int handle) { // TODO Auto-generated method stub } @Override public void UnRegisterSong(int handle) { // TODO Auto-generated method stub } } package s; import p.mobj_t; import data.sounds.musicenum_t; import data.sounds.sfxenum_t; /** Does nothing. Just allows me to code without * commenting out ALL sound-related code. Hopefully * it will be superseded by a real sound driver one day. * * @author Velktron * */ public class DummySoundDriver implements IDoomSound{ @Override public void Init(int sfxVolume, int musicVolume) { // TODO Auto-generated method stub } @Override public void Start() { // TODO Auto-generated method stub } @Override public void StartSound(ISoundOrigin origin, int sound_id) { // TODO Auto-generated method stub } @Override public void StartSound(ISoundOrigin origin, sfxenum_t sound_id) { // TODO Auto-generated method stub } @Override public void StartSoundAtVolume(ISoundOrigin origin, int sound_id, int volume) { // TODO Auto-generated method stub } @Override public void StopSound(ISoundOrigin origin) { // TODO Auto-generated method stub } @Override public void ChangeMusic(int musicnum, boolean looping) { // TODO Auto-generated method stub } @Override public void StopMusic() { // TODO Auto-generated method stub } @Override public void PauseSound() { // TODO Auto-generated method stub } @Override public void ResumeSound() { // TODO Auto-generated method stub } @Override public void UpdateSounds(mobj_t listener) { // TODO Auto-generated method stub } @Override public void SetMusicVolume(int volume) { // TODO Auto-generated method stub } @Override public void SetSfxVolume(int volume) { // TODO Auto-generated method stub } @Override public void StartMusic(int music_id) { // TODO Auto-generated method stub } @Override public void StartMusic(musicenum_t music_id) { // TODO Auto-generated method stub } @Override public void ChangeMusic(musicenum_t musicnum, boolean looping) { // TODO Auto-generated method stub } } package s; import java.util.ArrayList; import java.util.concurrent.Semaphore; import javax.sound.sampled.AudioSystem; import javax.sound.sampled.DataLine; import javax.sound.sampled.FloatControl; import javax.sound.sampled.LineUnavailableException; import javax.sound.sampled.SourceDataLine; import javax.sound.sampled.FloatControl.Type; import data.sounds; import data.sounds.sfxenum_t; import doom.DoomStatus; /** David Martel's sound driver for Mocha Doom. Excellent work! * * However, it's based on Java Audiolines, and as such has a number * of drawbacks: * * a) Sounds are forcibly blown to be stereo, 16-bit otherwise it's * impossible to get panning controls. * b) Volume, master gain, panning, pitch etc. controls are NOT guaranteed * to be granted across different OSes , and your mileage may vary. It's * fairly OK under Windows and OS X, but Linux is very clunky. The only * control that is -somewhat- guaranteed is the volume one. * c) Spawns as many threads as channels. Even if semaphore waiting it used, * that can be taxing for slower systems. * * @author David * @author Velktron * */ public class DavidSFXModule extends AbstractSoundDriver{ ArrayList<DoomSound> cachedSounds=new ArrayList<DoomSound>(); public final float[] linear2db; private SoundWorker[] channels; private Thread[] soundThread; public DavidSFXModule(DoomStatus DS,int numChannels) { super(DS,numChannels); linear2db=computeLinear2DB(); } private float[] computeLinear2DB() { // Maximum volume is 0 db, minimum is ... -96 db. // We rig this so that half-scale actually gives quarter power, // and so is -6 dB. float[] tmp=new float[VOLUME_STEPS]; for (int i=0;i<VOLUME_STEPS;i++){ float linear=(float)(10*Math.log10((float)i/(float)VOLUME_STEPS)); // Hack. The minimum allowed value as of now is -80 db. if (linear<-36.0) linear=-36.0f; tmp[i]= linear; } return tmp; } @Override public boolean InitSound() { // Secure and configure sound device first. System.err.println("I_InitSound: "); // Initialize external data (all sounds) at start, keep static. initSound16(); // Cache sounds internally so they can be "fed" to AudioLine threads later. // These can be more than the usual built-in sounds. for (int i=0;i<sounds.S_sfx.length;i++){ DoomSound tmp=new DoomSound(sounds.S_sfx[i],DoomSound.DEFAULT_SAMPLES_FORMAT); cachedSounds.add(tmp); } System.err.print(" pre-cached all sound data\n"); // Finished initialization. System.err.print("I_InitSound: sound module ready\n"); return true; } @Override public void UpdateSound() { // In theory, we should update volume + panning for each active channel. // Ouch. Ouch Ouch. } @Override public void SubmitSound() { // Sound should be submitted to the sound threads, which they pretty much // do themselves. } @Override public void ShutdownSound() { // Wait till all pending sounds are finished. boolean done = false; int i; while ( !done) { for( i=0 ; i<numChannels && !(channels[i].isPlaying()) ; i++); if (i==numChannels) done=true; } for( i=0 ; i<numChannels; i++){ channels[i].terminate=true; channels[i].wait.release(); try { this.soundThread[i].join(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } // Done. return; } @Override public void SetChannels(int numChannels) { channels= new SoundWorker[numChannels]; soundThread= new Thread[numChannels]; // This is actually called from IDoomSound. for (int i = 0; i < numChannels; i++) { channels[i]=new SoundWorker(i); soundThread[i] = new Thread(channels[i]); soundThread[i].start(); } } /** This one will only create datalines for common clip/audioline samples * directly. * * @param c * @param sfxid */ private final void createDataLineForChannel(int c, int sfxid){ // None? Make a new one. if (channels[c].auline == null) { try { DoomSound tmp=cachedSounds.get(sfxid); // Sorry, Charlie. Gotta make a new one. DataLine.Info info = new DataLine.Info(SourceDataLine.class, DoomSound.DEFAULT_SAMPLES_FORMAT); channels[c].auline = (SourceDataLine) AudioSystem.getLine(info); channels[c].auline.open(tmp.format); } catch (LineUnavailableException e) { // TODO Auto-generated catch block e.printStackTrace(); } boolean errors=false; // Add individual volume control. if (channels[c].auline.isControlSupported(Type.MASTER_GAIN)) channels[c].vc=(FloatControl) channels[c].auline .getControl(Type.MASTER_GAIN); else { System.err.print("MASTER_GAIN, "); errors=true; if (channels[c].auline.isControlSupported(Type.VOLUME)) channels[c].vc=(FloatControl) channels[c].auline .getControl(Type.VOLUME); else System.err.print("VOLUME, "); } // Add individual pitch control. if (channels[c].auline.isControlSupported(Type.SAMPLE_RATE)){ channels[c].pc=(FloatControl) channels[c].auline .getControl(Type.SAMPLE_RATE); } else { errors=true; System.err.print("SAMPLE_RATE, "); } // Add individual pan control if (channels[c].auline.isControlSupported(Type.BALANCE)){ channels[c].bc=(FloatControl) channels[c].auline .getControl(FloatControl.Type.BALANCE); } else { System.err.print("BALANCE, "); errors=true; if (channels[c].auline.isControlSupported(Type.PAN)){ channels[c].bc=(FloatControl) channels[c].auline .getControl(FloatControl.Type.PAN); } else { System.err.print("PANNING "); } } if (errors) System.err.printf("for channel %d NOT supported!\n",c); channels[c].auline.start(); } } /* UNUSED version, designed to work on any type of sample (in theory). Requires a DoomSound container for separate format information. private final void createDataLineForChannel(int c, DoomSound sound){ if (channels[c].auline == null) { AudioFormat format = sound.ais.getFormat(); DataLine.Info info = new DataLine.Info(SourceDataLine.class, format); try { channels[c].auline = (SourceDataLine) AudioSystem.getLine(info); channels[c].auline.open(format); } catch (LineUnavailableException e) { // TODO Auto-generated catch block e.printStackTrace(); } // Add individual volume control. if (channels[c].auline.isControlSupported(Type.MASTER_GAIN)) channels[c].vc=(FloatControl) channels[c].auline .getControl(Type.MASTER_GAIN); else { System.err.printf("MASTER_GAIN for channel %d NOT supported!\n",c); if (channels[c].auline.isControlSupported(Type.VOLUME)) channels[c].vc=(FloatControl) channels[c].auline .getControl(Type.VOLUME); else System.err.printf("VOLUME for channel %d NOT supported!\n",c); } // Add individual pitch control. if (channels[c].auline.isControlSupported(Type.SAMPLE_RATE)){ channels[c].pc=(FloatControl) channels[c].auline .getControl(Type.SAMPLE_RATE); } else { System.err.printf("SAMPLE_RATE for channel %d NOT supported!\n",c); } // Add individual pan control (TODO: proper positioning). if (channels[c].auline.isControlSupported(Type.BALANCE)){ channels[c].bc=(FloatControl) channels[c].auline .getControl(FloatControl.Type.BALANCE); } else { System.err.printf("BALANCE for channel %d NOT supported!\n",c); if (channels[c].auline.isControlSupported(Type.PAN)){ channels[c].bc=(FloatControl) channels[c].auline .getControl(FloatControl.Type.PAN); } else { System.err.printf("PAN for channel %d NOT supported!\n",c); } } channels[c].auline.start(); } } */ @Override protected int addsfx( int sfxid,int volume,int pitch, int seperation) { int i; int rc = -1; int oldest = DS.gametic; int oldestnum = 0; int slot; int rightvol; int leftvol; // Chainsaw troubles. // Play these sound effects only one at a time. if ( sfxid == sfxenum_t.sfx_sawup.ordinal() || sfxid == sfxenum_t.sfx_sawidl.ordinal() || sfxid == sfxenum_t.sfx_sawful.ordinal() || sfxid == sfxenum_t.sfx_sawhit.ordinal() || sfxid == sfxenum_t.sfx_stnmov.ordinal() || sfxid == sfxenum_t.sfx_pistol.ordinal() ) { // Loop all channels, check. for (i=0 ; i<numChannels ; i++) { // Active, and using the same SFX? if ( (channels[i].isPlaying()) && (channelids[i] == sfxid) ) { // Reset. channels[i].stopSound(); // We are sure that iff, // there will only be one. break; } } } // Loop all channels to find oldest SFX. for (i=0; (i<numChannels) && (channels[i]!=null); i++) { if (channelstart[i] < oldest) { oldestnum = i; oldest = channelstart[i]; } } // Tales from the cryptic. // If we found a channel, fine. // If not, we simply overwrite the first one, 0. // Probably only happens at startup. if (i == numChannels) slot = oldestnum; else slot = i; // Okay, in the less recent channel, // we will handle the new SFX. // Set pointer to raw data. // Create a dataline for the "lucky" channel, // or reuse an existing one if it exists. createDataLineForChannel(slot,sfxid); // Reset current handle number, limited to 0..100. if (handlenums==0) // was !handlenums, so it's actually 1...100? handlenums = MAXHANDLES; // Assign current handle number. // Preserved so sounds could be stopped (unused). channelhandles[slot]= rc = handlenums--; channelstart[slot] = DS.gametic; // Separation, that is, orientation/stereo. // range is: 1 - 256 seperation += 1; // Per left/right channel. // x^2 seperation, // adjust volume properly. leftvol = volume - ((volume*seperation*seperation) >> 16); ///(256*256); seperation = seperation - 257; rightvol = volume - ((volume*seperation*seperation) >> 16); // Sanity check, clamp volume. if (rightvol < 0 || rightvol > 127) DS.I.Error("rightvol out of bounds"); if (leftvol < 0 || leftvol > 127) DS.I.Error("leftvol out of bounds"); // Preserve sound SFX id, // e.g. for avoiding duplicates of chainsaw. channelids[slot] = sfxid; channels[slot].setVolume(volume); channels[slot].setPanning(seperation+256); channels[slot].addSound(cachedSounds.get(sfxid).data, handlenums); channels[slot].setPitch(pitch); if (D) System.err.println(channelStatus()); if (D) System.err.printf("Playing %d vol %d on channel %d\n",rc,volume,slot); // You tell me. return rc; } @Override public void StopSound(int handle) { // Which channel has it? int hnd=getChannelFromHandle(handle); if (hnd>=0) channels[hnd].stopSound(); } @Override public boolean SoundIsPlaying(int handle) { return getChannelFromHandle(handle)!=BUSY_HANDLE; } @Override public void UpdateSoundParams(int handle, int vol, int sep, int pitch) { // This should be called on sounds that are ALREADY playing. We really need // to retrieve channels from their handles. //System.err.printf("Updating sound with handle %d vol %d sep %d pitch %d\n",handle,vol,sep,pitch); int i=getChannelFromHandle(handle); // None has it? if (i!=BUSY_HANDLE){ //System.err.printf("Updating sound with handle %d in channel %d\n",handle,i); channels[i].setVolume(vol); channels[i].setPitch(pitch); channels[i].setPanning(sep); } } /** Internal use. * * @param handle * @return the channel that has the handle, or -2 if none has it. */ private int getChannelFromHandle(int handle){ // Which channel has it? for (int i=0;i<numChannels;i++){ if (channelhandles[i]==handle) return i; } return BUSY_HANDLE; } /** A Thread for playing digital sound effects. * * Obviously you need as many as channels? * * In order not to end up in a hell of effects, * certain types of sounds must be limited to 1 per object. * */ private class SoundWorker implements Runnable { public Semaphore wait; // Holds the worker still until there's a new sound FloatControl vc; // linear volume control FloatControl bc; // balance/panning control FloatControl pc; // pitch control byte[] currentSoundSync; byte[] currentSound; public SoundWorker(int id){ this.id=id; this.handle=IDLE_HANDLE; wait=new Semaphore(1); } int id; /** Used to find out whether the same object is continuously making * sounds. E.g. the player, ceilings etc. In that case, they must be * interrupted. */ int handle; public boolean terminate; SourceDataLine auline; /** This is how you tell the thread to play a sound, * I suppose. */ public void addSound(byte[] ds, int handle) { if (D) System.out.printf("Added handle %d to channel %d\n",handle,id); this.handle=handle; this.currentSound=ds; this.auline.stop(); this.auline.start(); this.wait.release(); } /** Accepts volume in "Doom" format (0-127). * * @param volume */ public void setVolume(int volume){ if (vc!=null){ if (vc.getType()==FloatControl.Type.MASTER_GAIN) { float vol = linear2db[volume]; vc.setValue(vol); } else if (vc.getType()==FloatControl.Type.VOLUME){ float vol = vc.getMinimum()+(vc.getMaximum()-vc.getMinimum())*(float)volume/127f; vc.setValue(vol); } } } public void setPanning(int sep){ // Q: how does Doom's sep map to stereo panning? // A: Apparently it's 0-255 L-R. if (bc!=null){ float pan= bc.getMinimum()+(bc.getMaximum()-bc.getMinimum())*(float)(sep)/ISoundDriver.PANNING_STEPS; //System.err.printf("Panning %d %f %f %f\n",sep,bc.getMinimum(),bc.getMaximum(),pan); bc.setValue(pan); } } /** Expects a steptable value between 16K and 256K, with * 64K being the middle. * * @param pitch */ public void setPitch(int pitch){ if (pc!=null){ float pan= (float) (pc.getValue()*((float)pitch/65536.0)); pc.setValue(pan); } } public void run() { System.err.printf("Sound thread %d started\n",id); while (!terminate) { currentSoundSync = currentSound; if (currentSoundSync != null) { try { auline.write(currentSoundSync, 0, currentSoundSync.length); } catch (Exception e) { e.printStackTrace(); return; } finally { // The previous steps are actually VERY fast. // However this one waits until the data has been // consumed, Interruptions/signals won't reach here, // so it's pointless trying to interrupt the actual filling. //long a=System.nanoTime(); auline.drain(); //long b=System.nanoTime(); //System.out.printf("Channel %d completed in %f.\n",id,(float)(b-a)/1000000000f); } // Report that this channel is free. currentSound = null; // Remove its handle. //System.out.printf("Channel %d with handle %d done. Marking as free\n",id,handle); if (handle>0) channelhandles[this.id]=IDLE_HANDLE; this.handle=IDLE_HANDLE; } // If we don't sleep at least a bit here, busy waiting becomes // way too taxing. Waiting on a semaphore (triggered by adding a new sound) // seems like a better method. try { wait.acquire(); } catch (InterruptedException e) { } } } public void stopSound() { auline.stop(); auline.flush(); //System.out.printf("Channel %d with handle %d interrupted. Marking as free\n",id,handle); channelhandles[this.id]=IDLE_HANDLE; this.handle=IDLE_HANDLE; currentSound = null; auline.start(); } public boolean isPlaying() { //System.out.printf("Channel %d with handle %d queried\n",id,handle); return (this.handle!=IDLE_HANDLE||this.currentSound!=null); } } StringBuilder sb=new StringBuilder(); public String channelStatus(){ sb.setLength(0); for (int i=0;i<numChannels;i++){ if (channels[i].isPlaying()) sb.append(i); else sb.append('-'); } return sb.toString(); } } package s; import javax.sound.sampled.AudioFormat; import javax.sound.sampled.AudioFormat.Encoding; import data.sfxinfo_t; /** A class representing a sample in memory * Convenient for wrapping/mirroring it regardless of what it represents. * */ class DoomSound extends sfxinfo_t { /** This audio format is the one used by internal samples (16 bit, 11KHz, Stereo) * for Clips and AudioLines. Sure, it's not general enough... who cares though? */ public final static AudioFormat DEFAULT_SAMPLES_FORMAT=new AudioFormat(Encoding.PCM_SIGNED, ISoundDriver.SAMPLERATE, 16, 2, 4, ISoundDriver.SAMPLERATE, true); public final static AudioFormat DEFAULT_DOOM_FORMAT=new AudioFormat(Encoding.PCM_UNSIGNED, ISoundDriver.SAMPLERATE, 8, 1, 1, ISoundDriver.SAMPLERATE, true); public AudioFormat format; public DoomSound(AudioFormat format) { this.format=format; } public DoomSound(){ this.format=DEFAULT_DOOM_FORMAT; } public DoomSound(sfxinfo_t sfx,AudioFormat format){ this(format); this.data=sfx.data; this.pitch=sfx.pitch; this.link=sfx.link; this.lumpnum=sfx.lumpnum; this.name=sfx.name; this.priority=sfx.priority; this.singularity=sfx.singularity; this.usefulness=sfx.usefulness; this.volume=sfx.volume; } } package s; import data.sfxinfo_t; public class DummySFX implements ISoundDriver { @Override public boolean InitSound() { // Dummy is super-reliable ;-) return true; } @Override public void UpdateSound() { // TODO Auto-generated method stub } @Override public void SubmitSound() { // TODO Auto-generated method stub } @Override public void ShutdownSound() { // TODO Auto-generated method stub } @Override public int GetSfxLumpNum(sfxinfo_t sfxinfo) { // TODO Auto-generated method stub return 0; } @Override public int StartSound(int id, int vol, int sep, int pitch, int priority) { // TODO Auto-generated method stub return 0; } @Override public void StopSound(int handle) { // TODO Auto-generated method stub } @Override public boolean SoundIsPlaying(int handle) { // TODO Auto-generated method stub return false; } @Override public void UpdateSoundParams(int handle, int vol, int sep, int pitch) { // TODO Auto-generated method stub } @Override public void SetChannels(int numChannels) { // TODO Auto-generated method stub } } package s; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.nio.ByteBuffer; import java.nio.ByteOrder; import utils.C2JUtils; public class DoomToWave { static int MEMORYCACHE = 0x8000; static class RIFFHEAD { byte[] riff = new byte[4]; int length; byte[] wave = new byte[4]; public void pack(ByteBuffer b){ b.put(riff); b.putInt(length); b.put(wave); } public int size(){ return 12; } } RIFFHEAD headr = new RIFFHEAD(); static class CHUNK { byte[] name = new byte[4]; int size; public void pack(ByteBuffer b){ b.put(name); b.putInt(size); } public int size(){ return 8; } } CHUNK headc = new CHUNK(); static class WAVEFMT { byte[] fmt = new byte[4]; /* "fmt " */ int fmtsize; /*0x10*/ int tag; /*format tag. 1=PCM*/ int channel; /*1*/ int smplrate; int bytescnd; /*average bytes per second*/ int align; /*block alignment, in bytes*/ int nbits; /*specific to PCM format*/ public void pack(ByteBuffer b){ b.put(fmt); b.putInt(fmtsize); b.putChar((char) tag); b.putChar((char) channel); b.putInt(smplrate); b.putInt(bytescnd); b.putChar((char) align); b.putChar((char) nbits); } public int size(){ return 24; } } WAVEFMT headf = new WAVEFMT(); int SIZEOF_WAVEFMT = 24; static class WAVEDATA /*data*/ { byte[] data = new byte[4]; /* "data" */ int datasize; public void pack(ByteBuffer b){ b.put(data); b.putInt(datasize); } } WAVEDATA headw = new WAVEDATA(); int SIZEOF_WAVEDATA = 8; public void SNDsaveSound(InputStream is, OutputStream os) throws IOException { int type = DoomIO.freadint(is, 2);// peek_i16_le (buffer); int speed = DoomIO.freadint(is, 2);//peek_u16_le (buffer + 2); int datasize = DoomIO.freadint(is, 4);//peek_i32_le (buffer + 4); if (type!=3) System.out.println("Sound: weird type "+type+". Extracting anyway."); int headsize = 2 + 2 + 4; int size = is.available(); int phys_size = size /*- headsize*/; if (datasize > phys_size) { System.out.println("Sound %s: declared sample size %lu greater than lump size %lu ;"/*, lump_name (name), (unsigned long) datasize, (unsigned long) phys_size*/); System.out.println("Sound %s: truncating to lump size."/*, lump_name (name)*/); datasize = phys_size; } /* Sometimes the size of sound lump is greater than the declared sound size. */ else if (datasize < phys_size) { if (/*fullSND == TRUE*/true) /* Save entire lump */ datasize = phys_size; else { /*Warning ( "Sound %s: lump size %lu greater than declared sample size %lu ;", lump_name (name), (unsigned long) datasize, (unsigned long) phys_size); Warning ("Sound %s: truncating to declared sample size.", lump_name (name));*/ } } DoomIO.writeEndian = DoomIO.Endian.BIG; SNDsaveWave(is, os, speed, datasize); } public byte[] DMX2Wave(byte[] DMXSound) throws IOException { ByteBuffer is=ByteBuffer.wrap(DMXSound); is.order(ByteOrder.LITTLE_ENDIAN); int type = 0x0000FFFF&is.getShort();// peek_i16_le (buffer); int speed = 0x0000FFFF&is.getShort();//peek_u16_le (buffer + 2); int datasize = is.getInt();//peek_i32_le (buffer + 4); if (type!=3) System.out.println("Sound: weird type "+type+". Extracting anyway."); int headsize = 2 + 2 + 4; int size = is.remaining(); int phys_size = size /*- headsize*/; if (datasize > phys_size) { System.out.println("Sound %s: declared sample size %lu greater than lump size %lu ;"/*, lump_name (name), (unsigned long) datasize, (unsigned long) phys_size*/); System.out.println("Sound %s: truncating to lump size."/*, lump_name (name)*/); datasize = phys_size; } /* Sometimes the size of sound lump is greater than the declared sound size. */ else if (datasize < phys_size) { if (/*fullSND == TRUE*/true) /* Save entire lump */ datasize = phys_size; else { /*Warning ( "Sound %s: lump size %lu greater than declared sample size %lu ;", lump_name (name), (unsigned long) datasize, (unsigned long) phys_size); Warning ("Sound %s: truncating to declared sample size.", lump_name (name));*/ } } return SNDsaveWave(is, speed, datasize); } protected byte[] SNDsaveWave(ByteBuffer is, int speed, int size) throws IOException { // Size with header and data etc. byte[] output=new byte[headr.size()+headf.size() + SIZEOF_WAVEDATA+2*size]; ByteBuffer os=ByteBuffer.wrap(output); os.order(ByteOrder.LITTLE_ENDIAN); os.position(0); headr.riff = ("RIFF").getBytes(); int siz = 4 + SIZEOF_WAVEFMT + SIZEOF_WAVEDATA+2*size; headr.length = siz; headr.wave = C2JUtils.toByteArray("WAVE"); headr.pack(os); headf.fmt = C2JUtils.toByteArray("fmt "); headf.fmtsize = SIZEOF_WAVEFMT - 8; headf.tag = 1; headf.channel = 2; // Maes: HACK to force stereo lines. headf.smplrate = speed; headf.bytescnd = 2*speed; // Ditto. headf.align = 1; headf.nbits = 8; headf.pack(os); headw.data = C2JUtils.toByteArray("data"); headw.datasize = 2*size; //byte[] wtf=DoomIO.toByteArray(headw.datasize, 4); headw.pack(os); byte tmp; for (int i=0;i<size;i++) { tmp=is.get(); os.put(tmp); os.put(tmp); } return os.array(); } void SNDsaveWave(InputStream is, OutputStream os, int speed, int size) throws IOException { int wsize,sz=0; headr.riff = DoomIO.toByteArray("RIFF"); int siz = 4 + SIZEOF_WAVEFMT + SIZEOF_WAVEDATA+size; headr.length = siz; headr.wave = DoomIO.toByteArray("WAVE"); DoomIO.fwrite2(headr.riff, os); DoomIO.fwrite2(DoomIO.toByteArray(headr.length, 4), os); DoomIO.fwrite2(headr.wave, os); headf.fmt = DoomIO.toByteArray("fmt "); headf.fmtsize = SIZEOF_WAVEFMT - 8; headf.tag = 1; headf.channel = 1; // Maes: HACK to force stereo lines. headf.smplrate = speed; headf.bytescnd = speed; headf.align = 1; headf.nbits = 8; DoomIO.fwrite2(headf.fmt, os); DoomIO.fwrite2(DoomIO.toByteArray(headf.fmtsize, 4), os); DoomIO.fwrite2(DoomIO.toByteArray(headf.tag, 2), os); DoomIO.fwrite2(DoomIO.toByteArray(headf.channel, 2), os); DoomIO.fwrite2(DoomIO.toByteArray(headf.smplrate, 4), os); DoomIO.fwrite2(DoomIO.toByteArray(headf.bytescnd, 4), os); DoomIO.fwrite2(DoomIO.toByteArray(headf.align, 2), os); DoomIO.fwrite2(DoomIO.toByteArray(headf.nbits, 2), os); headw.data = DoomIO.toByteArray("data"); headw.datasize = size; DoomIO.fwrite2(headw.data, os); DoomIO.fwrite2(DoomIO.toByteArray(headw.datasize, 4), os); ByteArrayOutputStream shit=( ByteArrayOutputStream)os; byte[] crap=shit.toByteArray(); byte[] bytes = new byte[MEMORYCACHE]; for(wsize=0;wsize<size;wsize+=sz) { sz= (size-wsize>MEMORYCACHE)? MEMORYCACHE:(size-wsize); is.read(bytes, 0, sz); os.write(bytes, 0, sz); //if(fwrite((buffer+(wsize)),(size_t)sz,1,fp)!=1) // ProgError("%s: write error (%s)", fname (file), strerror (errno)); } } } package s; public class DSP { /** * QDSS Windowed Sinc ReSampling subroutine in Basic * * @param x * new sample point location (relative to old indexes) (e.g. every * other integer for 0.5x decimation) * @param indat * = original data array * @param alim * = size of data array * @param fmax * = low pass filter cutoff frequency * @param fsr * = sample rate * @param wnwdth * = width of windowed Sinc used as the low pass filter rem resamp() * returns a filtered new sample point */ public float resamp(float x, float[] indat, int alim, float fmax, float fsr, int wnwdth) { int i, j; float r_w, r_g, r_a; int r_snc, r_y; // some local variables r_g = 2 * fmax / fsr; // Calc gain correction factor r_y = 0; for (i = -wnwdth / 2; i < wnwdth / 2; i++) { // For 1 window width j = (int) (x + i); // Calc input sample index // calculate von Hann Window. Scale and calculate Sinc r_w = (float) (0.5 - 0.5 * Math.cos(2 * Math.PI * (0.5 + (j - x) / wnwdth))); r_a = (float) (2 * Math.PI * (j - x) * fmax / fsr); r_snc = 1; if (Math.abs(r_a) > 0) r_snc = (int) (Math.sin(r_a) / r_a); if ((j >= 0) && (j < alim)) { r_y = (int) (r_y + r_g * r_w * r_snc * indat[j]); } } return r_y; // return new filtered sample } /* * Ron Nicholson's QDSS ReSampler cookbook recipe QDSS = Quick, Dirty, * Simple and Short Version 0.1b - 2007-Aug-01 Copyright 2007 Ronald H. * Nicholson Jr. No warranties implied. Error checking, optimization, and * quality assessment of the "results" is left as an exercise for the * student. (consider this code Open Source under a BSD style license) IMHO. * YMMV. http://www.nicholson.com/rhn/dsp.html */ /** * R. Nicholson's QDDS FIR filter generator cookbook recipe QDDS = Quick, * Dirty, Dumb and Short version 0.6b - 2006-Dec-14, 2007-Sep-30 No * warranties implied. Error checking, optimization, and quality assessment * of the "results" is left as an exercise for the student. (consider this * code Open Source under a BSD style license) Some example filter * parameters: * * @param fsr * = 44100 : rem set fsr = sample rate * @param fc * = 0 : rem set fc = 0 for lowpass fc = center frequency for * bandpass filter fc = fsr/2 for a highpass * @param bw * = 3000 : rem bw = bandwidth, range 0 .. fsr/2 and bw >= fsr/n bw = * 3 db corner frequency for a lowpass bw = half the 3 db passband * for a bandpass filter * @param nt * = 128 : rem nt = number of taps + 1 (nt must be even) nt should be * > fsr / bw transition band will be around 3.5 * fsr / nt depending * on the window applied and ripple spec. * @param g * = 1 : rem g = filter gain for bandpass g = 0.5 , half the gain for * a lowpass filter * @return array of FIR taps */ public static double[] wsfiltgen(int nt, double fc, double fsr, double bw, double g) { double[] fir = new double[nt];// // fir(0) = 0 // fir(1) is the first tap // fir(nt/2) is the middle tap // fir(nt-1) is the last tap double a, ys, yg, yf, yw; for (int i = 1; i < nt; i++) { a = (i - nt / 2) * 2.0 * Math.PI * bw / fsr; // scale Sinc width ys = 1; if (Math.abs(a) > 0) ys = Math.sin(a) / a; // calculate Sinc function yg = g * (4.0 * bw / fsr); // correct window gain yw = 0.54 - 0.46 * Math.cos(i * 2.0 * Math.PI / nt); // Hamming // window yf = Math.cos((i - nt / 2) * 2.0 * Math.PI * fc / fsr); // spectral // shift to // fc fir[i] = yf * yw * yg * ys; // rem assign fir coeff. } return fir; } public static void main(String[] argv){ double[] fir=wsfiltgen(128,11025/2.0,22050,22050*3.0/4,0.5); System.out.println(fir); } public static byte[] crudeResample(byte[] input,int factor){ if (input==null || input.length<1) return null; final int LEN=input.length; byte[] res=new byte[LEN*factor]; int k=0; float start,end; res[0]=input[0]; for (int i=0;i<LEN;i++){ if (i==0) start=127; else start=0xFF&input[i]; if (i<LEN-1) end=0xFF&input[i+1]; else end=127; double slope=(end-start)/factor; res[k]=input[i]; //res[k+factor]=input[i+1]; for (int j=1;j<factor;j++){ double ratio=j/(double)factor; double value=start+slope*ratio; byte bval=(byte)Math.round(value); res[k+j]=bval; } k+=factor; } return res; } public static void filter(byte[] input,int samplerate, int cutoff){ double[] tmp=new double[input.length]; // Normalize for (int i=0;i<input.length;i++){ tmp[i]=(0xFF&input[i])/255.0; } filter(tmp,samplerate,cutoff,tmp.length); // De-normalize for (int i=0;i<input.length;i++){ input[i]=(byte) (0xFF&(int)(tmp[i]*255.0)); } } /** Taken from here * http://baumdevblog.blogspot.gr/2010/11/butterworth-lowpass-filter-coefficients.html */ private static void getLPCoefficientsButterworth2Pole(final int samplerate, final double cutoff, final double[] ax, final double[] by) { double PI = 3.1415926535897932385; double sqrt2 = 1.4142135623730950488; double QcRaw = (2 * PI * cutoff) / samplerate; // Find cutoff frequency in [0..PI] double QcWarp = Math.tan(QcRaw); // Warp cutoff frequency double gain = 1 / (1+sqrt2/QcWarp + 2/(QcWarp*QcWarp)); by[2] = (1 - sqrt2/QcWarp + 2/(QcWarp*QcWarp)) * gain; by[1] = (2 - 2 * 2/(QcWarp*QcWarp)) * gain; by[0] = 1; ax[0] = 1 * gain; ax[1] = 2 * gain; ax[2] = 1 * gain; } public static void filter(double[] samples, int smp, double cutoff,int count) { // Essentially a 3-tap filter? double[] ax=new double[3]; double[] by=new double[3]; double[] xv=new double[3]; double[] yv=new double[3]; getLPCoefficientsButterworth2Pole(smp, cutoff, ax, by); for (int i=0;i<count;i++) { xv[2] = xv[1]; xv[1] = xv[0]; xv[0] = samples[i]; yv[2] = yv[1]; yv[1] = yv[0]; yv[0] = (ax[0] * xv[0] + ax[1] * xv[1] + ax[2] * xv[2] - by[1] * yv[0] - by[2] * yv[1]); samples[i] = yv[0]; } } } package s; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import w.CacheableDoomObject; /** An object representation of Doom's sound format */ public class DMXSound implements CacheableDoomObject{ /** ushort, all Doom samples are "type 3". No idea how */ public int type; /** ushort, speed in Hz. */ public int speed; /** uint */ public int datasize; public byte[] data; @Override public void unpack(ByteBuffer buf) throws IOException { buf.order(ByteOrder.LITTLE_ENDIAN); type=buf.getChar(); speed=buf.getChar(); datasize=buf.getInt(); data=new byte[Math.min(buf.remaining(),datasize)]; buf.get(data); } } package s; import static data.sounds.S_sfx; import java.util.Collection; import java.util.HashMap; import javax.sound.sampled.AudioSystem; import javax.sound.sampled.Clip; import javax.sound.sampled.DataLine; import javax.sound.sampled.FloatControl; import javax.sound.sampled.LineUnavailableException; import javax.sound.sampled.FloatControl.Type; import w.DoomBuffer; import data.sounds.sfxenum_t; import doom.DoomStatus; /** Experimental Clip based driver. It does work, but it has no * tangible advantages over the Audioline or Classic one. If the * Audioline can be used, there's no reason to fall back to this * one. * * KNOWN ISSUES: * * a) Same general restrictions as audiolines (in fact, Clips ARE Audioline * in disguise) * b) Multiple instances of the same sound require multiple clips, so * even caching them is a half-baked solution, and if you have e.g. 40 imps * sound in a room.... * * * Currently unused. * * @author Velktron * */ public class ClipSFXModule extends AbstractSoundDriver{ HashMap<Integer,Clip> cachedSounds = new HashMap<Integer,Clip>(); // Either it's null (no clip is playing) or non-null (some clip is playing). Clip[] channels; public final float[] linear2db; public ClipSFXModule(DoomStatus DS, int numChannels) { super(DS,numChannels); linear2db=computeLinear2DB(); } private float[] computeLinear2DB() { // Maximum volume is 0 db, minimum is ... -96 db. // We rig this so that half-scale actually gives quarter power, // and so is -6 dB. float[] tmp=new float[VOLUME_STEPS]; for (int i=0;i<VOLUME_STEPS;i++){ float linear=(float)(20*Math.log10((float)i/(float)VOLUME_STEPS)); // Hack. The minimum allowed value as of now is -80 db. if (linear<-36.0) linear=-36.0f; tmp[i]= linear; } return tmp; } @Override public boolean InitSound() { // Secure and configure sound device first. System.err.println("I_InitSound: "); // We don't actually do this here (will happen only when we // create the first audio clip). // Initialize external data (all sounds) at start, keep static. initSound16(); System.err.print(" pre-cached all sound data\n"); // Finished initialization. System.err.print("I_InitSound: sound module ready\n"); return true; } /** Modified getsfx. The individual length of each sfx is not of interest. * However, they must be transformed into 16-bit, signed, stereo samples * beforehand, before being "fed" to the audio clips. * * @param sfxname * @param index * @return */ protected byte[] getsfx(String sfxname,int index) { byte[] sfx; byte[] paddedsfx; int i; int size; int paddedsize; String name; int sfxlump; // Get the sound data from the WAD, allocate lump // in zone memory. name = String.format("ds%s", sfxname).toUpperCase(); // Now, there is a severe problem with the // sound handling, in it is not (yet/anymore) // gamemode aware. That means, sounds from // DOOM II will be requested even with DOOM // shareware. // The sound list is wired into sounds.c, // which sets the external variable. // I do not do runtime patches to that // variable. Instead, we will use a // default sound for replacement. if (DS.W.CheckNumForName(name) == -1) sfxlump = DS.W.GetNumForName("dspistol"); else sfxlump = DS.W.GetNumForName(name); size = DS.W.LumpLength(sfxlump); sfx = DS.W.CacheLumpNumAsRawBytes(sfxlump, 0); // Size blown up to accommodate two channels and 16 bits. // Sampling rate stays the same. paddedsize = (size-8)*2*2; // Allocate from zone memory. paddedsfx = new byte[paddedsize]; // Skip first 8 bytes (header), blow up the data // to stereo, BIG ENDIAN, SIGNED, 16 bit. Don't expect any fancy DSP here! int sample=0; for (i = 8; i < size; i++){ // final short sam=(short) vol_lookup[127][0xFF&sfx[i]]; final short sam=(short) ((0xFF&sfx[i]-128)<<8); paddedsfx[sample++] = (byte) (0xFF&(sam>>8)); paddedsfx[sample++]=(byte) (0xFF&sam); paddedsfx[sample++]=(byte) (0xFF&(sam>>8)); paddedsfx[sample++]=(byte) (0xFF&sam); } // Remove the cached lump. DS.W.UnlockLumpNum(sfxlump); // Return allocated padded data. // So the first 8 bytes are useless? return paddedsfx; } @Override public void UpdateSound() { // We do nothing here, since the mixing is delegated to the OS // Just hope that it's more efficient that our own... } @Override public void SubmitSound() { // Dummy. Nothing actual to do here. } @Override public void ShutdownSound() { // Wait till all pending sounds are finished. boolean done = false; int i; // FIXME (below). //fprintf( stderr, "I_ShutdownSound: NOT finishing pending sounds\n"); //fflush( stderr ); while ( !done) { for( i=0 ; i<numChannels && ((channels[i]==null)||(!channels[i].isActive())) ; i++); // FIXME. No proper channel output. if (i==numChannels) done=true; } for( i=0 ; i<numChannels; i++){ if (channels[i]!=null) channels[i].close(); } // Free up resources taken up by cached clips. Collection<Clip> clips=this.cachedSounds.values(); for (Clip c:clips){ c.close(); } // Done. return; } @Override public void SetChannels(int numChannels) { channels= new Clip[numChannels]; } private final void getClipForChannel(int c, int sfxid){ // Try to see if we already have such a clip. Clip clip=this.cachedSounds.get(sfxid); boolean exists=false; // Does it exist? if (clip!=null){ // Well, it does, but we are not done yet. exists=true; // Is it NOT playing already? if (!clip.isActive()){ // Assign it to the channel. channels[c]=clip; return; } } // Sorry, Charlie. Gotta make a new one. DataLine.Info info = new DataLine.Info(Clip.class, DoomSound.DEFAULT_SAMPLES_FORMAT); try { clip = (Clip) AudioSystem.getLine(info); } catch (LineUnavailableException e) { // TODO Auto-generated catch block e.printStackTrace(); } try { clip.open( DoomSound.DEFAULT_SAMPLES_FORMAT, S_sfx[sfxid].data, 0, S_sfx[sfxid].data.length); } catch (LineUnavailableException e) { // TODO Auto-generated catch block e.printStackTrace(); } if (!exists) this.cachedSounds.put(sfxid,clip); channels[c]=clip; // Control[] cs=clip.getControls(); // // for (Control cc:cs){ // System.out.println("Control "+cc.getType().toString()); // } } // // This function adds a sound to the // list of currently active sounds, // which is maintained as a given number // (eight, usually) of internal channels. // Returns a handle. // protected short handlenums = 0; protected int addsfx ( int sfxid,int volume,int pitch,int seperation) { int i; int rc = -1; int oldest = DS.gametic; int oldestnum = 0; int slot; // Chainsaw troubles. // Play these sound effects only one at a time. if ( sfxid == sfxenum_t.sfx_sawup.ordinal() || sfxid == sfxenum_t.sfx_sawidl.ordinal() || sfxid == sfxenum_t.sfx_sawful.ordinal() || sfxid == sfxenum_t.sfx_sawhit.ordinal() || sfxid == sfxenum_t.sfx_stnmov.ordinal() || sfxid == sfxenum_t.sfx_pistol.ordinal() ) { // Loop all channels, check. for (i=0 ; i<numChannels ; i++) { // Active, and using the same SFX? if (channels[i]!=null && channels[i].isRunning() && channelids[i] == sfxid) { // Reset. channels[i].stop(); // We are sure that iff, // there will only be one. break; } } } // Loop all channels to find oldest SFX. for (i=0; (i<numChannels) && (channels[i]!=null); i++) { if (channelstart[i] < oldest) { oldestnum = i; oldest = channelstart[i]; } } // Tales from the cryptic. // If we found a channel, fine. // If not, we simply overwrite the first one, 0. // Probably only happens at startup. if (i == numChannels) slot = oldestnum; else slot = i; // Okay, in the less recent channel, // we will handle the new SFX. // We need to decide whether we can reuse an existing clip // or create a new one. In any case, when this method return // we should have a valid clip assigned to channel "slot". getClipForChannel(slot,sfxid); // Reset current handle number, limited to 0..100. if (handlenums==0) // was !handlenums, so it's actually 1...100? handlenums = MAXHANDLES; // Assign current handle number. // Preserved so sounds could be stopped (unused). channelhandles[slot]= rc = handlenums--; // Should be gametic, I presume. channelstart[slot] = DS.gametic; // Get the proper lookup table piece // for this volume level??? //channelleftvol_lookup[slot] = vol_lookup[leftvol]; //channelrightvol_lookup[slot] = vol_lookup[rightvol]; // Preserve sound SFX id, // e.g. for avoiding duplicates of chainsaw. channelids[slot] = sfxid; setVolume(slot,volume); setPanning(slot,seperation); //channels[slot].addSound(sound, handlenums); //channels[slot].setPitch(pitch); if(D) System.err.println(channelStatus()); if(D) System.err.printf("Playing %d vol %d on channel %d\n",rc,volume,slot); // Well...play it. // FIXME VERY BIG PROBLEM: stop() is blocking!!!! WTF ?! //channels[slot].stop(); //long a=System.nanoTime(); channels[slot].setFramePosition(0); channels[slot].start(); // b=System.nanoTime(); //System.err.printf("Sound playback completed in %d\n",(b-a)); // You tell me. return rc; } /** Accepts volume in "Doom" format (0-127). * * @param volume */ public void setVolume(int chan,int volume){ Clip c=channels[chan]; if (c.isControlSupported(Type.MASTER_GAIN)){ FloatControl vc=(FloatControl) c.getControl(Type.MASTER_GAIN); float vol = linear2db[volume]; vc.setValue(vol); } else if (c.isControlSupported(Type.VOLUME)){ FloatControl vc=(FloatControl) c.getControl(Type.VOLUME); float vol = vc.getMinimum()+(vc.getMaximum()-vc.getMinimum())*(float)volume/127f; vc.setValue(vol); } } public void setPanning(int chan,int sep){ Clip c=channels[chan]; if (c.isControlSupported(Type.PAN)){ FloatControl bc=(FloatControl) c.getControl(Type.PAN); // Q: how does Doom's sep map to stereo panning? // A: Apparently it's 0-255 L-R. float pan= bc.getMinimum()+(bc.getMaximum()-bc.getMinimum())*(float)sep/ISoundDriver.PANNING_STEPS; bc.setValue(pan); } } @Override public void StopSound(int handle) { // Which channel has it? int hnd=getChannelFromHandle(handle); if (hnd>=0) { channels[hnd].stop(); channels[hnd]=null; } } @Override public boolean SoundIsPlaying(int handle) { return getChannelFromHandle(handle)!=BUSY_HANDLE; } @Override public void UpdateSoundParams(int handle, int vol, int sep, int pitch) { // This should be called on sounds that are ALREADY playing. We really need // to retrieve channels from their handles. //System.err.printf("Updating sound with handle %d vol %d sep %d pitch %d\n",handle,vol,sep,pitch); int i=getChannelFromHandle(handle); // None has it? if (i!=BUSY_HANDLE){ //System.err.printf("Updating sound with handle %d in channel %d\n",handle,i); setVolume(i,vol); setPanning(i,sep); //channels[i].setPanning(sep); } } /** Internal use. * * @param handle * @return the channel that has the handle, or -2 if none has it. */ private int getChannelFromHandle(int handle){ // Which channel has it? for (int i=0;i<numChannels;i++){ if (channelhandles[i]==handle) return i; } return BUSY_HANDLE; } StringBuilder sb=new StringBuilder(); public String channelStatus(){ sb.setLength(0); for (int i=0;i<numChannels;i++){ if (channels[i]!=null && channels[i].isActive()) sb.append(i); else sb.append('-'); } return sb.toString(); } } package s; import static data.Tables.ANGLETOFINESHIFT; import static data.Tables.BITS32; import static data.Tables.finesine; import static data.sounds.S_sfx; import static m.fixed_t.FRACBITS; import static m.fixed_t.FixedMul; import p.mobj_t; import data.Defines; import data.musicinfo_t; import data.sfxinfo_t; import data.sounds; import data.sounds.musicenum_t; import data.sounds.sfxenum_t; import doom.DoomStatus; /** Some stuff that is not implementation dependant * This includes channel management, sound priorities, * positioning, distance attenuation etc. It's up to * lower-level "drivers" to actually implements those. * This particular class needs not be a dummy itself, but * the drivers it "talks" to might be. * * * */ public class AbstractDoomAudio implements IDoomSound{ protected final DoomStatus<?,?> DS; protected final IMusic IMUS; protected final ISoundDriver ISND; protected final int numChannels; protected final static boolean D=false; /** the set of channels available. These are "soft" descriptor channels, not to be confused with actual hardware audio lines, which are an entirely different concern. */ protected final channel_t[] channels; // These are not used, but should be (menu). // Maximum volume of a sound effect. // Internal default is max out of 0-15. protected int snd_SfxVolume = 15; // Maximum volume of music. Useless so far. protected int snd_MusicVolume = 15; // whether songs are mus_paused protected boolean mus_paused; // music currently being played protected musicinfo_t mus_playing; protected int nextcleanup; public AbstractDoomAudio(DoomStatus<?,?> DS, int numChannels){ this.DS = DS; this.numChannels=numChannels; this.channels=new channel_t[numChannels]; this.IMUS=DS.IMUS; this.ISND=DS.ISND; } /** Volume, pitch, separation & priority packed for parameter passing */ protected class vps_t{ int volume; int pitch; int sep; int priority; } /** * Initializes sound stuff, including volume * Sets channels, SFX and music volume, * allocates channel buffer, sets S_sfx lookup. */ public void Init ( int sfxVolume, int musicVolume ) { int i; System.err.printf("S_Init: default sfx volume %d\n", sfxVolume); this.snd_SfxVolume=sfxVolume; this.snd_MusicVolume=musicVolume; // Whatever these did with DMX, these are rather dummies now. // MAES: any implementation-dependant channel setup should start here. ISND.SetChannels(numChannels); SetSfxVolume(sfxVolume); // No music with Linux - another dummy. // MAES: these must be initialized somewhere, perhaps not here? IMUS.SetMusicVolume(musicVolume); // Allocating the internal channels for mixing // (the maximum numer of sounds rendered // simultaneously) within zone memory. // MAES: already done that in the constructor. // Free all channels for use for (i=0 ; i<numChannels ; i++){ channels[i]=new channel_t(); //channels[i].sfxinfo = null; } // no sounds are playing, and they are not mus_paused mus_paused = false; // Note that sounds have not been cached (yet). for (i=1 ; i<S_sfx.length ; i++) S_sfx[i].lumpnum = S_sfx[i].usefulness = -1; } // // Per level startup code. // Kills playing sounds at start of level, // determines music if any, changes music. // public void Start() { int cnum; int mnum; // kill all playing sounds at start of level // (trust me - a good idea) for (cnum=0 ; cnum<numChannels ; cnum++) if (channels[cnum].sfxinfo!=null) StopChannel(cnum); // start new music for the level mus_paused = false; if (DS.isCommercial()) mnum = musicenum_t.mus_runnin.ordinal() + DS.gamemap - 1; else { musicenum_t[] spmus= { // Song - Who? - Where? musicenum_t.mus_e3m4, // American e4m1 musicenum_t.mus_e3m2, // Romero e4m2 musicenum_t.mus_e3m3, // Shawn e4m3 musicenum_t.mus_e1m5, // American e4m4 musicenum_t.mus_e2m7, // Tim e4m5 musicenum_t.mus_e2m4, // Romero e4m6 musicenum_t.mus_e2m6, // J.Anderson e4m7 CHIRON.WAD musicenum_t.mus_e2m5, // Shawn e4m8 musicenum_t.mus_e1m9 // Tim e4m9 }; if (DS.gameepisode < 4) mnum = musicenum_t.mus_e1m1.ordinal() + (DS.gameepisode-1)*9 + DS.gamemap-1; else mnum = spmus[DS.gamemap-1].ordinal(); } // HACK FOR COMMERCIAL // if (commercial && mnum > mus_e3m9) // mnum -= mus_e3m9; ChangeMusic(mnum, true); nextcleanup = 15; } private vps_t vps=new vps_t(); public void StartSoundAtVolume ( ISoundOrigin origin_p, int sfx_id, int volume ) { boolean rc; int sep = 0; // This is set later. int pitch; int priority; sfxinfo_t sfx; int cnum; ISoundOrigin origin = (ISoundOrigin) origin_p; // Debug. //if (origin!=null && origin.type!=null) // System.err.printf( // "S_StartSoundAtVolume: playing sound %d (%s) from %s %d\n", // sfx_id, S_sfx[sfx_id].name , origin.type.toString(),origin.hashCode()); // check for bogus sound # if (sfx_id < 1 || sfx_id > NUMSFX){ Exception e=new Exception(); e.printStackTrace(); DS.I.Error("Bad sfx #: %d", sfx_id); } sfx = S_sfx[sfx_id]; // Initialize sound parameters if (sfx.link!=null) { pitch = sfx.pitch; priority = sfx.priority; volume += sfx.volume; if (volume < 1) return; if (volume > snd_SfxVolume) volume = snd_SfxVolume; } else { pitch = NORM_PITCH; priority = NORM_PRIORITY; } // Check to see if it is audible, // and if not, modify the params if ((origin!=null) && origin != DS.players[DS.consoleplayer].mo) { vps.volume=volume; vps.pitch=pitch; vps.sep=sep; rc = AdjustSoundParams(DS.players[DS.consoleplayer].mo, origin, vps); volume=vps.volume; pitch=vps.pitch; sep=vps.sep; if ( origin.getX() == DS.players[DS.consoleplayer].mo.x && origin.getY() == DS.players[DS.consoleplayer].mo.y) { sep = NORM_SEP; } if (!rc) { //System.err.printf("S_StartSoundAtVolume: Sound %d (%s) rejected because: inaudible\n", // sfx_id, S_sfx[sfx_id].name ); return; } } else { sep = NORM_SEP; } // hacks to vary the sfx pitches if (sfx_id >= sfxenum_t.sfx_sawup.ordinal() && sfx_id <= sfxenum_t.sfx_sawhit.ordinal()) { pitch += 8 - (DS.RND.M_Random()&15); if (pitch<0) pitch = 0; else if (pitch>255) pitch = 255; } else if (sfx_id != sfxenum_t.sfx_itemup.ordinal() && sfx_id != sfxenum_t.sfx_tink.ordinal()) { pitch += 16 - (DS.RND.M_Random()&31); if (pitch<0) pitch = 0; else if (pitch>255) pitch = 255; } // kill old sound StopSound(origin); // try to find a channel cnum = getChannel(origin, sfx); if (cnum<0) return; // // This is supposed to handle the loading/caching. // For some odd reason, the caching is done nearly // each time the sound is needed? // // get lumpnum if necessary if (sfx.lumpnum < 0) // Now, it crosses into specific territory. sfx.lumpnum = ISND.GetSfxLumpNum(sfx); /* #ifndef SNDSRV // cache data if necessary if (!sfx->data) { fprintf( stderr, "S_StartSoundAtVolume: 16bit and not pre-cached - wtf?\n"); // DOS remains, 8bit handling //sfx->data = (void *) W_CacheLumpNum(sfx->lumpnum, PU_MUSIC); // fprintf( stderr, // "S_StartSoundAtVolume: loading %d (lump %d) : 0x%x\n", // sfx_id, sfx->lumpnum, (int)sfx->data ); } #endif */ // increase the usefulness if (sfx.usefulness++ < 0) sfx.usefulness = 1; // Assigns the handle to one of the channels in the // mix/output buffer. This is when things actually // become hard (pun intended). // TODO: which channel? How do we know how the actual hardware // ones map with the "soft" ones? // Essentially we're begging to get an actual channel. channels[cnum].handle = ISND.StartSound(sfx_id, /*sfx->data,*/ volume, sep, pitch, priority); if (D) System.err.printf("Handle %d for channel %d for sound %s vol %d sep %d\n",channels[cnum].handle, cnum,sfx.name,volume,sep); } public void StartSound ( ISoundOrigin origin, sfxenum_t sfx_id ){ // MAES: necessary sanity check at this point. if (sfx_id!=null && sfx_id.ordinal()>0) StartSound(origin,sfx_id.ordinal()); } public void StartSound ( ISoundOrigin origin, int sfx_id ) { /* #ifdef SAWDEBUG // if (sfx_id == sfx_sawful) // sfx_id = sfx_itemup; #endif */ StartSoundAtVolume(origin, sfx_id, snd_SfxVolume); // UNUSED. We had problems, had we not? /* #ifdef SAWDEBUG { int i; int n; static mobj_t* last_saw_origins[10] = {1,1,1,1,1,1,1,1,1,1}; static int first_saw=0; static int next_saw=0; if (sfx_id == sfx_sawidl || sfx_id == sfx_sawful || sfx_id == sfx_sawhit) { for (i=first_saw;i!=next_saw;i=(i+1)%10) if (last_saw_origins[i] != origin) fprintf(stderr, "old origin 0x%lx != " "origin 0x%lx for sfx %d\n", last_saw_origins[i], origin, sfx_id); last_saw_origins[next_saw] = origin; next_saw = (next_saw + 1) % 10; if (next_saw == first_saw) first_saw = (first_saw + 1) % 10; for (n=i=0; i<numChannels ; i++) { if (channels[i].sfxinfo == &S_sfx[sfx_sawidl] || channels[i].sfxinfo == &S_sfx[sfx_sawful] || channels[i].sfxinfo == &S_sfx[sfx_sawhit]) n++; } if (n>1) { for (i=0; i<numChannels ; i++) { if (channels[i].sfxinfo == &S_sfx[sfx_sawidl] || channels[i].sfxinfo == &S_sfx[sfx_sawful] || channels[i].sfxinfo == &S_sfx[sfx_sawhit]) { fprintf(stderr, "chn: sfxinfo=0x%lx, origin=0x%lx, " "handle=%d\n", channels[i].sfxinfo, channels[i].origin, channels[i].handle); } } fprintf(stderr, "\n"); } } } #endif*/ } // This one is public. public void StopSound(ISoundOrigin origin) { int cnum; for (cnum=0 ; cnum<numChannels ; cnum++) { if (channels[cnum].sfxinfo!=null && channels[cnum].origin == origin) { // This one is not. StopChannel(cnum); break; } } } // // Stop and resume music, during game PAUSE. // public void PauseSound() { if (mus_playing!=null && !mus_paused) { IMUS.PauseSong(mus_playing.handle); mus_paused = true; } } public void ResumeSound() { if (mus_playing!=null && mus_paused) { IMUS.ResumeSong(mus_playing.handle); mus_paused = false; } } @Override public void UpdateSounds(mobj_t listener) { boolean audible; int cnum; //int volume; //int sep; //int pitch; sfxinfo_t sfx; channel_t c; // Clean up unused data. // This is currently not done for 16bit (sounds cached static). // DOS 8bit remains. /*if (gametic.nextcleanup) { for (i=1 ; i<NUMSFX ; i++) { if (S_sfx[i].usefulness < 1 && S_sfx[i].usefulness > -1) { if (--S_sfx[i].usefulness == -1) { Z_ChangeTag(S_sfx[i].data, PU_CACHE); S_sfx[i].data = 0; } } } nextcleanup = gametic + 15; }*/ for (cnum=0 ; cnum<numChannels ; cnum++) { c = channels[cnum]; sfx = c.sfxinfo; //System.out.printf("Updating channel %d %s\n",cnum,c); if (c.sfxinfo!=null) { if (ISND.SoundIsPlaying(c.handle)) { // initialize parameters vps.volume = snd_SfxVolume; vps.pitch = NORM_PITCH; vps.sep = NORM_SEP; sfx=c.sfxinfo; if (sfx.link!=null) { vps.pitch = sfx.pitch; vps.volume += sfx.volume; if (vps.volume < 1) { StopChannel(cnum); continue; } else if (vps.volume > snd_SfxVolume) { vps.volume = snd_SfxVolume; } } // check non-local sounds for distance clipping // or modify their params if (c.origin!=null && (listener != c.origin)) { audible = AdjustSoundParams(listener, c.origin, vps); if (!audible) { StopChannel(cnum); } else ISND.UpdateSoundParams(c.handle, vps.volume, vps.sep, vps.pitch); } } else { // if channel is allocated but sound has stopped, // free it StopChannel(cnum); } } } // kill music if it is a single-play && finished // if ( mus_playing // && !I_QrySongPlaying(mus_playing->handle) // && !mus_paused ) // S_StopMusic(); } public void SetMusicVolume(int volume) { if (volume < 0 || volume > 127) { DS.I.Error("Attempt to set music volume at %d", volume); } IMUS.SetMusicVolume(volume); snd_MusicVolume = volume; } public void SetSfxVolume(int volume) { if (volume < 0 || volume > 127) DS.I.Error("Attempt to set sfx volume at %d", volume); snd_SfxVolume = volume; } // // Starts some music with the music id found in sounds.h. // public void StartMusic(int m_id) { ChangeMusic(m_id, false); } // // Starts some music with the music id found in sounds.h. // public void StartMusic(musicenum_t m_id) { ChangeMusic(m_id.ordinal(), false); } public void ChangeMusic(musicenum_t musicnum, boolean looping ) { ChangeMusic(musicnum.ordinal(), false); } public void ChangeMusic ( int musicnum, boolean looping ) { musicinfo_t music = null; String namebuf; if ( (musicnum <= musicenum_t.mus_None.ordinal()) || (musicnum >= musicenum_t.NUMMUSIC.ordinal()) ) { DS.I.Error("Bad music number %d", musicnum); } else music = sounds.S_music[musicnum]; if (mus_playing == music) return; // shutdown old music StopMusic(); // get lumpnum if neccessary if (music.lumpnum==0) { namebuf=String.format("d_%s", music.name); music.lumpnum = DS.W.GetNumForName(namebuf); } // load & register it music.data = DS.W.CacheLumpNumAsRawBytes(music.lumpnum, Defines.PU_MUSIC); music.handle = IMUS.RegisterSong(music.data); // play it IMUS.PlaySong(music.handle, looping); SetMusicVolume(this.snd_MusicVolume); mus_playing = music; } public void StopMusic() { if (mus_playing!=null) { if (mus_paused) IMUS.ResumeSong(mus_playing.handle); IMUS.StopSong(mus_playing.handle); IMUS.UnRegisterSong(mus_playing.handle); //Z_ChangeTag(mus_playing->data, PU_CACHE); mus_playing.data = null; mus_playing = null; } } /** This is S_StopChannel. There's another StopChannel * with a similar contract in ISound. Don't confuse the two. * * * * @param cnum */ protected void StopChannel(int cnum) { int i; channel_t c = channels[cnum]; // Is it playing? if (c.sfxinfo!=null) { // stop the sound playing if (ISND.SoundIsPlaying(c.handle)) { /*#ifdef SAWDEBUG if (c.sfxinfo == &S_sfx[sfx_sawful]) fprintf(stderr, "stopped\n"); #endif*/ ISND.StopSound(c.handle); } // check to see // if other channels are playing the sound for (i=0 ; i<numChannels ; i++) { if (cnum != i && c.sfxinfo == channels[i].sfxinfo) { break; } } // degrade usefulness of sound data c.sfxinfo.usefulness--; c.sfxinfo = null; } } // // Changes volume, stereo-separation, and pitch variables // from the norm of a sound effect to be played. // If the sound is not audible, returns a 0. // Otherwise, modifies parameters and returns 1. // protected boolean AdjustSoundParams ( mobj_t listener, ISoundOrigin source, vps_t vps) { int approx_dist; int adx; int ady; long angle; // calculate the distance to sound origin // and clip it if necessary adx = Math.abs(listener.x - source.getX()); ady = Math.abs(listener.y - source.getY()); // From _GG1_ p.428. Appox. eucledian distance fast. approx_dist = adx + ady - ((adx < ady ? adx : ady)>>1); if (DS.gamemap != 8 && approx_dist > S_CLIPPING_DIST) { return false; } // angle of source to listener angle = rr.RendererState.PointToAngle(listener.x, listener.y, source.getX(), source.getY()); if (angle > listener.angle) angle = angle - listener.angle; else angle = angle + (0xffffffffL - listener.angle&BITS32); angle&=BITS32; angle >>= ANGLETOFINESHIFT; // stereo separation vps.sep = 128 - (FixedMul(S_STEREO_SWING,finesine[(int) angle])>>FRACBITS); // volume calculation if (approx_dist < S_CLOSE_DIST) { vps.volume = snd_SfxVolume; } else if (DS.gamemap == 8) { if (approx_dist > S_CLIPPING_DIST) approx_dist = S_CLIPPING_DIST; vps.volume = 15+ ((snd_SfxVolume-15) *((S_CLIPPING_DIST - approx_dist)>>FRACBITS)) / S_ATTENUATOR; } else { // distance effect vps.volume = (snd_SfxVolume * ((S_CLIPPING_DIST - approx_dist)>>FRACBITS)) / S_ATTENUATOR; // Let's do some maths here: S_CLIPPING_DIST-approx_dist // can be at most 0x04100000. shifting left means 0x0410, // or 1040 in decimal. // The unmultiplied max volume is 15, attenuator is 1040. // So snd_SfxVolume should be 0-127. } // MAES: pitch calculation for doppler effects. Nothing to write // home about. /* // calculate the relative speed between source and sound origin. // and clip it if necessary adx = Math.abs(listener.momx - source.momx); ady = Math.abs(listener.momy - source.momy); // From _GG1_ p.428. Appox. eucledian distance fast. // Here used for "approximate speed" approx_dist = adx + ady - ((adx < ady ? adx : ady)>>1); // The idea is that for low speeds, no doppler effect occurs. // For higher ones however, a shift occurs. We don't want this // to be annoying, so we'll only apply it for large speed differences // Then again, Doomguy can sprint like Carl Lewis... if (approx_dist>0x100000){ // Quickly decide sign of pitch based on speed vectors // angle of source (speed) to listener (speed) angle = rr.RendererState.PointToAngle(listener.momx, listener.momy, source.momx, source.momy); if ((0<=angle && angle<=Tables.ANG90)|| (180<=angle && angle<=Tables.ANG270)) vps.pitch+=(approx_dist>>16); else vps.pitch-=(approx_dist>>16); } if (vps.pitch<0) vps.pitch=0; if (vps.pitch>255) vps.pitch=255; */ return (vps.volume > 0); } // // S_getChannel : // If none available, return -1. Otherwise channel #. // protected int getChannel( ISoundOrigin origin,sfxinfo_t sfxinfo ) { // channel number to use int cnum; channel_t c; // Find an open channel // If it's null, OK, use that. // If it's an origin-specific sound and has the same origin, override. for (cnum=0 ; cnum<numChannels ; cnum++) { if (channels[cnum].sfxinfo==null) break; else if (origin!=null && channels[cnum].origin == origin) { StopChannel(cnum); break; } } // None available if (cnum == numChannels) { // Look for lower priority for (cnum=0 ; cnum<numChannels ; cnum++) if (channels[cnum].sfxinfo.priority >= sfxinfo.priority) break; if (cnum == numChannels) { // FUCK! No lower priority. Sorry, Charlie. return -1; } else { // Otherwise, kick out lower priority. StopChannel(cnum); } } c = channels[cnum]; // channel is decided to be cnum. c.sfxinfo = sfxinfo; c.origin = origin; return cnum; } /** Nice one. A sound should have a maximum duration in tics, * and we can give it a handle proportional to the future tics * it should play until. Ofc, this means the minimum timeframe * for cutting a sound off is just 1 tic. * * @param handle * @return */ /* public boolean SoundIsPlaying(int handle) { // Ouch. return (DS.gametic < handle); } */ } package s; import java.io.ByteArrayInputStream; import javax.sound.midi.InvalidMidiDataException; import javax.sound.midi.MidiDevice; import javax.sound.midi.MidiSystem; import javax.sound.midi.MidiUnavailableException; import javax.sound.midi.Receiver; import javax.sound.midi.Sequence; import javax.sound.midi.Sequencer; import javax.sound.midi.ShortMessage; import javax.sound.midi.SysexMessage; import javax.sound.midi.Transmitter; /** Concern separated from David Martel's MIDI & MUS player * for Mocha Doom. Greatly improved upon by finnw, perfecting volume changes * and MIDI device detection. * * @author David Martel * @author velktron * @author finnw * */ public class DavidMusicModule implements IMusic { public static final int CHANGE_VOLUME=7; public static final int CHANGE_VOLUME_FINE=9; Sequencer sequencer; VolumeScalingReceiver receiver; Transmitter transmitter; boolean songloaded; public DavidMusicModule(){ } @Override public void InitMusic() { try { int x=-1; MidiDevice.Info[] info = MidiSystem.getMidiDeviceInfo(); for (int i = 0; i < info.length; i++) { MidiDevice mdev=MidiSystem.getMidiDevice(info[i]); if (mdev instanceof Sequencer) x=i; // System.out.println(info[i].getName()+"\t\t\t"+ mdev.isOpen()+"\t"+mdev.hashCode()); } //System.out.printf("x %d y %d \n",x,y); //--This sets the Sequencer and Synthesizer //--The indices x and y correspond to the correct entries for the //--default Sequencer and Synthesizer, as determined above if (x!=-1) sequencer = (Sequencer) MidiSystem.getMidiDevice(info[x]); else sequencer = (Sequencer) MidiSystem.getSequencer(false); sequencer.open(); receiver = VolumeScalingReceiver.getInstance(); // Configure General MIDI level 1 sendSysexMessage(receiver, (byte)0xf0, (byte)0x7e, (byte)0x7f, (byte)9, (byte)1, (byte)0xf7); transmitter = sequencer.getTransmitter(); transmitter.setReceiver(receiver); } catch (MidiUnavailableException e) { e.printStackTrace(); } } private static void sendControlChange(Receiver receiver, int midiChan, int ctrlId, int value) { ShortMessage msg = new ShortMessage(); try { msg.setMessage(ShortMessage.CONTROL_CHANGE, midiChan, ctrlId, value); } catch (InvalidMidiDataException ex) { throw new RuntimeException(ex); } receiver.send(msg, -1); } private static void sendSysexMessage(Receiver receiver, byte... message) { SysexMessage msg = new SysexMessage(); try { msg.setMessage(message, message.length); } catch (InvalidMidiDataException ex) { throw new RuntimeException(ex); } receiver.send(msg, -1); } @Override public void ShutdownMusic() { sequencer.stop(); sequencer.close(); } @Override public void SetMusicVolume(int volume) { System.out.println("Midi volume set to "+volume); receiver.setGlobalVolume(volume / 127f); } @Override public void PauseSong(int handle) { if (songloaded) sequencer.stop(); } @Override public void ResumeSong(int handle) { if (songloaded){ System.out.println("Resuming song"); sequencer.start(); } } @Override public int RegisterSong(byte[] data) { try { Sequence sequence; ByteArrayInputStream bis; try { // If data is a midi file, load it directly bis = new ByteArrayInputStream(data); sequence = MidiSystem.getSequence(bis); } catch (InvalidMidiDataException ex) { // Well, it wasn't. Dude. bis = new ByteArrayInputStream(data); sequence = MusReader.getSequence(bis); } sequencer.stop(); // stops current music if any sequencer.setSequence(sequence); // Create a sequencer for the sequence songloaded=true; } catch (Exception e) { e.printStackTrace(); return -1; } // In good old C style, we return 0 upon success? return 0; } @Override public void PlaySong(int handle, boolean looping) { if (songloaded){ for (int midiChan = 0; midiChan < 16; ++ midiChan) { setPitchBendSensitivity(receiver, midiChan, 2); } if (looping) sequencer.setLoopCount(Sequencer.LOOP_CONTINUOUSLY); else sequencer.setLoopCount(0); sequencer.start(); // Start playing } } private void setPitchBendSensitivity(Receiver receiver, int midiChan, int semitones) { sendRegParamChange(receiver, midiChan, 0, 0, 2); } private void sendRegParamChange(Receiver receiver, int midiChan, int paramMsb, int paramLsb, int valMsb) { sendControlChange(receiver, midiChan, 101, paramMsb); sendControlChange(receiver, midiChan, 100, paramLsb); sendControlChange(receiver, midiChan, 6, valMsb); } @Override public void StopSong(int handle) { sequencer.stop(); } @Override public void UnRegisterSong(int handle) { // In theory, we should ask the sequencer to "forget" about the song. // However since we can register another without unregistering the first, // this is practically a dummy. songloaded=false; } } package s; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.Writer; import java.lang.reflect.Array; import java.lang.reflect.Field; import java.lang.reflect.Method; public class DoomIO { InputStream is; OutputStream os; public DoomIO(InputStream is, OutputStream os) { this.is = is; this.os = os; } public static int toUnsigned(byte signed) { int unsigned = (signed & 0xff); unsigned = (signed >= 0 ? signed : 256 + signed); unsigned = (256 + signed) % 256; return unsigned; } public static int fread(byte[] bytes, int size, int count, InputStream file) throws IOException { int retour = 0; do { if (file.read(bytes, retour*size, size) < size) return retour; retour++; } while (--count > 0); return retour; } public static int freadint(InputStream file) throws IOException { /*byte[] bytes = new byte[2]; if (fread(bytes, 2, 1, file) < 1) return -1; int retour = toUnsigned(bytes[1])*256 + toUnsigned(bytes[0]); return retour;*/ return freadint(file, 2); } public static int freadint(InputStream file, int nbBytes) throws IOException { byte[] bytes = new byte[nbBytes]; if (fread(bytes, nbBytes, 1, file) < 1) return -1; long retour = 0; for (int i = 0; i < nbBytes; i++) { retour += toUnsigned(bytes[i])*(long)Math.pow(256, i); } //toUnsigned(bytes[1])*256 + toUnsigned(bytes[0]); if (retour > (long)Math.pow(256, nbBytes)/2) retour -= (long)Math.pow(256, nbBytes); return (int)retour; } public static int fwrite2(byte[] ptr, int offset, int size, Object file) throws IOException { fwrite(ptr, offset, size, 1, file); return 0; } public static int fwrite2(byte[] ptr, int size, Object file) throws IOException { return fwrite2(ptr, 0, size, file); } public static int fwrite2(byte[] ptr, Object file) throws IOException { return fwrite2(ptr, 0, ptr.length, file); } public static void fwrite(String bytes, int size, int count, Object file) throws IOException { fwrite(toByteArray(bytes), size, count, file); } public static void fwrite(byte[] bytes, int size, int count, Object file) throws IOException { fwrite(bytes, 0, size, count, file); } public static void fwrite(byte[] bytes, int offset, int size, int count, Object file) throws IOException { if (file instanceof OutputStream) { /*byte[] b = bytes; if (bytes.length < size) { b = new byte[size]; copyBytes(from, to, offset) }*/ ((OutputStream)file).write(bytes, offset, Math.min(bytes.length, size)); for (int i = bytes.length; i < size; i++) ((OutputStream)file).write((byte)0); // padding effect if size is bigger than byte array } if (file instanceof Writer) { char[] ch = new char[bytes.length]; for (int i = 0; i < bytes.length; i++) { ch[i] = (char)toUnsigned(bytes[i]); } ((Writer)file).write(ch, offset, size); } } public static 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; } public static byte[] toByteArray(int str) { return toByteArray(str, 2); } public static enum Endian { BIG, LITTLE } public static Endian writeEndian = Endian.LITTLE; static int byteIdx(int i, int nbBytes) { return (writeEndian == Endian.BIG ? i : nbBytes-1-i); } public static void copyBytes(byte[] from, byte[] to, int offset) { for (byte b: from) { to[offset++] = b; } } public static byte[] toByteArray(Long str, int nbBytes) { return toByteArray(str.intValue(), nbBytes); } public static byte[] toByteArray(Short str, int nbBytes) { return toByteArray(str.intValue(), nbBytes); } public static byte[] toByteArray(int[] str, int nbBytes) { byte[] bytes = new byte[str.length*nbBytes]; for (int i = 0; i < str.length; i++) { copyBytes(toByteArray(str[i], nbBytes), bytes, i*nbBytes); } return bytes; } /* public static byte[] toByteArray(boolean[] bools, int nbBytes) { byte[] bytes = new byte[bools.length*nbBytes]; for (int i = 0; i < bools.length; i++) { copyBytes(toByteArray(bools[i], nbBytes), bytes, i*nbBytes); } return bytes; } */ /* public static byte[] toByteArray(Boolean bool, int nbBytes) { int val = (bool?1:0); return toByteArray(val, nbBytes); }*/ public static byte[] toByteArray(Integer str, int nbBytes) { Long val = str.longValue(); if (val < 0) val = (long)Math.pow(256, nbBytes) + val; byte[] bytes = new byte[nbBytes]; long tmp = val; for (int i = 0; i <nbBytes-1; i++) { bytes[byteIdx(i, nbBytes)] = (byte)(tmp%256); tmp = tmp / 256; } bytes[byteIdx(nbBytes-1, nbBytes)] = (byte)(tmp); return bytes; } private static Field getField(Class clazz, String fieldName) throws NoSuchFieldException { try { return clazz.getDeclaredField(fieldName); } catch (NoSuchFieldException e) { Class superClass = clazz.getSuperclass(); if (superClass == null) { throw e; } else { return getField(superClass, fieldName); } } } public static void linkBA(Object obj, Object fieldName, Object stream, int size) { if (stream instanceof OutputStream) { try { Object val = null; if (fieldName instanceof String) { val = getField(obj.getClass(), (String)fieldName).get(obj); if (val instanceof Enum) { val = ((Enum)val).ordinal(); } } if (fieldName instanceof Integer) { val = fieldName; } Method method = DoomIO.class.getMethod("toByteArray", val.getClass(), int.class); byte[] bytes = (byte[])method.invoke(null, val, size); ((OutputStream)stream).write(bytes); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } if (stream instanceof InputStream) { try { if (fieldName instanceof String) { Field field = obj.getClass().getField((String)fieldName); assigner(obj, field, (InputStream)stream, size); } if (fieldName instanceof Integer) { ((InputStream)stream).read(new byte[size]); } } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } // public static int freadint(InputStream file, int nbBytes) throws IOException { } public static void assigner(Object obj, Field field, InputStream is, int size) throws IOException, IllegalArgumentException, IllegalAccessException { Class c = field.getType(); if (c.isArray()) { Object a = field.get(obj); int len = Array.getLength(a); for (int i = 0; i < len; i++) { int val = DoomIO.freadint((InputStream)is, size); Object o = Array.get(a, i); Array.set(a, i, assignValue(val, o, o.getClass())); } return; } int val = DoomIO.freadint((InputStream)is, size); Object v = assignValue(val, field.get(obj), field.getType()); field.set(obj, v); /*Object[] enums = c.getEnumConstants(); if (enums != null) { int val = DoomIO.freadint((InputStream)is, size); field.set(obj, enums[val]); } else { int val = DoomIO.freadint((InputStream)is, size); field.set(obj, val); }*/ } public static Object assignValue(int val, Object objToReplace, Class classe) { if (classe.isAssignableFrom(Boolean.class) || classe.isAssignableFrom(boolean.class)) { return (val == 0 ? false : true); } Object[] enums = classe.getEnumConstants(); if (enums != null) { //int val = DoomIO.freadint((InputStream)is, size); return enums[val]; //field.set(obj, enums[val]); } else { //int val = DoomIO.freadint((InputStream)is, size); //field.set(obj, val); } return val; } public static String baToString(byte[] bytes) { String str = ""; for (int i = 0; i < bytes.length && bytes[i] != 0; i++) str += (char)bytes[i]; return str; } public static int indexOfArray(Object[] a, Object o) { for (int i = 0; i < a.length/* Array.getLength(a)*/; i++) { if (/*Array.get(a, i)*/a[i] == o) return i; } return -1; } } package s; public final class degenmobj_t implements ISoundOrigin { private final int x, y, z; public degenmobj_t(int x, int y, int z) { this.x = x; this.y = y; this.z = z; } public degenmobj_t(int x, int y) { this.x = x; this.y = y; this.z = 0; } @Override public final int getX() { return x; } @Override public final int getY() { return y; } @Override public final int getZ() { return z; } } package s; /** Use this instead of that degemobj_t crud */ public interface ISoundOrigin { public int getX(); public int getY(); public int getZ(); } package s; public class AudioChunk{ public AudioChunk() { buffer=new byte[s.ISoundDriver.MIXBUFFERSIZE]; setStuff(0,0); this.free=true; } public void setStuff(int chunk, int time){ this.chunk = chunk; this.time = time; } public int chunk; public int time; public byte[] buffer; public boolean free; } package s; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.Writer; public class QMusToMid { public static final int NOTMUSFILE = 1; /* Not a MUS file */ public static final int COMUSFILE = 2; /* Can't open MUS file */ public static final int COTMPFILE = 3; /* Can't open TMP file */ public static final int CWMIDFILE = 4; /* Can't write MID file */ public static final int MUSFILECOR = 5; /* MUS file corrupted */ public static final int TOOMCHAN = 6; /* Too many channels */ public static final int MEMALLOC = 7; /* Memory allocation error */ /* some (old) compilers mistake the "MUS\x1A" construct (interpreting it as "MUSx1A") */ public static final String MUSMAGIC = "MUS\032"; /* this seems to work */ public static final String MIDIMAGIC = "MThd\000\000\000\006\000\001"; public static final String TRACKMAGIC1 = "\000\377\003\035"; public static final String TRACKMAGIC2 = "\000\377\057\000"; public static final String TRACKMAGIC3 = "\000\377\002\026"; public static final String TRACKMAGIC4 = "\000\377\131\002\000\000"; public static final String TRACKMAGIC5 = "\000\377\121\003\011\243\032"; public static final String TRACKMAGIC6 = "\000\377\057\000"; public static final int EOF = -1; public static class Ptr<a> { a val; public Ptr(a val) { this.val = val; } public a get() { return val; } public void set(a newval) { val = newval; } } public static class MUSheader { byte[] ID = new byte[4]; /* identifier "MUS" 0x1A */ int ScoreLength; int ScoreStart; int channels; /* count of primary channels */ int SecChannels; /* count of secondary channels (?) */ int InstrCnt; int dummy; /* variable-length part starts here */ int[] instruments; } public static class Track { long current; byte vel; long DeltaTime; byte LastEvent; byte[] data; /* Primary data */ } long TRACKBUFFERSIZE = 65536L ; /* 64 Ko */ void TWriteByte( int MIDItrack, byte byte_, Track track[] ) { long pos ; pos = track[MIDItrack].current ; if( pos < TRACKBUFFERSIZE ) track[MIDItrack].data[(int)pos] = byte_ ; else { System.out.println("ERROR : Track buffer full.\n"+ "Increase the track buffer size (option -size).\n" ) ; System.exit(1); } track[MIDItrack].current++ ; } void TWriteVarLen( int tracknum, long value, Track[] track ) { long buffer ; buffer = value & 0x7f ; while( (value >>= 7) != 0 ) { buffer <<= 8 ; buffer |= 0x80 ; buffer += (value & 0x7f) ; } while( true ) { TWriteByte( tracknum, (byte)buffer, track ) ; if( (buffer & 0x80) != 0 ) buffer >>= 8 ; else break; } } int ReadMUSheader( MUSheader MUSh, InputStream file ) { try { if( DoomIO.fread( MUSh.ID, 4, 1, file ) != 1 ) return COMUSFILE ; /*if( strncmp( MUSh->ID, MUSMAGIC, 4 ) ) return NOTMUSFILE ;*/ if( (MUSh.ScoreLength = DoomIO.freadint(file)) == -1 ) return COMUSFILE ; if( (MUSh.ScoreStart = DoomIO.freadint(file)) == -1 ) return COMUSFILE ; if( (MUSh.channels = DoomIO.freadint(file)) == -1 ) return COMUSFILE ; if( (MUSh.SecChannels = DoomIO.freadint(file)) == -1 ) return COMUSFILE ; if( (MUSh.InstrCnt = DoomIO.freadint(file)) == -1 ) return COMUSFILE ; if( (MUSh.dummy = DoomIO.freadint(file)) == -1 ) return COMUSFILE ; MUSh.instruments = new int[MUSh.InstrCnt]; for (int i = 0; i < MUSh.InstrCnt; i++) { if ((MUSh.instruments[i] = DoomIO.freadint(file)) == -1) { return COMUSFILE ; } } return 0 ; } catch (Exception e) { e.printStackTrace(); return COMUSFILE; } } int WriteMIDheader( int ntrks, int division, Object file ) { try { //_D_: those two lines for testing purposes only //fisTest.close(); //fisTest = new FileInputStream("C:\\Users\\David\\Desktop\\qmus2mid\\test.mid"); DoomIO.fwrite( MIDIMAGIC , 10, 1, file ) ; DoomIO.fwrite2( DoomIO.toByteArray(ntrks), 2, file) ; DoomIO.fwrite2( DoomIO.toByteArray(division), 2, file ) ; } catch (IOException e) { e.printStackTrace(); } return 0 ; } byte last(int e) { return (byte)(e & 0x80); } byte event_type(int e) { return (byte)((e & 0x7F) >> 4); } byte channel(int e) { return (byte)(e & 0x0F); } void TWriteString( char tracknum, String string, int length, Track[] track ) { int i ; for( i = 0 ; i < length ; i++ ) TWriteByte( tracknum, (byte)string.charAt(i), track ) ; } void WriteTrack( int tracknum, Object file, Track[] track ) { long size ; int quot, rem ; try { /* Do we risk overflow here ? */ size = track[tracknum].current+4 ; DoomIO.fwrite( "MTrk", 4, 1, file ); if( tracknum == 0) size += 33 ; DoomIO.fwrite2( DoomIO.toByteArray((int)size, 4), 4, file ) ; if( tracknum == 0) DoomIO.fwrite( TRACKMAGIC1 + "Quick MUS->MID ! by S.Bacquet", 33, 1, file ) ; quot = (int) (track[tracknum].current / 4096) ; rem = (int) (track[tracknum].current - quot*4096) ; DoomIO.fwrite(track[tracknum].data, (int)track[tracknum].current, 1, file ) ; DoomIO.fwrite( TRACKMAGIC2, 4, 1, file ) ; } catch (Exception e) { e.printStackTrace(); } } void WriteFirstTrack( Object file ) { try { byte[] size = DoomIO.toByteArray(43, 4); DoomIO.fwrite( "MTrk", 4, 1, file ) ; DoomIO.fwrite2( size, 4, file ) ; DoomIO.fwrite( TRACKMAGIC3 , 4, 1, file ) ; DoomIO.fwrite( "QMUS2MID (C) S.Bacquet", 22, 1, file ) ; DoomIO.fwrite( TRACKMAGIC4, 6, 1, file ) ; DoomIO.fwrite( TRACKMAGIC5, 7, 1, file ) ; DoomIO.fwrite( TRACKMAGIC6, 4, 1, file ) ; } catch (Exception e) { e.printStackTrace(); } } long ReadTime( InputStream file ) throws IOException { long time = 0 ; int byte_ ; do { byte_ = getc( file ) ; if( byte_ != EOF ) time = (time << 7) + (byte_ & 0x7F) ; } while( (byte_ != EOF) && ((byte_ & 0x80) != 0) ) ; return time ; } byte FirstChannelAvailable( byte[] MUS2MIDchannel ) { int i ; byte old15 = MUS2MIDchannel[15], max = -1 ; MUS2MIDchannel[15] = -1 ; for( i = 0 ; i < 16 ; i++ ) if( MUS2MIDchannel[i] > max ) max = MUS2MIDchannel[i] ; MUS2MIDchannel[15] = old15 ; return (max == 8 ? 10 : (byte)(max+1)) ; } int getc(InputStream is) throws IOException { return is.read(); } int qmus2mid( InputStream mus, Object mid, boolean nodisplay, int division, int BufferSize, boolean nocomp ) throws IOException { Track[] track = new Track[16] ; for (int i = 0; i < track.length; i++) track[i] = new Track(); int TrackCnt = 0 ; byte et, MUSchannel, MIDIchannel, MIDItrack, NewEvent ; int i, event, data, r ; MUSheader MUSh = new MUSheader() ; long DeltaTime, TotalTime = 0, time, min, n = 0 ; byte[] MUS2MIDcontrol = new byte[] { 0, /* Program change - not a MIDI control change */ 0x00, /* Bank select */ 0x01, /* Modulation pot */ 0x07, /* Volume */ 0x0A, /* Pan pot */ 0x0B, /* Expression pot */ 0x5B, /* Reverb depth */ 0x5D, /* Chorus depth */ 0x40, /* Sustain pedal */ 0x43, /* Soft pedal */ 0x78, /* All sounds off */ 0x7B, /* All notes off */ 0x7E, /* Mono */ 0x7F, /* Poly */ 0x79 /* Reset all controllers */ }; byte[] MIDIchan2track = new byte[16]; byte[] MUS2MIDchannel = new byte[16] ; char ouch = 0, sec ; DoomIO.writeEndian = DoomIO.Endian.LITTLE; r = ReadMUSheader( MUSh, mus ) ; if( r != 0) { return r ; } /* if( fseek( file_mus, MUSh.ScoreStart, SEEK_SET ) ) { Close() ; return MUSFILECOR ; }*/ if( !nodisplay ) System.out.println( mus+" ("+mus.available()+" bytes) contains "+MUSh.channels+" melodic channel"+ (MUSh.channels >= 2 ? "s" : "")+"\n"); if( MUSh.channels > 15 ) /* <=> MUSchannels+drums > 16 */ { return TOOMCHAN ; } for( i = 0 ; i < 16 ; i++ ) { MUS2MIDchannel[i] = -1 ; track[i].current = 0 ; track[i].vel = 64 ; track[i].DeltaTime = 0 ; track[i].LastEvent = 0 ; track[i].data = null ; } if( BufferSize != 0) { TRACKBUFFERSIZE = ((long) BufferSize) << 10 ; if( !nodisplay ) System.out.println( "Track buffer size set to "+BufferSize+" KB.\n") ; } if( !nodisplay ) { System.out.println( "Converting..." ) ; } event = getc( mus ) ; et = event_type( event ) ; MUSchannel = channel( event ) ; while( (et != 6) && mus.available() > 0 && (event != EOF) ) { if( MUS2MIDchannel[MUSchannel] == -1 ) { MIDIchannel = MUS2MIDchannel[MUSchannel ] = (MUSchannel == 15 ? 9 : FirstChannelAvailable( MUS2MIDchannel)) ; MIDItrack = MIDIchan2track[MIDIchannel] = (byte)TrackCnt++ ; if( (track[MIDItrack].data = new byte[(int)TRACKBUFFERSIZE]) == null ) { return MEMALLOC ; } } else { MIDIchannel = MUS2MIDchannel[MUSchannel] ; MIDItrack = MIDIchan2track [MIDIchannel] ; } TWriteVarLen( MIDItrack, track[MIDItrack].DeltaTime, track ) ; track[MIDItrack].DeltaTime = 0 ; switch( et ) { case 0 : /* release note */ NewEvent = (byte)(0x90 | MIDIchannel) ; if( (NewEvent != track[MIDItrack].LastEvent) || (nocomp) ) { TWriteByte( MIDItrack, NewEvent, track ) ; track[MIDItrack].LastEvent = NewEvent ; } else n++ ; data = getc( mus ) ; TWriteByte( MIDItrack, (byte)data, track ) ; TWriteByte( MIDItrack, (byte)0, track ) ; break ; case 1 : NewEvent = (byte)(0x90 | MIDIchannel) ; if( (NewEvent != track[MIDItrack].LastEvent) || (nocomp) ) { TWriteByte( MIDItrack, NewEvent, track ) ; track[MIDItrack].LastEvent = NewEvent ; } else n++ ; data = getc( mus ) ; TWriteByte( MIDItrack, (byte)(data & 0x7F), track ) ; if( (data & 0x80) != 0 ) track[MIDItrack].vel = (byte)getc( mus ) ; TWriteByte( MIDItrack, (byte)track[MIDItrack].vel, track ) ; break ; case 2 : NewEvent = (byte)(0xE0 | MIDIchannel) ; if( (NewEvent != track[MIDItrack].LastEvent) || (nocomp) ) { TWriteByte( MIDItrack, NewEvent, track ) ; track[MIDItrack].LastEvent = NewEvent ; } else n++ ; data = getc( mus ) ; TWriteByte( MIDItrack, (byte)((data & 1) << 6), track ) ; TWriteByte( MIDItrack, (byte)(data >> 1), track ) ; break ; case 3 : NewEvent = (byte)(0xB0 | MIDIchannel) ; if( (NewEvent != track[MIDItrack].LastEvent) || (nocomp) ) { TWriteByte( MIDItrack, NewEvent, track ) ; track[MIDItrack].LastEvent = NewEvent ; } else n++ ; data = getc( mus ) ; TWriteByte( MIDItrack, MUS2MIDcontrol[data], track ) ; if( data == 12 ) TWriteByte( MIDItrack, (byte)(MUSh.channels+1), track ) ; else TWriteByte( MIDItrack, (byte)0, track ) ; break ; case 4 : data = getc( mus ) ; if( data != 0 ) { NewEvent = (byte)(0xB0 | MIDIchannel) ; if( (NewEvent != track[MIDItrack].LastEvent) || (nocomp) ) { TWriteByte( MIDItrack, NewEvent, track ) ; track[MIDItrack].LastEvent = NewEvent ; } else n++ ; TWriteByte( MIDItrack, MUS2MIDcontrol[data], track ) ; } else { NewEvent = (byte)(0xC0 | MIDIchannel) ; if( (NewEvent != track[MIDItrack].LastEvent) || (nocomp) ) { TWriteByte( MIDItrack, NewEvent, track ) ; track[MIDItrack].LastEvent = NewEvent ; } else n++ ; } data = getc( mus ) ; TWriteByte( MIDItrack, (byte)data, track ) ; break ; case 5 : case 7 : return MUSFILECOR ; default : break ; } if( last( event ) != 0 ) { DeltaTime = ReadTime( mus ) ; TotalTime += DeltaTime ; for( i = 0 ; i < (int) TrackCnt ; i++ ) track[i].DeltaTime += DeltaTime ; } event = getc( mus ) ; if( event != EOF ) { et = event_type( event ) ; MUSchannel = channel( event ) ; } else ouch = 1 ; } if( !nodisplay ) System.out.println( "done !\n" ) ; if( ouch != 0 ) System.out.println( "WARNING : There are bytes missing at the end of "+mus+".\n "+ "The end of the MIDI file might not fit the original one.\n") ; if( division == 0 ) division = 89 ; else if( !nodisplay ) System.out.println( "Ticks per quarter note set to "+division+".\n") ; if( !nodisplay ) { if( division != 89 ) { time = TotalTime / 140 ; min = time / 60 ; sec = (char) (time - min*60) ; //System.out.println( "Playing time of the MUS file : %u'%.2u''.\n", min, sec ) ; } time = (TotalTime * 89) / (140 * division) ; min = time / 60 ; sec = (char) (time - min*60) ; if( division != 89 ) System.out.println( " MID file" ) ; else System.out.println( "Playing time: "+min+"min "+sec+"sec") ; } if( !nodisplay ) { System.out.println("Writing..." ) ; } WriteMIDheader( TrackCnt+1, division, mid ) ; WriteFirstTrack( mid ) ; for( i = 0 ; i < (int) TrackCnt ; i++ ) WriteTrack( i, mid, track ) ; if( !nodisplay ) System.out.println( "done !\n" ) ; if( !nodisplay && (!nocomp) ) System.out.println( "Compression : %u%%.\n"/*, (100 * n) / (n+ (long) ftell( mid ))*/ ) ; return 0 ; } int convert( String mus, String mid, boolean nodisplay, int div, int size, boolean nocomp, Ptr<Integer> ow ) throws IOException { InputStream is = new BufferedInputStream(new FileInputStream(new File(mid))); OutputStream os = new BufferedOutputStream(new FileOutputStream(new File(mid))); int error; //struct stat file_data ; char[] buffer = new char[30] ; /* we don't need _all_ that checking, do we ? */ /* Answer : it's more user-friendly */ /*#ifdef MSDOG if( access( mus, 0 ) ) { System.out.println( "ERROR : %s does not exist.\n", mus ) ; return 1 ; } if( !access( mid, 0 ) ) { if( !*ow ) { System.out.println( "Can't overwrite %s.\n", mid ) ; return 2 ; } if( *ow == 1 ) { System.out.println( "%s exists : overwrite (Y=Yes,N=No,A=yes for All,Q=Quit)" " ? [Y]\b\b", mid ) ; fflush( stdout ) ; do n = toupper( getxkey() ) ; while( (n != 'Y') && (n != 'N') && (n != K_Return) && (n != 'A') && (n != 'Q')) ; switch( n ) { case 'N' : System.out.println( "N\n%s NOT converted.\n", mus ) ; return 3 ; case 'A' : System.out.println( "A" ) ; *ow = 2 ; break ; case 'Q' : System.out.println( "Q\nQMUS2MID aborted.\n" ) ; exit( 0 ) ; break ; default : break ; } System.out.println( "\n" ) ; } } #else*/ /*if ( ow.get() == 0 ) { file = fopen(mid, "r"); if ( file ) { fclose(file); System.out.println( "qmus2mid: file %s exists, not removed.\n", mid ) ; return 2 ; } }*/ /*#endif*/ return convert(is, os, nodisplay, div, size, nocomp, ow); } int convert( InputStream mus, Object mid, boolean nodisplay, int div, int size, boolean nocomp, Ptr<Integer> ow ) throws IOException { int error = qmus2mid( mus, mid, nodisplay, div, size, nocomp ) ; if( error != 0 ) { System.out.println( "ERROR : " ) ; switch( error ) { case NOTMUSFILE : System.out.println( "%s is not a MUS file.\n"/*, mus*/ ) ; break ; case COMUSFILE : System.out.println( "Can't open %s for read.\n"/*, mus*/ ) ; break ; case COTMPFILE : System.out.println( "Can't open temp file.\n" ) ; break ; case CWMIDFILE : System.out.println( "Can't write %s (?).\n"/*, mid */) ; break ; case MUSFILECOR : System.out.println( "%s is corrupted.\n"/*, mus*/) ; break ; case TOOMCHAN : System.out.println( "%s contains more than 16 channels.\n"/*, mus*/ ) ; break ; case MEMALLOC : System.out.println( "Not enough memory.\n" ) ; break ; default : break ; } return 4 ; } if( !nodisplay ) { System.out.println( mus+" converted successfully.\n") ; /*if( (file = fopen( mid, "rb" )) != NULL ) { //stat( mid, &file_data ) ; fclose( file ) ; sSystem.out.println( buffer, " : %lu bytes", (long) file_data.st_size ) ; }*/ /*System.out.println( "%s (%scompressed) written%s.\n", mid, nocomp ? "NOT " : "", file ? buffer : "" ) ;*/ } return 0 ; } // int CheckParm( char[] check, int argc, char *argv[] ) // { // int i; // // for ( i = 1 ; i<argc ; i++ ) // /*#ifdef MSDOG // if( !stricmp( check, argv[i] ) ) // #else*/ // if( !strcmp( check, argv[i] ) ) // /*#endif*/ // return i ; // // return 0; // } void PrintHeader( ) { // System.out.println( "===============================================================================\n" // " Quick MUS->MID v2.0 ! (C) 1995,96 Sebastien Bacquet\n" // " E-mail : bacquet@iie.cnam.fr\n" // "===============================================================================\n" ) ; } void PrintSyntax( ) { // PrintHeader() ; // System.out.println( // #ifdef MSDOG // "\nSyntax : QMUS2MID musfile1[.mus] {musfile2[.mus] ... | " // "midifile.mid} [options]\n" // " Wildcards are accepted.\n" // " Options are :\n" // " -query : Query before processing\n" // " -ow : OK, overwrite (without query)\n" // #else // "\nSyntax : QMUS2MID musfile midifile [options]\n" // " Options are :\n" // #endif // " -noow : Don't overwrite !\n" // " -nodisp : Display nothing ! (except errors)\n" // " -nocomp : Don't compress !\n" // " -size ### : Set the track buffer size to ### (in KB). " // "Default = 64 KB\n" // " -t ### : Ticks per quarter note. Default = 89\n" // ) ; } int main( int argc, char[] argv ) { int div = 0, ow = 1, nocomp = 0, size = 0, n ; boolean nodisplay = false; /*#ifdef MSDOG int FileCount, query = 0, i, line = 0 ; char mus[MAXPATH], mid[MAXPATH], drive[MAXDRIVE], middrive[MAXDRIVE], dir[MAXDIR], middir[MAXDIR], musname[MAXFILE], midname[MAXFILE], ext[MAXEXT] ; struct stat s ; #else*/ String mus, mid; /*#endif*/ /*#ifndef MSDOG if ( !LittleEndian() ) { System.out.println("\nSorry, this program presently only works on " "little-endian machines... \n\n"); exit( EXIT_FAILURE ) ; } #endif*/ /*#ifdef MSDOG if( (argc == 1) || (argv[1][0] == '-') ) #else if( argc < 3 ) #endif { PrintSyntax() ; exit( EXIT_FAILURE ) ; }*/ /*#ifdef MSDOG if( (strrchr( argv[1], '*' ) != NULL) || (strrchr( argv[1], '?' ) != NULL) ) { PrintHeader() ; System.out.println( "Sorry, there is nothing matching %s...\n", argv[1] ) ; exit( EXIT_FAILURE ) ; } strncpy( mus, argv[1], MAXPATH ) ; strupr( mus ) ; if( !(fnsplit( mus, drive, dir, musname, NULL ) & FILENAME) ) { PrintSyntax() ; exit( EXIT_FAILURE ) ; } #else*/ //strncpy( mus, argv[1], FILENAME_MAX ) ; //strncpy( mid, argv[2], FILENAME_MAX ) ; /*#endif*/ /*#ifdef MSDOG if( CheckParm( "-query", argc, argv ) ) query = 1 ; #endif*/ /* if( CheckParm( "-nodisp", argc, argv ) ) nodisplay = 1 ; */ if( !nodisplay ) PrintHeader() ; /*if( (n = CheckParm( "-size", argc, argv )) != 0 ) size = atoi( argv[n+1] ) ;*/ /*#ifdef MSDOG if( CheckParm( "-ow", argc, argv ) ) ow += 1 ; #endif if( CheckParm( "-noow", argc, argv ) ) ow -= 1 ; if( (n = CheckParm( "-t", argc, argv )) != 0 ) div = atoi( argv[n+1] ) ; if( CheckParm( "-nocomp", argc, argv ) ) nocomp = 1 ;*/ /*#ifdef MSDOG for( FileCount = 1 ; (FileCount < argc) && (argv[FileCount][0] != '-') ; FileCount++ ) ; FileCount-- ; midname[0] = middrive[0] = middir[0] = 0 ; if( FileCount == 2 ) { if( fnsplit( argv[FileCount], middrive, middir, midname, ext ) & FILENAME ) { if( stricmp( ext, ".MID" ) ) midname[0] = middrive[0] = middir[0] = 0 ; else { strcpy( mid, argv[FileCount--] ) ; strupr( mid ) ; } } else FileCount-- ; } if( FileCount > 2 ) { if( fnsplit( argv[FileCount], middrive, middir, NULL, NULL ) & FILENAME ) midname[0] = middrive[0] = middir[0] = 0 ; else FileCount-- ; } for( i = 0 ; i < FileCount ; i++ ) { strupr( argv[i+1] ) ; n = fnsplit( argv[i+1], drive, dir, musname, ext ) ; if( !(n & EXTENSION) || !stricmp( ext, ".MUS" ) ) { stat( argv[i+1], &s ) ; if( !S_ISDIR( s.st_mode ) ) { fnmerge( mus, drive, dir, musname, ".MUS" ) ; if( line && !nodisplay ) System.out.println( "\n" ) ; if( query ) { System.out.println( "Convert %s ? (Y=Yes,N=No,A=yes for All,Q=Quit)" " [Y]\b\b", mus ) ; fflush( stdout ) ; do n = toupper( getxkey() ) ; while( (n != 'Y') && (n != 'N') && (n != K_Return) && (n != 'A') && (n != 'Q')) ; switch( n ) { case 'N' : System.out.println( "N\n%s NOT converted.\n", mus ) ; line = 1 ; continue ; break ; case 'Q' : System.out.println( "Q\nQMUS2MID aborted.\n" ) ; exit( 0 ) ; break ; case 'A' : query = 0 ; System.out.println( "A\n" ) ; break ; default : System.out.println( "\n" ) ; break ; } } if( !midname[0] ) { fnmerge( mid, middrive, middir, musname, ".MID" ) ; strupr( mid ) ; } convert( mus, mid, nodisplay, div, size, nocomp, &ow ) ; line = 1 ; } } } if( !line && !nodisplay && !query ) System.out.println( "Sorry, there is no MUS file matching...\n" ) ; #else*/ //convert( mus, mid, nodisplay, div, size, nocomp, ow ) ; /* #endif*/ return 0; } } package s; import static data.sounds.S_sfx; import java.util.HashMap; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.Semaphore; import javax.sound.sampled.AudioFormat; import javax.sound.sampled.AudioSystem; import javax.sound.sampled.DataLine; import javax.sound.sampled.SourceDataLine; import pooling.AudioChunkPool; import data.sounds.sfxenum_t; import doom.DoomStatus; /** * A close recreation of the classic linux doom sound mixer. * * PROS: * a) Very faithful to pitch and stereo effects, and original * volume ramping. * b) Uses only one audioline and one playback thread * * CONS: * a) May sound a bit off if production/consumption rates don't match * b) Sounds awful when mixing too many sounds together, just like the original. * * @author Maes */ public class ClassicDoomSoundDriver extends AbstractSoundDriver { protected final Semaphore produce; protected final Semaphore consume; protected int chunk = 0; // protected FileOutputStream fos; // protected DataOutputStream dao; // The one and only line protected SourceDataLine line = null; protected HashMap<Integer, byte[]> cachedSounds = new HashMap<Integer, byte[]>(); public ClassicDoomSoundDriver(DoomStatus<?,?> DS, int numChannels) { super(DS,numChannels); channelleftvol_lookup = new int[numChannels][]; channelrightvol_lookup = new int[numChannels][]; channelstep = new int[numChannels]; channelstepremainder = new int[numChannels]; channels = new byte[numChannels][]; p_channels = new int[numChannels]; channelsend = new int[numChannels]; produce = new Semaphore(1); consume = new Semaphore(1); produce.drainPermits(); mixbuffer= new byte[MIXBUFFERSIZE]; } /** The channel step amount... */ protected final int[] channelstep; /** ... and a 0.16 bit remainder of last step. */ protected final int[] channelstepremainder; /** * The channel data pointers, start and end. These were referred to as * "channels" in two different source files: s_sound.c and i_sound.c. In * s_sound.c they are actually channel_t (they are only informational). In * i_sound.c they are actual data channels. */ protected byte[][] channels; /** * MAES: we'll have to use this for actual pointing. channels[] holds just * the data. */ protected int[] p_channels; /** * The second one is supposed to point at "the end", so I'll make it an int. */ protected int[] channelsend; /** Hardware left and right channel volume lookup. */ protected final int[][] channelleftvol_lookup, channelrightvol_lookup; protected volatile boolean mixed = false; /** * This function loops all active (internal) sound channels, retrieves a * given number of samples from the raw sound data, modifies it according to * the current (internal) channel parameters, mixes the per channel samples * into the global mixbuffer, clamping it to the allowed range, and sets up * everything for transferring the contents of the mixbuffer to the (two) * hardware channels (left and right, that is). This function currently * supports only 16bit. */ public void UpdateSound() { mixed = false; // Mix current sound data. // Data, from raw sound, for right and left. int sample = 0; int dl; int dr; // Pointers in global mixbuffer, left, right, end. // Maes: those were explicitly signed short pointers... int leftout; int rightout; int leftend; // Step in mixbuffer, left and right, thus two. int step; // Mixing channel index. int chan; // POINTERS to Left and right channel // which are in global mixbuffer, alternating. leftout = 0; rightout = 2; step = 4; // Determine end, for left channel only // (right channel is implicit). // MAES: this implies that the buffer will only mix // that many samples at a time, and that the size is just right. // Thus, it must be flushed (p_mixbuffer=0) before reusing it. leftend = SAMPLECOUNT * step; for (chan = 0; chan < numChannels; chan++) { if (channels[chan] != null) // SOME mixing has taken place. mixed = true; } // Mix sounds into the mixing buffer. // Loop over step*SAMPLECOUNT, // that is SAMPLECOUNT values for two channels. while (leftout < leftend) { // Reset left/right value. dl = 0; dr = 0; // Love thy L2 chache - made this a loop. // Now more channels could be set at compile time // as well. Thus loop those channels. for (chan = 0; chan < numChannels; chan++) { // if (D) System.err.printf("Checking channel %d\n",chan); // Check channel, if active. // MAES: this means that we must point to raw data here. if (channels[chan] != null) { int channel_pointer = p_channels[chan]; // Get the raw data from the channel. // Maes: this is supposed to be an 8-bit unsigned value. sample = 0x00FF & channels[chan][channel_pointer]; // Add left and right part for this channel (sound) // to the current data. Adjust volume accordingly. // Q: could this be optimized by converting samples to 16-bit // at load time, while also allowing for stereo samples? // A: Only for the stereo part. You would still look a lookup // for the CURRENT volume level. dl += channelleftvol_lookup[chan][sample]; dr += channelrightvol_lookup[chan][sample]; // This should increment the index inside a channel, but is // expressed in 16.16 fixed point arithmetic. channelstepremainder[chan] += channelstep[chan]; // The actual channel pointer is increased here. // The above trickery allows playing back different pitches. // The shifting retains only the integer part. channel_pointer += channelstepremainder[chan] >> 16; // This limits it to the "decimal" part in order to // avoid undue accumulation. channelstepremainder[chan] &= 0xFFFF; // Check whether we are done. Also to avoid overflows. if (channel_pointer >= channelsend[chan]) { // Reset pointer for a channel. if (D) System.err .printf( "Channel %d handle %d pointer %d thus done, stopping\n", chan, this.channelhandles[chan], channel_pointer); channels[chan] = null; channel_pointer = 0; } // Write pointer back, so we know where a certain channel // is the next time UpdateSounds is called. p_channels[chan] = channel_pointer; } } // for all channels. // MAES: at this point, the actual values for a single sample // (YIKES!) are in d1 and d2. We must use the leftout/rightout // pointers to write them back into the mixbuffer. // Clamp to range. Left hardware channel. // Remnant of 8-bit mixing code? That must have raped ears // and made them bleed. // if (dl > 127) *leftout = 127; // else if (dl < -128) *leftout = -128; // else *leftout = dl; if (dl > 0x7fff) dl = 0x7fff; else if (dl < -0x8000) dl = -0x8000; // Write left channel mixbuffer[leftout] = (byte) ((dl & 0xFF00) >>> 8); mixbuffer[leftout + 1] = (byte) (dl & 0x00FF); // Same for right hardware channel. if (dr > 0x7fff) dr = 0x7fff; else if (dr < -0x8000) dr = -0x8000; // Write right channel. mixbuffer[rightout] = (byte) ((dr & 0xFF00) >>> 8); mixbuffer[rightout + 1] = (byte) (dr & 0x00FF); // Increment current pointers in mixbuffer. leftout += 4; rightout += 4; } // End leftend/leftout while // Q: how do we know whether the mixbuffer isn't entirely used // and instead it has residual garbage samples in it? // A: DOOM kind of padded samples in memory, so presumably // they all played silence. // Q: what's the purpose of channelremainder etc? // A: pitch variations were done with fractional pointers 16.16 // style. } /** * SFX API Note: this was called by S_Init. However, whatever they did in * the old DPMS based DOS version, this were simply dummies in the Linux * version. See soundserver initdata(). */ @Override public void SetChannels(int numChannels) { // Init internal lookups (raw data, mixing buffer, channels). // This function sets up internal lookups used during // the mixing process. int steptablemid = 128; // Okay, reset internal mixing channels to zero. for (int i = 0; i < this.numChannels; i++) { channels[i] = null; } generateStepTable(steptablemid); generateVolumeLUT(); } protected MixServer SOUNDSRV; protected Thread SOUNDTHREAD; @Override public boolean InitSound() { // Secure and configure sound device first. System.out.println("I_InitSound: "); // We only need a single data line. // PCM, signed, 16-bit, stereo, 22025 KHz, 2048 bytes per "frame", // maximum of 44100/2048 "fps" AudioFormat format = new AudioFormat(SAMPLERATE, 16, 2, true, true); DataLine.Info info = new DataLine.Info(SourceDataLine.class, format); if (AudioSystem.isLineSupported(info)) try { line = (SourceDataLine) AudioSystem.getSourceDataLine(format); line.open(format,AUDIOLINE_BUFFER); } catch (Exception e) { e.printStackTrace(); System.err.print("Could not play signed 16 data\n"); return false; } if (line != null) { System.err.print(" configured audio device\n"); line.start(); } else { System.err.print(" could not configure audio device\n"); return false; } // This was here only for debugging purposes /* * try { fos=new FileOutputStream("test.raw"); dao=new * DataOutputStream(fos); } catch (FileNotFoundException e) { * Auto-generated catch block e.printStackTrace(); } */ SOUNDSRV = new MixServer(line); SOUNDTHREAD = new Thread(SOUNDSRV); SOUNDTHREAD.start(); // Initialize external data (all sounds) at start, keep static. System.err.print("I_InitSound: "); super.initSound8(); System.err.print(" pre-cached all sound data\n"); // Now initialize mixbuffer with zero. initMixBuffer(); // Finished initialization. System.err.print("I_InitSound: sound module ready\n"); return true; } @Override protected int addsfx(int sfxid, int volume, int step, int seperation) { int i; int rc = -1; int oldest = DS.gametic; int oldestnum = 0; int slot; int rightvol; int leftvol; int broken=-1; // Chainsaw troubles. // Play these sound effects only one at a time. if ((sfxid >= sfxenum_t.sfx_sawup.ordinal() && sfxid <= sfxenum_t.sfx_sawhit.ordinal()) || sfxid == sfxenum_t.sfx_stnmov.ordinal() || sfxid == sfxenum_t.sfx_pistol.ordinal()) { // Loop all channels, check. for (i = 0; i < numChannels; i++) { // Active, and using the same SFX? if ((channels[i] != null) && (channelids[i] == sfxid)) { // Reset. this.p_channels[i] = 0; this.channels[i]=null; // We are sure that iff, // there will only be one. broken=i; break; } } } // Loop all channels to find oldest SFX. if (broken>=0) { i=broken; oldestnum=broken; } else for (i = 0; (i < numChannels) && (channels[i] != null); i++) { if (channelstart[i] < oldest) { oldestnum = i; } } oldest = channelstart[oldestnum]; // Tales from the cryptic. // If we found a channel, fine. // If not, we simply overwrite the first one, 0. // Probably only happens at startup. if (i == numChannels) slot = oldestnum; else slot = i; // Okay, in the less recent channel, // we will handle the new SFX. // Set pointer to raw data. channels[slot] = S_sfx[sfxid].data; // MAES: if you don't zero-out the channel pointer here, it gets ugly p_channels[slot] = 0; // Set pointer to end of raw data. channelsend[slot] = lengths[sfxid]; // Reset current handle number, limited to 0..100. if (handlenums == 0) // was !handlenums, so it's actually 1...100? handlenums = 100; // Assign current handle number. // Preserved so sounds could be stopped (unused). // Maes: this should really be decreasing, otherwide handles // should start at 0 and go towards 100. Just saying. channelhandles[slot] = rc = handlenums--; // Set stepping??? // Kinda getting the impression this is never used. // MAES: you're wrong amigo. channelstep[slot] = step; // ??? channelstepremainder[slot] = 0; // Should be gametic, I presume. channelstart[slot] = DS.gametic; // Separation, that is, orientation/stereo. // range is: 1 - 256 seperation += 1; // Per left/right channel. // x^2 seperation, // adjust volume properly. leftvol = volume - ((volume * seperation * seperation) >> 16); // /(256*256); seperation = seperation - 257; rightvol = volume - ((volume * seperation * seperation) >> 16); // Sanity check, clamp volume. // Maes: better to clamp than to crash, no? if (rightvol < 0) rightvol=0; if (rightvol >127) rightvol=127; if (leftvol < 0) leftvol=0; if (leftvol >127) leftvol=127; // Get the proper lookup table piece // for this volume level??? channelleftvol_lookup[slot] = vol_lookup[leftvol]; channelrightvol_lookup[slot] = vol_lookup[rightvol]; // Preserve sound SFX id, // e.g. for avoiding duplicates of chainsaw. channelids[slot] = sfxid; if (D) System.err.println(channelStatus()); if (D) System.err.printf( "Playing sfxid %d handle %d length %d vol %d on channel %d\n", sfxid, rc, S_sfx[sfxid].data.length, volume, slot); // You tell me. return rc; } @Override public void ShutdownSound() { boolean done = false; // Unlock sound thread if it's waiting. produce.release(); int i; while (!done) { for (i = 0; i < numChannels && (channels[i] == null); i++) { } // System.err.printf("%d channels died off\n",i); UpdateSound(); SubmitSound(); if (i == numChannels) done = true; } this.line.drain(); SOUNDSRV.terminate = true; produce.release(); try { SOUNDTHREAD.join(); } catch (InterruptedException e) { // Well, I don't care. } line.close(); } protected class MixServer implements Runnable { public boolean terminate = false; public MixServer(SourceDataLine line) { this.auline = line; } private SourceDataLine auline; private ArrayBlockingQueue<AudioChunk> audiochunks = new ArrayBlockingQueue<AudioChunk>(BUFFER_CHUNKS * 2); public void addChunk(AudioChunk chunk) { audiochunks.offer(chunk); } public volatile int currstate = 0; public void run() { while (!terminate) { // while (timing[mixstate]<=mytime){ // Try acquiring a produce permit before going on. try { // System.err.println("Waiting for a permit..."); produce.acquire(); // System.err.println("Got a permit"); } catch (InterruptedException e) { // Well, ouch. e.printStackTrace(); } int chunks = 0; // System.err.printf("Audio queue has %d chunks\n",audiochunks.size()); // Play back only at most a given number of chunks once you reach // this spot int atMost=Math.min(ISoundDriver.BUFFER_CHUNKS,audiochunks.size()); while (atMost-->0){ AudioChunk chunk = null; try { chunk = audiochunks.take(); } catch (InterruptedException e1) { // Should not block } // Play back all chunks present in a buffer ASAP auline.write(chunk.buffer, 0, MIXBUFFERSIZE); chunks++; // No matter what, give the chunk back! chunk.free = true; audiochunkpool.checkIn(chunk); } // Signal that we consumed a whole buffer and we are ready for // another one. consume.release(); } } } @Override public boolean SoundIsPlaying(int handle) { int c = getChannelFromHandle(handle); return (c != -2 && channels[c] == null); } /** * Internal use. * * @param handle * @return the channel that has the handle, or -2 if none has it. */ protected int getChannelFromHandle(int handle) { // Which channel has it? for (int i = 0; i < numChannels; i++) { if (channelhandles[i] == handle) return i; } return BUSY_HANDLE; } @Override public void StopSound(int handle) { // Which channel has it? int hnd = getChannelFromHandle(handle); if (hnd >= 0) { channels[hnd] = null; p_channels[hnd] = 0; this.channelhandles[hnd] = IDLE_HANDLE; } } @Override public void SubmitSound() { // It's possible for us to stay silent and give the audio // queue a chance to get drained. if (mixed) { silence=0; AudioChunk gunk = audiochunkpool.checkOut(); // Ha ha you're ass is mine! gunk.free = false; // System.err.printf("Submitted sound chunk %d to buffer %d \n",chunk,mixstate); // Copy the currently mixed chunk into its position inside the // master buffer. System.arraycopy(mixbuffer, 0, gunk.buffer, 0, MIXBUFFERSIZE); this.SOUNDSRV.addChunk(gunk); // System.err.println(chunk++); chunk++; // System.err.println(chunk); if (consume.tryAcquire()) produce.release(); } else { silence++; // MAES: attempt to fix lingering noise error if (silence >ISoundDriver.BUFFER_CHUNKS*5){ line.flush(); silence=0; } // System.err.println("SILENT_CHUNK"); // this.SOUNDSRV.addChunk(SILENT_CHUNK); } // line.write(mixbuffer, 0, mixbuffer.length); } private int silence=0; @Override public void UpdateSoundParams(int handle, int vol, int sep, int pitch) { int chan = this.getChannelFromHandle(handle); // Per left/right channel. // x^2 seperation, // adjust volume properly. int leftvol = vol - ((vol * sep * sep) >> 16); // /(256*256); sep = sep - 257; int rightvol = vol - ((vol * sep * sep) >> 16); // Sanity check, clamp volume. if (rightvol < 0 || rightvol > 127) DS.I.Error("rightvol out of bounds"); if (leftvol < 0 || leftvol > 127) DS.I.Error("leftvol out of bounds"); // Get the proper lookup table piece // for this volume level??? channelleftvol_lookup[chan] = vol_lookup[leftvol]; channelrightvol_lookup[chan] = vol_lookup[rightvol]; // Well, if you can get pitch to change too... this.channelstep[chan] = steptable[pitch]; channelsend[chan] = this.lengths[this.channelids[chan]]; } protected StringBuilder sb = new StringBuilder(); public String channelStatus() { sb.setLength(0); for (int i = 0; i < numChannels; i++) { if (channels[i] != null) sb.append(i); else sb.append('-'); } return sb.toString(); } protected final AudioChunk SILENT_CHUNK = new AudioChunk(); protected final AudioChunkPool audiochunkpool = new AudioChunkPool(); } package s; import data.sfxinfo_t; import data.sounds.sfxenum_t; import static data.Defines.TICRATE; //Emacs style mode select -*- C++ -*- //----------------------------------------------------------------------------- // // $Id: ISoundDriver.java,v 1.1 2012/11/08 17:12:42 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: // System interface, sound. Anything implementation-specific should // implement this. // //----------------------------------------------------------------------------- public interface ISoundDriver { public static final int VOLUME_STEPS = 128; public static final int PANNING_STEPS = 256; public static final int IDLE_HANDLE = -1; public static final int BUSY_HANDLE = -2; // Needed for calling the actual sound output // We mix 1024 samples each time, but we only call UpdateSound() // 1 time out of three. public static final int NUM_CHANNELS = 8; // It is 2 for 16bit, and 2 for two channels. public static final int BUFMUL = 4; public static final int SAMPLERATE = 22050; // Hz // Update all 30 millisecs, approx. 30fps synchronized. // Linux resolution is allegedly 10 millisecs, // scale is microseconds. public static final int SOUND_INTERVAL = 500; /** Yes, it's possible to select a different sound frame rate */ public static final int SND_FRAME_RATE =21; // Was 512, but if you mix that many samples per tic you will // eventually outrun the buffer :-/ I fail to see the mathematical // justification behind this, unless they simply wanted the buffer to // be a nice round number in size. public static final int SAMPLECOUNT =SAMPLERATE/SND_FRAME_RATE; public static final int MIXBUFFERSIZE =(SAMPLECOUNT*BUFMUL); public static final int SAMPLESIZE = 16 ; // 16bit public static final int NUMSFX = sfxenum_t.NUMSFX.ordinal() ; public static final int MAXHANDLES = 100; /** How many audio chunks/frames to mix before submitting them to * the output. */ public static final int BUFFER_CHUNKS=5; /** Ths audio buffer size of the audioline itself. * Increasing this is the only effective way to combat output stuttering on * slower machines. */ public static final int AUDIOLINE_BUFFER=2*BUFFER_CHUNKS*MIXBUFFERSIZE; public static final int SOUND_PERIOD = 1000/SND_FRAME_RATE; // in ms /** Init at program start. Return false if device invalid, * so that caller can decide best course of action. * The suggested one is to swap the sound "driver" for a dummy. * * @return */ boolean InitSound(); // ... update sound buffer and audio device at runtime... void UpdateSound(); void SubmitSound(); // ... shut down and relase at program termination. void ShutdownSound(); // // SFX I/O // // Initialize channels? void SetChannels(int numChannels); // Get raw data lump index for sound descriptor. int GetSfxLumpNum (sfxinfo_t sfxinfo ); // Starts a sound in a particular sound channel. int StartSound ( int id, int vol, int sep, int pitch, int priority ); // Stops a sound channel. void StopSound(int handle); /** Called by S_*() functions to see if a channel is still playing. Returns false if no longer playing, true if playing. This is a relatively "high level" function, so its accuracy relies on what the "system specific" sound code reports back */ boolean SoundIsPlaying(int handle); /* Updates the volume, separation, and pitch of a sound channel. */ void UpdateSoundParams ( int handle, int vol, int sep, int pitch ); } package s; import w.DoomBuffer; import doom.DoomStatus; /** A variation of the Classic Sound Driver, decoding the DP-lumps * instead of the DS. A better way would be to build-in an * automatic "WAV to SPEAKER" conversion, but that can wait... * * @author Maes * */ public class SpeakerDoomSoundDriver extends ClassicDoomSoundDriver { public SpeakerDoomSoundDriver(DoomStatus DS, int numChannels) { super(DS, numChannels); // TODO Auto-generated constructor stub } /** Rigged so it gets SPEAKER sounds instead of regular ones */ @Override protected byte[] getsfx ( String sfxname, int[] len, int index ) { byte[] sfx; byte[] paddedsfx; int i; int size; int paddedsize; String name; int sfxlump; // Get the sound data from the WAD, allocate lump // in zone memory. name=String.format("dp%s", sfxname).toUpperCase(); // Now, there is a severe problem with the // sound handling, in it is not (yet/anymore) // gamemode aware. That means, sounds from // DOOM II will be requested even with DOOM // shareware. // The sound list is wired into sounds.c, // which sets the external variable. // I do not do runtime patches to that // variable. Instead, we will use a // default sound for replacement. if ( DS.W.CheckNumForName(name) == -1 ) sfxlump = DS.W.GetNumForName("dppistol"); else sfxlump = DS.W.GetNumForName(name); // We must first load and convert it to raw samples. SpeakerSound SP=(SpeakerSound) DS.W.CacheLumpNum(sfxlump, 0,SpeakerSound.class); sfx = SP.toRawSample(); size = sfx.length; // MAES: A-ha! So that's how they do it. // SOund effects are padded to the highest multiple integer of // the mixing buffer's size (with silence) paddedsize = ((size-8 + (SAMPLECOUNT-1)) / SAMPLECOUNT) * SAMPLECOUNT; // Allocate from zone memory. paddedsfx = new byte[paddedsize]; // Now copy and pad. The first 8 bytes are header info, so we discard them. System.arraycopy(sfx,8, paddedsfx, 0,size-8 ); for (i=size-8 ; i<paddedsize ; i++) paddedsfx[i] = (byte) 127; // Hmm....silence? for (i=size-8 ; i<paddedsize ; i++) paddedsfx[i] = (byte) 127; // Remove the cached lump. DS.W.UnlockLumpNum(sfxlump); if (D) System.out.printf("SFX %d size %d padded to %d\n",index,size,paddedsize); // Preserve padded length. len[index] = paddedsize; // Return allocated padded data. // So the first 8 bytes are useless? return paddedsfx; } } package p; import doom.think_t; import rr.SectorAction; import rr.line_t; import rr.sector_t; public class slidedoor_t extends SectorAction { sdt_e type; line_t line; int frame; int whichDoorIndex; int timer; sector_t frontsector; sector_t backsector; sd_e status; public slidedoor_t(){ type=sdt_e.sdt_closeOnly; status=sd_e.sd_closing; function=think_t.T_SlidingDoor; } } package p; import java.io.IOException; import i.DoomStatusAware; import rr.subsector_t; import defines.skill_t; public interface ILevelLoader extends DoomStatusAware { // Lump order in a map WAD: each map needs a couple of lumps // to provide a complete scene geometry description. public static final int ML_LABEL = 0; /** A separator, name, ExMx or MAPxx */ public static final int ML_THINGS = 1; /** Monsters, items.. */ public static final int ML_LINEDEFS = 2; /** LineDefs, from editing */ public static final int ML_SIDEDEFS = 3; /** SideDefs, from editing */ public static final int ML_VERTEXES = 4; /** Vertices, edited and BSP splits generated */ public static final int ML_SEGS = 5; /** LineSegs, from LineDefs split by BSP */ public static final int ML_SSECTORS = 6; /** SubSectors, list of LineSegs */ public static final int ML_NODES = 7; /** BSP nodes */ public static final int ML_SECTORS = 8; /** Sectors, from editing */ public static final int ML_REJECT = 9; /** LUT, sector-sector visibility */ public static final int ML_BLOCKMAP = 10; // Maintain single and multi player starting spots. public static final int MAX_DEATHMATCH_STARTS = 10; /** Expected lump names for verification */ public static final String[] LABELS={"MAPNAME","THINGS","LINEDEFS","SIDEDEFS", "VERTEXES","SEGS","SSECTORS","NODES", "SECTORS","REJECT","BLOCKMAP"}; /** P_SetupLevel * * @param episode * @param map * @param playermask * @param skill * @throws IOException */ void SetupLevel(int episode, int map, int playermask, skill_t skill) throws IOException; /** * P_SetThingPosition Links a thing into both a block and a subsector based * on it's x y. Sets thing.subsector properly * * * @param thing */ void SetThingPosition(mobj_t thing); /** * R_PointInSubsector * * MAES: it makes more sense to have this here. * * @param x fixed * @param y fixed * */ subsector_t PointInSubsector(int x, int y); } package p; public enum floor_e { // lower floor to highest surrounding floor lowerFloor, // lower floor to lowest surrounding floor lowerFloorToLowest, // lower floor to highest surrounding floor VERY FAST turboLower, // raise floor to lowest surrounding CEILING raiseFloor, // raise floor to next highest surrounding floor raiseFloorToNearest, // raise floor to shortest height texture around it raiseToTexture, // lower floor to lowest surrounding floor // and change floorpic lowerAndChange, raiseFloor24, raiseFloor24AndChange, raiseFloorCrush, // raise to next highest floor, turbo-speed raiseFloorTurbo, donutRaise, raiseFloor512 } package p; import java.io.DataInputStream; import java.io.IOException; import java.nio.ByteBuffer; import w.DoomIO; import w.IPackableDoomObject; import w.IReadableDoomObject; import data.state_t; public class pspdef_t implements IReadableDoomObject,IPackableDoomObject{ public pspdef_t(){ state=new state_t(); } /** a NULL state means not active */ public state_t state; public int tics; /** fixed_t */ public int sx, sy; // When read from disk. public int readstate; @Override public void read(DataInputStream f) throws IOException { //state=data.info.states[f.readLEInt()]; readstate=DoomIO.readLEInt(f); tics=DoomIO.readLEInt(f); sx=DoomIO.readLEInt(f); sy=DoomIO.readLEInt(f); } @Override public void pack(ByteBuffer f) throws IOException { if (state==null) f.putInt(0); else f.putInt(state.id); f.putInt(tics); f.putInt(sx); f.putInt(sy); } } package p; import static data.Defines.BLOCKMAPPADDING; import static data.Defines.MAPBLOCKSHIFT; import static data.Defines.MAPBLOCKUNITS; import static data.Defines.NF_SUBSECTOR; import static data.Defines.PU_LEVEL; import static m.fixed_t.FRACBITS; import static p.mobj_t.MF_NOBLOCKMAP; import static p.mobj_t.MF_NOSECTOR; import static utils.C2JUtils.flags; import m.BBox; import i.IDoomSystem; import rr.Renderer; import rr.TextureManager; import rr.line_t; import rr.node_t; import rr.sector_t; import rr.seg_t; import rr.side_t; import rr.subsector_t; import rr.vertex_t; import s.IDoomSound; import utils.C2JUtils; import v.DoomVideoRenderer; import w.IWadLoader; import data.Limits; import data.mapthing_t; import doom.DoomStatus; /** * The idea is to lump common externally readable properties that need DIRECT * ACCESS (aka not behindsetters/getters) here, as well as some common shared * internal structures/status objects. If you need access to stuff like the * blockmap/reject table etc. then you should ask for this class. If you only * need access to some methods like e.g. SetupLevel, you can simply use the * ILevelLoader interface. * * @author velktron */ public abstract class AbstractLevelLoader implements ILevelLoader { // ///////////////// Status objects /////////////////// IDoomSystem I; IWadLoader W; DoomStatus<?,?> DM; DoomVideoRenderer<?,?> V; Renderer<?,?> R; TextureManager<?> TM; Actions P; IDoomSound S; // // MAP related Lookup tables. // Store VERTEXES, LINEDEFS, SIDEDEFS, etc. // public int numvertexes; public vertex_t[] vertexes; public int numsegs; public seg_t[] segs; public int numsectors; public sector_t[] sectors; public int numsubsectors; public subsector_t[] subsectors; public int numnodes; public node_t[] nodes; public int numlines; public line_t[] lines; public int numsides; public side_t[] sides; // BLOCKMAP // Created from axis aligned bounding box // of the map, a rectangular array of // blocks of size ... // Used to speed up collision detection // by spatial subdivision in 2D. // /** Blockmap size. */ protected int bmapwidth, bmapheight; // size in mapblocks /** * killough 3/1/98: remove blockmap limit internally. Maes 29/9/2011: Header * stripped during loading and pointers pre-modified, so there's no double * arraying. */ public int[] blockmap; // was short -- killough /** * Maes 29/9/2011: Used only during loading. No more dichotomy with * blockmap. */ protected int[] blockmaplump; // was short -- killough /** (fixed_t) origin of block map */ public int bmaporgx, bmaporgy; /** for thing chains */ public mobj_t[] blocklinks; /** * REJECT For fast sight rejection. Speeds up enemy AI by skipping detailed * LineOf Sight calculation. Without special effect, this could be used as a * PVS lookup as well. */ protected byte[] rejectmatrix; // Maintain single and multi player starting spots. // 1/11/98 killough: Remove limit on deathmatch starts protected mapthing_t[] deathmatchstarts; // killough protected int num_deathmatchstarts; // killough // mapthing_t* deathmatch_p; protected int deathmatch_p; protected mapthing_t[] playerstarts = new mapthing_t[Limits.MAXPLAYERS]; public AbstractLevelLoader(DoomStatus<?,?> DC) { this.updateStatus(DC); } @Override public void updateStatus(DoomStatus<?,?> DC) { this.W = DC.W; this.DM = DC; this.P = DC.P; this.R = DC.R; this.I = DC.I; this.S = DC.S; this.TM = DC.TM; } /** * P_SetThingPosition Links a thing into both a block and a subsector based * on it's x y. Sets thing.subsector properly */ @Override public void SetThingPosition(mobj_t thing) { final subsector_t ss; final sector_t sec; int blockx; int blocky; final mobj_t link; // link into subsector ss = PointInSubsector(thing.x, thing.y); thing.subsector = ss; if (!flags(thing.flags, MF_NOSECTOR)) { // invisible things don't go into the sector links sec = ss.sector; thing.sprev = null; thing.snext = sec.thinglist; if (sec.thinglist != null) sec.thinglist.sprev = thing; sec.thinglist = thing; } // link into blockmap if (!flags(thing.flags, MF_NOBLOCKMAP)) { // inert things don't need to be in blockmap blockx = getSafeBlockX(thing.x - bmaporgx); blocky = getSafeBlockY(thing.y - bmaporgy); // Valid block? if (blockx >= 0 && blockx < bmapwidth && blocky >= 0 && blocky < bmapheight) { // Get said block. link = blocklinks[blocky * bmapwidth + blockx]; thing.bprev = null; // Thing is put at head of block... thing.bnext = link; if (link != null) // block links back at thing... // This will work link.bprev = thing; // "thing" is now effectively the new head // Iterators only follow "bnext", not "bprev". // If link was null, then thing is the first entry. blocklinks[blocky * bmapwidth + blockx] = thing; } else { // thing is off the map thing.bnext = thing.bprev = null; } } } @Override public subsector_t PointInSubsector(int x, int y) { node_t node; int side; int nodenum; // single subsector is a special case if (numnodes == 0) return subsectors[0]; nodenum = numnodes - 1; while ((nodenum & NF_SUBSECTOR) == 0) { node = nodes[nodenum]; side = node.PointOnSide(x, y); nodenum = node.children[side]; } return subsectors[nodenum & ~NF_SUBSECTOR]; } // // jff 10/6/98 // New code added to speed up calculation of internal blockmap // Algorithm is order of nlines*(ncols+nrows) not nlines*ncols*nrows // protected static final int blkshift = 7; /* * places to shift rel position for * cell num */ protected static final int blkmask = ((1 << blkshift) - 1);/* * mask for rel * position * within cell */ protected static final int blkmargin = 0; /* size guardband around map used */ // jff 10/8/98 use guardband>0 // jff 10/12/98 0 ok with + 1 in rows,cols protected class linelist_t // type used to list lines in each block { public int num; public linelist_t next; } /** * Subroutine to add a line number to a block list It simply returns if the * line is already in the block * * @param lists * @param count * @param done * @param blockno * @param lineno */ private final void AddBlockLine(linelist_t[] lists, int[] count, boolean[] done, int blockno, int lineno) { long a=System.nanoTime(); linelist_t l; if (done[blockno]) return; l = new linelist_t(); l.num = lineno; l.next = lists[blockno]; lists[blockno] = l; count[blockno]++; done[blockno] = true; long b=System.nanoTime(); total+=(b-a); } long total=0; /** * Actually construct the blockmap lump from the level data This finds the * intersection of each linedef with the column and row lines at the left * and bottom of each blockmap cell. It then adds the line to all block * lists touching the intersection. MAES 30/9/2011: Converted to Java. It's * important that LINEDEFS and VERTEXES are already read-in and defined, so * it is necessary to change map lump ordering for this to work. */ protected final void CreateBlockMap() { int xorg, yorg; // blockmap origin (lower left) int nrows, ncols; // blockmap dimensions linelist_t[] blocklists = null; // array of pointers to lists of lines int[] blockcount = null; // array of counters of line lists boolean[] blockdone = null; // array keeping track of blocks/line int NBlocks; // number of cells = nrows*ncols int linetotal = 0; // total length of all blocklists int map_minx = Integer.MAX_VALUE; // init for map limits search int map_miny = Integer.MAX_VALUE; int map_maxx = Integer.MIN_VALUE; int map_maxy = Integer.MIN_VALUE; long a = System.nanoTime(); // scan for map limits, which the blockmap must enclose for (int i = 0; i < numvertexes; i++) { int t; if ((t = vertexes[i].x) < map_minx) map_minx = t; else if (t > map_maxx) map_maxx = t; if ((t = vertexes[i].y) < map_miny) map_miny = t; else if (t > map_maxy) map_maxy = t; } map_minx >>= FRACBITS; // work in map coords, not fixed_t map_maxx >>= FRACBITS; map_miny >>= FRACBITS; map_maxy >>= FRACBITS; // set up blockmap area to enclose level plus margin xorg = map_minx - blkmargin; yorg = map_miny - blkmargin; ncols = (map_maxx + blkmargin - xorg + 1 + blkmask) >> blkshift; // jff // 10/12/98 nrows = (map_maxy + blkmargin - yorg + 1 + blkmask) >> blkshift; // +1 // needed // for NBlocks = ncols * nrows; // map exactly 1 cell // create the array of pointers on NBlocks to blocklists // also create an array of linelist counts on NBlocks // finally make an array in which we can mark blocks done per line // CPhipps - calloc's blocklists = new linelist_t[NBlocks]; blockcount = new int[NBlocks]; blockdone = new boolean[NBlocks]; // initialize each blocklist, and enter the trailing -1 in all // blocklists // note the linked list of lines grows backwards for (int i = 0; i < NBlocks; i++) { blocklists[i] = new linelist_t(); blocklists[i].num = -1; blocklists[i].next = null; blockcount[i]++; } // For each linedef in the wad, determine all blockmap blocks it // touches, // and add the linedef number to the blocklists for those blocks for (int i = 0; i < numlines; i++) { int x1 = lines[i].v1x >> FRACBITS; // lines[i] map coords int y1 = lines[i].v1y >> FRACBITS; int x2 = lines[i].v2x >> FRACBITS; int y2 = lines[i].v2y >> FRACBITS; int dx = x2 - x1; int dy = y2 - y1; boolean vert = dx == 0; // lines[i] slopetype boolean horiz = dy == 0; boolean spos = (dx ^ dy) > 0; boolean sneg = (dx ^ dy) < 0; int bx, by; // block cell coords int minx = x1 > x2 ? x2 : x1; // extremal lines[i] coords int maxx = x1 > x2 ? x1 : x2; int miny = y1 > y2 ? y2 : y1; int maxy = y1 > y2 ? y1 : y2; // no blocks done for this linedef yet C2JUtils.memset(blockdone, false, NBlocks); // The line always belongs to the blocks containing its endpoints bx = (x1 - xorg) >> blkshift; by = (y1 - yorg) >> blkshift; AddBlockLine(blocklists, blockcount, blockdone, by * ncols + bx, i); bx = (x2 - xorg) >> blkshift; by = (y2 - yorg) >> blkshift; AddBlockLine(blocklists, blockcount, blockdone, by * ncols + bx, i); // For each column, see where the line along its left edge, which // it contains, intersects the Linedef i. Add i to each // corresponding // blocklist. if (!vert) // don't interesect vertical lines with columns { for (int j = 0; j < ncols; j++) { // intersection of Linedef with x=xorg+(j<<blkshift) // (y-y1)*dx = dy*(x-x1) // y = dy*(x-x1)+y1*dx; int x = xorg + (j << blkshift); // (x,y) is intersection int y = (dy * (x - x1)) / dx + y1; int yb = (y - yorg) >> blkshift; // block row number int yp = (y - yorg) & blkmask; // y position within block if (yb < 0 || yb > nrows - 1) // outside blockmap, continue continue; if (x < minx || x > maxx) // line doesn't touch column continue; // The cell that contains the intersection point is always // added AddBlockLine(blocklists, blockcount, blockdone, ncols * yb + j, i); // if the intersection is at a corner it depends on the // slope // (and whether the line extends past the intersection) // which // blocks are hit if (yp == 0) // intersection at a corner { if (sneg) // \ - blocks x,y-, x-,y { if (yb > 0 && miny < y) AddBlockLine(blocklists, blockcount, blockdone, ncols * (yb - 1) + j, i); if (j > 0 && minx < x) AddBlockLine(blocklists, blockcount, blockdone, ncols * yb + j - 1, i); } else if (spos) // / - block x-,y- { if (yb > 0 && j > 0 && minx < x) AddBlockLine(blocklists, blockcount, blockdone, ncols * (yb - 1) + j - 1, i); } else if (horiz) // - - block x-,y { if (j > 0 && minx < x) AddBlockLine(blocklists, blockcount, blockdone, ncols * yb + j - 1, i); } } else if (j > 0 && minx < x) // else not at corner: x-,y AddBlockLine(blocklists, blockcount, blockdone, ncols * yb + j - 1, i); } } // For each row, see where the line along its bottom edge, which // it contains, intersects the Linedef i. Add i to all the // corresponding // blocklists. if (!horiz) { for (int j = 0; j < nrows; j++) { // intersection of Linedef with y=yorg+(j<<blkshift) // (x,y) on Linedef i satisfies: (y-y1)*dx = dy*(x-x1) // x = dx*(y-y1)/dy+x1; int y = yorg + (j << blkshift); // (x,y) is intersection int x = (dx * (y - y1)) / dy + x1; int xb = (x - xorg) >> blkshift; // block column number int xp = (x - xorg) & blkmask; // x position within block if (xb < 0 || xb > ncols - 1) // outside blockmap, continue continue; if (y < miny || y > maxy) // line doesn't touch row continue; // The cell that contains the intersection point is always // added AddBlockLine(blocklists, blockcount, blockdone, ncols * j + xb, i); // if the intersection is at a corner it depends on the // slope // (and whether the line extends past the intersection) // which // blocks are hit if (xp == 0) // intersection at a corner { if (sneg) // \ - blocks x,y-, x-,y { if (j > 0 && miny < y) AddBlockLine(blocklists, blockcount, blockdone, ncols * (j - 1) + xb, i); if (xb > 0 && minx < x) AddBlockLine(blocklists, blockcount, blockdone, ncols * j + xb - 1, i); } else if (vert) // | - block x,y- { if (j > 0 && miny < y) AddBlockLine(blocklists, blockcount, blockdone, ncols * (j - 1) + xb, i); } else if (spos) // / - block x-,y- { if (xb > 0 && j > 0 && miny < y) AddBlockLine(blocklists, blockcount, blockdone, ncols * (j - 1) + xb - 1, i); } } else if (j > 0 && miny < y) // else not on a corner: x,y- AddBlockLine(blocklists, blockcount, blockdone, ncols * (j - 1) + xb, i); } } } // Add initial 0 to all blocklists // count the total number of lines (and 0's and -1's) C2JUtils.memset(blockdone, false, NBlocks); linetotal = 0; for (int i = 0; i < NBlocks; i++) { AddBlockLine(blocklists, blockcount, blockdone, i, 0); linetotal += blockcount[i]; } // Create the blockmap lump // blockmaplump = malloc_IfSameLevel(blockmaplump, 4 + NBlocks + // linetotal); blockmaplump = new int[(int) (4 + NBlocks + linetotal)]; // blockmap header blockmaplump[0] = bmaporgx = xorg << FRACBITS; blockmaplump[1] = bmaporgy = yorg << FRACBITS; blockmaplump[2] = bmapwidth = ncols; blockmaplump[3] = bmapheight = nrows; // offsets to lists and block lists for (int i = 0; i < NBlocks; i++) { linelist_t bl = blocklists[i]; int offs = blockmaplump[4 + i] = // set offset to block's list (i != 0 ? blockmaplump[4 + i - 1] : 4 + NBlocks) + (i != 0 ? blockcount[i - 1] : 0); // add the lines in each block's list to the blockmaplump // delete each list node as we go while (bl != null) { linelist_t tmp = bl.next; blockmaplump[offs++] = bl.num; bl = tmp; } } long b = System.nanoTime(); System.err.printf("Blockmap generated in %f sec\n", (b - a) / 1e9); System.err.printf("Time spend in AddBlockLine : %f sec\n", total / 1e9); } // jff 10/6/98 // End new code added to speed up calculation of internal blockmap // // P_VerifyBlockMap // // haleyjd 03/04/10: do verification on validity of blockmap. // protected boolean VerifyBlockMap(int count) { int x, y; int p_maxoffs = count; for (y = 0; y < bmapheight; y++) { for (x = 0; x < bmapwidth; x++) { int offset; int p_list; int tmplist; int blockoffset; offset = y * bmapwidth + x; blockoffset = offset + 4; // That's where the shit starts. // check that block offset is in bounds if (blockoffset >= p_maxoffs) { System.err .printf("P_VerifyBlockMap: block offset overflow\n"); return false; } offset = blockmaplump[blockoffset]; // check that list offset is in bounds if (offset < 4 || offset >= count) { System.err .printf("P_VerifyBlockMap: list offset overflow\n"); return false; } p_list = offset; // scan forward for a -1 terminator before maxoffs for (tmplist = p_list;; tmplist++) { // we have overflowed the lump? if (tmplist >= p_maxoffs) { System.err.printf("P_VerifyBlockMap: open blocklist\n"); return false; } if (blockmaplump[tmplist] == -1) // found -1 break; } // scan the list for out-of-range linedef indicies in list for (tmplist = p_list; blockmaplump[tmplist] != -1; tmplist++) { if (blockmaplump[tmplist] < 0 || blockmaplump[tmplist] >= numlines) { System.err .printf("P_VerifyBlockMap: index >= numlines\n"); return false; } } } } return true; } // cph - convenient sub-function protected void AddLineToSector(line_t li, sector_t sector) { int[] bbox = sector.blockbox; sector.lines[sector.linecount++] = li; BBox.AddToBox(bbox, li.v1.x, li.v1.y); BBox.AddToBox(bbox, li.v2.x, li.v2.y); } /** * Compute density of reject table. Aids choosing LUT optimizations. * * @return */ protected float rejectDensity() { // float[] rowdensity=new float[numsectors]; float tabledensity; int tcount = 0; for (int i = 0; i < numsectors; i++) { // int colcount=0; for (int j = 0; j < numsectors; j++) { // Determine subsector entries in REJECT table. int pnum = i * numsectors + j; int bytenum = pnum >> 3; int bitnum = 1 << (pnum & 7); // Check in REJECT table. if (!flags(rejectmatrix[bytenum], bitnum)) { tcount++; // colcount++; } } // rowdensity[i]=((float)colcount/numsectors); } tabledensity = (float) tcount / (numsectors * numsectors); return tabledensity; } protected static int[] POKE_REJECT = new int[] { 1, 2, 4, 8, 16, 32, 64, 128 }; /** * Updates Reject table dynamically based on what expensive LOS checks say. * It does decrease the "reject density" the longer the level runs, however * its by no means perfect, and results in many sleeping monsters. When * called, visibility between sectors x and y will be set to "false" for the * rest of the level, aka they will be rejected based on subsequent sight * checks. * * @param x * @param y */ protected void pokeIntoReject(int x, int y) { // Locate bit pointer e.g. for a 4x4 table, x=2 and y=3 give // 3*4+2=14 final int pnum = y * numsectors + x; // Which byte? // 14= 1110 >>3 = 0001 so // Byte 0 Byte 1 // xxxxxxxx xxxxxxxx // ^ // 0.....bits......16 // We are writing inside the second Byte 1 final int bytenum = pnum >> 3; // OK, so how we pinpoint that one bit? // 1110 & 0111 = 0110 = 6 so it's the sixth bit // of the second byte final int bitnum = pnum & 7; // This sets only that one bit, and the reject lookup will be faster // next time. rejectmatrix[bytenum] |= POKE_REJECT[bitnum]; System.out.println(rejectDensity()); } protected void retrieveFromReject(int x, int y, boolean value) { // Locate bit pointer e.g. for a 4x4 table, x=2 and y=3 give // 3*4+2=14 final int pnum = y * numsectors + x; // Which byte? // 14= 1110 >>3 = 0001 so // Byte 0 Byte 1 // xxxxxxxx xxxxxxxx // ^ // 0.....bits......16 // We are writing inside the second Byte 1 final int bytenum = pnum >> 3; // OK, so how we pinpoint that one bit? // 1110 & 0111 = 0110 = 6 so it's the sixth bit // of the second byte final int bitnum = pnum & 7; // This sets only that one bit, and the reject lookup will be faster // next time. rejectmatrix[bytenum] |= POKE_REJECT[bitnum]; System.out.println(rejectDensity()); } // Keeps track of lines that belong to a sector, to exclude e.g. // orphaned ones from the blockmap. protected boolean[] used_lines; // MAES: extensions to support 512x512 blockmaps. // They represent the maximum negative number which represents // a positive offset, otherwise they are left at -257, which // never triggers a check. // If a blockmap index is ever LE than either, then // its actual value is to be interpreted as 0x01FF&x. // Full 512x512 blockmaps get this value set to -1. // A 511x511 blockmap would still have a valid negative number // e.g. -1..510, so they would be set to -2 public int blockmapxneg=-257; public int blockmapyneg=-257; /** * Returns an int[] array with orgx, orgy, and number of blocks. Order is: * orgx,orgy,bckx,bcky * * @return */ protected final int[] getMapBoundingBox(boolean playable) { int minx = Integer.MAX_VALUE; int miny = Integer.MAX_VALUE; int maxx = Integer.MIN_VALUE; int maxy = Integer.MIN_VALUE; // Scan linedefs to detect extremes for (int i = 0; i < this.lines.length; i++) { if (playable || used_lines[i]) { if (lines[i].v1x > maxx) maxx = lines[i].v1x; if (lines[i].v1x < minx) minx = lines[i].v1x; if (lines[i].v1y > maxy) maxy = lines[i].v1y; if (lines[i].v1y < miny) miny = lines[i].v1y; if (lines[i].v2x > maxx) maxx = lines[i].v2x; if (lines[i].v2x < minx) minx = lines[i].v2x; if (lines[i].v2y > maxy) maxy = lines[i].v2y; if (lines[i].v2y < miny) miny = lines[i].v2y; } } System.err.printf("Map bounding %d %d %d %d\n", minx >> FRACBITS, miny >> FRACBITS, maxx >> FRACBITS, maxy >> FRACBITS); // Blow up bounding to the closest 128-sized block, adding 8 units as // padding. // This seems to be the "official" formula. int orgx = -BLOCKMAPPADDING + MAPBLOCKUNITS * (minx / MAPBLOCKUNITS); int orgy = -BLOCKMAPPADDING + MAPBLOCKUNITS * (miny / MAPBLOCKUNITS); int bckx = ((BLOCKMAPPADDING + maxx) - orgx); int bcky = ((BLOCKMAPPADDING + maxy) - orgy); System.err.printf("%d %d %d %d\n", orgx >> FRACBITS, orgy >> FRACBITS, 1 + (bckx >> MAPBLOCKSHIFT), 1 + (bcky >> MAPBLOCKSHIFT)); return new int[] { orgx, orgy, bckx, bcky }; } protected void LoadReject(int lumpnum) { byte[] tmpreject = new byte[0]; // _D_: uncommented the rejectmatrix variable, this permitted changing // level to work try { tmpreject = W.CacheLumpNumAsRawBytes(lumpnum, PU_LEVEL); } catch (Exception e) { // Any exception at this point means missing REJECT lump. Fuck that, // and move on. // If everything goes OK, tmpreject will contain the REJECT lump's // data // BUT, alas, we're not done yet. } // Sanity check on matrix. // E.g. a 5-sector map will result in ceil(25/8)=4 bytes. // If the reject table is broken/corrupt, too bad. It will all be // zeroes. // Much better than overflowing. // TODO: build-in a REJECT-matrix rebuilder? rejectmatrix = new byte[(int) (Math .ceil((this.numsectors * this.numsectors) / 8.0))]; System.arraycopy(tmpreject, 0, rejectmatrix, 0, Math.min(tmpreject.length, rejectmatrix.length)); // Do warn on atypical reject map lengths, but use either default // all-zeroes one, // or whatever you happened to read anyway. if (tmpreject.length < rejectmatrix.length) System.err.printf("BROKEN REJECT MAP! Length %d expected %d\n", tmpreject.length, rejectmatrix.length); // Maes: purely academic. Most maps are well above 0.68 // System.out.printf("Reject table density: %f",rejectDensity()); } /** Gets the proper blockmap block for a given X 16.16 Coordinate, sanitized * for 512-wide blockmaps. * * @param blockx * @return */ public final int getSafeBlockX(int blockx){ blockx>>=MAPBLOCKSHIFT; return (blockx<=this.blockmapxneg)?blockx&0x1FF:blockx; } public final int getSafeBlockX(long blockx){ blockx>>=MAPBLOCKSHIFT; return (int) ((blockx<=this.blockmapxneg)?blockx&0x1FF:blockx); } /** Gets the proper blockmap block for a given Y 16.16 Coordinate, sanitized * for 512-wide blockmaps. * * @param blocky * @return */ public final int getSafeBlockY(int blocky){ blocky>>=MAPBLOCKSHIFT; return (blocky<=this.blockmapyneg)?blocky&0x1FF:blocky; } public final int getSafeBlockY(long blocky){ blocky>>=MAPBLOCKSHIFT; return (int) ((blocky<=this.blockmapyneg)?blocky&0x1FF:blocky); } /// Sector tag stuff, lifted off Boom /** Hash the sector tags across the sectors and linedefs. * Call in SpawnSpecials. */ public void InitTagLists() { int i; for (i=numsectors; --i>=0; ) // Initially make all slots empty. sectors[i].firsttag = -1; for (i=numsectors; --i>=0; ) // Proceed from last to first sector { // so that lower sectors appear first int j = sectors[i].tag % numsectors; // Hash func sectors[i].nexttag = sectors[j].firsttag; // Prepend sector to chain sectors[j].firsttag = i; } // killough 4/17/98: same thing, only for linedefs for (i=numlines; --i>=0; ) // Initially make all slots empty. lines[i].firsttag = -1; for (i=numlines; --i>=0; ) // Proceed from last to first linedef { // so that lower linedefs appear first int j = lines[i].tag % numlines; // Hash func lines[i].nexttag = lines[j].firsttag; // Prepend linedef to chain lines[j].firsttag = i; } } } package p; // // P_PLATS // public enum plat_e { up, down, waiting, in_stasis } package p; public class slideframe_t { int[] frontFrames = new int[4]; int[] backFrames = new int[4]; } package p; import static m.fixed_t.FixedDiv; import static m.fixed_t.FixedMul; import static utils.C2JUtils.eval; public class MapUtils { /** * AproxDistance * Gives an estimation of distance (not exact) * * @param dx fixed_t * @param dy fixed_t * @return fixed_t */ // public static int AproxDistance ( int dx, int dy ) { dx = Math.abs(dx); dy = Math.abs(dy); if (dx < dy) return dx+dy-(dx>>1); return dx+dy-(dy>>1); } /** * P_InterceptVector * Returns the fractional intercept point * along the first divline. * This is only called by the addthings * and addlines traversers. * * @return int to be treated as fixed_t */ public static int InterceptVector ( divline_t v2, divline_t v1 ) { int frac, num,den; // fixed_t den = FixedMul (v1.dy>>8,v2.dx) - FixedMul(v1.dx>>8,v2.dy); if (den == 0) return 0; // I_Error ("P_InterceptVector: parallel"); num = FixedMul ( (v1.x - v2.x)>>8 ,v1.dy ) +FixedMul ( (v2.y - v1.y)>>8, v1.dx ); frac = FixedDiv (num , den); return frac; /* #else // UNUSED, float debug. float frac; float num; float den; float v1x; float v1y; float v1dx; float v1dy; float v2x; float v2y; float v2dx; float v2dy; v1x = (float)v1.x/FRACUNIT; v1y = (float)v1.y/FRACUNIT; v1dx = (float)v1.dx/FRACUNIT; v1dy = (float)v1.dy/FRACUNIT; v2x = (float)v2.x/FRACUNIT; v2y = (float)v2.y/FRACUNIT; v2dx = (float)v2.dx/FRACUNIT; v2dy = (float)v2.dy/FRACUNIT; den = v1dy*v2dx - v1dx*v2dy; if (den == 0) return 0; // parallel num = (v1x - v2x)*v1dy + (v2y - v1y)*v1dx; frac = num / den; return frac*FRACUNIT; #endif */ } /* cph - this is killough's 4/19/98 version of P_InterceptVector and * P_InterceptVector2 (which were interchangeable). We still use this * in compatibility mode. */ private static final int P_InterceptVector2(final divline_t v2, final divline_t v1) { int den; return eval(den = FixedMul(v1.dy>>8, v2.dx) - FixedMul(v1.dx>>8, v2.dy)) ? FixedDiv(FixedMul((v1.x - v2.x)>>8, v1.dy) + FixedMul((v2.y - v1.y)>>8, v1.dx), den) : 0; } /** Used by CrossSubSector * * @param v2 * @param v1 * @return */ public static final int P_InterceptVector(final divline_t v2, final divline_t v1) { if (false/*compatibility_level < prboom_4_compatibility*/) return P_InterceptVector2(v2, v1); else { /* cph - This was introduced at prboom_4_compatibility - no precision/overflow problems */ long den = (long)v1.dy * v2.dx - (long)v1.dx * v2.dy; den >>= 16; if (!eval(den)) return 0; return (int)(((long)(v1.x - v2.x) * v1.dy - (long)(v1.y - v2.y) * v1.dx) / den); } } /** * P_InterceptVector2 Returns the fractional intercept point along the * first divline. This is only called by the addthings and addlines * traversers. * * @param v2 * @param v1 * @returnP_InterceptVector2 */ public static final int InterceptVector2(divline_t v2, divline_t v1) { int frac; // fixed_t int num; // fixed_t int den; // fixed_t den = FixedMul(v1.dy >> 8, v2.dx) - FixedMul(v1.dx >> 8, v2.dy); if (den == 0) return 0; // I_Error ("P_InterceptVector: parallel"); num = FixedMul((v1.x - v2.x) >> 8, v1.dy) + FixedMul((v2.y - v1.y) >> 8, v1.dx); frac = FixedDiv(num, den); return frac; } } package p; import static m.fixed_t.MAPFRACUNIT; import static data.Defines.TIC_MUL; public final class ChaseDirections { public static final int DI_EAST = 0; public static final int DI_NORTHEAST = 1; public static final int DI_NORTH = 2; public static final int DI_NORTHWEST = 3; public static final int DI_WEST = 4; public static final int DI_SOUTHWEST = 5; public static final int DI_SOUTH = 6; public static final int DI_SOUTHEAST = 7; public static final int DI_NODIR = 8; public static final int NUMDIR = 9; // // P_NewChaseDir related LUT. // public final static int opposite[] = { DI_WEST, DI_SOUTHWEST, DI_SOUTH, DI_SOUTHEAST, DI_EAST, DI_NORTHEAST, DI_NORTH, DI_NORTHWEST, DI_NODIR }; public final static int diags[] = { DI_NORTHWEST, DI_NORTHEAST, DI_SOUTHWEST, DI_SOUTHEAST }; public final static int[] xspeed = { MAPFRACUNIT, 47000/TIC_MUL, 0, -47000/TIC_MUL, -MAPFRACUNIT, -47000/TIC_MUL, 0, 47000/TIC_MUL }; // all // fixed public final static int[] yspeed = { 0, 47000/TIC_MUL, MAPFRACUNIT, 47000/TIC_MUL, 0, -47000/TIC_MUL, -MAPFRACUNIT, -47000/TIC_MUL }; // all // fixed } package p; public interface ISightChecker { public void setZStartTopBOttom(int zstart, int top, int bottom); public void setSTrace(mobj_t t1, mobj_t t2); public boolean CrossBSPNode(int bspnum); } package p; import defines.*; import static p.ChaseDirections.*; import static p.DoorDefines.*; import static data.Defines.BASETHRESHOLD; import static data.Defines.FLOATSPEED; import static data.Defines.ITEMQUESIZE; import static data.Defines.MAPBLOCKSHIFT; import static data.Defines.MAPBLOCKSIZE; import static data.Defines.MAPBTOFRAC; import static data.Defines.MELEERANGE; import static data.Defines.MISSILERANGE; import static rr.line_t.*; import static data.Defines.MTF_AMBUSH; import static data.Defines.NUMCARDS; import static data.Defines.ONCEILINGZ; import static data.Defines.ONFLOORZ; import static data.Defines.PST_DEAD; import static data.Defines.PST_LIVE; import static data.Defines.PST_REBORN; import static data.Defines.PT_ADDLINES; import static data.Defines.PT_ADDTHINGS; import static data.Defines.PT_EARLYOUT; import static data.Defines.USERANGE; import static data.Defines.VIEWHEIGHT; import static data.Defines.pw_invulnerability; import static data.Limits.CEILSPEED; import static data.Limits.MAXCEILINGS; import static data.Limits.MAXINT; import static data.Limits.MAXMOVE; import static data.Limits.MAXPLAYERS; import static data.Limits.MAXRADIUS; import static data.Limits.MAXSPECIALCROSS; import static data.Limits.NUMMOBJTYPES; import static data.Tables.*; import static data.info.mobjinfo; import static data.info.states; import static doom.englsh.PD_BLUEK; import static doom.englsh.PD_BLUEO; import static doom.englsh.PD_REDK; import static doom.englsh.PD_REDO; import static doom.englsh.PD_YELLOWK; import static doom.englsh.PD_YELLOWO; import static m.BBox.BOXBOTTOM; import static m.BBox.BOXLEFT; import static m.BBox.BOXRIGHT; import static m.BBox.BOXTOP; import static m.fixed_t.FRACBITS; import static m.fixed_t.FRACUNIT; import static m.fixed_t.MAPFRACUNIT; import static m.fixed_t.FixedDiv; import static m.fixed_t.FixedMul; import static p.ChaseDirections.DI_EAST; import static p.ChaseDirections.DI_NODIR; import static p.ChaseDirections.DI_NORTH; import static p.ChaseDirections.DI_SOUTH; import static p.ChaseDirections.DI_SOUTHEAST; import static p.ChaseDirections.DI_WEST; import static p.ChaseDirections.diags; import static p.ChaseDirections.opposite; import static p.MapUtils.AproxDistance; import static utils.C2JUtils.*; import static p.mobj_t.*; import rr.line_t; import rr.sector_t; import rr.side_t; import rr.subsector_t; import utils.C2JUtils; import data.Tables; import data.mapthing_t; import data.mobjinfo_t; import data.mobjtype_t; import data.state_t; import data.sounds.sfxenum_t; import doom.DoomStatus; import doom.player_t; import doom.think_t; import doom.thinker_t; import doom.weapontype_t; /** Action functions need to be aware of: * Random number generator (RND) * Broader gameplay context (DoomGame) * Some door actions * Playfield checks. * etc. etc. so they COULD be made into a separate file * * @author admin * */ public class Actions extends UnifiedGameMap { // // CEILINGS // private ceiling_t[] activeceilings=new ceiling_t[MAXCEILINGS]; /** This needs to be called before loading, otherwise * crushers won't be able to be restarted. */ public void ClearCeilingsBeforeLoading(){ this.activeceilings=new ceiling_t[MAXCEILINGS]; /* for (int i = 0; i < MAXPLATS; i++) { this.activeceilings[i] = null; } */ } /** * T_MoveCeiling */ protected void MoveCeiling (ceiling_t ceiling) { result_e res; switch(ceiling.direction) { case 0: // IN STASIS break; case 1: // UP res = MovePlane(ceiling.sector, ceiling.speed, ceiling.topheight, false,1,ceiling.direction); if (!eval(DM.leveltime&7)) { switch(ceiling.type) { case silentCrushAndRaise: break; default: S.StartSound(ceiling.sector.soundorg,sfxenum_t.sfx_stnmov); // ? break; } } if (res == result_e.pastdest) { switch(ceiling.type) { case raiseToHighest: RemoveActiveCeiling(ceiling); break; case silentCrushAndRaise: S.StartSound(ceiling.sector.soundorg, sfxenum_t.sfx_pstop); case fastCrushAndRaise: case crushAndRaise: ceiling.direction = -1; break; default: break; } } break; case -1: // DOWN res = MovePlane(ceiling.sector, ceiling.speed, ceiling.bottomheight, ceiling.crush,1,ceiling.direction); if (!eval(DM.leveltime&7)) { switch(ceiling.type) { case silentCrushAndRaise: break; default: S.StartSound(ceiling.sector.soundorg, sfxenum_t.sfx_stnmov); } } if (res == result_e.pastdest) { switch(ceiling.type) { case silentCrushAndRaise: S.StartSound(ceiling.sector.soundorg, sfxenum_t.sfx_pstop); case crushAndRaise: ceiling.speed = CEILSPEED; case fastCrushAndRaise: ceiling.direction = 1; break; case lowerAndCrush: case lowerToFloor: RemoveActiveCeiling(ceiling); break; default: break; } } else // ( res != result_e.pastdest ) { if (res == result_e.crushed) { switch(ceiling.type) { case silentCrushAndRaise: case crushAndRaise: case lowerAndCrush: ceiling.speed = CEILSPEED / 8; break; default: break; } } } break; } } // // Add an active ceiling // public void AddActiveCeiling(ceiling_t c) { int i; for (i = 0; i < getMaxCeilings();i++) { if (getActiveCeilings()[i] == null) { getActiveCeilings()[i] = c; return; } } // Needs rezising setActiveceilings(C2JUtils.resize(c, getActiveCeilings(), 2*getActiveCeilings().length)); } // // Remove a ceiling's thinker // protected void RemoveActiveCeiling(ceiling_t c) { int i; for (i = 0;i < getMaxCeilings();i++) { if (getActiveCeilings()[i] == c) { getActiveCeilings()[i].sector.specialdata = null; RemoveThinker (getActiveCeilings()[i]); getActiveCeilings()[i] = null; break; } } } // // Restart a ceiling that's in-stasis // protected void ActivateInStasisCeiling(line_t line) { int i; for (i = 0;i < getMaxCeilings();i++) { if (getActiveCeilings()[i]!=null && (getActiveCeilings()[i].tag == line.tag) && (getActiveCeilings()[i].direction == 0)) { getActiveCeilings()[i].direction = getActiveCeilings()[i].olddirection; getActiveCeilings()[i].function = think_t.T_MoveCeiling; FUNS.doWireThinker( getActiveCeilings()[i]); } } } /** * MOVE A FLOOR TO IT'S DESTINATION (UP OR DOWN) * */ protected void MoveFloor(floormove_t floor) { result_e res; res = MovePlane(floor.sector, floor.speed, floor.floordestheight, floor.crush,0,floor.direction); if (!eval(DM.leveltime&7)) S.StartSound(floor.sector.soundorg, sfxenum_t.sfx_stnmov); if (res == result_e.pastdest) { floor.sector.specialdata = null; if (floor.direction == 1) { switch(floor.type) { case donutRaise: floor.sector.special = (short) floor.newspecial; floor.sector.floorpic = floor.texture; default: break; } } else if (floor.direction == -1) { switch(floor.type) //TODO: check if a null floor.type is valid or a bug // MAES: actually, type should always be set to something. // In C, this means "zero" or "null". In Java, we must make sure // it's actually set to something all the time. { case lowerAndChange: floor.sector.special = (short) floor.newspecial; floor.sector.floorpic = floor.texture; default: break; } } RemoveThinker(floor); S.StartSound(floor.sector.soundorg, sfxenum_t.sfx_pstop); } } // // EV.DoCeiling // Move a ceiling up/down and all around! // protected boolean DoCeiling( line_t line, ceiling_e type ) { int secnum=-1; boolean rtn=false; sector_t sec; ceiling_t ceiling; // Reactivate in-stasis ceilings...for certain types. switch(type) { case fastCrushAndRaise: case silentCrushAndRaise: case crushAndRaise: ActivateInStasisCeiling(line); default: break; } while ((secnum = FindSectorFromLineTag(line,secnum)) >= 0) { sec = LL.sectors[secnum]; if (sec.specialdata!=null) continue; // new door thinker rtn = true; ceiling = new ceiling_t(); sec.specialdata = ceiling; ceiling.function = think_t.T_MoveCeiling; AddThinker (ceiling); ceiling.sector = sec; ceiling.crush = false; switch(type) { case fastCrushAndRaise: ceiling.crush = true; ceiling.topheight = sec.ceilingheight; ceiling.bottomheight = sec.floorheight + (8*FRACUNIT); ceiling.direction = -1; ceiling.speed = CEILSPEED * 2; break; case silentCrushAndRaise: case crushAndRaise: ceiling.crush = true; ceiling.topheight = sec.ceilingheight; case lowerAndCrush: case lowerToFloor: ceiling.bottomheight = sec.floorheight; if (type != ceiling_e.lowerToFloor) ceiling.bottomheight += 8*FRACUNIT; ceiling.direction = -1; ceiling.speed = CEILSPEED; break; case raiseToHighest: ceiling.topheight = sec.FindHighestCeilingSurrounding(); ceiling.direction = 1; ceiling.speed = CEILSPEED; break; } ceiling.tag = sec.tag; ceiling.type = type; AddActiveCeiling(ceiling); } return rtn; } /** * Special Stuff that can not be categorized * * (I'm sure it has something to do with John Romero's obsession * with fucking stuff and making them his bitches). * *@param line * */ protected boolean DoDonut(line_t line) { sector_t s1; sector_t s2; sector_t s3; int secnum; boolean rtn; int i; floormove_t floor; secnum = -1; rtn = false; while ((secnum = FindSectorFromLineTag(line,secnum)) >= 0) { s1 = LL.sectors[secnum]; // ALREADY MOVING? IF SO, KEEP GOING... if (s1.specialdata!=null) continue; rtn = true; s2 = s1.lines[0].getNextSector(s1); for (i = 0;i < s2.linecount;i++) { if ((!eval(s2.lines[i].flags & ML_TWOSIDED)) || (s2.lines[i].backsector == s1)) continue; s3 = s2.lines[i].backsector; // Spawn rising slime floor = new floormove_t(); s2.specialdata = floor; floor.function = think_t.T_MoveFloor; AddThinker (floor); floor.type = floor_e.donutRaise; floor.crush = false; floor.direction = 1; floor.sector = s2; floor.speed = FLOORSPEED / 2; floor.texture = s3.floorpic; floor.newspecial = 0; floor.floordestheight = s3.floorheight; // Spawn lowering donut-hole floor = new floormove_t(); s1.specialdata = floor; floor.function = think_t.T_MoveFloor; AddThinker (floor); floor.type = floor_e.lowerFloor; floor.crush = false; floor.direction = -1; floor.sector = s1; floor.speed = FLOORSPEED / 2; floor.floordestheight = s3.floorheight; break; } } return rtn; } // // HANDLE FLOOR TYPES // protected boolean DoFloor( line_t line,floor_e floortype ) { int secnum=-1; boolean rtn=false; int i; sector_t sec; floormove_t floor; while ((secnum = FindSectorFromLineTag(line,secnum)) >= 0) { sec = LL.sectors[secnum]; // ALREADY MOVING? IF SO, KEEP GOING... if (sec.specialdata!=null) continue; // new floor thinker rtn = true; floor = new floormove_t(); sec.specialdata = floor; floor.function = think_t.T_MoveFloor; AddThinker (floor); floor.type = floortype; floor.crush = false; switch(floortype) { case lowerFloor: floor.direction = -1; floor.sector = sec; floor.speed = FLOORSPEED; floor.floordestheight = sec.FindHighestFloorSurrounding(); break; case lowerFloorToLowest: floor.direction = -1; floor.sector = sec; floor.speed = FLOORSPEED; floor.floordestheight = sec.FindLowestFloorSurrounding(); break; case turboLower: floor.direction = -1; floor.sector = sec; floor.speed = FLOORSPEED * 4; floor.floordestheight = sec.FindHighestFloorSurrounding(); if (floor.floordestheight != sec.floorheight) floor.floordestheight += 8*FRACUNIT; break; case raiseFloorCrush: floor.crush = true; case raiseFloor: floor.direction = 1; floor.sector = sec; floor.speed = FLOORSPEED; floor.floordestheight = sec.FindLowestCeilingSurrounding(); if (floor.floordestheight > sec.ceilingheight) floor.floordestheight = sec.ceilingheight; floor.floordestheight -= (8*FRACUNIT)* eval(floortype == floor_e.raiseFloorCrush); break; case raiseFloorTurbo: floor.direction = 1; floor.sector = sec; floor.speed = FLOORSPEED*4; floor.floordestheight = sec.FindNextHighestFloor(sec.floorheight); break; case raiseFloorToNearest: floor.direction = 1; floor.sector = sec; floor.speed = FLOORSPEED; floor.floordestheight = sec.FindNextHighestFloor(sec.floorheight); break; case raiseFloor24: floor.direction = 1; floor.sector = sec; floor.speed = FLOORSPEED; floor.floordestheight = floor.sector.floorheight + 24 * FRACUNIT; break; case raiseFloor512: floor.direction = 1; floor.sector = sec; floor.speed = FLOORSPEED; floor.floordestheight = floor.sector.floorheight + 512 * FRACUNIT; break; case raiseFloor24AndChange: floor.direction = 1; floor.sector = sec; floor.speed = FLOORSPEED; floor.floordestheight = floor.sector.floorheight + 24 * FRACUNIT; sec.floorpic = line.frontsector.floorpic; sec.special = line.frontsector.special; break; case raiseToTexture: { int minsize = MAXINT; side_t side; floor.direction = 1; floor.sector = sec; floor.speed = FLOORSPEED; for (i = 0; i < sec.linecount; i++) { if (twoSided (secnum, i) ) { side = getSide(secnum,i,0); if (side.bottomtexture >= 0) if (TM.getTextureheight(side.bottomtexture) < minsize) minsize = TM.getTextureheight(side.bottomtexture); side = getSide(secnum,i,1); if (side.bottomtexture >= 0) if (TM.getTextureheight(side.bottomtexture) < minsize) minsize = TM.getTextureheight(side.bottomtexture); } } floor.floordestheight = floor.sector.floorheight + minsize; } break; case lowerAndChange: floor.direction = -1; floor.sector = sec; floor.speed = FLOORSPEED; floor.floordestheight = sec.FindLowestFloorSurrounding(); floor.texture = sec.floorpic; for (i = 0; i < sec.linecount; i++) { if ( twoSided(secnum, i) ) { if (getSide(secnum,i,0).sector.id == secnum) { sec = getSector(secnum,i,1); if (sec.floorheight == floor.floordestheight) { floor.texture = sec.floorpic; floor.newspecial = sec.special; break; } } else { sec = getSector(secnum,i,0); if (sec.floorheight == floor.floordestheight) { floor.texture = sec.floorpic; floor.newspecial = sec.special; break; } } } } default: break; } } return rtn; } // // EV_CeilingCrushStop // Stop a ceiling from crushing! // int CeilingCrushStop(line_t line) { int i; int rtn; rtn = 0; for (i = 0;i < getMaxCeilings();i++) { if (getActiveCeilings()[i]!=null && (getActiveCeilings()[i].tag == line.tag) && (getActiveCeilings()[i].direction != 0)) { getActiveCeilings()[i].olddirection = getActiveCeilings()[i].direction; // MAES: don't set it to NOP here, otherwise its thinker will be // removed and it won't be possible to restart it. getActiveCeilings()[i].function = null; getActiveCeilings()[i].direction = 0; // in-stasis rtn = 1; } } return rtn; } /** * BUILD A STAIRCASE! */ protected boolean BuildStairs(line_t line, stair_e type ) { int secnum; int height; int i; int newsecnum; int texture; boolean ok; boolean rtn; sector_t sec; sector_t tsec; floormove_t floor; int stairsize = 0; int speed=0; // shut up compiler secnum = -1; rtn = false; while ((secnum = FindSectorFromLineTag(line,secnum)) >= 0) { sec = LL.sectors[secnum]; // ALREADY MOVING? IF SO, KEEP GOING... if (sec.specialdata!=null) continue; // new floor thinker rtn = true; floor = new floormove_t (); sec.specialdata = floor; floor.function = think_t.T_MoveFloor; AddThinker (floor); floor.direction = 1; floor.sector = sec; switch(type) { case build8: speed = FLOORSPEED/4; stairsize = 8*FRACUNIT; break; case turbo16: speed = FLOORSPEED*4; stairsize = 16*FRACUNIT; break; } floor.speed = speed; height = sec.floorheight + stairsize; floor.floordestheight = height; texture = sec.floorpic; // Find next sector to raise // 1. Find 2-sided line with same sector side[0] // 2. Other side is the next sector to raise do { ok = false; for (i = 0;i < sec.linecount;i++) { if ( !eval((sec.lines[i]).flags & ML_TWOSIDED) ) continue; tsec = (sec.lines[i]).frontsector; newsecnum = tsec.id; if (secnum != newsecnum) continue; tsec = (sec.lines[i]).backsector; newsecnum = tsec.id; if (tsec.floorpic != texture) continue; height += stairsize; if (tsec.specialdata!=null) continue; sec = tsec; secnum = newsecnum; floor = new floormove_t(); sec.specialdata = floor; floor.function = think_t.T_MoveFloor; AddThinker (floor); floor.direction = 1; floor.sector = sec; floor.speed = speed; floor.floordestheight = height; ok = true; break; } } while(ok); } return rtn; } // // VERTICAL DOORS // /** * T_VerticalDoor */ protected void VerticalDoor (vldoor_t door) { result_e res; switch(door.direction) { case 0: // WAITING if (!eval(--door.topcountdown)) { switch(door.type) { case blazeRaise: door.direction = -1; // time to go back down S.StartSound(door.sector.soundorg,sfxenum_t.sfx_bdcls); break; case normal: door.direction = -1; // time to go back down S.StartSound(door.sector.soundorg, sfxenum_t.sfx_dorcls); break; case close30ThenOpen: door.direction = 1; S.StartSound(door.sector.soundorg, sfxenum_t.sfx_doropn); break; default: break; } } break; case 2: // INITIAL WAIT if (!eval(--door.topcountdown)) { switch(door.type) { case raiseIn5Mins: door.direction = 1; door.type = vldoor_e.normal; S.StartSound(door.sector.soundorg, sfxenum_t.sfx_doropn); break; default: break; } } break; case -1: // DOWN res = MovePlane(door.sector, door.speed, door.sector.floorheight, false,1,door.direction); if (res == result_e.pastdest) { switch(door.type) { case blazeRaise: case blazeClose: door.sector.specialdata = null; RemoveThinker (door); // unlink and free S.StartSound(door.sector.soundorg, sfxenum_t.sfx_bdcls); break; case normal: case close: door.sector.specialdata = null; RemoveThinker (door); // unlink and free break; case close30ThenOpen: door.direction = 0; door.topcountdown = 35*30; break; default: break; } } else if (res == result_e.crushed) { switch(door.type) { case blazeClose: case close: // DO NOT GO BACK UP! break; default: door.direction = 1; S.StartSound(door.sector.soundorg, sfxenum_t.sfx_doropn); break; } } break; case 1: // UP res = MovePlane(door.sector, door.speed, door.topheight, false,1,door.direction); if (res == result_e.pastdest) { switch(door.type) { case blazeRaise: case normal: door.direction = 0; // wait at top door.topcountdown = door.topwait; break; case close30ThenOpen: case blazeOpen: case open: door.sector.specialdata = null; RemoveThinker (door); // unlink and free break; default: break; } } break; } } /** * EV_DoLockedDoor * Move a locked door up/down */ protected boolean DoLockedDoor ( line_t line, vldoor_e type, mobj_t thing ) { player_t p; p = thing.player; if (p==null) return false; switch(line.special) { case 99: // Blue Lock case 133: /* if ( p==null ) return false; */ if (!p.cards[card_t.it_bluecard.ordinal()] && !p.cards[card_t.it_blueskull.ordinal()]) { p.message = PD_BLUEO; S.StartSound(null,sfxenum_t.sfx_oof); return false; } break; case 134: // Red Lock case 135: /* if ( p==null ) return false; */ if (!p.cards[card_t.it_redcard.ordinal()] && !p.cards[card_t.it_redskull.ordinal()]) { p.message = PD_REDO; S.StartSound(null,sfxenum_t.sfx_oof); return false; } break; case 136: // Yellow Lock case 137: /* if ( p==null ) return false; */ if (!p.cards[card_t.it_yellowcard.ordinal()] && !p.cards[card_t.it_yellowskull.ordinal()]) { p.message = PD_YELLOWO; S.StartSound(null,sfxenum_t.sfx_oof); return false; } break; } return DoDoor(line,type); } protected boolean DoDoor(line_t line,vldoor_e type) { int secnum; boolean rtn=false; sector_t sec; vldoor_t door; secnum = -1; while ((secnum = FindSectorFromLineTag(line,secnum)) >= 0) { sec = LL.sectors[secnum]; if (sec.specialdata!=null) continue; // new door thinker rtn = true; door = new vldoor_t(); sec.specialdata = door; door.function = think_t.T_VerticalDoor; AddThinker (door); door.sector = sec; door.type = type; door.topwait = VDOORWAIT; door.speed = VDOORSPEED; switch(type) { case blazeClose: door.topheight = sec.FindLowestCeilingSurrounding(); door.topheight -= 4*FRACUNIT; door.direction = -1; door.speed = VDOORSPEED * 4; S.StartSound(door.sector.soundorg, sfxenum_t.sfx_bdcls); break; case close: door.topheight = sec.FindLowestCeilingSurrounding(); door.topheight -= 4*FRACUNIT; door.direction = -1; S.StartSound(door.sector.soundorg, sfxenum_t.sfx_dorcls); break; case close30ThenOpen: door.topheight = sec.ceilingheight; door.direction = -1; S.StartSound(door.sector.soundorg, sfxenum_t.sfx_dorcls); break; case blazeRaise: case blazeOpen: door.direction = 1; door.topheight = sec.FindLowestCeilingSurrounding(); door.topheight -= 4*FRACUNIT; door.speed = VDOORSPEED * 4; if (door.topheight != sec.ceilingheight) S.StartSound(door.sector.soundorg, sfxenum_t.sfx_bdopn); break; case normal: case open: door.direction = 1; door.topheight = sec.FindLowestCeilingSurrounding(); door.topheight -= 4*FRACUNIT; if (door.topheight != sec.ceilingheight) S.StartSound(door.sector.soundorg, sfxenum_t.sfx_doropn); break; default: break; } } return rtn; } /** * EV_VerticalDoor : open a door manually, no tag value */ protected void VerticalDoor(line_t line, mobj_t thing ) { player_t player; //int secnum; sector_t sec; vldoor_t door; int side; side = 0; // only front sides can be used // Check for locks player = thing.player; switch(line.special) { case 26: // Blue Lock case 32: if ( player ==null) return; if (!player.cards[card_t.it_bluecard.ordinal()] && !player.cards[card_t.it_blueskull.ordinal()]) { player.message = PD_BLUEK; S.StartSound(null,sfxenum_t.sfx_oof); return; } break; case 27: // Yellow Lock case 34: if ( player ==null) return; if (!player.cards[card_t.it_yellowcard.ordinal()] && !player.cards[card_t.it_yellowskull.ordinal()]) { player.message = PD_YELLOWK; S.StartSound(null,sfxenum_t.sfx_oof); return; } break; case 28: // Red Lock case 33: if ( player ==null) return; if (!player.cards[card_t.it_redcard.ordinal()] && !player.cards[card_t.it_redskull.ordinal()]) { player.message = PD_REDK; S.StartSound(null,sfxenum_t.sfx_oof); return; } break; } // if the sector has an active thinker, use it sec = LL.sides[ line.sidenum[side^1]].sector; // secnum = sec.id; if (sec.specialdata!=null) { if (sec.specialdata instanceof plat_t) // [MAES]: demo sync for e1nm0646: emulates active plat_t interpreted // as door. TODO: add our own overflow handling class. door = ((plat_t)sec.specialdata).asVlDoor(LL.sectors); else door = (vldoor_t) sec.specialdata; switch(line.special) { case 1: // ONLY FOR "RAISE" DOORS, NOT "OPEN"s case 26: case 27: case 28: case 117: if (door.direction == -1) door.direction = 1; // go back up else { if (thing.player==null) return; // JDC: bad guys never close doors door.direction = -1; // start going down immediately } return; } } // for proper sound switch(line.special) { case 117: // BLAZING DOOR RAISE case 118: // BLAZING DOOR OPEN S.StartSound(sec.soundorg,sfxenum_t.sfx_bdopn); break; case 1: // NORMAL DOOR SOUND case 31: S.StartSound(sec.soundorg,sfxenum_t.sfx_doropn); break; default: // LOCKED DOOR SOUND S.StartSound(sec.soundorg,sfxenum_t.sfx_doropn); break; } // new door thinker door = new vldoor_t(); sec.specialdata = door; door.function = think_t.T_VerticalDoor; AddThinker (door); door.sector = sec; door.direction = 1; door.speed = VDOORSPEED; door.topwait = VDOORWAIT; switch(line.special) { case 1: case 26: case 27: case 28: door.type = vldoor_e.normal; break; case 31: case 32: case 33: case 34: door.type = vldoor_e.open; line.special = 0; break; case 117: // blazing door raise door.type = vldoor_e.blazeRaise; door.speed = VDOORSPEED*4; break; case 118: // blazing door open door.type = vldoor_e.blazeOpen; line.special = 0; door.speed = VDOORSPEED*4; break; } // find the top and bottom of the movement range door.topheight = sec.FindLowestCeilingSurrounding(); door.topheight -= 4*FRACUNIT; } // // P_BulletSlope // Sets a slope so a near miss is at aproximately // the height of the intended target // int bulletslope; void P_BulletSlope (mobj_t mo) { long an; // see which target is to be aimed at // FIXME: angle can already be negative here. // Not a problem if it's just moving about (accumulation will work) // but it needs to be sanitized before being used in any function. an = mo.angle; //_D_: &BITS32 will be used later in this function, by fine(co)sine() bulletslope = AimLineAttack (mo, an/*&BITS32*/, 16*64*FRACUNIT); if (!eval(linetarget)) { an += 1<<26; bulletslope = AimLineAttack (mo, an/*&BITS32*/, 16*64*FRACUNIT); if (!eval(linetarget)) { an -= 2<<26; bulletslope = AimLineAttack (mo, an/*&BITS32*/, 16*64*FRACUNIT); } // Give it one more try, with freelook if (mo.player.lookdir!=0 && !eval(linetarget)) { an += 2<<26; an&=BITS32; bulletslope = (mo.player.lookdir<<FRACBITS)/173; } } } // // P_GunShot // void P_GunShot ( mobj_t mo, boolean accurate ) { long angle; int damage; damage = 5*(RND.P_Random ()%3+1); angle = mo.angle; if (!accurate) angle += (RND.P_Random()-RND.P_Random())<<18; LineAttack (mo, angle, MISSILERANGE, bulletslope, damage); } boolean Move (mobj_t actor) { // fixed_t int tryx; int tryy; line_t ld; // warning: 'catch', 'throw', and 'try' // are all C++ reserved words boolean try_ok; boolean good; if (actor.movedir == DI_NODIR) return false; if (actor.movedir >= 8) I.Error ("Weird actor.movedir!"); tryx = actor.x + actor.info.speed*xspeed[actor.movedir]; tryy = actor.y + actor.info.speed*yspeed[actor.movedir]; try_ok = TryMove (actor, tryx, tryy); if (!try_ok) { // open any specials if (eval(actor.flags & MF_FLOAT) && floatok) { // must adjust height if (actor.z < tmfloorz) actor.z += FLOATSPEED; else actor.z -= FLOATSPEED; actor.flags |= MF_INFLOAT; return true; } if (numspechit==0) return false; actor.movedir = DI_NODIR; good = false; while ((numspechit--)>0) { ld = spechit[numspechit]; // if the special is not a door // that can be opened, // return false if (UseSpecialLine (actor, ld,false)) good = true; } return good; } else { actor.flags &= ~MF_INFLOAT; } if (! eval(actor.flags & MF_FLOAT) ) actor.z = actor.floorz; return true; } //dirtype private int d1,d2; void NewChaseDir (mobj_t actor) { // fixed_t int deltax,deltay; int tdir; int olddir; // dirtypes int turnaround; if (actor.target==null) I.Error ("P_NewChaseDir: called with no target"); olddir = actor.movedir; turnaround=opposite[olddir]; deltax = actor.target.x - actor.x; deltay = actor.target.y - actor.y; if (deltax>10*FRACUNIT) d1= DI_EAST; else if (deltax<-10*FRACUNIT) d1= DI_WEST; else d1=DI_NODIR; if (deltay<-10*FRACUNIT) d2= DI_SOUTH; else if (deltay>10*FRACUNIT) d2= DI_NORTH; else d2=DI_NODIR; // try direct route if (d1 != DI_NODIR && d2 != DI_NODIR) { actor.movedir = diags[(eval(deltay<0)<<1)+eval(deltax>0)]; if (actor.movedir != turnaround && TryWalk(actor)) return; } // try other directions if (RND.P_Random() > 200 || Math.abs(deltay)>Math.abs(deltax)) { tdir=d1; d1=d2; d2=tdir; } if (d1==turnaround) d1=DI_NODIR; if (d2==turnaround) d2=DI_NODIR; if (d1!=DI_NODIR) { actor.movedir = d1; if (TryWalk(actor)) { // either moved forward or attacked return; } } if (d2!=DI_NODIR) { actor.movedir =d2; if (TryWalk(actor)) return; } // there is no direct path to the player, // so pick another direction. if (olddir!=DI_NODIR) { actor.movedir =olddir; if (TryWalk(actor)) return; } // randomly determine direction of search if (eval(RND.P_Random()&1)) { for ( tdir=DI_EAST; tdir<=DI_SOUTHEAST; tdir++ ) { if (tdir!=turnaround) { actor.movedir =tdir; if ( TryWalk(actor) ) return; } } } else { for ( tdir=DI_SOUTHEAST; tdir != (DI_EAST-1); tdir-- ) { if (tdir!=turnaround) { actor.movedir =tdir; if ( TryWalk(actor) ) return; } } } if (turnaround != DI_NODIR) { actor.movedir =turnaround; if ( TryWalk(actor) ) return; } actor.movedir = DI_NODIR; // can not move } /** * TryWalk * Attempts to move actor on * in its current (ob.moveangle) direction. * If blocked by either a wall or an actor * returns FALSE * If move is either clear or blocked only by a door, * returns TRUE and sets... * If a door is in the way, * an OpenDoor call is made to start it opening. */ private boolean TryWalk (mobj_t actor) { if (!Move (actor)) { return false; } actor.movecount = RND.P_Random()&15; return true; } /** * P_NightmareRespawn */ void NightmareRespawn (mobj_t mobj) { int x,y, z; // fixed subsector_t ss; mobj_t mo; mapthing_t mthing; x = mobj.spawnpoint.x << FRACBITS; y = mobj.spawnpoint.y << FRACBITS; // somthing is occupying it's position? if (!CheckPosition (mobj, x, y) ) return; // no respwan // spawn a teleport fog at old spot // because of removal of the body? mo = SpawnMobj (mobj.x, mobj.y, mobj.subsector.sector.floorheight , mobjtype_t.MT_TFOG); // initiate teleport sound S.StartSound(mo, sfxenum_t.sfx_telept); // spawn a teleport fog at the new spot ss = LL.PointInSubsector (x,y); mo = SpawnMobj (x, y, ss.sector.floorheight , mobjtype_t.MT_TFOG); S.StartSound(mo, sfxenum_t.sfx_telept); // spawn the new monster mthing = mobj.spawnpoint; // spawn it if (eval(mobj.info.flags & MF_SPAWNCEILING)) z = ONCEILINGZ; else z = ONFLOORZ; // inherit attributes from deceased one mo = SpawnMobj (x,y,z, mobj.type); mo.spawnpoint = mobj.spawnpoint; mo.angle = ANG45 * (mthing.angle/45); if (eval(mthing.options & MTF_AMBUSH)) mo.flags |= MF_AMBUSH; mo.reactiontime = 18; // remove the old monster, RemoveMobj (mobj); } /** P_SpawnMobj * * @param x fixed * @param y fixed * @param z fixed * @param type * @return */ public mobj_t SpawnMobj ( int x, int y, int z, mobjtype_t type ) { mobj_t mobj; state_t st; mobjinfo_t info; // MAES: I tried this approach but it's not really worth it. // Testing with NUTS.WAD yielded pretty much identical results, // and that was without using locking. Just let the GC do its // job. // mobj = mobjpool.checkOut(); mobj=new mobj_t(this); info = mobjinfo[type.ordinal()]; mobj.type = type; mobj.info = info; mobj.x = x; mobj.y = y; mobj.radius = info.radius; mobj.height = info.height; mobj.flags = info.flags; mobj.health = info.spawnhealth; if (DM.gameskill != skill_t.sk_nightmare) mobj.reactiontime = info.reactiontime; mobj.lastlook = RND.P_Random () % MAXPLAYERS; // do not set the state with P_SetMobjState, // because action routines can not be called yet st = states[info.spawnstate.ordinal()]; mobj.state = st; mobj.tics = st.tics; mobj.sprite = st.sprite; mobj.frame = st.frame; // set subsector and/or block links LL.SetThingPosition (mobj); mobj.floorz = mobj.subsector.sector.floorheight; mobj.ceilingz = mobj.subsector.sector.ceilingheight; if (z == ONFLOORZ) mobj.z = mobj.floorz; else if (z == ONCEILINGZ) mobj.z = mobj.ceilingz - mobj.info.height; else mobj.z = z; mobj.function=think_t.P_MobjThinker; AddThinker (mobj); return mobj; } /** * P_RespawnSpecials */ void RespawnSpecials () { int x, y,z; // fixed subsector_t ss; mobj_t mo; mapthing_t mthing; int i; // only respawn items in deathmatch (deathmatch!=2) if (!DM.altdeath) return; // // nothing left to respawn? if (iquehead == iquetail) return; // wait at least 30 seconds if (DM.leveltime - itemrespawntime[iquetail] < 30*35) return; mthing = itemrespawnque[iquetail]; x = mthing.x << FRACBITS; y = mthing.y << FRACBITS; // spawn a teleport fog at the new spot ss = LL.PointInSubsector (x,y); mo = SpawnMobj (x, y, ss.sector.floorheight , mobjtype_t.MT_IFOG); S.StartSound(mo, sfxenum_t.sfx_itmbk); // find which type to spawn for (i=0 ; i< mobjtype_t.NUMMOBJTYPES.ordinal() ; i++) { if (mthing.type == mobjinfo[i].doomednum) break; } // spawn it if (eval(mobjinfo[i].flags &MF_SPAWNCEILING)) z = ONCEILINGZ; else z = ONFLOORZ; mo = SpawnMobj (x,y,z, mobjtype_t.values()[i]); mo.spawnpoint = mthing; mo.angle = ANG45 * (mthing.angle/45); // pull it from the que iquetail = (iquetail+1)&(ITEMQUESIZE-1); } /** * P_SpawnPlayer * Called when a player is spawned on the level. * Most of the player structure stays unchanged * between levels. */ public void SpawnPlayer (mapthing_t mthing) { player_t p; int x; int y; int z; mobj_t mobj; int i; // not playing? if (!DM.playeringame[mthing.type-1]) return; p = DM.players[mthing.type-1]; if (p.playerstate == PST_REBORN) p.PlayerReborn(); //DM.PlayerReborn (mthing.type-1); x = mthing.x << FRACBITS; y = mthing.y << FRACBITS; z = ONFLOORZ; mobj = SpawnMobj (x,y,z, mobjtype_t.MT_PLAYER); // set color translations for player sprites if (mthing.type > 1) mobj.flags |= (mthing.type-1)<<MF_TRANSSHIFT; mobj.angle = ANG45 * (mthing.angle/45); mobj.player = p; mobj.health = p.health[0]; p.mo = mobj; p.playerstate = PST_LIVE; p.refire = 0; p.message = null; p.damagecount = 0; p.bonuscount = 0; p.extralight = 0; p.fixedcolormap = 0; p.viewheight = VIEWHEIGHT; // setup gun psprite p.SetupPsprites (); // give all cards in death match mode if (DM.deathmatch) for (i=0 ; i<NUMCARDS ; i++) p.cards[i] = true; if (mthing.type-1 == DM.consoleplayer) { // wake up the status bar ST.Start (); // wake up the heads up text HU.Start (); } } /** * P_SpawnMapThing * The fields of the mapthing should * already be in host byte order. */ mobj_t SpawnMapThing (mapthing_t mthing) { int i; int bit; mobj_t mobj; int x; int y; int z; // count deathmatch start positions if (mthing.type == 11) { if (DM.deathmatch_p < 10/*DM.deathmatchstarts[10]*/) { // memcpy (deathmatch_p, mthing, sizeof(*mthing)); DM.deathmatchstarts[DM.deathmatch_p]=new mapthing_t(mthing); DM.deathmatch_p++; } return null; } if (mthing.type <= 0) { // Ripped from Chocolate Doom :-p // Thing type 0 is actually "player -1 start". // For some reason, Vanilla Doom accepts/ignores this. // MAES: no kidding. return null; } // check for players specially if (mthing.type <= 4 && mthing.type > 0) // killough 2/26/98 -- fix crashes { // save spots for respawning in network games DM.playerstarts[mthing.type-1]=new mapthing_t(mthing); if (!DM.deathmatch) SpawnPlayer (mthing); return null; } // check for apropriate skill level if (!DM.netgame && eval(mthing.options & 16) ) return null; if (DM.gameskill == skill_t.sk_baby) bit = 1; else if (DM.gameskill == skill_t.sk_nightmare) bit = 4; else bit = 1<<(DM.gameskill.ordinal()-1); if (!eval(mthing.options & bit) ) return null; // find which type to spawn for (i=0 ; i< NUMMOBJTYPES ; i++) if (mthing.type == mobjinfo[i].doomednum) break; // phares 5/16/98: // Do not abort because of an unknown thing. Ignore it, but post a // warning message for the player. if (i==NUMMOBJTYPES) { System.err.printf ("P_SpawnMapThing: Unknown type %d at (%d, %d)", mthing.type, mthing.x, mthing.y); return null; } // don't spawn keycards and players in deathmatch if (DM.deathmatch && eval(mobjinfo[i].flags & MF_NOTDMATCH)) return null; // don't spawn any monsters if -nomonsters if (DM.nomonsters && ( i == mobjtype_t.MT_SKULL.ordinal() || eval(mobjinfo[i].flags & MF_COUNTKILL)) ) { return null; } // spawn it x = mthing.x << FRACBITS; y = mthing.y << FRACBITS; if (eval(mobjinfo[i].flags & MF_SPAWNCEILING)) z = ONCEILINGZ; else z = ONFLOORZ; mobj = SpawnMobj (x,y,z, mobjtype_t.values()[i]); mobj.spawnpoint.copyFrom(mthing); if (mobj.tics > 0) mobj.tics = 1 + (RND.P_Random () % mobj.tics); if (eval(mobj.flags & MF_COUNTKILL)) DM.totalkills++; if (eval(mobj.flags & MF_COUNTITEM)) DM.totalitems++; mobj.angle = ANG45 * (mthing.angle/45); if (eval(mthing.options & MTF_AMBUSH)) mobj.flags |= MF_AMBUSH; return mobj; } /** P_SpawnBlood * * @param x fixed * @param y fixed * @param z fixed * @param damage */ void SpawnBlood ( int x, int y, int z, int damage ) { mobj_t th; z += ((RND.P_Random()-RND.P_Random())<<10); th = SpawnMobj (x,y,z, mobjtype_t.MT_BLOOD); th.momz = FRACUNIT*2; th.tics -= RND.P_Random()&3; if (th.tics < 1) th.tics = 1; if (damage <= 12 && damage >= 9) th.SetMobjState (statenum_t.S_BLOOD2); else if (damage < 9) th.SetMobjState (statenum_t.S_BLOOD3); } /** P_SpawnPuff * * @param x fixed * @param y fixed * @param z fixed * */ void SpawnPuff ( int x, int y, int z ) { mobj_t th; z += ((RND.P_Random()-RND.P_Random())<<10); th = SpawnMobj (x,y,z, mobjtype_t.MT_PUFF); th.momz = FRACUNIT; th.tics -= RND.P_Random()&3; if (th.tics < 1) th.tics = 1; // don't make punches spark on the wall if (attackrange == MELEERANGE) th.SetMobjState (statenum_t.S_PUFF3); } /** * P_SpawnMissile */ protected mobj_t SpawnMissile ( mobj_t source, mobj_t dest, mobjtype_t type ) { mobj_t th; long an; // angle_t int dist; th = SpawnMobj (source.x, source.y, source.z + 4*8*FRACUNIT, type); if (th.info.seesound!=null) S.StartSound(th, th.info.seesound); th.target = source; // where it came from an = R.PointToAngle2 (source.x, source.y, dest.x, dest.y)&BITS32; // fuzzy player if (eval(dest.flags & MF_SHADOW)) an += (RND.P_Random()-RND.P_Random())<<20; th.angle = an&BITS32; //an >>= ANGLETOFINESHIFT; th.momx = FixedMul (th.info.speed, finecosine(an)); th.momy = FixedMul (th.info.speed, finesine(an)); dist = AproxDistance (dest.x - source.x, dest.y - source.y); dist = dist / th.info.speed; if (dist < 1) dist = 1; th.momz = (dest.z - source.z) / dist; CheckMissileSpawn (th); return th; } /** * P_SpawnPlayerMissile * Tries to aim at a nearby monster */ public void SpawnPlayerMissile(mobj_t source, mobjtype_t type) { mobj_t th; long an; // angle_t int x, y, z, slope; // think_t // see which target is to be aimed at an = source.angle; slope = AimLineAttack(source, an, 16 * 64 * FRACUNIT); if (linetarget == null) { an += 1 << 26; an &= BITS32; slope = AimLineAttack(source, an, 16 * 64 * FRACUNIT); if (linetarget == null) { an -= 2 << 26; an &= BITS32; slope = AimLineAttack(source, an, 16 * 64 * FRACUNIT); } if (linetarget == null) { an = source.angle & BITS32; // angle should be "sane"..right? // Just this line allows freelook. slope = ((source.player.lookdir)<<FRACBITS)/173; } } x = source.x; y = source.y; z = source.z + 4 * 8 * FRACUNIT+slope; th = SpawnMobj(x, y, z, type); if (th.info.seesound != null) S.StartSound(th, th.info.seesound); th.target = source; th.angle = an; th.momx = FixedMul(th.info.speed, finecosine(an)); th.momy = FixedMul(th.info.speed, finesine(an)); th.momz = FixedMul(th.info.speed, slope); CheckMissileSpawn(th); } // // P_DamageMobj // Damages both enemies and players // "inflictor" is the thing that caused the damage // creature or missile, can be NULL (slime, etc) // "source" is the thing to target after taking damage // creature or NULL // Source and inflictor are the same for melee attacks. // Source can be NULL for slime, barrel explosions // and other environmental stuff. // public void DamageMobj ( mobj_t target, mobj_t inflictor, mobj_t source, int damage ) { long ang; // unsigned int saved; player_t player; int thrust; // fixed_t int temp; if ( !eval(target.flags& MF_SHOOTABLE)) return; // shouldn't happen... if (target.health <= 0) return; if ( eval(target.flags & MF_SKULLFLY )) { target.momx = target.momy = target.momz = 0; } player = target.player; if ((player!=null) && DM.gameskill == skill_t.sk_baby) damage >>= 1; // take half damage in trainer mode // Some close combat weapons should not // inflict thrust and push the victim out of reach, // thus kick away unless using the chainsaw. if ((inflictor !=null) && !eval(target.flags& MF_NOCLIP) && (source==null || source.player==null || source.player.readyweapon != weapontype_t.wp_chainsaw)) { ang = R.PointToAngle2 ( inflictor.x, inflictor.y, target.x, target.y)&BITS32; thrust = damage*(MAPFRACUNIT>>3)*100/target.info.mass; // make fall forwards sometimes if ( (damage < 40) && (damage > target.health) && (target.z - inflictor.z > 64*FRACUNIT) && eval(RND.P_Random()&1) ) { ang += ANG180; thrust *= 4; } //ang >>= ANGLETOFINESHIFT; target.momx += FixedMul (thrust, finecosine(ang)); target.momy += FixedMul (thrust, finesine(ang)); } // player specific if (player!=null) { // end of game hell hack if (target.subsector.sector.special == 11 && damage >= target.health) { damage = target.health - 1; } // Below certain threshold, // ignore damage in GOD mode, or with INVUL power. if ( damage < 1000 && ( eval(player.cheats&player_t.CF_GODMODE)) || player.powers[pw_invulnerability]!=0 ) { return; } if (player.armortype!=0) { if (player.armortype == 1) saved = damage/3; else saved = damage/2; if (player.armorpoints[0] <= saved) { // armor is used up saved = player.armorpoints[0]; player.armortype = 0; } player.armorpoints[0] -= saved; damage -= saved; } player.health[0] -= damage; // mirror mobj health here for Dave if (player.health[0] < 0) player.health[0] = 0; player.attacker = source; player.damagecount += damage; // add damage after armor / invuln if (player.damagecount > 100) player.damagecount = 100; // teleport stomp does 10k points... temp = damage < 100 ? damage : 100; if (player == DM.players[DM.consoleplayer]) I.Tactile (40,10,40+temp*2); } // do the damage target.health -= damage; if (target.health <= 0) { KillMobj (source, target); return; } if ( (RND.P_Random () < target.info.painchance) && !eval(target.flags&MF_SKULLFLY) ) { target.flags |= MF_JUSTHIT; // fight back! target.SetMobjState (target.info.painstate); } target.reactiontime = 0; // we're awake now... if ( ((target.threshold==0) || (target.type == mobjtype_t.MT_VILE)) && (source!=null) && (source != target) && (source.type != mobjtype_t.MT_VILE)) { // if not intent on another player, // chase after this one target.target = source; target.threshold = BASETHRESHOLD; if (target.state == states[target.info.spawnstate.ordinal()] && target.info.seestate != statenum_t.S_NULL) target.SetMobjState (target.info.seestate); } } // // KillMobj // public void KillMobj ( mobj_t source, mobj_t target ) { mobjtype_t item; mobj_t mo; // Maes: this seems necessary in order for barrel damage // to propagate inflictors. target.target=source; target.flags &= ~(MF_SHOOTABLE|MF_FLOAT|MF_SKULLFLY); if (target.type != mobjtype_t.MT_SKULL) target.flags &= ~MF_NOGRAVITY; target.flags |= MF_CORPSE|MF_DROPOFF; target.height >>= 2; if (source!=null && source.player!=null) { // count for intermission if ((target.flags & MF_COUNTKILL)!=0) source.player.killcount++; if (target.player!=null) //; <-- _D_: that semicolon caused a bug! source.player.frags[target.player.identify()]++; // It's probably intended to increment the frags of source player vs target player. Lookup? } else if (!DM.netgame && ((target.flags & MF_COUNTKILL)!=0) ) { // count all monster deaths, // even those caused by other monsters DM.players[0].killcount++; } if (target.player!=null) { // count environment kills against you if (source==null) // TODO: some way to indentify which one of the // four possiblelayers is the current player target.player.frags[target.player.identify()]++; target.flags &= ~MF_SOLID; target.player.playerstate = PST_DEAD; target.player.DropWeapon (); // in PSPR if (target.player == DM.players[DM.consoleplayer] && DM.automapactive) { // don't die in auto map, // switch view prior to dying AM.Stop (); } } if (target.health < -target.info.spawnhealth && target.info.xdeathstate!=statenum_t.S_NULL) { target.SetMobjState(target.info.xdeathstate); } else target.SetMobjState (target.info.deathstate); target.tics -= RND.P_Random()&3; if (target.tics < 1) target.tics = 1; // I_StartSound (&actor.r, actor.info.deathsound); // Drop stuff. // This determines the kind of object spawned // during the death frame of a thing. switch (target.type) { case MT_WOLFSS: case MT_POSSESSED: item = mobjtype_t.MT_CLIP; break; case MT_SHOTGUY: item = mobjtype_t.MT_SHOTGUN; break; case MT_CHAINGUY: item = mobjtype_t.MT_CHAINGUN; break; default: return; } mo = SpawnMobj (target.x,target.y,ONFLOORZ, item); mo.flags |= MF_DROPPED; // special versions of items } // // TELEPORTATION // int Teleport ( line_t line, int side, mobj_t thing ) { int i; int tag; mobj_t m; mobj_t fog; int an; thinker_t thinker; sector_t sector; int oldx, oldy, oldz; // fixed_t // don't teleport missiles if ((thing.flags & MF_MISSILE)!=0) return 0; // Don't teleport if hit back of line, // so you can get out of teleporter. if (side == 1) return 0; tag = line.tag; for (i = 0; i < LL.numsectors; i++) { if (LL.sectors[ i ].tag == tag ) { thinker = thinkercap.next; for (thinker = thinkercap.next; thinker != thinkercap; thinker = thinker.next) { // not a mobj if (thinker.function != think_t.P_MobjThinker) continue; m = (mobj_t)thinker; // not a teleportman if (m.type != mobjtype_t.MT_TELEPORTMAN ) continue; sector = m.subsector.sector; // wrong sector if (sector.id != i ) continue; oldx = thing.x; oldy = thing.y; oldz = thing.z; if (!TeleportMove (thing, m.x, m.y)) return 0; thing.z = thing.floorz; //fixme: not needed? if (thing.player!=null) { thing.player.viewz = thing.z+thing.player.viewheight; thing.player.lookdir = 0; // Reset lookdir } // spawn teleport fog at source and destination fog = SpawnMobj (oldx, oldy, oldz, mobjtype_t.MT_TFOG); S.StartSound( fog, sfxenum_t.sfx_telept); an = Tables.toBAMIndex(m.angle); fog = SpawnMobj (m.x+20*finecosine[an], m.y+20*finesine[an] , thing.z, mobjtype_t.MT_TFOG); // emit sound, where? S.StartSound (fog, sfxenum_t.sfx_telept); // don't move for a bit if (thing.player!=null) thing.reactiontime = 18; thing.angle = m.angle; thing.momx = thing.momy = thing.momz = 0; return 1; } } } return 0; } // //EVENTS //Events are operations triggered by using, crossing, //or shooting special lines, or by timed thinkers. // /** * P_CrossSpecialLine - TRIGGER * Called every time a thing origin is about * to cross a line with a non 0 special. */ void CrossSpecialLine ( line_t line, int side, mobj_t thing ) { //line_t line; boolean ok; //line = LL.lines[linenum]; // Triggers that other things can activate if (thing.player==null) { // Things that should NOT trigger specials... switch(thing.type) { case MT_ROCKET: case MT_PLASMA: case MT_BFG: case MT_TROOPSHOT: case MT_HEADSHOT: case MT_BRUISERSHOT: return; // break; default: break; } ok = false; switch(line.special) { case 39: // TELEPORT TRIGGER case 97: // TELEPORT RETRIGGER case 125: // TELEPORT MONSTERONLY TRIGGER case 126: // TELEPORT MONSTERONLY RETRIGGER case 4: // RAISE DOOR case 10: // PLAT DOWN-WAIT-UP-STAY TRIGGER case 88: // PLAT DOWN-WAIT-UP-STAY RETRIGGER ok = true; break; } if (!ok) return; } // Note: could use some const's here. switch (line.special) { // TRIGGERS. // All from here to RETRIGGERS. case 2: // Open Door DoDoor(line,vldoor_e.open); line.special = 0; break; case 3: // Close Door DoDoor(line,vldoor_e.close); line.special = 0; break; case 4: // Raise Door DoDoor(line,vldoor_e.normal); line.special = 0; break; case 5: // Raise Floor DoFloor(line,floor_e.raiseFloor); line.special = 0; break; case 6: // Fast Ceiling Crush & Raise DoCeiling(line,ceiling_e.fastCrushAndRaise); line.special = 0; break; case 8: // Build Stairs BuildStairs(line,stair_e.build8); line.special = 0; break; case 10: // PlatDownWaitUp PEV.DoPlat(line,plattype_e.downWaitUpStay,0); line.special = 0; break; case 12: // Light Turn On - brightest near LEV.LightTurnOn(line,0); line.special = 0; break; case 13: // Light Turn On 255 LEV.LightTurnOn(line,255); line.special = 0; break; case 16: // Close Door 30 DoDoor(line,vldoor_e.close30ThenOpen); line.special = 0; break; case 17: // Start Light Strobing LEV.StartLightStrobing(line); line.special = 0; break; case 19: // Lower Floor DoFloor(line,floor_e.lowerFloor); line.special = 0; break; case 22: // Raise floor to nearest height and change texture PEV.DoPlat(line,plattype_e.raiseToNearestAndChange,0); line.special = 0; break; case 25: // Ceiling Crush and Raise DoCeiling(line,ceiling_e.crushAndRaise); line.special = 0; break; case 30: // Raise floor to shortest texture height // on either side of lines. DoFloor(line,floor_e.raiseToTexture); line.special = 0; break; case 35: // Lights Very Dark LEV.LightTurnOn(line,35); line.special = 0; break; case 36: // Lower Floor (TURBO) DoFloor(line,floor_e.turboLower); line.special = 0; break; case 37: // LowerAndChange DoFloor(line,floor_e.lowerAndChange); line.special = 0; break; case 38: // Lower Floor To Lowest DoFloor( line, floor_e.lowerFloorToLowest ); line.special = 0; break; case 39: // TELEPORT! Teleport( line, side, thing ); line.special = 0; break; case 40: // RaiseCeilingLowerFloor DoCeiling( line, ceiling_e.raiseToHighest ); DoFloor( line, floor_e.lowerFloorToLowest ); line.special = 0; break; case 44: // Ceiling Crush DoCeiling( line, ceiling_e.lowerAndCrush ); line.special = 0; break; case 52: // EXIT! DM.ExitLevel (); break; case 53: // Perpetual Platform Raise PEV.DoPlat(line,plattype_e.perpetualRaise,0); line.special = 0; break; case 54: // Platform Stop PEV.StopPlat(line); line.special = 0; break; case 56: // Raise Floor Crush DoFloor(line,floor_e.raiseFloorCrush); line.special = 0; break; case 57: // Ceiling Crush Stop CeilingCrushStop(line); line.special = 0; break; case 58: // Raise Floor 24 DoFloor(line,floor_e.raiseFloor24); line.special = 0; break; case 59: // Raise Floor 24 And Change DoFloor(line,floor_e.raiseFloor24AndChange); line.special = 0; break; case 104: // Turn lights off in sector(tag) LEV.TurnTagLightsOff(line); line.special = 0; break; case 108: // Blazing Door Raise (faster than TURBO!) DoDoor (line,vldoor_e.blazeRaise); line.special = 0; break; case 109: // Blazing Door Open (faster than TURBO!) DoDoor (line,vldoor_e.blazeOpen); line.special = 0; break; case 100: // Build Stairs Turbo 16 BuildStairs(line,stair_e.turbo16); line.special = 0; break; case 110: // Blazing Door Close (faster than TURBO!) DoDoor (line,vldoor_e.blazeClose); line.special = 0; break; case 119: // Raise floor to nearest surr. floor DoFloor(line,floor_e.raiseFloorToNearest); line.special = 0; break; case 121: // Blazing PlatDownWaitUpStay PEV.DoPlat(line,plattype_e.blazeDWUS,0); line.special = 0; break; case 124: // Secret EXIT DM.SecretExitLevel (); break; case 125: // TELEPORT MonsterONLY if (thing.player==null) { Teleport( line, side, thing ); line.special = 0; } break; case 130: // Raise Floor Turbo DoFloor(line,floor_e.raiseFloorTurbo); line.special = 0; break; case 141: // Silent Ceiling Crush & Raise DoCeiling(line,ceiling_e.silentCrushAndRaise); line.special = 0; break; // RETRIGGERS. All from here till end. case 72: // Ceiling Crush DoCeiling( line, ceiling_e.lowerAndCrush ); break; case 73: // Ceiling Crush and Raise DoCeiling(line,ceiling_e.crushAndRaise); break; case 74: // Ceiling Crush Stop CeilingCrushStop(line); break; case 75: // Close Door DoDoor(line,vldoor_e.close); break; case 76: // Close Door 30 DoDoor(line,vldoor_e.close30ThenOpen); break; case 77: // Fast Ceiling Crush & Raise DoCeiling(line,ceiling_e.fastCrushAndRaise); break; case 79: // Lights Very Dark LEV.LightTurnOn(line,35); break; case 80: // Light Turn On - brightest near LEV.LightTurnOn(line,0); break; case 81: // Light Turn On 255 LEV.LightTurnOn(line,255); break; case 82: // Lower Floor To Lowest DoFloor( line, floor_e.lowerFloorToLowest ); break; case 83: // Lower Floor DoFloor(line,floor_e.lowerFloor); break; case 84: // LowerAndChange DoFloor(line,floor_e.lowerAndChange); break; case 86: // Open Door DoDoor(line,vldoor_e.open); break; case 87: // Perpetual Platform Raise PEV.DoPlat(line,plattype_e.perpetualRaise,0); break; case 88: // PlatDownWaitUp PEV.DoPlat(line,plattype_e.downWaitUpStay,0); break; case 89: // Platform Stop PEV.StopPlat(line); break; case 90: // Raise Door DoDoor(line,vldoor_e.normal); break; case 91: // Raise Floor DoFloor(line,floor_e.raiseFloor); break; case 92: // Raise Floor 24 DoFloor(line,floor_e.raiseFloor24); break; case 93: // Raise Floor 24 And Change DoFloor(line,floor_e.raiseFloor24AndChange); break; case 94: // Raise Floor Crush DoFloor(line,floor_e.raiseFloorCrush); break; case 95: // Raise floor to nearest height // and change texture. PEV.DoPlat(line,plattype_e.raiseToNearestAndChange,0); break; case 96: // Raise floor to shortest texture height // on either side of lines. DoFloor(line,floor_e.raiseToTexture); break; case 97: // TELEPORT! Teleport( line, side, thing ); break; case 98: // Lower Floor (TURBO) DoFloor(line,floor_e.turboLower); break; case 105: // Blazing Door Raise (faster than TURBO!) DoDoor (line,vldoor_e.blazeRaise); break; case 106: // Blazing Door Open (faster than TURBO!) DoDoor (line,vldoor_e.blazeOpen); break; case 107: // Blazing Door Close (faster than TURBO!) DoDoor (line,vldoor_e.blazeClose); break; case 120: // Blazing PlatDownWaitUpStay. PEV.DoPlat(line,plattype_e.blazeDWUS,0); break; case 126: // TELEPORT MonsterONLY. if (thing.player==null) Teleport( line, side, thing ); break; case 128: // Raise To Nearest Floor DoFloor(line,floor_e.raiseFloorToNearest); break; case 129: // Raise Floor Turbo DoFloor(line,floor_e.raiseFloorTurbo); break; } } ///////////////// MOVEMENT'S ACTIONS //////////////////////// /** fixed_t */ int[] tmbbox=new int[4]; mobj_t tmthing; long tmflags; /** fixed_t */ int tmx, tmy; /** If "floatok" true, move would be ok if within "tmfloorz - tmceilingz". */ public boolean floatok; /** fixed_t */ public int tmfloorz, tmceilingz, tmdropoffz; // keep track of the line that lowers the ceiling, // so missiles don't explode against sky hack walls public line_t ceilingline; public line_t[] spechit=new line_t[MAXSPECIALCROSS]; public int numspechit; protected final void ResizeSpechits() { spechit=C2JUtils.resize(spechit[0],spechit,spechit.length*2); } ////////////////// PTR Traverse Interception Functions /////////////////////// public class PTR_AimTraverse implements PTR_InterceptFunc{ public boolean invoke (intercept_t in) { line_t li; mobj_t th; int slope; int thingtopslope; int thingbottomslope; int dist; if (in.isaline) { li = (line_t) in.d(); if ( !eval(li.flags & ML_TWOSIDED) ) return false; // stop // Crosses a two sided line. // A two sided line will restrict // the possible target ranges. LineOpening (li); if (openbottom >= opentop) return false; // stop dist = FixedMul (attackrange, in.frac); if (li.frontsector.floorheight != li.backsector.floorheight) { slope = FixedDiv (openbottom - shootz , dist); if (slope > bottomslope) bottomslope = slope; } if (li.frontsector.ceilingheight != li.backsector.ceilingheight) { slope = FixedDiv (opentop - shootz , dist); if (slope < topslope) topslope = slope; } if (topslope <= bottomslope) return false; // stop return true; // shot continues } // shoot a thing th = (mobj_t) in.d(); if (th == shootthing) return true; // can't shoot self if (!eval(th.flags&MF_SHOOTABLE)) return true; // corpse or something // check angles to see if the thing can be aimed at dist = FixedMul (attackrange, in.frac); thingtopslope = FixedDiv (th.z+th.height - shootz , dist); if (thingtopslope < bottomslope) return true; // shot over the thing thingbottomslope = FixedDiv (th.z - shootz, dist); if (thingbottomslope > topslope) return true; // shot under the thing // this thing can be hit! if (thingtopslope > topslope) thingtopslope = topslope; if (thingbottomslope < bottomslope) thingbottomslope = bottomslope; aimslope = (thingtopslope+thingbottomslope)/2; linetarget = th; return false; // don't go any farther } } /** * PTR_ShootTraverse * * 9/5/2011: Accepted _D_'s fix */ public class PTR_ShootTraverse implements PTR_InterceptFunc { public boolean invoke(intercept_t in){ int x,y,z,frac; // fixed_t line_t li; mobj_t th; int slope,dist,thingtopslope,thingbottomslope; // fixed_t if (in.isaline) { li = (line_t) in.d(); if (li.special!=0) ShootSpecialLine (shootthing, li); if ( !eval(li.flags& ML_TWOSIDED) ) return gotoHitLine(in, li); // crosses a two sided line LineOpening (li); dist = FixedMul (attackrange, in.frac); if (li.frontsector.floorheight != li.backsector.floorheight) { slope = FixedDiv (openbottom - shootz , dist); if (slope > aimslope) return gotoHitLine(in, li); } if (li.frontsector.ceilingheight != li.backsector.ceilingheight) { slope = FixedDiv (opentop - shootz , dist); if (slope < aimslope) return gotoHitLine(in, li); } // shot continues return true; } // shoot a thing th = (mobj_t) in.d(); if (th == shootthing) return true; // can't shoot self if (!eval(th.flags&MF_SHOOTABLE)) return true; // corpse or something // check angles to see if the thing can be aimed at dist = FixedMul (attackrange, in.frac); thingtopslope = FixedDiv (th.z+th.height - shootz , dist); if (thingtopslope < aimslope) return true; // shot over the thing thingbottomslope = FixedDiv (th.z - shootz, dist); if (thingbottomslope > aimslope) return true; // shot under the thing // hit thing // position a bit closer frac = in.frac - FixedDiv (10*FRACUNIT,attackrange); x = trace.x + FixedMul (trace.dx, frac); y = trace.y + FixedMul (trace.dy, frac); z = shootz + FixedMul (aimslope, FixedMul(frac, attackrange)); // Spawn bullet puffs or blod spots, // depending on target type. if (eval(((mobj_t)in.d()).flags & MF_NOBLOOD)) SpawnPuff (x,y,z); else SpawnBlood (x,y,z, la_damage); if (la_damage!=0) DamageMobj (th, shootthing, shootthing, la_damage); // don't go any farther return false; } //_D_: NOTE: this function was added, because replacing a goto by a boolean flag caused a bug if shooting a single sided line protected boolean gotoHitLine(intercept_t in, line_t li) { int x, y, z, frac; // position a bit closer frac = in.frac - FixedDiv (4*FRACUNIT,attackrange); x = trace.x + FixedMul (trace.dx, frac); y = trace.y + FixedMul (trace.dy, frac); z = shootz + FixedMul (aimslope, FixedMul(frac, attackrange)); if (li.frontsector.ceilingpic == TM.getSkyFlatNum()) { // don't shoot the sky! if (z > li.frontsector.ceilingheight) return false; // it's a sky hack wall if (li.backsector!=null && li.backsector.ceilingpic == TM.getSkyFlatNum()) return false; } // Spawn bullet puffs. SpawnPuff (x,y,z); // don't go any farther return false; } } // // PTR_SlideTraverse // public class PTR_SlideTraverse implements PTR_InterceptFunc{ public boolean invoke (intercept_t in) { line_t li; if (!in.isaline) I.Error ("PTR_SlideTraverse: not a line?"); li = (line_t) in.d(); if ( ! eval(li.flags &ML_TWOSIDED) ) { if (li.PointOnLineSide (slidemo.x, slidemo.y)) { // don't hit the back side return true; } return isblocking(in,li); } // set openrange, opentop, openbottom LineOpening (li); if ((openrange < slidemo.height)|| // doesn't fit (opentop - slidemo.z < slidemo.height)|| // mobj is too high (openbottom - slidemo.z > 24*FRACUNIT )) // too big a step up { if (in.frac < bestslidefrac) { secondslidefrac = bestslidefrac; secondslideline = bestslideline; bestslidefrac = in.frac; bestslideline = li; } return false; // stop } else // this line doesn't block movement return true; } private final boolean isblocking(intercept_t in, line_t li){ // the line does block movement, // see if it is closer than best so far if (in.frac < bestslidefrac) { secondslidefrac = bestslidefrac; secondslideline = bestslideline; bestslidefrac = in.frac; bestslideline = li; } return false; // stop } } // // USE LINES // public class PTR_UseTraverse implements PTR_InterceptFunc{ public boolean invoke (intercept_t in) { boolean side; // FIXME: some sanity check here? line_t line=(line_t) in.d(); if (line.special==0) { LineOpening (line); if (openrange <= 0) { S.StartSound(A.usething, sfxenum_t.sfx_noway); // can't use through a wall return false; } // not a special line, but keep checking return true ; } side = false; if (line.PointOnLineSide (A.usething.x, A.usething.y)) side = true; // return false; // don't use back side A.UseSpecialLine (A.usething, line, side); // can't use for than one special line in a row return false; } } // //P_BlockThingsIterator // final boolean BlockThingsIterator ( int x, int y, PIT_MobjFunction func) { mobj_t mobj; if ( x<0 || y<0 || x>=LL.bmapwidth || y>=LL.bmapheight) { return true; } for (mobj = LL.blocklinks[y*LL.bmapwidth+x] ; mobj!=null ; mobj = (mobj_t) mobj.bnext) { if (!func.invoke(mobj ) ) return false; } return true; } // // TELEPORT MOVE // // // P_TeleportMove // public boolean TeleportMove ( mobj_t thing, int x, //fixed int y ) { int xl; int xh; int yl; int yh; int bx; int by; subsector_t newsubsec; // kill anything occupying the position tmthing = thing; tmflags = thing.flags; tmx = x; tmy = y; tmbbox[BOXTOP] = y + tmthing.radius; tmbbox[BOXBOTTOM] = y - tmthing.radius; tmbbox[BOXRIGHT] = x + tmthing.radius; tmbbox[BOXLEFT] = x - tmthing.radius; newsubsec = LL.PointInSubsector (x,y); ceilingline = null; // The base floor/ceiling is from the subsector // that contains the point. // Any contacted lines the step closer together // will adjust them. tmfloorz = tmdropoffz = newsubsec.sector.floorheight; tmceilingz = newsubsec.sector.ceilingheight; R.increaseValidCount(1); // This is r_main's ? numspechit = 0; // stomp on any things contacted xl = LL.getSafeBlockX(tmbbox[BOXLEFT] - LL.bmaporgx - MAXRADIUS); xh = LL.getSafeBlockX(tmbbox[BOXRIGHT] - LL.bmaporgx + MAXRADIUS); yl = LL.getSafeBlockY(tmbbox[BOXBOTTOM] - LL.bmaporgy - MAXRADIUS); yh = LL.getSafeBlockY(tmbbox[BOXTOP] - LL.bmaporgy + MAXRADIUS); for (bx=xl ; bx<=xh ; bx++) for (by=yl ; by<=yh ; by++) if (!BlockThingsIterator(bx,by,StompThing)) return false; // the move is ok, // so link the thing into its new position UnsetThingPosition (thing); thing.floorz = tmfloorz; thing.ceilingz = tmceilingz; thing.x = x; thing.y = y; LL.SetThingPosition (thing); return true; } // // MOVEMENT CLIPPING // /** * P_CheckPosition * This is purely informative, nothing is modified * (except things picked up). * * in: * a mobj_t (can be valid or invalid) * a position to be checked * (doesn't need to be related to the mobj_t.x,y) * * during: * special things are touched if MF_PICKUP * early out on solid lines? * * out: * newsubsec * floorz * ceilingz * tmdropoffz * the lowest point contacted * (monsters won't move to a dropoff) * speciallines[] * numspeciallines * @param thing * @param x fixed_t * @param y fixed_t */ public boolean CheckPosition ( mobj_t thing, int x, int y ) { int xl; int xh; int yl; int yh; int bx; int by; subsector_t newsubsec; tmthing = thing; tmflags = thing.flags; tmx = x; tmy = y; tmbbox[BOXTOP] = y + tmthing.radius; tmbbox[BOXBOTTOM] = y - tmthing.radius; tmbbox[BOXRIGHT] = x + tmthing.radius; tmbbox[BOXLEFT] = x - tmthing.radius; newsubsec = LL.PointInSubsector (x,y); ceilingline = null; // The base floor / ceiling is from the subsector // that contains the point. // Any contacted lines the step closer together // will adjust them. tmfloorz = tmdropoffz = newsubsec.sector.floorheight; tmceilingz = newsubsec.sector.ceilingheight; R.increaseValidCount(1); numspechit = 0; if ( eval(tmflags &MF_NOCLIP )) return true; // Check things first, possibly picking things up. // The bounding box is extended by MAXRADIUS // because mobj_ts are grouped into mapblocks // based on their origin point, and can overlap // into adjacent blocks by up to MAXRADIUS units. xl = LL.getSafeBlockX(tmbbox[BOXLEFT] - LL.bmaporgx - MAXRADIUS); xh = LL.getSafeBlockX(tmbbox[BOXRIGHT] - LL.bmaporgx + MAXRADIUS); yl = LL.getSafeBlockY(tmbbox[BOXBOTTOM] - LL.bmaporgy - MAXRADIUS); yh = LL.getSafeBlockY(tmbbox[BOXTOP] - LL.bmaporgy + MAXRADIUS); for (bx=xl ; bx<=xh ; bx++) for (by=yl ; by<=yh ; by++) if (!BlockThingsIterator(bx,by,CheckThing)) return false; // check lines xl = LL.getSafeBlockX(tmbbox[BOXLEFT] - LL.bmaporgx); xh = LL.getSafeBlockX(tmbbox[BOXRIGHT] - LL.bmaporgx); yl = LL.getSafeBlockY(tmbbox[BOXBOTTOM] - LL.bmaporgy); yh = LL.getSafeBlockY(tmbbox[BOXTOP] - LL.bmaporgy); // Maes's quick and dirty blockmap extension hack // E.g. for an extension of 511 blocks, max negative is -1. // A full 512x512 blockmap doesn't have negative indexes. if (xl<=LL.blockmapxneg) xl=0x1FF&xl; // Broke width boundary if (xh<=LL.blockmapxneg) xh=0x1FF&xh; // Broke width boundary if (yl<=LL.blockmapyneg) yl=0x1FF&yl; // Broke height boundary if (yh<=LL.blockmapyneg) yh=0x1FF&yh; // Broke height boundary for (bx=xl ; bx<=xh ; bx++) for (by=yl ; by<=yh ; by++) if (!BlockLinesIterator (bx,by,CheckLine)) return false; return true; } /** // P_TryMove // Attempt to move to a new position, // crossing special lines unless MF_TELEPORT is set. * @param x fixed_t * @param y fixed_t * */ boolean TryMove ( mobj_t thing, int x, int y ) { int oldx, oldy; // fixed_t boolean side, oldside; // both were int line_t ld; floatok = false; if (!CheckPosition (thing, x, y)) return false; // solid wall or thing if ( !eval(thing.flags& MF_NOCLIP) ) { if (tmceilingz - tmfloorz < thing.height) return false; // doesn't fit floatok = true; if ( !eval(thing.flags&MF_TELEPORT) &&tmceilingz - thing.z < thing.height) return false; // mobj must lower itself to fit if ( !eval(thing.flags&MF_TELEPORT) && tmfloorz - thing.z > 24*FRACUNIT ) return false; // too big a step up if ( !eval(thing.flags&(MF_DROPOFF|MF_FLOAT)) && tmfloorz - tmdropoffz > 24*FRACUNIT ) return false; // don't stand over a dropoff } // the move is ok, // so link the thing into its new position UnsetThingPosition (thing); oldx = thing.x; oldy = thing.y; thing.floorz = tmfloorz; thing.ceilingz = tmceilingz; thing.x = x; thing.y = y; LL.SetThingPosition (thing); // if any special lines were hit, do the effect if (! eval(thing.flags&(MF_TELEPORT|MF_NOCLIP)) ) { while (numspechit-->0) { // see if the line was crossed ld = spechit[numspechit]; side = ld.PointOnLineSide (thing.x, thing.y ); oldside = ld.PointOnLineSide (oldx, oldy ); if (side != oldside) { if (ld.special!=0) CrossSpecialLine (ld, oldside?1:0, thing); } } } return true; } // // P_ThingHeightClip // Takes a valid thing and adjusts the thing.floorz, // thing.ceilingz, and possibly thing.z. // This is called for all nearby monsters // whenever a sector changes height. // If the thing doesn't fit, // the z will be set to the lowest value // and false will be returned. // boolean ThingHeightClip (mobj_t thing) { boolean onfloor; onfloor = (thing.z == thing.floorz); CheckPosition (thing, thing.x, thing.y); // what about stranding a monster partially off an edge? thing.floorz = tmfloorz; thing.ceilingz = tmceilingz; if (onfloor) { // walking monsters rise and fall with the floor thing.z = thing.floorz; } else { // don't adjust a floating monster unless forced to if (thing.z+thing.height > thing.ceilingz) thing.z = thing.ceilingz - thing.height; } if (thing.ceilingz - thing.floorz < thing.height) return false; return true; } // // SLIDE MOVE // Allows the player to slide along any angled walls. // int bestslidefrac; // fixed int secondslidefrac; line_t bestslideline; line_t secondslideline; mobj_t slidemo; int tmxmove; //fixed_t int tmymove; // // P_HitSlideLine // Adjusts the xmove / ymove // so that the next move will slide along the wall. // private void HitSlideLine (line_t ld) { boolean side; // all angles long lineangle, moveangle,deltaangle; // fixed_t int movelen, newlen; if (ld.slopetype == slopetype_t.ST_HORIZONTAL) { tmymove = 0; return; } if (ld.slopetype == slopetype_t.ST_VERTICAL) { tmxmove = 0; return; } side = ld.PointOnLineSide (slidemo.x, slidemo.y); lineangle = R.PointToAngle2 (0,0, ld.dx, ld.dy); if (side == true) lineangle += ANG180; moveangle = R.PointToAngle2 (0,0, tmxmove, tmymove); deltaangle = (moveangle-lineangle)&BITS32; if (deltaangle > ANG180) deltaangle += ANG180; // system.Error ("SlideLine: ang>ANG180"); //lineangle >>>= ANGLETOFINESHIFT; //deltaangle >>>= ANGLETOFINESHIFT; movelen = AproxDistance (tmxmove, tmymove); newlen = FixedMul (movelen, finecosine(deltaangle)); tmxmove = FixedMul (newlen, finecosine(lineangle)); tmymove = FixedMul (newlen, finesine(lineangle)); } protected final static int FUDGE=0x800;///(FRACUNIT/MAPFRACUNIT); // // P_SlideMove // The momx / momy move is bad, so try to slide // along a wall. // Find the first line hit, move flush to it, // and slide along it // // This is a kludgy mess. // void SlideMove (mobj_t mo) { // fixed_t int leadx,leady,trailx,traily,newx,newy; int hitcount; slidemo = mo; hitcount = 0; do { if (++hitcount == 3) { // goto stairstep stairstep(mo); return; } // don't loop forever // trace along the three leading corners if (mo.momx > 0) { leadx = mo.x + mo.radius; trailx = mo.x - mo.radius; } else { leadx = mo.x - mo.radius; trailx = mo.x + mo.radius; } if (mo.momy > 0) { leady = mo.y + mo.radius; traily = mo.y - mo.radius; } else { leady = mo.y - mo.radius; traily = mo.y + mo.radius; } bestslidefrac = FRACUNIT+1; PathTraverse ( leadx, leady, leadx+mo.momx, leady+mo.momy, PT_ADDLINES, SlideTraverse ); PathTraverse ( trailx, leady, trailx+mo.momx, leady+mo.momy, PT_ADDLINES, SlideTraverse ); PathTraverse ( leadx, traily, leadx+mo.momx, traily+mo.momy, PT_ADDLINES, SlideTraverse ); // move up to the wall if (bestslidefrac == FRACUNIT+1) { // the move most have hit the middle, so stairstep stairstep(mo); return; } // don't loop forever // fudge a bit to make sure it doesn't hit bestslidefrac -= FUDGE; if (bestslidefrac > 0) { newx = FixedMul (mo.momx, bestslidefrac); newy = FixedMul (mo.momy, bestslidefrac); if (!TryMove (mo, mo.x+newx, mo.y+newy)) { // goto stairstep stairstep(mo); return; } // don't loop forever } // Now continue along the wall. // First calculate remainder. bestslidefrac = FRACUNIT-(bestslidefrac+FUDGE); if (bestslidefrac > FRACUNIT) bestslidefrac = FRACUNIT; if (bestslidefrac <= 0) return; tmxmove = FixedMul (mo.momx, bestslidefrac); tmymove = FixedMul (mo.momy, bestslidefrac); HitSlideLine (bestslideline); // clip the moves mo.momx = tmxmove; mo.momy = tmymove; } // goto retry while (!TryMove (mo, mo.x+tmxmove, mo.y+tmymove)); } /** Fugly "goto stairstep" simulation * * @param mo */ private final void stairstep(mobj_t mo){ if (!TryMove (mo, mo.x, mo.y + mo.momy)) TryMove (mo, mo.x + mo.momx, mo.y); } // // P_XYMovement // protected final static int STOPSPEED = 0x1000; protected final static int FRICTION = 0xe800; public void XYMovement (mobj_t mo) { //System.out.println("XYMovement"); int ptryx, ptryy; // pointers to fixed_t ??? player_t player; int xmove, ymove; // fixed_t if ((mo.momx==0) && (mo.momy==0)) { if ((mo.flags & MF_SKULLFLY)!=0) { // the skull slammed into something mo.flags &= ~MF_SKULLFLY; mo.momx = mo.momy = mo.momz = 0; mo.SetMobjState (mo.info.spawnstate); } return; } player = mo.player; if (mo.momx > MAXMOVE) mo.momx = MAXMOVE; else if (mo.momx < -MAXMOVE) mo.momx = -MAXMOVE; if (mo.momy > MAXMOVE) mo.momy = MAXMOVE; else if (mo.momy < -MAXMOVE) mo.momy = -MAXMOVE; xmove = mo.momx; ymove = mo.momy; do { if (xmove > MAXMOVE/2 || ymove > MAXMOVE/2) { ptryx = mo.x + xmove/2; ptryy = mo.y + ymove/2; xmove >>= 1; ymove >>= 1; } else { ptryx = mo.x + xmove; ptryy = mo.y + ymove; xmove = ymove = 0; } if (!TryMove (mo, ptryx, ptryy)) { // blocked move if (mo.player!=null) { // try to slide along it SlideMove (mo); } else if (eval(mo.flags & MF_MISSILE)) { // explode a missile if (ceilingline!=null && ceilingline.backsector!=null && ceilingline.backsector.ceilingpic == TM.getSkyFlatNum()) { // Hack to prevent missiles exploding // against the sky. // Does not handle sky floors. RemoveMobj (mo); return; } ExplodeMissile (mo); } else mo.momx = mo.momy = 0; } } while ((xmove | ymove)!=0); // slow down if (player!=null && eval(player.cheats & player_t.CF_NOMOMENTUM)) { // debug option for no sliding at all mo.momx = mo.momy = 0; return; } if (eval(mo.flags & (MF_MISSILE | MF_SKULLFLY)) ) return; // no friction for missiles ever if (mo.z > mo.floorz) return; // no friction when airborne if (eval(mo.flags & MF_CORPSE)) { // do not stop sliding // if halfway off a step with some momentum if (mo.momx > FRACUNIT/4 || mo.momx < -FRACUNIT/4 || mo.momy > FRACUNIT/4 || mo.momy < -FRACUNIT/4) { if (mo.floorz != mo.subsector.sector.floorheight) return; } } if (mo.momx > -STOPSPEED && mo.momx < STOPSPEED && mo.momy > -STOPSPEED && mo.momy < STOPSPEED && (player==null || (player.cmd.forwardmove== 0 && player.cmd.sidemove == 0 ) ) ) { // if in a walking frame, stop moving // TODO: we need a way to get state indexed inside of states[], to sim pointer arithmetic. // FIX: added an "id" field. if ( player!=null&&(int)(player.mo.state.id- statenum_t.S_PLAY_RUN1.ordinal()) < 4) player.mo.SetMobjState (statenum_t.S_PLAY); mo.momx = 0; mo.momy = 0; } else { mo.momx = FixedMul (mo.momx, FRICTION); mo.momy = FixedMul (mo.momy, FRICTION); } } // // P_LineAttack // /** who got hit (or NULL) */ public mobj_t linetarget; mobj_t shootthing; // Height if not aiming up or down // ???: use slope for monsters? int shootz; // fixed_t int la_damage; int attackrange; // fixed_t int aimslope; // fixed_t // // // /** P_AimLineAttack * @param t1 * @param angle long * @param distance int */ int AimLineAttack ( mobj_t t1, long angle, int distance ) { int x2,y2; shootthing = t1; x2 = t1.x + (distance>>FRACBITS)*finecosine(angle); y2 = t1.y + (distance>>FRACBITS)*finesine(angle); shootz = t1.z + (t1.height>>1) + 8*FRACUNIT; // can't shoot outside view angles topslope = 100*FRACUNIT/160; bottomslope = -100*FRACUNIT/160; attackrange = distance; linetarget = null; PathTraverse ( t1.x, t1.y, x2, y2, PT_ADDLINES|PT_ADDTHINGS, AimTraverse ); if (linetarget!=null) return aimslope; return 0; } /** * P_LineAttack * If damage == 0, it is just a test trace * that will leave linetarget set. * * @param t1 * @param angle angle_t * @param distance fixed_t * @param slope fixed_t * @param damage */ void LineAttack ( mobj_t t1, long angle, int distance, int slope, int damage ) { int x2,y2; shootthing = t1; la_damage = damage; x2 = t1.x + (distance>>FRACBITS)*finecosine(angle); y2 = t1.y + (distance>>FRACBITS)*finesine(angle); shootz = t1.z + (t1.height>>1) + 8*FRACUNIT; attackrange = distance; aimslope = slope; PathTraverse ( t1.x, t1.y, x2, y2, PT_ADDLINES|PT_ADDTHINGS, ShootTraverse ); } // // USE LINES // mobj_t usething; boolean UseTraverse (intercept_t in) { boolean side; // FIXME: some sanity check here? line_t line=(line_t) in.d(); if (line.special==0) { LineOpening (line); if (openrange <= 0) { S.StartSound(usething, sfxenum_t.sfx_noway); // can't use through a wall return false; } // not a special line, but keep checking return true ; } side = false; if (line.PointOnLineSide (usething.x, usething.y)) side = true; // return false; // don't use back side UseSpecialLine (usething, line, side); // can't use for than one special line in a row return false; } /** * P_UseSpecialLine * Called when a thing uses a special line. * Only the front sides of lines are usable. */ public boolean UseSpecialLine ( mobj_t thing, line_t line, boolean side ) { // Err... // Use the back sides of VERY SPECIAL lines... if (side) { switch(line.special) { case 124: // Sliding door open&close // SL.EV_SlidingDoor(line, thing); break; default: return false; //break; } } // Switches that other things can activate. //_D_: little bug fixed here, see linuxdoom source if (thing.player==/*!=*/null) { // never open secret doors if (eval(line.flags& ML_SECRET)) return false; switch(line.special) { case 1: // MANUAL DOOR RAISE case 32: // MANUAL BLUE case 33: // MANUAL RED case 34: // MANUAL YELLOW break; default: return false; //break; } } // do something switch (line.special) { // MANUALS case 1: // Vertical Door case 26: // Blue Door/Locked case 27: // Yellow Door /Locked case 28: // Red Door /Locked case 31: // Manual door open case 32: // Blue locked door open case 33: // Red locked door open case 34: // Yellow locked door open case 117: // Blazing door raise case 118: // Blazing door open VerticalDoor (line, thing); break; //UNUSED - Door Slide Open&Close case 124: // NOTE: clashes with secret level exit. //SL.EV_SlidingDoor (line, thing); break; // SWITCHES case 7: // Build Stairs if (BuildStairs(line,stair_e.build8)) SW.ChangeSwitchTexture(line,false); break; case 9: // Change Donut if (DoDonut(line)) SW.ChangeSwitchTexture(line,false); break; case 11: // Exit level SW.ChangeSwitchTexture(line,false); DM.ExitLevel (); break; case 14: // Raise Floor 32 and change texture if (PEV.DoPlat(line,plattype_e.raiseAndChange,32)) SW.ChangeSwitchTexture(line,false); break; case 15: // Raise Floor 24 and change texture if (PEV.DoPlat(line,plattype_e.raiseAndChange,24)) SW.ChangeSwitchTexture(line,false); break; case 18: // Raise Floor to next highest floor if (DoFloor(line, floor_e.raiseFloorToNearest)) SW.ChangeSwitchTexture(line,false); break; case 20: // Raise Plat next highest floor and change texture if (PEV.DoPlat(line,plattype_e.raiseToNearestAndChange,0)) SW.ChangeSwitchTexture(line,false); break; case 21: // PlatDownWaitUpStay if (PEV.DoPlat(line,plattype_e.downWaitUpStay,0)) SW.ChangeSwitchTexture(line,false); break; case 23: // Lower Floor to Lowest if (DoFloor(line,floor_e.lowerFloorToLowest)) SW.ChangeSwitchTexture(line,false); break; case 29: // Raise Door if (DoDoor(line,vldoor_e.normal)) SW.ChangeSwitchTexture(line,false); break; case 41: // Lower Ceiling to Floor if (DoCeiling(line,ceiling_e.lowerToFloor)) SW.ChangeSwitchTexture(line,false); break; case 71: // Turbo Lower Floor if (DoFloor(line,floor_e.turboLower)) SW.ChangeSwitchTexture(line,false); break; case 49: // Ceiling Crush And Raise if (DoCeiling(line,ceiling_e.crushAndRaise)) SW.ChangeSwitchTexture(line,false); break; case 50: // Close Door if (DoDoor(line,vldoor_e.close)) SW.ChangeSwitchTexture(line,false); break; case 51: // Secret EXIT SW.ChangeSwitchTexture(line,false); DM.SecretExitLevel (); break; case 55: // Raise Floor Crush if (DoFloor(line,floor_e.raiseFloorCrush)) SW.ChangeSwitchTexture(line,false); break; case 101: // Raise Floor if (DoFloor(line,floor_e.raiseFloor)) SW.ChangeSwitchTexture(line,false); break; case 102: // Lower Floor to Surrounding floor height if (DoFloor(line,floor_e.lowerFloor)) SW.ChangeSwitchTexture(line,false); break; case 103: // Open Door if (DoDoor(line,vldoor_e.open)) SW.ChangeSwitchTexture(line,false); break; case 111: // Blazing Door Raise (faster than TURBO!) if (DoDoor (line,vldoor_e.blazeRaise)) SW.ChangeSwitchTexture(line,false); break; case 112: // Blazing Door Open (faster than TURBO!) if (DoDoor (line,vldoor_e.blazeOpen)) SW.ChangeSwitchTexture(line,false); break; case 113: // Blazing Door Close (faster than TURBO!) if (DoDoor (line,vldoor_e.blazeClose)) SW.ChangeSwitchTexture(line,false); break; case 122: // Blazing PlatDownWaitUpStay if (PEV.DoPlat(line,plattype_e.blazeDWUS,0)) SW.ChangeSwitchTexture(line,false); break; case 127: // Build Stairs Turbo 16 if (BuildStairs(line,stair_e.turbo16)) SW.ChangeSwitchTexture(line,false); break; case 131: // Raise Floor Turbo if (DoFloor(line,floor_e.raiseFloorTurbo)) SW.ChangeSwitchTexture(line,false); break; case 133: // BlzOpenDoor BLUE case 135: // BlzOpenDoor RED case 137: // BlzOpenDoor YELLOW if (DoLockedDoor (line,vldoor_e.blazeOpen,thing)) SW.ChangeSwitchTexture(line,false); break; case 140: // Raise Floor 512 if (DoFloor(line,floor_e.raiseFloor512)) SW.ChangeSwitchTexture(line,false); break; // BUTTONS case 42: // Close Door if (DoDoor(line,vldoor_e.close)) SW.ChangeSwitchTexture(line,true); break; case 43: // Lower Ceiling to Floor if (DoCeiling(line,ceiling_e.lowerToFloor)) SW.ChangeSwitchTexture(line,true); break; case 45: // Lower Floor to Surrounding floor height if (DoFloor(line,floor_e.lowerFloor)) SW.ChangeSwitchTexture(line,true); break; case 60: // Lower Floor to Lowest if (DoFloor(line,floor_e.lowerFloorToLowest)) SW.ChangeSwitchTexture(line,true); break; case 61: // Open Door if (DoDoor(line,vldoor_e.open)) SW.ChangeSwitchTexture(line,true); break; case 62: // PlatDownWaitUpStay if (PEV.DoPlat(line,plattype_e.downWaitUpStay,1)) SW.ChangeSwitchTexture(line,true); break; case 63: // Raise Door if (DoDoor(line,vldoor_e.normal)) SW.ChangeSwitchTexture(line,true); break; case 64: // Raise Floor to ceiling if (DoFloor(line,floor_e.raiseFloor)) SW.ChangeSwitchTexture(line,true); break; case 66: // Raise Floor 24 and change texture if (PEV.DoPlat(line,plattype_e.raiseAndChange,24)) SW.ChangeSwitchTexture(line,true); break; case 67: // Raise Floor 32 and change texture if (PEV.DoPlat(line,plattype_e.raiseAndChange,32)) SW.ChangeSwitchTexture(line,true); break; case 65: // Raise Floor Crush if (DoFloor(line,floor_e.raiseFloorCrush)) SW.ChangeSwitchTexture(line,true); break; case 68: // Raise Plat to next highest floor and change texture if (PEV.DoPlat(line,plattype_e.raiseToNearestAndChange,0)) SW.ChangeSwitchTexture(line,true); break; case 69: // Raise Floor to next highest floor if (DoFloor(line, floor_e.raiseFloorToNearest)) SW.ChangeSwitchTexture(line,true); break; case 70: // Turbo Lower Floor if (DoFloor(line,floor_e.turboLower)) SW.ChangeSwitchTexture(line,true); break; case 114: // Blazing Door Raise (faster than TURBO!) if (DoDoor (line,vldoor_e.blazeRaise)) SW.ChangeSwitchTexture(line,true); break; case 115: // Blazing Door Open (faster than TURBO!) if (DoDoor (line,vldoor_e.blazeOpen)) SW.ChangeSwitchTexture(line,true); break; case 116: // Blazing Door Close (faster than TURBO!) if (DoDoor (line,vldoor_e.blazeClose)) SW.ChangeSwitchTexture(line,true); break; case 123: // Blazing PlatDownWaitUpStay if (PEV.DoPlat(line,plattype_e.blazeDWUS,0)) SW.ChangeSwitchTexture(line,true); break; case 132: // Raise Floor Turbo if (DoFloor(line,floor_e.raiseFloorTurbo)) SW.ChangeSwitchTexture(line,true); break; case 99: // BlzOpenDoor BLUE case 134: // BlzOpenDoor RED case 136: // BlzOpenDoor YELLOW if (DoLockedDoor (line,vldoor_e.blazeOpen,thing)) SW.ChangeSwitchTexture(line,true); break; case 138: // Light Turn On LEV.LightTurnOn(line,255); SW.ChangeSwitchTexture(line,true); break; case 139: // Light Turn Off LEV.LightTurnOn(line,35); SW.ChangeSwitchTexture(line,true); break; } return true; } /** * P_UseLines * Looks for special lines in front of the player to activate. */ public void UseLines (player_t player) { int angle; int x1,y1,x2,y2; //System.out.println("Uselines"); usething = player.mo; // Normally this shouldn't cause problems? angle = Tables.toBAMIndex(player.mo.angle); x1 = player.mo.x; y1 = player.mo.y; x2 = x1 + (USERANGE>>FRACBITS)*finecosine[angle]; y2 = y1 + (USERANGE>>FRACBITS)*finesine[angle]; PathTraverse ( x1, y1, x2, y2, PT_ADDLINES, UseTraverse ); } // // RADIUS ATTACK // mobj_t bombsource; mobj_t bombspot; int bombdamage; /** * P_RadiusAttack * Source is the creature that caused the explosion at spot. */ void RadiusAttack ( mobj_t spot, mobj_t source, int damage ) { int x; int y; int xl; int xh; int yl; int yh; int dist; // fixed_t dist = (damage+MAXRADIUS)<<FRACBITS; yh = LL.getSafeBlockY(spot.y + dist - LL.bmaporgy); yl = LL.getSafeBlockY(spot.y - dist - LL.bmaporgy); xh = LL.getSafeBlockX(spot.x + dist - LL.bmaporgx); xl = LL.getSafeBlockX(spot.x - dist - LL.bmaporgx); bombspot = spot; bombsource = source; bombdamage = damage; for (y=yl ; y<=yh ; y++) for (x=xl ; x<=xh ; x++) BlockThingsIterator (x, y, RadiusAttack ); } // // SECTOR HEIGHT CHANGING // After modifying a sectors floor or ceiling height, // call this routine to adjust the positions // of all things that touch the sector. // // If anything doesn't fit anymore, true will be returned. // If crunch is true, they will take damage // as they are being crushed. // If Crunch is false, you should set the sector height back // the way it was and call P_ChangeSector again // to undo the changes. // boolean crushchange; boolean nofit; // // P_ChangeSector // boolean ChangeSector ( sector_t sector, boolean crunch ) { int x; int y; nofit = false; crushchange = crunch; // re-check heights for all things near the moving sector for (x=sector.blockbox[BOXLEFT] ; x<= sector.blockbox[BOXRIGHT] ; x++) for (y=sector.blockbox[BOXBOTTOM];y<= sector.blockbox[BOXTOP] ; y++) BlockThingsIterator (x, y, ChangeSector); return nofit; } /** * P_BlockLinesIterator * The validcount flags are used to avoid checking lines * that are marked in multiple mapblocks, * so increment validcount before the first call * to P_BlockLinesIterator, then make one or more calls * to it. */ public boolean BlockLinesIterator (int x,int y,PIT_LineFunction func) { int offset; int lineinblock; line_t ld; if (x<0 || y<0 || x>=LL.bmapwidth || y>=LL.bmapheight) { return true; } // This gives us the index to look up (in blockmap) offset = y*LL.bmapwidth+x; // The index contains yet another offset, but this time offset = LL.blockmap[offset]; // MAES: blockmap terminating marker is always -1 final int validcount=R.getValidCount(); // [SYNC ISSUE]: don't skip offset+1 :-/ for (int list=offset;(lineinblock=LL.blockmap[list])!=-1;list++){ ld = LL.lines[lineinblock]; //System.out.println(ld); if (ld.validcount == validcount) continue; // line has already been checked ld.validcount = validcount; if ( !func.invoke(ld) ) return false; } return true; // everything was checked } /** * P_PathTraverse * Traces a line from x1,y1 to x2,y2, * calling the traverser function for each. * Returns true if the traverser function returns true * for all lines. */ private final boolean PathTraverse(int x1, int y1, int x2, int y2, int flags, PTR_InterceptFunc trav) { // System.out.println("Pathtraverse "+x1+" , " +y1+" to "+x2 +" , " // +y2); final int xt1, yt1; final int xt2, yt2; final long _x1, _x2, _y1, _y2; final int mapx1, mapy1; final int xstep, ystep; int partial; int xintercept, yintercept; int mapx; int mapy; int mapxstep; int mapystep; int count; earlyout = eval(flags& PT_EARLYOUT); R.increaseValidCount(1); intercept_p = 0; if (((x1 - LL.bmaporgx) & (MAPBLOCKSIZE - 1)) == 0) x1 += FRACUNIT; // don't side exactly on a line if (((y1 - LL.bmaporgy) & (MAPBLOCKSIZE - 1)) == 0) y1 += FRACUNIT; // don't side exactly on a line trace.x = x1; trace.y = y1; trace.dx = x2 - x1; trace.dy = y2 - y1; // Code developed in common with entryway // for prBoom+ _x1 = (long) x1 - LL.bmaporgx; _y1 = (long) y1 - LL.bmaporgy; xt1 = (int) (_x1 >> MAPBLOCKSHIFT); yt1 = (int) (_y1 >> MAPBLOCKSHIFT); mapx1 = (int) (_x1 >> MAPBTOFRAC); mapy1 = (int) (_y1 >> MAPBTOFRAC); _x2 = (long) x2 - LL.bmaporgx; _y2 = (long) y2 - LL.bmaporgy; xt2 = (int) (_x2 >> MAPBLOCKSHIFT); yt2 = (int) (_y2 >> MAPBLOCKSHIFT); x1 -= LL.bmaporgx; y1 -= LL.bmaporgy; x2 -= LL.bmaporgx; y2 -= LL.bmaporgy; if (xt2 > xt1) { mapxstep = 1; partial = FRACUNIT - (mapx1&(FRACUNIT-1)); ystep = FixedDiv (y2-y1,Math.abs(x2-x1)); } else if (xt2 < xt1) { mapxstep = -1; partial = mapx1&(FRACUNIT-1); ystep = FixedDiv (y2-y1,Math.abs(x2-x1)); } else { mapxstep = 0; partial = FRACUNIT; ystep = 256*FRACUNIT; } yintercept = (int) (mapy1 + FixedMul (partial, ystep)); if (yt2 > yt1) { mapystep = 1; partial = FRACUNIT - (mapy1&(FRACUNIT-1)); xstep = FixedDiv (x2-x1,Math.abs(y2-y1)); } else if (yt2 < yt1) { mapystep = -1; partial = mapy1&(FRACUNIT-1); xstep = FixedDiv (x2-x1,Math.abs(y2-y1)); } else { mapystep = 0; partial = FRACUNIT; xstep = 256*FRACUNIT; } xintercept = (int) (mapx1 + FixedMul (partial, xstep)); // Step through map blocks. // Count is present to prevent a round off error // from skipping the break. mapx = xt1; mapy = yt1; for (count = 0 ; count < 64 ; count++) { if (eval(flags &PT_ADDLINES)) { if (!BlockLinesIterator (mapx, mapy,AddLineIntercepts)) return false; // early out } if (eval(flags &PT_ADDTHINGS)) { if (!BlockThingsIterator (mapx, mapy,AddThingIntercepts)) return false; // early out } if (mapx == xt2 && mapy == yt2) { break; } boolean changeX = (yintercept >> FRACBITS) == mapy; boolean changeY = (xintercept >> FRACBITS) == mapx; if (changeX) { yintercept += ystep; mapx += mapxstep; } else //[MAES]: this fixed sync issues. Lookup linuxdoom if (changeY) { xintercept += xstep; mapy += mapystep; } } // go through the sorted list //System.out.println("Some intercepts found"); return TraverseIntercepts ( trav, FRACUNIT ); } // end method // // FLOORS // private static final int FLOORSPEED= MAPFRACUNIT; /** Move a plane (floor or ceiling) and check for crushing * @param sector * @param speed fixed * @param dest fixed * @param crush * @param floorOrCeiling * @param direction */ result_e MovePlane ( sector_t sector, int speed, int dest, boolean crush, int floorOrCeiling, int direction ) { boolean flag; int lastpos; // fixed_t switch(floorOrCeiling) { case 0: // FLOOR switch(direction) { case -1: // DOWN if (sector.floorheight - speed < dest) { lastpos = sector.floorheight; sector.floorheight = dest; flag = ChangeSector(sector,crush); if (flag == true) { sector.floorheight =lastpos; ChangeSector(sector,crush); //return crushed; } return result_e.pastdest; } else { lastpos = sector.floorheight; sector.floorheight -= speed; flag = ChangeSector(sector,crush); if (flag == true) { sector.floorheight = lastpos; ChangeSector(sector,crush); return result_e.crushed; } } break; case 1: // UP if (sector.floorheight + speed > dest) { lastpos = sector.floorheight; sector.floorheight = dest; flag = ChangeSector(sector,crush); if (flag == true) { sector.floorheight = lastpos; ChangeSector(sector,crush); //return crushed; } return result_e.pastdest; } else { // COULD GET CRUSHED lastpos = sector.floorheight; sector.floorheight += speed; flag =ChangeSector(sector,crush); if (flag == true) { if (crush == true) return result_e.crushed; sector.floorheight = lastpos; ChangeSector(sector,crush); return result_e.crushed; } } break; } break; case 1: // CEILING switch(direction) { case -1: // DOWN if (sector.ceilingheight - speed < dest) { lastpos = sector.ceilingheight; sector.ceilingheight = dest; flag = ChangeSector(sector,crush); if (flag == true) { sector.ceilingheight = lastpos; ChangeSector(sector,crush); //return crushed; } return result_e.pastdest; } else { // COULD GET CRUSHED lastpos = sector.ceilingheight; sector.ceilingheight -= speed; flag = ChangeSector(sector,crush); if (flag == true) { if (crush == true) return result_e.crushed; sector.ceilingheight = lastpos; ChangeSector(sector,crush); return result_e.crushed; } } break; case 1: // UP if (sector.ceilingheight + speed > dest) { lastpos = sector.ceilingheight; sector.ceilingheight = dest; flag =ChangeSector(sector,crush); if (flag == true) { sector.ceilingheight = lastpos; ChangeSector(sector,crush); //return crushed; } return result_e.pastdest; } else { lastpos = sector.ceilingheight; sector.ceilingheight += speed; flag = ChangeSector(sector,crush); // UNUSED /* if (flag == true) { sector.ceilingheight = lastpos; P_ChangeSector(sector,crush); return crushed; } */ } break; } break; } return result_e.ok; } /** P_CheckMissileSpawn * Moves the missile forward a bit * and possibly explodes it right there. * * @param th */ void CheckMissileSpawn (mobj_t th) { th.tics -= RND.P_Random()&3; if (th.tics < 1) th.tics = 1; // move a little forward so an angle can // be computed if it immediately explodes th.x += (th.momx>>1); th.y += (th.momy>>1); th.z += (th.momz>>1); if (!TryMove (th, th.x, th.y)) ExplodeMissile (th); } // //P_Ticker // public void Ticker () { int i; // run the tic if (DM.paused) return; // pause if in menu and at least one tic has been run if ( !DM.netgame && DM.menuactive && !DM.demoplayback && DM.players[DM.consoleplayer].viewz != 1) { return; } for (i=0 ; i<MAXPLAYERS ; i++) if (DM.playeringame[i]) DM.players[i].PlayerThink (); RunThinkers (); SPECS.UpdateSpecials (); // In specials. Merge? RespawnSpecials (); // for par times DM.leveltime++; } /** * P_ShootSpecialLine - IMPACT SPECIALS * Called when a thing shoots a special line. */ public void ShootSpecialLine ( mobj_t thing, line_t line ) { boolean ok; // Impacts that other things can activate. if (thing.player==null) { ok = false; switch(line.special) { case 46: // OPEN DOOR IMPACT ok = true; break; } if (!ok) return; } switch(line.special) { case 24: // RAISE FLOOR DoFloor(line,floor_e.raiseFloor); SW.ChangeSwitchTexture(line,false); break; case 46: // OPEN DOOR DoDoor(line,vldoor_e.open); SW.ChangeSwitchTexture(line,true); break; case 47: // RAISE FLOOR NEAR AND CHANGE PEV.DoPlat(line,plattype_e.raiseToNearestAndChange,0); SW.ChangeSwitchTexture(line,false); break; } } /** P_SpawnSpecials * After the map has been loaded, scan for specials * that spawn thinkers */ void SpawnSpecials () { sector_t sector; int i; /*int episode; episode = 1; if (W.CheckNumForName("texture2") >= 0) episode = 2; */ // See if -TIMER needs to be used. SPECS.levelTimer = false; i = DM.CM.CheckParm("-avg"); if (eval(i) && DM.deathmatch) { SPECS.levelTimer = true; SPECS.levelTimeCount = 20 * 60 * 35; } i = DM.CM.CheckParm("-timer"); if (eval(i) && DM.deathmatch) { int time; time = Integer.parseInt(DM.CM.getArgv(i+1)) * 60 * 35; SPECS.levelTimer = true; SPECS.levelTimeCount = time; } // Init special SECTORs. //sector = LL.sectors; for (i=0 ; i<LL.numsectors ; i++) { sector=LL.sectors[i]; if (!eval(sector.special)) continue; switch (sector.special) { case 1: // FLICKERING LIGHTS sector.SpawnLightFlash (); break; case 2: // STROBE FAST sector.SpawnStrobeFlash(FASTDARK,0); break; case 3: // STROBE SLOW sector.SpawnStrobeFlash(SLOWDARK,0); break; case 4: // STROBE FAST/DEATH SLIME sector.SpawnStrobeFlash(FASTDARK,0); sector.special = 4; break; case 8: // GLOWING LIGHT sector.SpawnGlowingLight(); break; case 9: // SECRET SECTOR DM.totalsecret++; break; case 10: // DOOR CLOSE IN 30 SECONDS sector.SpawnDoorCloseIn30 (); break; case 12: // SYNC STROBE SLOW sector.SpawnStrobeFlash (SLOWDARK, 1); break; case 13: // SYNC STROBE FAST sector.SpawnStrobeFlash ( FASTDARK, 1); break; case 14: // DOOR RAISE IN 5 MINUTES sector.SpawnDoorRaiseIn5Mins (i); break; case 17: sector.SpawnFireFlicker(); break; } } // Init line EFFECTs SPECS.numlinespecials = 0; for (i = 0;i < LL.numlines; i++) { switch(LL.lines[i].special) { case 48: // EFFECT FIRSTCOL SCROLL+ // Maes 6/4/2012: removed length limit. if (SPECS.numlinespecials==SPECS.linespeciallist.length) SPECS.resizeLinesSpecialList(); SPECS.linespeciallist[SPECS.numlinespecials] = LL.lines[i]; SPECS.numlinespecials++; break; } } // Init other misc stuff for (i = 0;i < getMaxCeilings();i++) getActiveCeilings()[i] = null; PEV.initActivePlats(); SW.initButtonList(); // UNUSED: no horizonal sliders. // if (SL!=null) { // SL.updateStatus(DM); // SL.P_InitSlidingDoorFrames(); //} } /** * Move a plat up and down */ void PlatRaise(plat_t plat) { result_e res; switch(plat.status) { case up: res = MovePlane(plat.sector, plat.speed, plat.high, plat.crush,0,1); if (plat.type == plattype_e.raiseAndChange || plat.type == plattype_e.raiseToNearestAndChange) { if (!eval(DM.leveltime&7)) S.StartSound(plat.sector.soundorg, sfxenum_t.sfx_stnmov); } if (res == result_e.crushed && (!plat.crush)) { plat.count = plat.wait; plat.status = plat_e.down; S.StartSound(plat.sector.soundorg, sfxenum_t.sfx_pstart); } else { if (res == result_e.pastdest) { plat.count = plat.wait; plat.status = plat_e.waiting; S.StartSound(plat.sector.soundorg, sfxenum_t.sfx_pstop); switch(plat.type) { case blazeDWUS: case downWaitUpStay: PEV.RemoveActivePlat(plat); break; case raiseAndChange: case raiseToNearestAndChange: PEV.RemoveActivePlat(plat); break; default: break; } } } break; case down: res = MovePlane(plat.sector,plat.speed,plat.low,false,0,-1); if (res == result_e.pastdest) { plat.count = plat.wait; plat.status = plat_e.waiting; S.StartSound(plat.sector.soundorg,sfxenum_t.sfx_pstop); } break; case waiting: if (--plat.count==0) { if (plat.sector.floorheight == plat.low) plat.status = plat_e.up; else plat.status = plat_e.down; S.StartSound(plat.sector.soundorg,sfxenum_t.sfx_pstart); } case in_stasis: break; } } public Actions(DoomStatus DC){ super(DC); this.A=this; SlideTraverse=new PTR_SlideTraverse(); AimTraverse=new PTR_AimTraverse(); ShootTraverse=new PTR_ShootTraverse(); UseTraverse=new PTR_UseTraverse(); AddLineIntercepts=new PIT_AddLineIntercepts(); AddThingIntercepts=new PIT_AddThingIntercepts(); VileCheck=new PIT_VileCheck(); CheckLine=new PIT_CheckLine(); StompThing=new PIT_StompThing(); CheckThing=new PIT_CheckThing(); RadiusAttack=new PIT_RadiusAttack(); ChangeSector=new PIT_ChangeSector(); } // // P_RunThinkers // public void RunThinkers() { thinker_t currentthinker; currentthinker = thinkercap.next; while (currentthinker != thinkercap) { if (currentthinker.function == think_t.NOP) { // time to remove it currentthinker.next.prev = currentthinker.prev; currentthinker.prev.next = currentthinker.next; // Problem: freeing was done explicitly on think_t's, not mobj_t's. /*try { // According to certian gurus, this method is faster than instanceof // and almost on par with id checking. // // http://stackoverflow.com/questions/103564/the-performance-impact-of-using-instanceof-in-java if (currentthinker.getClass()==mobj_t.class) mobjpool.checkIn((mobj_t)currentthinker); } catch (ClassCastException e){ // Object will simply be destroyed without reuse, in this case. } */ // Z_Free (currentthinker); } else { if (currentthinker.acp1!=null) // Execute thinker's function. currentthinker.acp1.invoke((mobj_t) currentthinker); else if (currentthinker.acpss!=null) { currentthinker.acpss.invoke(currentthinker); } } currentthinker = currentthinker.next; } } public void setActiveceilings(ceiling_t[] activeceilings) { this.activeceilings = activeceilings; } public final ceiling_t[] getActiveCeilings() { return activeceilings; } public final int getMaxCeilings() { return activeceilings.length; } ///////////////////// PIT AND PTR FUNCTIONS ////////////////// PTR_InterceptFunc SlideTraverse; PTR_InterceptFunc AimTraverse; PTR_InterceptFunc ShootTraverse; PTR_InterceptFunc UseTraverse; PIT_AddLineIntercepts AddLineIntercepts; PIT_AddThingIntercepts AddThingIntercepts; PIT_VileCheck VileCheck; PIT_CheckLine CheckLine; PIT_StompThing StompThing; PIT_CheckThing CheckThing; PIT_RadiusAttack RadiusAttack; PIT_ChangeSector ChangeSector; /** * PIT_VileCheck * Detect a corpse that could be raised. */ class PIT_VileCheck implements PIT_MobjFunction { public mobj_t corpsehit; public mobj_t vileobj; public int viletryx; public int viletryy; public boolean invoke (mobj_t thing) { int maxdist; boolean check; if (!eval(thing.flags &MF_CORPSE) ) return true; // not a monster if (thing.tics != -1) return true; // not lying still yet if (thing.info.raisestate == statenum_t.S_NULL) return true; // monster doesn't have a raise state maxdist = thing.info.radius + mobjinfo[mobjtype_t.MT_VILE.ordinal()].radius; if ( Math.abs(thing.x - viletryx) > maxdist || Math.abs(thing.y - viletryy) > maxdist ) return true; // not actually touching corpsehit = thing; corpsehit.momx = corpsehit.momy = 0; corpsehit.height <<= 2; check = CheckPosition (corpsehit, corpsehit.x, corpsehit.y); corpsehit.height >>= 2; if (!check) return true; // doesn't fit here return false; // got one, so stop checking } } /** * PIT_ChangeSector */ private class PIT_ChangeSector implements PIT_MobjFunction { public boolean invoke (mobj_t thing) { mobj_t mo; if (ThingHeightClip (thing)) { // keep checking return true; } // crunch bodies to giblets if (thing.health <= 0) { thing.SetMobjState(statenum_t.S_GIBS); thing.flags &= ~MF_SOLID; thing.height = 0; thing.radius = 0; // keep checking return true; } // crunch dropped items if (eval(thing.flags& MF_DROPPED)) { A.RemoveMobj (thing); // keep checking return true; } if (! eval(thing.flags & MF_SHOOTABLE) ) { // assume it is bloody gibs or something return true; } nofit = true; if (crushchange && !eval(DM.leveltime&3) ) { A.DamageMobj( thing,null,null,10); // spray blood in a random direction mo = A.SpawnMobj (thing.x, thing.y, thing.z + thing.height/2, mobjtype_t.MT_BLOOD); mo.momx = (RND.P_Random() - RND.P_Random ())<<12; mo.momy = (RND.P_Random() - RND.P_Random ())<<12; } // keep checking (crush other things) return true; } } /** PIT_CheckLine * Adjusts tmfloorz and tmceilingz as lines are contacted * */ protected class PIT_CheckLine implements PIT_LineFunction { public boolean invoke(line_t ld){ if (tmbbox[BOXRIGHT] <= ld.bbox[BOXLEFT] || tmbbox[BOXLEFT] >= ld.bbox[BOXRIGHT] || tmbbox[BOXTOP] <= ld.bbox[BOXBOTTOM] || tmbbox[BOXBOTTOM] >= ld.bbox[BOXTOP] ) return true; if (ld.BoxOnLineSide (tmbbox) != -1) return true; // A line has been hit // The moving thing's destination position will cross // the given line. // If this should not be allowed, return false. // If the line is special, keep track of it // to process later if the move is proven ok. // NOTE: specials are NOT sorted by order, // so two special lines that are only 8 pixels apart // could be crossed in either order. if (ld.backsector==null) return false; // one sided line if (!eval(tmthing.flags& MF_MISSILE) ) { if ( eval(ld.flags& ML_BLOCKING) ) return false; // explicitly blocking everything if ( (tmthing.player==null) && eval(ld.flags& ML_BLOCKMONSTERS )) return false; // block monsters only } // set openrange, opentop, openbottom LineOpening (ld); // adjust floor / ceiling heights if (opentop < tmceilingz) { tmceilingz = opentop; ceilingline = ld; } if (openbottom > tmfloorz) tmfloorz = openbottom; if (lowfloor < tmdropoffz) tmdropoffz = lowfloor; // if contacted a special line, add it to the list if (ld.special!=0) { spechit[numspechit] = ld; numspechit++; // Let's be proactive about this. if (numspechit>=spechit.length) ResizeSpechits(); } return true; } } /**PIT_CheckThing */ private class PIT_CheckThing implements PIT_MobjFunction { public boolean invoke (mobj_t thing) { int blockdist; // fixed_t boolean solid; int damage; if ((thing.flags & (MF_SOLID|MF_SPECIAL|MF_SHOOTABLE) )==0) return true; blockdist = thing.radius + tmthing.radius; if ( Math.abs(thing.x - tmx) >= blockdist || Math.abs(thing.y - tmy) >= blockdist ) { // didn't hit it return true; } // don't clip against self if (thing == tmthing) return true; // check for skulls slamming into things if ((tmthing.flags & MF_SKULLFLY)!=0) { damage = ((RND.P_Random()%8)+1)*tmthing.info.damage; A.DamageMobj (thing, tmthing, tmthing, damage); tmthing.flags &= ~MF_SKULLFLY; tmthing.momx = tmthing.momy = tmthing.momz = 0; tmthing.SetMobjState (tmthing.info.spawnstate); return false; // stop moving } // missiles can hit other things if (eval(tmthing.flags & MF_MISSILE)) { // see if it went over / under if (tmthing.z > thing.z + thing.height) return true; // overhead if (tmthing.z+tmthing.height < thing.z) return true; // underneath if (tmthing.target!=null && ( tmthing.target.type == thing.type || (tmthing.target.type == mobjtype_t.MT_KNIGHT && thing.type == mobjtype_t.MT_BRUISER)|| (tmthing.target.type == mobjtype_t.MT_BRUISER && thing.type == mobjtype_t.MT_KNIGHT) ) ) { // Don't hit same species as originator. if (thing == tmthing.target) return true; if (thing.type != mobjtype_t.MT_PLAYER) { // Explode, but do no damage. // Let players missile other players. return false; } } if (! eval(thing.flags &MF_SHOOTABLE) ) { // didn't do any damage return !eval(thing.flags & MF_SOLID); } // damage / explode damage = ((RND.P_Random()%8)+1)*tmthing.info.damage; A.DamageMobj (thing, tmthing, tmthing.target, damage); // don't traverse any more return false; } // check for special pickup if (eval(thing.flags & MF_SPECIAL)) { solid = eval(thing.flags&MF_SOLID); if (eval(tmflags&MF_PICKUP)) { // can remove thing A.TouchSpecialThing (thing, tmthing); } return !solid; } return !eval(thing.flags &MF_SOLID); } } /** * PIT_RadiusAttack * "bombsource" is the creature * that caused the explosion at "bombspot". */ private class PIT_RadiusAttack implements PIT_MobjFunction { public boolean invoke (mobj_t thing) { int dx,dy,dist; // fixed_t if (!eval(thing.flags & MF_SHOOTABLE) ) return true; // Boss spider and cyborg // take no damage from concussion. if (thing.type == mobjtype_t.MT_CYBORG || thing.type == mobjtype_t.MT_SPIDER) return true; dx = Math.abs(thing.x - bombspot.x); dy = Math.abs(thing.y - bombspot.y); dist = dx>dy ? dx : dy; dist = (dist - thing.radius) >> FRACBITS; if (dist < 0) dist = 0; if (dist >= bombdamage) return true; // out of range if ( EN.CheckSight (thing, bombspot) ) { // must be in direct path A.DamageMobj (thing, bombspot, bombsource, bombdamage - dist); } return true; } } /** *PIT_StompThing */ private class PIT_StompThing implements PIT_MobjFunction { public boolean invoke (mobj_t thing) { int blockdist; // fixed_t if ((thing.flags & MF_SHOOTABLE)==0 ) return true; blockdist = thing.radius + tmthing.radius; if ( Math.abs(thing.x - tmx) >= blockdist || Math.abs(thing.y - tmy) >= blockdist ) { // didn't hit it return true; } // don't clip against self if (thing == tmthing) return true; // monsters don't stomp things except on boss level if ( (tmthing.player==null) && (DM.gamemap != 30)) return false; A.DamageMobj (thing, tmthing, tmthing, 10000); // in interaction return true; } } } package p; public interface Interceptable { } package p; import rr.line_t; import s.degenmobj_t; public class button_t implements Resettable{ public line_t line; public bwhere_e where; public int btexture; public int btimer; public degenmobj_t soundorg; public button_t(){ this.btexture=0; this.btimer=0; this.where=bwhere_e.top; } public void reset(){ this.line=null; this.where=bwhere_e.top; this.btexture=0; this.btimer=0; this.soundorg=null; } } package p; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import w.CacheableDoomObject; import w.DoomBuffer; /** * Source animation definition. Made readable for compatibility with Boom's * SWANTBLS system. * * @author velktron */ public class animdef_t implements CacheableDoomObject { public animdef_t() { } public animdef_t(boolean istexture, String endname, String startname, int speed) { super(); this.istexture = istexture; this.endname = endname; this.startname = startname; this.speed = speed; } /** if false, it is a flat, and will NOT be used as a texture. Unless you * use "flats on walls functionality of course. */ public boolean istexture; /** The END name and START name of a texture, given in this order when reading a lump * The animation system is agnostic to the actual names of of the "in-between" * frames, it's purely pointer based, and only the start/end are constant. It only * counts the actual number of existing textures during initialization time. * */ public String endname,startname; public int speed; public String toString() { return String.format("%s %s %s %d", istexture, startname, endname, speed); } @Override public void unpack(ByteBuffer buf) throws IOException { // Like most Doom structs... buf.order(ByteOrder.LITTLE_ENDIAN); this.istexture = (buf.get() != 0); this.startname = DoomBuffer.getNullTerminatedString(buf, 9); this.endname = DoomBuffer.getNullTerminatedString(buf, 9); this.speed = buf.getInt(); } public static int size() { return 23; } } package p; /** YEAH, I'M USING THE CONSTANTS INTERFACE PATTERN. DEAL WITH IT */ public interface MobjFlags { // // MF_ flags for mobjs. // Call P_SpecialThing when touched. public static final long MF_SPECIAL = 1; // Blocks. public static final long MF_SOLID = 2; // Can be hit. public static final long MF_SHOOTABLE = 4; // Don't use the sector links (invisible but touchable). public static final long MF_NOSECTOR = 8; // Don't use the blocklinks (inert but displayable) public static final long MF_NOBLOCKMAP = 16; // Not to be activated by sound, deaf monster. public static final long MF_AMBUSH = 32; // Will try to attack right back. public static final long MF_JUSTHIT = 64; // Will take at least one step before attacking. public static final long MF_JUSTATTACKED = 128; // On level spawning (initial position), // hang from ceiling instead of stand on floor. public static final long MF_SPAWNCEILING = 256; // Don't apply gravity (every tic), // that is, object will float, keeping current height // or changing it actively. public static final long MF_NOGRAVITY = 512; // Movement flags. // This allows jumps from high places. public static final long MF_DROPOFF = 0x400; // For players, will pick up items. public static final long MF_PICKUP = 0x800; // Player cheat. ??? public static final int MF_NOCLIP = 0x1000; // Player: keep info about sliding along walls. public static final int MF_SLIDE = 0x2000; // Allow moves to any height, no gravity. // For active floaters, e.g. cacodemons, pain elementals. public static final int MF_FLOAT = 0x4000; // Don't cross lines // ??? or look at heights on teleport. public static final int MF_TELEPORT = 0x8000; // Don't hit same species, explode on block. // Player missiles as well as fireballs of various kinds. public static final int MF_MISSILE = 0x10000; // Dropped by a demon, not level spawned. // E.g. ammo clips dropped by dying former humans. public static final int MF_DROPPED = 0x20000; // Use fuzzy draw (shadow demons or spectres), // temporary player invisibility powerup. public static final int MF_SHADOW = 0x40000; // Flag: don't bleed when shot (use puff), // barrels and shootable furniture shall not bleed. public static final long MF_NOBLOOD = 0x80000; // Don't stop moving halfway off a step, // that is, have dead bodies slide down all the way. public static final long MF_CORPSE = 0x100000; // Floating to a height for a move, ??? // don't auto float to target's height. public static final long MF_INFLOAT = 0x200000; // On kill, count this enemy object // towards intermission kill total. // Happy gathering. public static final long MF_COUNTKILL = 0x400000; // On picking up, count this item object // towards intermission item total. public static final long MF_COUNTITEM = 0x800000; // Special handling: skull in flight. // Neither a cacodemon nor a missile. public static final long MF_SKULLFLY = 0x1000000; // Don't spawn this object // in death match mode (e.g. key cards). public static final long MF_NOTDMATCH = 0x2000000; // Player sprites in multiplayer modes are modified // using an internal color lookup table for re-indexing. // If 0x4 0x8 or 0xc, // use a translation table for player colormaps public static final long MF_TRANSLATION = 0xc000000; // Hmm ???. public static final long MF_TRANSSHIFT = 26; public static final long MF_UNUSED2 =(0x0000000010000000); public static final long MF_UNUSED3 =(0x0000000020000000); // Translucent sprite? // phares public static final long MF_TRANSLUCENT =(0x0000000040000000); // this is free LONGLONG(0x0000000100000000) // these are greater than an int. That's why the flags below are now uint_64_t public static final long MF_TOUCHY = (0x0000000100000000L); public static final long MF_BOUNCES =(0x0000000200000000L); public static final long MF_FRIEND = (0x0000000400000000L); public static final long MF_RESSURECTED =(0x0000001000000000L); public static final long MF_NO_DEPTH_TEST =(0x0000002000000000L); public static final long MF_FOREGROUND = (0x0000004000000000L); } package p; import m.IRandom; import rr.SectorAction; // // P_LIGHTS // public class fireflicker_t extends SectorAction{ private IRandom RND; public int count; public int maxlight; public int minlight; public fireflicker_t(IRandom RND){ this.RND=RND; } // // T_FireFlicker // public void FireFlicker() { int amount; if (--count != 0) return; amount = (RND.P_Random() & 3) * 16; if (sector.lightlevel - amount < minlight) sector.lightlevel = (short) minlight; else sector.lightlevel = (short) (maxlight - amount); count = 4; } } package p; // // P_CEILNG // public enum ceiling_e { lowerToFloor, raiseToHighest, lowerAndCrush, crushAndRaise, fastCrushAndRaise, silentCrushAndRaise; } package p; import doom.player_t; public interface ActionType2{ public void invoke ( player_t player, pspdef_t psp ); } package p; import static p.DoorDefines.GLOWSPEED; import java.io.DataInputStream; import java.io.IOException; import java.nio.ByteBuffer; import rr.SectorAction; import w.DoomIO; public class glow_t extends SectorAction{ public int minlight; public int maxlight; public int direction; // // Spawn glowing light // public void Glow() { switch (direction) { case -1: // DOWN sector.lightlevel -= GLOWSPEED; if (sector.lightlevel <= minlight) { sector.lightlevel += GLOWSPEED; direction = 1; } break; case 1: // UP sector.lightlevel += GLOWSPEED; if (sector.lightlevel >= maxlight) { sector.lightlevel -= GLOWSPEED; direction = -1; } break; } } @Override public void read(DataInputStream f) throws IOException{ super.read(f); // Call thinker reader first super.sectorid=DoomIO.readLEInt(f); // Sector index minlight=DoomIO.readLEInt(f); maxlight=DoomIO.readLEInt(f); direction=DoomIO.readLEInt(f); } @Override public void pack(ByteBuffer b) throws IOException{ super.pack(b); //12 b.putInt(super.sectorid); // 16 b.putInt(minlight);//20 b.putInt(maxlight);//24 b.putInt(direction);//38 } } package p; import static rr.line_t.ML_TWOSIDED; import static data.Defines.*; import static data.Limits.MAXPLAYERS; import static data.Limits.MAXRADIUS; import static m.BBox.BOXBOTTOM; import static m.BBox.BOXLEFT; import static m.BBox.BOXRIGHT; import static m.BBox.BOXTOP; import static m.fixed_t.FRACBITS; import static m.fixed_t.FixedDiv; import static utils.C2JUtils.flags; import java.io.IOException; import java.nio.ByteOrder; import m.BBox; import rr.line_t; import rr.node_t; import rr.sector_t; import rr.seg_t; import rr.side_t; import rr.subsector_t; import rr.vertex_t; import s.degenmobj_t; import utils.C2JUtils; import w.DoomBuffer; import data.maplinedef_t; import data.mapnode_t; import data.mapsector_t; import data.mapseg_t; import data.mapsidedef_t; import data.mapsubsector_t; import data.mapthing_t; import data.mapvertex_t; import defines.*; import doom.DoomStatus; //Emacs style mode select -*- C++ -*- //----------------------------------------------------------------------------- // // $Id: LevelLoader.java,v 1.44 2012/09/24 17:16:23 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: // Do all the WAD I/O, get map description, // set up initial state and misc. LUTs. // //----------------------------------------------------------------------------- public class LevelLoader extends AbstractLevelLoader{ public static final String rcsid = "$Id: LevelLoader.java,v 1.44 2012/09/24 17:16:23 velktron Exp $"; public LevelLoader(DoomStatus<?,?> DC) { super(DC); // Traditional loader sets limit. deathmatchstarts=new mapthing_t[MAX_DEATHMATCH_STARTS]; } /** * P_LoadVertexes * @throws IOException */ public void LoadVertexes (int lump) throws IOException { // Make a lame-ass attempt at loading some vertexes. // Determine number of lumps: // total lump length / vertex record length. numvertexes = W.LumpLength (lump) / mapvertex_t.sizeOf(); // Load data into cache. // MAES: we now have a mismatch between memory/disk: in memory, we need an array. // On disk, we have a single lump/blob. Thus, we need to find a way to deserialize this... vertexes=W.CacheLumpNumIntoArray(lump,numvertexes,vertex_t.class); // Copy and convert vertex coordinates, // MAES: not needed. Intermediate mapvertex_t struct skipped. } /** * P_LoadSegs * @throws IOException */ public void LoadSegs (int lump) throws IOException { mapseg_t[] data; mapseg_t ml; seg_t li; line_t ldef; int linedef; int side; // Another disparity between disk/memory. Treat it the same as VERTEXES. numsegs = W.LumpLength (lump) / mapseg_t.sizeOf(); segs = C2JUtils.createArrayOfObjects(seg_t.class,numsegs); data = W.CacheLumpNumIntoArray(lump,numsegs,mapseg_t.class); // We're not done yet! for (int i=0 ; i<numsegs ; i++) { li=segs[i]; ml=data[i]; li.v1 = vertexes[ml.v1]; li.v2 = vertexes[ml.v2]; li.assignVertexValues(); li.angle = ((ml.angle)<<16)&0xFFFFFFFFL; li.offset = (ml.offset)<<16; linedef = ml.linedef; li.linedef = ldef= lines[linedef]; side = ml.side; li.sidedef = sides[ldef.sidenum[side]]; li.frontsector = sides[ldef.sidenum[side]].sector; if (flags(ldef.flags,ML_TWOSIDED)){ // MAES: Fix double sided without back side. E.g. Linedef 16103 in Europe.wad if (ldef.sidenum[side^1]!=line_t.NO_INDEX) li.backsector = sides[ldef.sidenum[side^1]].sector; // Fix two-sided with no back side. //else { //li.backsector=null; //ldef.flags^=ML_TWOSIDED; //} } else { li.backsector = null; } } } /** * P_LoadSubsectors * @throws IOException */ public void LoadSubsectors (int lump) throws IOException { mapsubsector_t ms; subsector_t ss; mapsubsector_t[] data; numsubsectors = W.LumpLength (lump) / mapsubsector_t.sizeOf(); subsectors = C2JUtils.createArrayOfObjects(subsector_t.class,numsubsectors); // Read "mapsubsectors" data=W.CacheLumpNumIntoArray(lump,numsubsectors, mapsubsector_t.class); for (int i=0 ; i<numsubsectors ; i++) { ms=data[i]; ss=subsectors[i]; ss.numlines = ms.numsegs; ss.firstline = ms.firstseg; } } /** * P_LoadSectors * @throws IOException */ public void LoadSectors (int lump) throws IOException { mapsector_t[] data; mapsector_t ms; sector_t ss; numsectors = W.LumpLength (lump) / mapsector_t.sizeOf(); sectors = C2JUtils.createArrayOfObjects(sector_t.class,numsectors); // Read "mapsectors" data=W.CacheLumpNumIntoArray(lump,numsectors,mapsector_t.class); for (int i=0 ; i<numsectors ; i++) { ms = data[i]; ss = sectors[i]; ss.floorheight = ms.floorheight<<FRACBITS; ss.ceilingheight = ms.ceilingheight<<FRACBITS; ss.floorpic = (short) TM.FlatNumForName(ms.floorpic); ss.ceilingpic = (short) TM.FlatNumForName(ms.ceilingpic); ss.lightlevel = ms.lightlevel; ss.special = ms.special; ss.tag = ms.tag; ss.thinglist = null; ss.id=i; ss.TL=this.P; ss.RND=this.DM.RND; } } /** * P_LoadNodes * @throws IOException */ public void LoadNodes (int lump) throws IOException { mapnode_t[] data; int i; int j; int k; mapnode_t mn; node_t no; numnodes = W.LumpLength (lump) / mapnode_t.sizeOf(); nodes = C2JUtils.createArrayOfObjects(node_t.class,numnodes); // Read "mapnodes" data = W.CacheLumpNumIntoArray(lump,numnodes,mapnode_t.class); for (i=0 ; i<numnodes ; i++) { mn=data[i]; no=nodes[i]; no.x = mn.x<<FRACBITS; no.y = mn.y<<FRACBITS; no.dx = mn.dx<<FRACBITS; no.dy = mn.dy<<FRACBITS; for (j=0 ; j<2 ; j++) { // e6y: support for extended nodes no.children[j] = (char) mn.children[j]; // e6y: support for extended nodes if (no.children[j] == 0xFFFF) { no.children[j] = 0xFFFFFFFF; } else if (flags(no.children[j], NF_SUBSECTOR_CLASSIC)) { // Convert to extended type no.children[j] &= ~NF_SUBSECTOR_CLASSIC; // haleyjd 11/06/10: check for invalid subsector reference if (no.children[j] >= numsubsectors) { System.err .printf( "P_LoadNodes: BSP tree references invalid subsector %d.\n", no.children[j]); no.children[j] = 0; } no.children[j] |= NF_SUBSECTOR; } for (k=0 ; k<4 ; k++) no.bbox[j].set(k, mn.bbox[j][k]<<FRACBITS); } } } /** * P_LoadThings * @throws IOException */ public void LoadThings (int lump) throws IOException { mapthing_t[] data; mapthing_t mt; int numthings; boolean spawn; numthings = W.LumpLength (lump) / mapthing_t.sizeOf(); // VERY IMPORTANT: since now caching is near-absolute, // the mapthing_t instances must be CLONED rather than just // referenced, otherwise missing mobj bugs start happening. data=W.CacheLumpNumIntoArray(lump,numthings,mapthing_t.class); for (int i=0 ; i<numthings ; i++) { mt = data[i]; spawn = true; // Do not spawn cool, new monsters if !commercial if ( !DM.isCommercial()) { switch(mt.type) { case 68: // Arachnotron case 64: // Archvile case 88: // Boss Brain case 89: // Boss Shooter case 69: // Hell Knight case 67: // Mancubus case 71: // Pain Elemental case 65: // Former Human Commando case 66: // Revenant case 84: // Wolf SS spawn = false; break; } } if (spawn == false) break; // Do spawn all other stuff. // MAES: we have loaded the shit with the proper endianness, so no fucking around, bitch. /*mt.x = SHORT(mt.x); mt.y = SHORT(mt.y); mt.angle = SHORT(mt.angle); mt.type = SHORT(mt.type); mt.options = SHORT(mt.options);*/ //System.out.printf("Spawning %d %s\n",i,mt.type); P.SpawnMapThing (mt); } // Status may have changed. It's better to release the resources anyway //W.UnlockLumpNum(lump); } /** * P_LoadLineDefs * Also counts secret lines for intermissions. * @throws IOException */ public void LoadLineDefs (int lump) throws IOException { maplinedef_t[] data; maplinedef_t mld; line_t ld; vertex_t v1; vertex_t v2; numlines = W.LumpLength (lump) / maplinedef_t.sizeOf(); lines = C2JUtils.createArrayOfObjects(line_t.class,numlines); // Check those actually used in sectors, later on. used_lines=new boolean[numlines]; // read "maplinedefs" data = W.CacheLumpNumIntoArray(lump,numlines,maplinedef_t.class); for (int i=0 ; i<numlines ; i++) { mld = data[i]; ld = lines[i]; ld.flags = mld.flags; ld.special = mld.special; ld.tag = mld.tag; v1 = ld.v1 = vertexes[(char)mld.v1]; v2 = ld.v2 = vertexes[(char)mld.v2]; ld.dx = v2.x - v1.x; ld.dy = v2.y - v1.y; // Map value semantics. ld.assignVertexValues(); if (ld.dx==0) ld.slopetype = slopetype_t.ST_VERTICAL; else if (ld.dy==0) ld.slopetype = slopetype_t.ST_HORIZONTAL; else { if (FixedDiv (ld.dy , ld.dx) > 0) ld.slopetype = slopetype_t.ST_POSITIVE; else ld.slopetype = slopetype_t.ST_NEGATIVE; } if (v1.x < v2.x) { ld.bbox[BOXLEFT] = v1.x; ld.bbox[BOXRIGHT] = v2.x; } else { ld.bbox[BOXLEFT] = v2.x; ld.bbox[BOXRIGHT] = v1.x; } if (v1.y < v2.y) { ld.bbox[BOXBOTTOM] = v1.y; ld.bbox[BOXTOP] = v2.y; } else { ld.bbox[BOXBOTTOM] = v2.y; ld.bbox[BOXTOP] = v1.y; } ld.sidenum[0] = mld.sidenum[0]; ld.sidenum[1] = mld.sidenum[1]; // Sanity check for two-sided without two valid sides. if (flags(ld.flags,ML_TWOSIDED)) { if ((ld.sidenum[0] == line_t.NO_INDEX) || (ld.sidenum[1] == line_t.NO_INDEX)){ // Well, dat ain't so tu-sided now, ey esse? ld.flags^=ML_TWOSIDED; } } // Front side defined without a valid frontsector. if (ld.sidenum[0] != line_t.NO_INDEX){ ld.frontsector = sides[ld.sidenum[0]].sector; if (ld.frontsector==null){ // // Still null? Bad map. Map to dummy. ld.frontsector=dummy_sector; } } else ld.frontsector = null; // back side defined without a valid backsector. if (ld.sidenum[1] != line_t.NO_INDEX){ ld.backsector = sides[ld.sidenum[1]].sector; if (ld.backsector==null){ // Still null? Bad map. Map to dummy. ld.backsector=dummy_sector; } } else ld.backsector = null; // If at least one valid sector is defined, then it's not null. if (ld.frontsector!=null || ld.backsector!=null) { this.used_lines[i]=true; } } } /** * P_LoadSideDefs */ public void LoadSideDefs (int lump) throws IOException { mapsidedef_t[] data; mapsidedef_t msd; side_t sd; numsides = W.LumpLength (lump) / mapsidedef_t.sizeOf(); sides = C2JUtils.createArrayOfObjects(side_t.class,numsides); data= W.CacheLumpNumIntoArray(lump,numsides,mapsidedef_t.class); for (int i=0 ; i<numsides ; i++) { msd = data[i]; sd = sides[i]; sd.textureoffset = (msd.textureoffset)<<FRACBITS; sd.rowoffset = (msd.rowoffset)<<FRACBITS; sd.toptexture = (short) TM.TextureNumForName(msd.toptexture); sd.bottomtexture = (short) TM.TextureNumForName(msd.bottomtexture); sd.midtexture = (short) TM.TextureNumForName(msd.midtexture); if (msd.sector<0) sd.sector=dummy_sector; else sd.sector = sectors[msd.sector]; } } // MAES 22/5/2011 This hack added for PHOBOS2.WAD, in order to // accomodate for some linedefs having a sector number of "-1". // Any negative sector will get rewired to this dummy sector. // PROBABLY, this will handle unused sector/linedefes cleanly? sector_t dummy_sector=new sector_t(); /** * P_LoadBlockMap * @throws IOException * * TODO: generate BLOCKMAP dynamically to * handle missing cases and increase accuracy. * */ public void LoadBlockMap (int lump) throws IOException { int count=0; if (DM.CM.CheckParmBool("-blockmap") || W.LumpLength(lump) < 8 || (count = W.LumpLength(lump) / 2) >= 0x10000) // e6y CreateBlockMap(); else { DoomBuffer data=(DoomBuffer)W.CacheLumpNum(lump,PU_LEVEL, DoomBuffer.class); count=W.LumpLength(lump)/2; blockmaplump=new int[count]; data.setOrder(ByteOrder.LITTLE_ENDIAN); data.rewind(); data.readCharArray(blockmaplump, count); // Maes: first four shorts are header data. bmaporgx = blockmaplump[0]<<FRACBITS; bmaporgy = blockmaplump[1]<<FRACBITS; bmapwidth = blockmaplump[2]; bmapheight = blockmaplump[3]; // MAES: use killough's code to convert terminators to -1 beforehand for (int i = 4; i < count; i++) { short t = (short) blockmaplump[i]; // killough 3/1/98 blockmaplump[i] = (int) (t == -1 ? -1l : t & 0xffff); } // haleyjd 03/04/10: check for blockmap problems // http://www.doomworld.com/idgames/index.php?id=12935 if (!VerifyBlockMap(count)) { System.err .printf("P_LoadBlockMap: erroneous BLOCKMAP lump may cause crashes.\n"); System.err .printf("P_LoadBlockMap: use \"-blockmap\" command line switch for rebuilding\n"); } } count = bmapwidth*bmapheight; // IMPORTANT MODIFICATION: no need to have both blockmaplump AND blockmap. // If the offsets in the lump are OK, then we can modify them (remove 4) // and copy the rest of the data in one single data array. This avoids // reserving memory for two arrays (we can't simply alias one in Java) blockmap=new int[blockmaplump.length-4]; // Offsets are relative to START OF BLOCKMAP, and IN SHORTS, not bytes. for (int i=0;i<blockmaplump.length-4;i++){ // Modify indexes so that we don't need two different lumps. // Can probably be further optimized if we simply shift everything backwards. // and reuse the same memory space. if (i<count) blockmaplump[i]=blockmaplump[i+4]-4; else{ // Make terminators definitively -1, different that 0xffff short t = (short) blockmaplump[i+4]; // killough 3/1/98 blockmaplump[i] = (int) (t == -1 ? -1l : t & 0xffff); } } // clear out mobj chains // ATTENTION! BUG!!! // If blocklinks are "cleared" to void -but instantiated- objects, // very bad bugs happen, especially the second time a level is re-instantiated. // Probably caused other bugs as well, as an extra object would appear in iterators. if (blocklinks!=null && blocklinks.length==count) for (int i=0;i<count;i++) blocklinks[i]=null; else blocklinks = new mobj_t[count]; // Bye bye. Not needed. blockmap=blockmaplump; } /** * P_GroupLines * Builds sector line lists and subsector sector numbers. * Finds block bounding boxes for sectors. */ public void GroupLines () { int total; line_t li; sector_t sector; subsector_t ss; seg_t seg; int[] bbox=new int[4]; int block; // look up sector number for each subsector for (int i=0 ; i<numsubsectors ; i++) { ss = subsectors[i]; seg = segs[ss.firstline]; ss.sector = seg.sidedef.sector; } //linebuffer=new line_t[numsectors][0]; // count number of lines in each sector total = 0; for (int i = 0; i < numlines; i++) { li = lines[i]; total++; li.frontsector.linecount++; if ((li.backsector != null) && (li.backsector != li.frontsector)) { li.backsector.linecount++; total++; } } // build line tables for each sector // MAES: we don't really need this in Java. // linebuffer = new line_t[total]; // int linebuffercount=0; // We scan through ALL sectors. for (int i=0 ; i<numsectors ; i++) { sector = sectors[i]; BBox.ClearBox(bbox); //sector->lines = linebuffer; // We can just construct line tables of the correct size // for each sector. int countlines=0; // We scan through ALL lines.... // System.out.println(i+ ": looking for sector -> "+sector); for (int j=0 ; j<numlines ; j++) { li=lines[j]; //System.out.println(j+ " front "+li.frontsector+ " back "+li.backsector); if (li.frontsector == sector || li.backsector == sector) { // This sector will have one more line. countlines++; // Expand bounding box... BBox.AddToBox(bbox, li.v1.x, li.v1.y); BBox.AddToBox (bbox, li.v2.x, li.v2.y); } } // So, this sector must have that many lines. sector.lines=new line_t[countlines]; int addedlines=0; int pointline=0; // Add actual lines into sectors. for (int j=0 ; j<numlines ; j++) { li=lines[j]; // If if (li.frontsector == sector || li.backsector == sector) { // This sector will have one more line. sectors[i].lines[pointline++]=lines[j]; addedlines++; } } if (addedlines != sector.linecount) I.Error ("P_GroupLines: miscounted"); // set the degenmobj_t to the middle of the bounding box sector.soundorg=new degenmobj_t(((bbox[BOXRIGHT]+bbox[BOXLEFT])/2), ((bbox[BOXTOP]+bbox[BOXBOTTOM])/2),(sector.ceilingheight-sector.floorheight)/2); // adjust bounding box to map blocks block = (bbox[BOXTOP]-bmaporgy+MAXRADIUS)>>MAPBLOCKSHIFT; block = block >= bmapheight ? bmapheight-1 : block; sector.blockbox[BOXTOP]=block; block = (bbox[BOXBOTTOM]-bmaporgy-MAXRADIUS)>>MAPBLOCKSHIFT; block = block < 0 ? 0 : block; sector.blockbox[BOXBOTTOM]=block; block = (bbox[BOXRIGHT]-bmaporgx+MAXRADIUS)>>MAPBLOCKSHIFT; block = block >= bmapwidth ? bmapwidth-1 : block; sector.blockbox[BOXRIGHT]=block; block = (bbox[BOXLEFT]-bmaporgx-MAXRADIUS)>>MAPBLOCKSHIFT; block = block < 0 ? 0 : block; sector.blockbox[BOXLEFT]=block; } } @Override public void SetupLevel ( int episode, int map, int playermask, skill_t skill) { int i; String lumpname; int lumpnum; try{ DM.totalkills = DM.totalitems = DM.totalsecret = DM.wminfo.maxfrags = 0; DM.wminfo.partime = 180; for (i=0 ; i<MAXPLAYERS ; i++) { DM.players[i].killcount = DM.players[i].secretcount = DM.players[i].itemcount = 0; } // Initial height of PointOfView // will be set by player think. DM.players[DM.consoleplayer].viewz = 1; // Make sure all sounds are stopped before Z_FreeTags. S.Start (); /* #if 0 // UNUSED if (debugfile) { Z_FreeTags (PU_LEVEL, MAXINT); Z_FileDumpHeap (debugfile); } else #endif */ // Z_FreeTags (PU_LEVEL, PU_PURGELEVEL-1); // UNUSED W_Profile (); P.InitThinkers (); // if working with a development map, reload it W.Reload (); // find map name if ( DM.isCommercial()) { if (map<10) lumpname="MAP0"+map; else lumpname="MAP"+map; } else { lumpname = ("E"+ (char)( '0' + episode)+ "M"+ (char)( '0' + map) ); } lumpnum = W.GetNumForName (lumpname); DM.leveltime = 0; if (!W.verifyLumpName(lumpnum+ML_BLOCKMAP, LABELS[ML_BLOCKMAP])) System.err.println("Blockmap missing!"); // note: most of this ordering is important this.LoadVertexes (lumpnum+ML_VERTEXES); this.LoadSectors (lumpnum+ML_SECTORS); this.LoadSideDefs (lumpnum+ML_SIDEDEFS); this.LoadLineDefs (lumpnum+ML_LINEDEFS); this.LoadSubsectors (lumpnum+ML_SSECTORS); this.LoadNodes (lumpnum+ML_NODES); this.LoadSegs (lumpnum+ML_SEGS); // MAES: in order to apply optimizations and rebuilding, order must be changed. this.LoadBlockMap (lumpnum+ML_BLOCKMAP); //this.SanitizeBlockmap(); //this.getMapBoundingBox(); this.LoadReject(lumpnum+ML_REJECT); this.GroupLines (); DM.bodyqueslot = 0; // Reset to "deathmatch starts" DM.deathmatch_p = 0; this.LoadThings (lumpnum+ML_THINGS); // if deathmatch, randomly spawn the active players if (DM.deathmatch) { for (i=0 ; i<MAXPLAYERS ; i++) if (DM.playeringame[i]) { DM.players[i].mo = null; // DM.DeathMatchSpawnPlayer (i); } } // clear special respawning que P.iquehead = P.iquetail = 0; // set up world state P.SpawnSpecials (); // build subsector connect matrix // UNUSED P_ConnectSubsectors (); // preload graphics if (DM.precache){ TM.PrecacheLevel (); // MAES: thinkers are separate than texture management. Maybe split sprite management as well? R.PreCacheThinkers(); } } catch (Exception e){ System.err.println("Error while loading level"); e.printStackTrace(); } } } //$Log: LevelLoader.java,v $ //Revision 1.44 2012/09/24 17:16:23 velktron //Massive merge between HiColor and HEAD. There's no difference from now on, and development continues on HEAD. // //Revision 1.43.2.2 2012/09/24 16:57:16 velktron //Addressed generics warnings. // //Revision 1.43.2.1 2012/03/26 09:53:44 velktron //Use line_t.NO_INDEX for good measure, when possible. // //Revision 1.43 2011/11/03 15:19:51 velktron //Adapted to using ISpriteManager // //Revision 1.42 2011/10/07 16:05:52 velktron //Now using line_t for ML_* definitions. // //Revision 1.41 2011/10/06 16:44:32 velktron //Proper support for extended nodes, made reject loading into a separate method. // //Revision 1.40 2011/09/30 15:20:24 velktron //Very modified, useless SanitizeBlockmap method ditched. //Common utility methods moved to superclass. Shares blockmap checking and generation //with Boom-derived code. Now capable of running Europe.wad. //TODO: Blockmap generation can be really slow on large levels. //Optimize better for Java, or parallelize. // //Revision 1.39 2011/09/29 17:22:08 velktron //Blockchain terminators are now -1 (extended) // //Revision 1.38 2011/09/29 17:11:32 velktron //Blockmap optimizations. // //Revision 1.37 2011/09/29 15:17:48 velktron //SetupLevel can propagate exceptions. // //Revision 1.36 2011/09/29 13:28:01 velktron //Extends AbstractLevelLoader // //Revision 1.35 2011/09/27 18:04:36 velktron //Fixed major blockmap bug // //Revision 1.34 2011/09/27 16:00:20 velktron //Minor blockmap stuff. // //Revision 1.33 2011/08/24 15:52:04 velktron //Sets proper ISoundOrigin for sectors (height, too) // //Revision 1.32 2011/08/24 15:00:34 velktron //Improved version, now using createArrayOfObjects. Much better syntax. // //Revision 1.31 2011/08/23 16:17:22 velktron //Got rid of Z remnants. // //Revision 1.30 2011/07/27 21:26:19 velktron //Quieted down debugging for v1.5 release // //Revision 1.29 2011/07/25 19:56:53 velktron //reject matrix size bugfix, fron danmaku branch. // //Revision 1.28 2011/07/22 15:37:52 velktron //Began blockmap autogen code...still WIP // //Revision 1.27 2011/07/20 16:14:45 velktron //Bullet-proofing vs missing or corrupt REJECT table. TODO: built-in system to re-compute it. // //Revision 1.26 2011/06/18 23:25:33 velktron //Removed debugginess // //Revision 1.25 2011/06/18 23:21:26 velktron //-id // //Revision 1.24 2011/06/18 23:18:24 velktron //Added sanitization for broken two-sided sidedefs, and semi-support for extended blockmaps. // //Revision 1.23 2011/05/24 11:31:47 velktron //Adapted to IDoomStatusBar // //Revision 1.22 2011/05/22 21:09:34 velktron //Added spechit overflow handling, and unused linedefs (with -1 sector) handling. // //Revision 1.21 2011/05/21 14:53:57 velktron //Adapted to use new gamemode system. // //Revision 1.20 2011/05/20 14:52:23 velktron //Moved several function from the Renderer and Action code in here, since it made more sense. // //Revision 1.19 2011/05/18 16:55:44 velktron //TEMPORARY TESTING VERSION, DO NOT USE // //Revision 1.18 2011/05/17 16:51:20 velktron //Switched to DoomStatus // //Revision 1.17 2011/05/10 10:39:18 velktron //Semi-playable Techdemo v1.3 milestone // //Revision 1.16 2011/05/05 17:24:22 velktron //Started merging more of _D_'s changes. // //Revision 1.15 2010/12/20 17:15:08 velktron //Made the renderer more OO -> TextureManager and other changes as well. // //Revision 1.14 2010/11/22 21:41:22 velktron //Parallel rendering...sort of.It works, but either the barriers are broken or it's simply not worthwhile at this point :-/ // //Revision 1.13 2010/11/22 14:54:53 velktron //Greater objectification of sectors etc. // //Revision 1.12 2010/11/22 01:17:16 velktron //Fixed blockmap (for the most part), some actions implemented and functional, ambient animation/lighting functional. // //Revision 1.11 2010/11/14 20:00:21 velktron //Bleeding floor bug fixed! // //Revision 1.10 2010/11/03 16:48:04 velktron //"Bling" view angles fixed (perhaps related to the "bleeding line bug"?) // //Revision 1.9 2010/09/27 02:27:29 velktron //BEASTLY update // //Revision 1.8 2010/09/23 20:36:45 velktron //*** empty log message *** // //Revision 1.7 2010/09/23 15:11:57 velktron //A bit closer... // //Revision 1.6 2010/09/22 16:40:02 velktron //MASSIVE changes in the status passing model. //DoomMain and DoomGame unified. //Doomstat merged into DoomMain (now status and game functions are one). // //Most of DoomMain implemented. Possible to attempt a "classic type" start but will stop when reading sprites. // //Revision 1.5 2010/09/21 15:53:37 velktron //Split the Map ...somewhat... // //Revision 1.4 2010/09/14 15:34:01 velktron //The enormity of this commit is incredible (pun intended) // //Revision 1.3 2010/09/08 15:22:18 velktron //x,y coords in some structs as value semantics. Possible speed increase? // //Revision 1.2 2010/09/02 15:56:54 velktron //Bulk of unified renderer copyediting done. // //Some changes like e.g. global separate limits class and instance methods for seg_t and node_t introduced. // //Revision 1.1 2010/09/01 15:53:42 velktron //Graphics data loader implemented....still need to figure out how column caching works, though. // //Revision 1.4 2010/08/19 23:14:49 velktron //Automap // //Revision 1.3 2010/08/13 14:06:36 velktron //Endlevel screen fully functional! // //Revision 1.2 2010/08/11 16:31:34 velktron //Map loading works! Check out LevelLoaderTester for more. // //Revision 1.1 2010/08/10 16:41:57 velktron //Threw some work into map loading. // package p; public enum result_e { ok, crushed, pastdest } package p; import static rr.line_t.ML_BLOCKING; import static data.Defines.BT_ATTACK; import static data.Defines.MAPBLOCKSHIFT; import static data.Defines.MELEERANGE; import static data.Defines.MISSILERANGE; import static data.Defines.PST_DEAD; import static data.Defines.pw_strength; import static data.Limits.MAXPLAYERS; import static data.Limits.MAXRADIUS; import static data.Limits.MAXSKULLS; import static data.Limits.NUMBRAINTARGETS; import static data.Tables.ANG180; import static data.Tables.ANG270; import static data.Tables.ANG45; import static data.Tables.ANG90; import static data.Tables.BITS32; import static data.Tables.FINEANGLES; import static data.Tables.FINEMASK; import static data.Tables.finecosine; import static data.Tables.finesine; import static data.info.mobjinfo; import static data.info.states; import static doom.items.weaponinfo; import static doom.player_t.LOWERSPEED; import static doom.player_t.RAISESPEED; import static doom.player_t.WEAPONBOTTOM; import static doom.player_t.WEAPONTOP; import static doom.player_t.ps_flash; import static doom.player_t.ps_weapon; import static m.fixed_t.FRACUNIT; import static m.fixed_t.MAPFRACUNIT; import static m.fixed_t.FixedMul; import static p.ChaseDirections.DI_NODIR; import static p.ChaseDirections.xspeed; import static p.ChaseDirections.yspeed; import static p.MapUtils.AproxDistance; import static p.mobj_t.MF_AMBUSH; import static p.mobj_t.MF_COUNTKILL; import static p.mobj_t.MF_JUSTATTACKED; import static p.mobj_t.MF_SHADOW; import static p.mobj_t.MF_SHOOTABLE; import static p.mobj_t.MF_SKULLFLY; import static p.mobj_t.MF_SOLID; import static utils.C2JUtils.eval; import static utils.C2JUtils.flags; import i.DoomStatusAware; import p.UnifiedGameMap.Enemies; import m.IRandom; import rr.Renderer; import rr.line_t; import s.IDoomSound; import data.Tables; import data.mobjinfo_t; import data.mobjtype_t; import data.state_t; import data.sounds.sfxenum_t; import defines.skill_t; import defines.statenum_t; import doom.DoomStatus; import doom.IDoomGame; import doom.player_t; import doom.think_t; import doom.thinker_t; import doom.weapontype_t; public class ActionFunctions implements DoomStatusAware{ public ActionFunctions(){ MobjThinker=new P_MobjThinker(); WeaponReady=new A_WeaponReady(); Lower=new A_Lower(); Raise=new A_Raise(); Punch=new A_Punch(); ReFire=new A_ReFire(); FirePistol=new A_FirePistol(); Light0=new A_Light0(); Light1=new A_Light1(); Light2=new A_Light2(); FireShotgun=new A_FireShotgun(); FireShotgun2=new A_FireShotgun2(); CheckReload=new A_CheckReload(); OpenShotgun2=new A_OpenShotgun2(); LoadShotgun2=new A_LoadShotgun2(); CloseShotgun2=new A_CloseShotgun2(); FireCGun=new A_FireCGun(); GunFlash=new A_GunFlash(); FireMissile=new A_FireMissile(); Saw=new A_Saw(); FirePlasma=new A_FirePlasma(); BFGsound=new A_BFGsound(); FireBFG=new A_FireBFG(); BFGSpray=new A_BFGSpray(); Explode=new A_Explode(); Pain=new A_Pain(); PlayerScream=new A_PlayerScream(); Fall=new A_Fall(); XScream=new A_XScream(); Look=new A_Look(); Chase=new A_Chase(); FaceTarget=new A_FaceTarget(); PosAttack=new A_PosAttack(); Scream=new A_Scream(); SPosAttack=new A_SPosAttack(); VileChase=new A_VileChase(); VileStart=new A_VileStart(); VileTarget=new A_VileTarget(); VileAttack=new A_VileAttack(); StartFire=new A_StartFire(); Fire=new A_Fire(); FireCrackle=new A_FireCrackle(); Tracer=new A_Tracer(); SkelWhoosh=new A_SkelWhoosh(); SkelFist=new A_SkelFist(); SkelMissile=new A_SkelMissile(); FatRaise=new A_FatRaise(); FatAttack1=new A_FatAttack1(); FatAttack2=new A_FatAttack2(); FatAttack3=new A_FatAttack3(); BossDeath=new A_BossDeath(); CPosAttack=new A_CPosAttack(); CPosRefire=new A_CPosRefire(); TroopAttack=new A_TroopAttack(); SargAttack=new A_SargAttack(); HeadAttack=new A_HeadAttack(); BruisAttack=new A_BruisAttack(); SkullAttack=new A_SkullAttack(); Metal=new A_Metal(); SpidRefire=new A_SpidRefire(); BabyMetal=new A_BabyMetal(); BspiAttack=new A_BspiAttack(); Hoof=new A_Hoof(); CyberAttack=new A_CyberAttack(); PainAttack=new A_PainAttack(); PainDie=new A_PainDie(); KeenDie=new A_KeenDie(); BrainPain=new A_BrainPain(); BrainScream=new A_BrainScream(); BrainDie=new A_BrainDie(); BrainAwake=new A_BrainAwake(); BrainSpit=new A_BrainSpit(); SpawnSound=new A_SpawnSound(); SpawnFly=new A_SpawnFly(); BrainExplode=new A_BrainExplode(); // these need special handling. FireFlicker=new T_FireFlicker(); LightFlash=new T_LightFlash(); StrobeFlash=new T_StrobeFlash(); Glow=new T_Glow(); MoveCeiling=new T_MoveCeiling(); MoveFloor=new T_MoveFloor(); VerticalDoor=new T_VerticalDoor(); PlatRaise=new T_PlatRaise(); SlidingDoor=new T_SlidingDoor(); } public ActionFunctions(DoomStatus<?,?> DS, Enemies EN) { this(); this.EN=EN; updateStatus(DS); } @Override public void updateStatus(DoomStatus<?,?> DS){ this.A=DS.P; this.RND=DS.RND; this.R=DS.R; this.S=DS.S; this.LL=DS.LL; this.DS=DS.DM; this.DG=DS.DG; // this.SL=DS.SL; } protected Actions A; protected IRandom RND; protected Renderer<?,?> R; protected IDoomSound S; protected Enemies EN; protected AbstractLevelLoader LL; protected DoomStatus<?,?> DS; protected IDoomGame DG; protected SlideDoor SL; ActionType2 WeaponReady; ActionType2 Lower; ActionType2 Raise; ActionType2 Punch; ActionType2 ReFire; ActionType2 FirePistol; ActionType2 Light0; ActionType2 Light1; ActionType2 Light2; ActionType2 FireShotgun; ActionType2 FireShotgun2; ActionType2 CheckReload; ActionType2 OpenShotgun2; ActionType2 LoadShotgun2; ActionType2 CloseShotgun2; ActionType2 FireCGun; ActionType2 GunFlash; ActionType2 FireMissile; ActionType2 Saw; ActionType2 FirePlasma; ActionType2 BFGsound; ActionType2 FireBFG; ActionType1 BFGSpray; ActionType1 Explode; ActionType1 Pain; ActionType1 PlayerScream; ActionType1 Fall; ActionType1 XScream; ActionType1 Look; ActionType1 Chase; ActionType1 FaceTarget; ActionType1 PosAttack; ActionType1 Scream; ActionType1 SPosAttack; ActionType1 VileChase; ActionType1 VileStart; ActionType1 VileTarget; ActionType1 VileAttack; ActionType1 StartFire; ActionType1 Fire; ActionType1 FireCrackle; ActionType1 Tracer; ActionType1 SkelWhoosh; ActionType1 SkelFist; ActionType1 SkelMissile; ActionType1 FatRaise; ActionType1 FatAttack1; ActionType1 FatAttack2; ActionType1 FatAttack3; ActionType1 BossDeath; ActionType1 CPosAttack; ActionType1 CPosRefire; ActionType1 TroopAttack; ActionType1 SargAttack; ActionType1 HeadAttack; ActionType1 BruisAttack; ActionType1 SkullAttack; ActionType1 Metal; ActionType1 SpidRefire; ActionType1 BabyMetal; ActionType1 BspiAttack; ActionType1 Hoof; ActionType1 CyberAttack; ActionType1 PainAttack; ActionType1 PainDie; ActionType1 KeenDie; ActionType1 BrainPain; ActionType1 BrainScream; ActionType1 BrainDie; ActionType1 BrainAwake; ActionType1 BrainSpit; ActionType1 SpawnSound; ActionType1 SpawnFly; ActionType1 BrainExplode; ActionType1 MobjThinker; ActionTypeSS<?> FireFlicker; ActionTypeSS<?> LightFlash; ActionTypeSS<?> StrobeFlash; ActionTypeSS<?> Glow; ActionTypeSS<?> MoveCeiling; ActionTypeSS<?> MoveFloor; ActionTypeSS<?> VerticalDoor; ActionTypeSS<?> PlatRaise; ActionTypeSS<?> SlidingDoor; /** Wires a state to an actual callback depending on its * enum. This eliminates the need to have a giant * switch statements in Actions, and should allow DEH * rewiring. * @param st */ public void doWireState(state_t st){ //System.out.println("Dispatching: "+action); if (st.action==null) return; switch (st.action){ case P_MobjThinker: st.acp1=MobjThinker; break; case A_Light0: st.acp2=Light0; break; case A_WeaponReady: st.acp2=WeaponReady; break; case A_Lower: st.acp2=Lower; break; case A_Raise: st.acp2=Raise; break; case A_Punch: st.acp2=Punch; break; case A_ReFire: st.acp2=ReFire; break; case A_FirePistol: st.acp2= FirePistol; break; case A_Light1: st.acp2=Light1; break; case A_FireShotgun: st.acp2=FireShotgun; break; case A_Light2: st.acp2=Light2; break; case A_FireShotgun2: st.acp2=FireShotgun2; break; case A_CheckReload: st.acp2=CheckReload; break; case A_OpenShotgun2: st.acp2=OpenShotgun2; break; case A_LoadShotgun2: st.acp2=LoadShotgun2; break; case A_CloseShotgun2: st.acp2=CloseShotgun2; break; case A_FireCGun: st.acp2=FireCGun; break; case A_GunFlash: st.acp2=GunFlash; break; case A_FireMissile: st.acp2=FireMissile; break; case A_Saw: st.acp2=Saw; break; case A_FirePlasma: st.acp2=FirePlasma; break; case A_BFGsound: st.acp2=BFGsound; break; case A_FireBFG: st.acp2=FireBFG; break; case A_BFGSpray: st.acp1=BFGSpray; break; case A_Explode: st.acp1=Explode; break; case A_Pain: st.acp1=Pain; break; case A_PlayerScream: st.acp1=PlayerScream; break; case A_Fall: st.acp1=Fall; break; case A_XScream: st.acp1=XScream; break; case A_Look: st.acp1=Look; break; case A_Chase: st.acp1=Chase; break; case A_FaceTarget: st.acp1=FaceTarget; break; case A_PosAttack: st.acp1=PosAttack; break; case A_Scream: st.acp1=Scream; break; case A_SPosAttack: st.acp1=SPosAttack; break; case A_VileChase: st.acp1=VileChase; break; case A_VileStart: st.acp1=VileStart; break; case A_VileTarget: st.acp1=VileTarget; break; case A_VileAttack: st.acp1=VileAttack; break; case A_StartFire: st.acp1=StartFire; break; case A_Fire: st.acp1=Fire; break; case A_FireCrackle: st.acp1=FireCrackle; break; case A_Tracer: st.acp1=Tracer; break; case A_SkelWhoosh: st.acp1=SkelWhoosh; break; case A_SkelFist: st.acp1=SkelFist; break; case A_SkelMissile: st.acp1=SkelMissile; break; case A_FatRaise: st.acp1=FatRaise; break; case A_FatAttack1: st.acp1=FatAttack1; break; case A_FatAttack2: st.acp1=FatAttack2; break; case A_FatAttack3: st.acp1= FatAttack3; break; case A_BossDeath: st.acp1=BossDeath; break; case A_CPosAttack: st.acp1=CPosAttack; break; case A_CPosRefire: st.acp1=CPosRefire; break; case A_TroopAttack: st.acp1=TroopAttack; break; case A_SargAttack: st.acp1=SargAttack; break; case A_HeadAttack: st.acp1=HeadAttack; break; case A_BruisAttack: st.acp1=BruisAttack; break; case A_SkullAttack: st.acp1=SkullAttack; break; case A_Metal: st.acp1=Metal; break; case A_SpidRefire: st.acp1=SpidRefire; break; case A_BabyMetal: st.acp1=BabyMetal; break; case A_BspiAttack: st.acp1=BspiAttack; break; case A_Hoof: st.acp1=Hoof; break; case A_CyberAttack: st.acp1=CyberAttack; break; case A_PainAttack: st.acp1=PainAttack; break; case A_PainDie: st.acp1=PainDie; break; case A_KeenDie: st.acp1=KeenDie; break; case A_BrainPain: st.acp1= BrainPain; break; case A_BrainScream: st.acp1=BrainScream; break; case A_BrainDie: st.acp1= BrainDie; break; case A_BrainAwake: st.acp1=BrainAwake; break; case A_BrainSpit: st.acp1=BrainSpit; break; case A_SpawnSound: st.acp1=SpawnSound; break; case A_SpawnFly: st.acp1=SpawnFly; break; case A_BrainExplode: st.acp1=BrainExplode; break; case T_FireFlicker: st.acpss=FireFlicker; break; case T_LightFlash: st.acpss=LightFlash; break; case T_StrobeFlash: st.acpss=StrobeFlash; break; case T_Glow: st.acpss=Glow; break; case T_MoveCeiling: st.acpss=MoveCeiling; break; case T_MoveFloor: st.acpss=MoveFloor; break; case T_VerticalDoor: //_D_: changed this to make it work st.acpss=VerticalDoor; break; case T_PlatRaise: st.acpss=PlatRaise; break; } } public void doWireThinker(thinker_t st){ //System.out.println("Dispatching: "+action); if (st==null) return; // we don't want anything to be called on this. if (st.function==null){ st.acp1=null; st.acp2=null; st.acpss=null; return; } switch (st.function){ case P_MobjThinker: st.acp1=MobjThinker; break; case A_Light0: st.acp2=Light0; break; case A_WeaponReady: st.acp2=WeaponReady; break; case A_Lower: st.acp2=Lower; break; case A_Raise: st.acp2=Raise; break; case A_Punch: st.acp2=Punch; break; case A_ReFire: st.acp2=ReFire; break; case A_FirePistol: st.acp2= FirePistol; break; case A_Light1: st.acp2=Light1; break; case A_FireShotgun: st.acp2=FireShotgun; break; case A_Light2: st.acp2=Light2; break; case A_FireShotgun2: st.acp2=FireShotgun2; break; case A_CheckReload: st.acp2=CheckReload; break; case A_OpenShotgun2: st.acp2=OpenShotgun2; break; case A_LoadShotgun2: st.acp2=LoadShotgun2; break; case A_CloseShotgun2: st.acp2=CloseShotgun2; break; case A_FireCGun: st.acp2=FireCGun; break; case A_GunFlash: st.acp2=GunFlash; break; case A_FireMissile: st.acp2=FireMissile; break; case A_Saw: st.acp2=Saw; break; case A_FirePlasma: st.acp2=FirePlasma; break; case A_BFGsound: st.acp2=BFGsound; break; case A_FireBFG: st.acp2=FireBFG; break; case A_BFGSpray: st.acp1=BFGSpray; break; case A_Explode: st.acp1=Explode; break; case A_Pain: st.acp1=Pain; break; case A_PlayerScream: st.acp1=PlayerScream; break; case A_Fall: st.acp1=Fall; break; case A_XScream: st.acp1=XScream; break; case A_Look: st.acp1=Look; break; case A_Chase: st.acp1=Chase; break; case A_FaceTarget: st.acp1=FaceTarget; break; case A_PosAttack: st.acp1=PosAttack; break; case A_Scream: st.acp1=Scream; break; case A_SPosAttack: st.acp1=SPosAttack; break; case A_VileChase: st.acp1=VileChase; break; case A_VileStart: st.acp1=VileStart; break; case A_VileTarget: st.acp1=VileTarget; break; case A_VileAttack: st.acp1=VileAttack; break; case A_StartFire: st.acp1=StartFire; break; case A_Fire: st.acp1=Fire; break; case A_FireCrackle: st.acp1=FireCrackle; break; case A_Tracer: st.acp1=Tracer; break; case A_SkelWhoosh: st.acp1=SkelWhoosh; break; case A_SkelFist: st.acp1=SkelFist; break; case A_SkelMissile: st.acp1=SkelMissile; break; case A_FatRaise: st.acp1=FatRaise; break; case A_FatAttack1: st.acp1=FatAttack1; break; case A_FatAttack2: st.acp1=FatAttack2; break; case A_FatAttack3: st.acp1= FatAttack3; break; case A_BossDeath: st.acp1=BossDeath; break; case A_CPosAttack: st.acp1=CPosAttack; break; case A_CPosRefire: st.acp1=CPosRefire; break; case A_TroopAttack: st.acp1=TroopAttack; break; case A_SargAttack: st.acp1=SargAttack; break; case A_HeadAttack: st.acp1=HeadAttack; break; case A_BruisAttack: st.acp1=BruisAttack; break; case A_SkullAttack: st.acp1=SkullAttack; break; case A_Metal: st.acp1=Metal; break; case A_SpidRefire: st.acp1=SpidRefire; break; case A_BabyMetal: st.acp1=BabyMetal; break; case A_BspiAttack: st.acp1=BspiAttack; break; case A_Hoof: st.acp1=Hoof; break; case A_CyberAttack: st.acp1=CyberAttack; break; case A_PainAttack: st.acp1=PainAttack; break; case A_PainDie: st.acp1=PainDie; break; case A_KeenDie: st.acp1=KeenDie; break; case A_BrainPain: st.acp1= BrainPain; break; case A_BrainScream: st.acp1=BrainScream; break; case A_BrainDie: st.acp1= BrainDie; break; case A_BrainAwake: st.acp1=BrainAwake; break; case A_BrainSpit: st.acp1=BrainSpit; break; case A_SpawnSound: st.acp1=SpawnSound; break; case A_SpawnFly: st.acp1=SpawnFly; break; case A_BrainExplode: st.acp1=BrainExplode; break; case T_FireFlicker: st.acpss=FireFlicker; break; case T_LightFlash: st.acpss=LightFlash; break; case T_StrobeFlash: st.acpss=StrobeFlash; break; case T_Glow: st.acpss=Glow; break; case T_MoveCeiling: st.acpss=MoveCeiling; break; case T_MoveFloor: st.acpss=MoveFloor; break; case T_VerticalDoor: //_D_: changed this to make it work st.acpss=VerticalDoor; break; case T_PlatRaise: st.acpss=PlatRaise; break; case T_SlidingDoor: st.acpss=SlidingDoor; break; } } /** Causes object to move and perform actions. * Can only be called through the Actions dispatcher. * * @param mobj */ class P_MobjThinker implements ActionType1{ public void invoke(mobj_t mobj){ // momentum movement if (mobj.momx != 0 || mobj.momy != 0 || (eval(mobj.flags& MF_SKULLFLY))) { A.XYMovement(mobj); // FIXME: decent NOP/NULL/Nil function pointer please. if (mobj.function == think_t.NOP){ return; // mobj was removed } } if ((mobj.z != mobj.floorz) || mobj.momz != 0) { mobj.ZMovement(); // FIXME: decent NOP/NULL/Nil function pointer please. if (mobj.function == think_t.NOP){ return; // mobj was removed } } // cycle through states, // calling action functions at transitions if (mobj.tics != -1) { mobj.tics--; // you can cycle through multiple states in a tic if (!eval(mobj.tics)) if (!mobj.SetMobjState(mobj.state.nextstate)) return; // freed itself } else { // check for nightmare respawn if (!eval(mobj.flags& MF_COUNTKILL)) return; if (!DS.respawnmonsters) return; mobj.movecount++; if (mobj.movecount < 12 * 35) return; if (eval(DS.leveltime& 31)) return; if (RND.P_Random() > 4) return; A.NightmareRespawn(mobj); } } } class T_FireFlicker implements ActionTypeSS<fireflicker_t>{ @Override public void invoke(fireflicker_t a) { a.FireFlicker(); } } class T_LightFlash implements ActionTypeSS<lightflash_t>{ @Override public void invoke(lightflash_t a) { a.LightFlash(); } } class T_StrobeFlash implements ActionTypeSS<strobe_t>{ @Override public void invoke(strobe_t a) { a.StrobeFlash(); } } class T_Glow implements ActionTypeSS<glow_t>{ @Override public void invoke(glow_t a) { a.Glow(); } } class T_MoveCeiling implements ActionTypeSS<ceiling_t>{ @Override public void invoke(ceiling_t a) { A.MoveCeiling(a); } } class T_MoveFloor implements ActionTypeSS<floormove_t>{ @Override public void invoke(floormove_t a) { A.MoveFloor(a); } } class T_VerticalDoor implements ActionTypeSS<vldoor_t>{ @Override public void invoke(vldoor_t a) { A.VerticalDoor(a); } } class T_SlidingDoor implements ActionTypeSS<slidedoor_t> { @Override public void invoke(slidedoor_t door) { switch (door.status) { case sd_opening: if (door.timer-- == 0) { if (++door.frame == SlideDoor.SNUMFRAMES) { // IF DOOR IS DONE OPENING... LL.sides[door.line.sidenum[0]].midtexture = 0; LL.sides[door.line.sidenum[1]].midtexture = 0; door.line.flags &= ML_BLOCKING ^ 0xff; if (door.type == sdt_e.sdt_openOnly) { door.frontsector.specialdata = null; A.RemoveThinker(door); break; } door.timer = SlideDoor.SDOORWAIT; door.status = sd_e.sd_waiting; } else { // IF DOOR NEEDS TO ANIMATE TO NEXT FRAME... door.timer = SlideDoor.SWAITTICS; LL.sides[door.line.sidenum[0]].midtexture = (short) SL.slideFrames[door.whichDoorIndex].frontFrames[door.frame]; LL.sides[door.line.sidenum[1]].midtexture = (short) SL.slideFrames[door.whichDoorIndex].backFrames[door.frame]; } } break; case sd_waiting: // IF DOOR IS DONE WAITING... if (door.timer-- == 0) { // CAN DOOR CLOSE? if (door.frontsector.thinglist != null || door.backsector.thinglist != null) { door.timer = SlideDoor.SDOORWAIT; break; } // door.frame = SNUMFRAMES-1; door.status = sd_e.sd_closing; door.timer = SlideDoor.SWAITTICS; } break; case sd_closing: if (door.timer-- == 0) { if (--door.frame < 0) { // IF DOOR IS DONE CLOSING... door.line.flags |= ML_BLOCKING; door.frontsector.specialdata = null; A.RemoveThinker(door); break; } else { // IF DOOR NEEDS TO ANIMATE TO NEXT FRAME... door.timer = SlideDoor.SWAITTICS; LL.sides[door.line.sidenum[0]].midtexture = (short) SL.slideFrames[door.whichDoorIndex].frontFrames[door.frame]; LL.sides[door.line.sidenum[1]].midtexture = (short) SL.slideFrames[door.whichDoorIndex].backFrames[door.frame]; } } break; } } } class T_PlatRaise implements ActionTypeSS<plat_t>{ @Override public void invoke(plat_t a) { A.PlatRaise(a); } } // // A_FaceTarget // public class A_FaceTarget implements ActionType1{ public void invoke(mobj_t actor) { if (actor.target==null) return; actor.flags &= ~MF_AMBUSH; actor.angle = R.PointToAngle2 (actor.x, actor.y, actor.target.x, actor.target.y)&BITS32; if (eval(actor.target.flags & MF_SHADOW)) actor.angle += (RND.P_Random()-RND.P_Random())<<21; actor.angle&=BITS32; } } // // A_PosAttack // class A_PosAttack implements ActionType1{ public void invoke(mobj_t actor) { int angle; int damage; int slope; if (actor.target==null) return; FaceTarget.invoke(actor); angle = (int) actor.angle; slope = A.AimLineAttack (actor, angle, MISSILERANGE); S.StartSound(actor, sfxenum_t.sfx_pistol); angle += (RND.P_Random()-RND.P_Random())<<20; damage = ((RND.P_Random()%5)+1)*3; A.LineAttack (actor, angle, MISSILERANGE, slope, damage); } } class A_SPosAttack implements ActionType1{ public void invoke(mobj_t actor) { int i; long angle; long bangle; int damage; int slope; if (actor.target==null) return; S.StartSound(actor, sfxenum_t.sfx_shotgn); A_FaceTarget (actor); bangle = actor.angle; slope = A.AimLineAttack (actor, bangle, MISSILERANGE); for (i=0 ; i<3 ; i++) { angle = bangle + ((RND.P_Random()-RND.P_Random())<<20); damage = ((RND.P_Random()%5)+1)*3; A.LineAttack (actor, angle, MISSILERANGE, slope, damage); } } } class A_CPosAttack implements ActionType1{ public void invoke(mobj_t actor) { long angle; long bangle; int damage; int slope; if (actor.target==null) return; S.StartSound(actor, sfxenum_t.sfx_shotgn); A_FaceTarget (actor); bangle = actor.angle; slope = A.AimLineAttack (actor, bangle, MISSILERANGE); angle = bangle + ((RND.P_Random()-RND.P_Random())<<20); damage = ((RND.P_Random()%5)+1)*3; A.LineAttack (actor, angle, MISSILERANGE, slope, damage); } } class A_CPosRefire implements ActionType1{ public void invoke(mobj_t actor){ // keep firing unless target got out of sight A_FaceTarget (actor); if (RND.P_Random () < 40) return; if (actor.target==null || actor.target.health <= 0 || !EN.CheckSight (actor, actor.target) ) { actor.SetMobjState ( actor.info.seestate); } } } class A_SpidRefire implements ActionType1{ public void invoke(mobj_t actor){ // keep firing unless target got out of sight A_FaceTarget (actor); if (RND.P_Random () < 10) return; if (actor.target==null || actor.target.health <= 0 || !EN.CheckSight (actor, actor.target) ) { actor.SetMobjState ( actor.info.seestate); } } } class A_BspiAttack implements ActionType1{ public void invoke(mobj_t actor){ if (actor.target==null) return; A_FaceTarget (actor); // launch a missile A.SpawnMissile (actor, actor.target, mobjtype_t.MT_ARACHPLAZ); } } // // A_TroopAttack // class A_TroopAttack implements ActionType1{ public void invoke(mobj_t actor){ int damage; if (actor.target==null) return; A_FaceTarget (actor); if (A.EN.CheckMeleeRange (actor)) { S.StartSound(actor, sfxenum_t.sfx_claw); damage = (RND.P_Random()%8+1)*3; A.DamageMobj (actor.target, actor, actor, damage); return; } // launch a missile A.SpawnMissile (actor, actor.target, mobjtype_t.MT_TROOPSHOT); } } class A_SargAttack implements ActionType1{ public void invoke(mobj_t actor){ int damage; if (actor.target==null) return; A_FaceTarget (actor); if (EN.CheckMeleeRange (actor)) { damage = ((RND.P_Random()%10)+1)*4; A.DamageMobj (actor.target, actor, actor, damage); } } } class A_HeadAttack implements ActionType1{ public void invoke(mobj_t actor){ int damage; if (actor.target==null) return; A_FaceTarget (actor); if (EN.CheckMeleeRange (actor)) { damage = (RND.P_Random()%6+1)*10; A.DamageMobj (actor.target, actor, actor, damage); return; } // launch a missile A.SpawnMissile (actor, actor.target, mobjtype_t.MT_HEADSHOT); } } class A_CyberAttack implements ActionType1{ public void invoke(mobj_t actor){ if (actor.target==null) return; A_FaceTarget (actor); A.SpawnMissile (actor, actor.target, mobjtype_t.MT_ROCKET); } } class A_BruisAttack implements ActionType1{ public void invoke(mobj_t actor){ int damage; if (actor.target==null) return; if (EN.CheckMeleeRange (actor)) { S.StartSound(actor, sfxenum_t.sfx_claw); damage = (RND.P_Random()%8+1)*10; A.DamageMobj (actor.target, actor, actor, damage); return; } // launch a missile A.SpawnMissile (actor, actor.target, mobjtype_t.MT_BRUISERSHOT); } } // // A_SkelMissile // class A_SkelMissile implements ActionType1{ public void invoke(mobj_t actor){ mobj_t mo; if (actor.target==null) return; A_FaceTarget (actor); actor.z += 16*FRACUNIT; // so missile spawns higher mo = A.SpawnMissile (actor, actor.target, mobjtype_t.MT_TRACER); actor.z -= 16*FRACUNIT; // back to normal mo.x += mo.momx; mo.y += mo.momy; mo.tracer = actor.target; } } private static final int TRACEANGLE = 0xc000000; class A_Tracer implements ActionType1{ public void invoke(mobj_t actor){ long exact; //angle_t int dist,slope; // fixed mobj_t dest; mobj_t th; if (eval(DS.gametic &3)) return; // spawn a puff of smoke behind the rocket A.SpawnPuff (actor.x, actor.y, actor.z); th = A.SpawnMobj (actor.x-actor.momx, actor.y-actor.momy, actor.z, mobjtype_t.MT_SMOKE); th.momz = MAPFRACUNIT; th.tics -= RND.P_Random()&3; if (th.tics < 1) th.tics = 1; // adjust direction dest = actor.tracer; if (dest==null || dest.health <= 0) return; // change angle exact = R.PointToAngle2 (actor.x, actor.y, dest.x, dest.y)&BITS32; // MAES: let's analyze the logic here... // So exact is the angle between the missile and its target. if (exact != actor.angle) // missile is already headed there dead-on. { if (exact - actor.angle > ANG180) { actor.angle -= TRACEANGLE; actor.angle&=BITS32; if (((exact - actor.angle)&BITS32) < ANG180) actor.angle = exact; } else { actor.angle += TRACEANGLE; actor.angle&=BITS32; if (((exact - actor.angle)&BITS32) > ANG180) actor.angle = exact; } } // MAES: fixed and sped up. int exact2 = Tables.toBAMIndex(actor.angle); actor.momx = FixedMul (actor.info.speed, finecosine[exact2]); actor.momy = FixedMul (actor.info.speed, finesine[exact2]); // change slope dist = AproxDistance (dest.x - actor.x, dest.y - actor.y); dist = dist / actor.info.speed; if (dist < 1) dist = 1; slope = (dest.z+40*FRACUNIT - actor.z) / dist; if (slope < actor.momz) actor.momz -= FRACUNIT/8; else actor.momz += FRACUNIT/8; } } class A_SkelWhoosh implements ActionType1{ public void invoke(mobj_t actor){ if (actor.target==null) return; A_FaceTarget (actor); S.StartSound(actor,sfxenum_t.sfx_skeswg); } } class A_SkelFist implements ActionType1{ public void invoke(mobj_t actor){ int damage; if (actor.target==null) return; A_FaceTarget (actor); if (EN.CheckMeleeRange (actor)) { damage = ((RND.P_Random()%10)+1)*6; S.StartSound(actor, sfxenum_t.sfx_skepch); A.DamageMobj (actor.target, actor, actor, damage); } } } // // A_VileChase // Check for ressurecting a body // class A_VileChase implements ActionType1{ public void invoke(mobj_t actor){ int xl; int xh; int yl; int yh; int bx; int by; mobjinfo_t info; mobj_t temp; if (actor.movedir != DI_NODIR) { // check for corpses to raise A.VileCheck.viletryx = actor.x + actor.info.speed*xspeed[actor.movedir]; A.VileCheck.viletryy = actor.y + actor.info.speed*yspeed[actor.movedir]; xl = LL.getSafeBlockX(A.VileCheck.viletryx - LL.bmaporgx - MAXRADIUS*2); xh = LL.getSafeBlockX(A.VileCheck.viletryx - LL.bmaporgx + MAXRADIUS*2); yl = LL.getSafeBlockY(A.VileCheck.viletryy - LL.bmaporgy - MAXRADIUS*2); yh = LL.getSafeBlockY(A.VileCheck.viletryy - LL.bmaporgy + MAXRADIUS*2); A.VileCheck.vileobj = actor; for (bx=xl ; bx<=xh ; bx++) { for (by=yl ; by<=yh ; by++) { // Call PIT_VileCheck to check // whether object is a corpse // that can be raised. if (!A.BlockThingsIterator(bx,by,A.VileCheck)) { // got one! temp = actor.target; actor.target = A.VileCheck.corpsehit; A_FaceTarget (actor); actor.target = temp; actor.SetMobjState ( statenum_t.S_VILE_HEAL1); S.StartSound(A.VileCheck.corpsehit, sfxenum_t.sfx_slop); info = A.VileCheck.corpsehit.info; A.VileCheck.corpsehit.SetMobjState (info.raisestate); A.VileCheck.corpsehit.height <<= 2; A.VileCheck.corpsehit.flags = info.flags; A.VileCheck.corpsehit.health = info.spawnhealth; A.VileCheck.corpsehit.target = null; return; } } } } // Return to normal attack. Chase.invoke (actor); } } // // A_VileStart // class A_VileStart implements ActionType1{ public void invoke(mobj_t actor){ S.StartSound(actor, sfxenum_t.sfx_vilatk); } } // // A_Fire // Keep fire in front of player unless out of sight // class A_StartFire implements ActionType1{ public void invoke(mobj_t actor){ S.StartSound(actor,sfxenum_t.sfx_flamst); Fire.invoke(actor); } } class A_FireCrackle implements ActionType1{ public void invoke(mobj_t actor){ S.StartSound(actor,sfxenum_t.sfx_flame); Fire.invoke(actor); } } // // A_FirePistol // class A_FirePistol implements ActionType2{ public void invoke( player_t player, pspdef_t psp ) { S.StartSound(player.mo, sfxenum_t.sfx_pistol); player.mo.SetMobjState ( statenum_t.S_PLAY_ATK2); player.ammo[weaponinfo[player.readyweapon.ordinal()].ammo.ordinal()]--; player.SetPsprite ( ps_flash, weaponinfo[player.readyweapon.ordinal()].flashstate); A.P_BulletSlope (player.mo); A.P_GunShot (player.mo, !eval(player.refire)); } } // // A_FireShotgun // class A_FireShotgun implements ActionType2{ public void invoke( player_t player, pspdef_t psp ) { int i; S.StartSound(player.mo, sfxenum_t.sfx_shotgn); player.mo.SetMobjState ( statenum_t.S_PLAY_ATK2); player.ammo[weaponinfo[player.readyweapon.ordinal()].ammo.ordinal()]--; player.SetPsprite ( ps_flash, weaponinfo[player.readyweapon.ordinal()].flashstate); A.P_BulletSlope (player.mo); for (i=0 ; i<7 ; i++) A.P_GunShot (player.mo, false); } } /** * A_FireShotgun2 */ class A_FireShotgun2 implements ActionType2{ public void invoke( player_t player, pspdef_t psp ) { int i; long angle; int damage; S.StartSound (player.mo, sfxenum_t.sfx_dshtgn); player.mo.SetMobjState (statenum_t.S_PLAY_ATK2); player.ammo[weaponinfo[player.readyweapon.ordinal()].ammo.ordinal()]-=2; player.SetPsprite ( ps_flash, weaponinfo[player.readyweapon.ordinal()].flashstate); A.P_BulletSlope (player.mo); for (i=0 ; i<20 ; i++) { damage = 5*(RND.P_Random ()%3+1); angle = player.mo.angle; angle += (RND.P_Random()-RND.P_Random())<<19; A.LineAttack (player.mo, angle, MISSILERANGE, A.bulletslope + ((RND.P_Random()-RND.P_Random())<<5), damage); } } } class A_Fire implements ActionType1{ public void invoke(mobj_t actor){ mobj_t dest; //long an; dest = actor.tracer; if (dest==null) return; // don't move it if the vile lost sight if (!EN.CheckSight (actor.target, dest) ) return; // an = dest.angle >>> ANGLETOFINESHIFT; A.UnsetThingPosition (actor); actor.x = dest.x + FixedMul (24*FRACUNIT, finecosine(dest.angle)); actor.y = dest.y + FixedMul (24*FRACUNIT, finesine(dest.angle)); actor.z = dest.z; LL.SetThingPosition (actor); } } // // A_VileTarget // Spawn the hellfire // class A_VileTarget implements ActionType1{ public void invoke(mobj_t actor){ mobj_t fog; if (actor.target==null) return; A_FaceTarget (actor); fog = A.SpawnMobj (actor.target.x, actor.target.y, actor.target.z, mobjtype_t.MT_FIRE); actor.tracer = fog; fog.target = actor; fog.tracer = actor.target; Fire.invoke(fog); } } // // A_VileAttack // class A_VileAttack implements ActionType1{ public void invoke(mobj_t actor){ mobj_t fire; //int an; if (actor.target==null) return; A_FaceTarget (actor); if (!EN.CheckSight (actor, actor.target) ) return; S.StartSound(actor, sfxenum_t.sfx_barexp); A.DamageMobj (actor.target, actor, actor, 20); actor.target.momz = 1000*MAPFRACUNIT/actor.target.info.mass; // an = actor.angle >> ANGLETOFINESHIFT; fire = actor.tracer; if (fire==null) return; // move the fire between the vile and the player fire.x = actor.target.x - FixedMul (24*FRACUNIT, finecosine(actor.angle)); fire.y = actor.target.y - FixedMul (24*FRACUNIT, finesine(actor.angle)); A.RadiusAttack (fire, actor, 70 ); } } // // Mancubus attack, // firing three missiles (bruisers) // in three different directions? // Doesn't look like it. // private static final long FATSPREAD = (ANG90/8); class A_FatRaise implements ActionType1{ public void invoke(mobj_t actor){ A_FaceTarget (actor); S.StartSound(actor, sfxenum_t.sfx_manatk); } } class A_FatAttack1 implements ActionType1{ public void invoke(mobj_t actor){ mobj_t mo; int an; A_FaceTarget (actor); // Change direction to ... actor.angle += FATSPREAD; A.SpawnMissile (actor, actor.target, mobjtype_t.MT_FATSHOT); mo = A.SpawnMissile (actor, actor.target, mobjtype_t.MT_FATSHOT); mo.angle += FATSPREAD; an = Tables.toBAMIndex(mo.angle); mo.momx = FixedMul (mo.info.speed, finecosine[an]); mo.momy = FixedMul (mo.info.speed, finesine[an]); } } class A_FatAttack2 implements ActionType1{ public void invoke(mobj_t actor){ mobj_t mo; int an; A_FaceTarget (actor); // Now here choose opposite deviation. actor.angle -= FATSPREAD; A.SpawnMissile (actor, actor.target, mobjtype_t.MT_FATSHOT); mo = A.SpawnMissile (actor, actor.target, mobjtype_t.MT_FATSHOT); mo.angle -= FATSPREAD*2; an = Tables.toBAMIndex(mo.angle); mo.momx = FixedMul (mo.info.speed, finecosine[an]); mo.momy = FixedMul (mo.info.speed, finesine[an]); } } class A_FatAttack3 implements ActionType1{ public void invoke(mobj_t actor){ mobj_t mo; int an; A_FaceTarget (actor); mo = A.SpawnMissile (actor, actor.target, mobjtype_t.MT_FATSHOT); mo.angle -= FATSPREAD/2; an = Tables.toBAMIndex(mo.angle); mo.momx = FixedMul (mo.info.speed, finecosine[an]); mo.momy = FixedMul (mo.info.speed, finesine[an]); mo = A.SpawnMissile (actor, actor.target, mobjtype_t.MT_FATSHOT); mo.angle += FATSPREAD/2; an = Tables.toBAMIndex(mo.angle); mo.momx = FixedMul (mo.info.speed, finecosine[an]); mo.momy = FixedMul (mo.info.speed, finesine[an]); } } private static final int SKULLSPEED = (20*MAPFRACUNIT); /** * SkullAttack * Fly at the player like a missile. */ class A_SkullAttack implements ActionType1{ public void invoke(mobj_t actor){ mobj_t dest; int an; int dist; if (actor.target==null) return; dest = actor.target; actor.flags |= MF_SKULLFLY; S.StartSound(actor, actor.info.attacksound); FaceTarget.invoke(actor); an = Tables.toBAMIndex(actor.angle); actor.momx = FixedMul (SKULLSPEED, finecosine[an]); actor.momy = FixedMul (SKULLSPEED, finesine[an]); dist = AproxDistance (dest.x - actor.x, dest.y - actor.y); dist = dist / SKULLSPEED; if (dist < 1) dist = 1; actor.momz = (dest.z+(dest.height>>1) - actor.z) / dist; } } /** * A_PainShootSkull * Spawn a lost soul and launch it at the target * It's not a valid callback like the others, actually. * No idea if some DEH patch does use it to cause * mayhem though. * */ private void A_PainShootSkull ( mobj_t actor, long angle ) { int x,y, z; // fixed mobj_t newmobj; int an; // angle int prestep; int count; thinker_t currentthinker; // count total number of skull currently on the level count = 0; currentthinker = A.thinkercap.next; while (currentthinker != A.thinkercap) { if ( (currentthinker.function == think_t.P_MobjThinker) && ((mobj_t)currentthinker).type == mobjtype_t.MT_SKULL) count++; currentthinker = currentthinker.next; } // if there are allready 20 skulls on the level, // don't spit another one if (count > MAXSKULLS) return; // okay, there's playe for another one an = Tables.toBAMIndex(angle); prestep = 4*FRACUNIT + 3*(actor.info.radius + mobjinfo[mobjtype_t.MT_SKULL.ordinal()].radius)/2; x = actor.x + FixedMul (prestep, finecosine[an]); y = actor.y + FixedMul (prestep, finesine[an]); z = actor.z + 8*FRACUNIT; newmobj = A.SpawnMobj (x , y, z, mobjtype_t.MT_SKULL); // Check for movements. if (!A.TryMove (newmobj, newmobj.x, newmobj.y)) { // kill it immediately A.DamageMobj (newmobj,actor,actor,10000); return; } newmobj.target = actor.target; SkullAttack.invoke (newmobj); } // // A_PainAttack // Spawn a lost soul and launch it at the target // class A_PainAttack implements ActionType1{ public void invoke(mobj_t actor){ if (actor.target==null) return; A_FaceTarget (actor); A_PainShootSkull (actor, actor.angle); } } class A_PainDie implements ActionType1{ public void invoke(mobj_t actor){ Fall.invoke (actor); A_PainShootSkull (actor, actor.angle+ANG90); A_PainShootSkull (actor, actor.angle+ANG180); A_PainShootSkull (actor, actor.angle+ANG270); } } class A_Scream implements ActionType1 { public void invoke(mobj_t actor){ int sound; switch (actor.info.deathsound) { case sfx_None: return; case sfx_podth1: case sfx_podth2: case sfx_podth3: sound = sfxenum_t.sfx_podth1.ordinal() + RND.P_Random ()%3; break; case sfx_bgdth1: case sfx_bgdth2: sound = sfxenum_t.sfx_bgdth1.ordinal() + RND.P_Random ()%2; break; default: sound = actor.info.deathsound.ordinal(); break; } // Check for bosses. if (actor.type==mobjtype_t.MT_SPIDER || actor.type == mobjtype_t.MT_CYBORG) { // full volume S.StartSound (null, sound); } else S.StartSound (actor, sound); } } /** * A_WeaponReady * The player can fire the weapon * or change to another weapon at this time. * Follows after getting weapon up, * or after previous attack/fire sequence. */ class A_WeaponReady implements ActionType2 { public void invoke( player_t player, pspdef_t psp ) { statenum_t newstate; int angle; // get out of attack state if (player.mo.state == states[statenum_t.S_PLAY_ATK1.ordinal()] || player.mo.state == states[statenum_t.S_PLAY_ATK2.ordinal()] ) { player.mo.SetMobjState (statenum_t.S_PLAY); } if (player.readyweapon == weapontype_t.wp_chainsaw && psp.state == states[statenum_t.S_SAW.ordinal()]) { S.StartSound(player.mo, sfxenum_t.sfx_sawidl); } // check for change // if player is dead, put the weapon away if (player.pendingweapon != weapontype_t.wp_nochange || !eval(player.health[0])) { // change weapon // (pending weapon should allready be validated) newstate = weaponinfo[player.readyweapon.ordinal()].downstate; player.SetPsprite ( player_t.ps_weapon, newstate); return; } // check for fire // the missile launcher and bfg do not auto fire if (eval(player.cmd.buttons & BT_ATTACK)) { if ( !player.attackdown || (player.readyweapon != weapontype_t.wp_missile && player.readyweapon != weapontype_t.wp_bfg) ) { player.attackdown = true; EN.FireWeapon (player); return; } } else player.attackdown = false; // bob the weapon based on movement speed angle = (128*DS.leveltime)&FINEMASK; psp.sx = FRACUNIT + FixedMul (player.bob, finecosine[angle]); angle &= FINEANGLES/2-1; psp.sy = player_t.WEAPONTOP + FixedMul (player.bob, finesine[angle]); } } // // A_Raise // class A_Raise implements ActionType2 { public void invoke( player_t player, pspdef_t psp ) { statenum_t newstate; //System.out.println("Trying to raise weapon"); //System.out.println(player.readyweapon + " height: "+psp.sy); psp.sy -= RAISESPEED; if (psp.sy > WEAPONTOP ) { //System.out.println("Not on top yet, exit and repeat."); return; } psp.sy = WEAPONTOP; // The weapon has been raised all the way, // so change to the ready state. newstate = weaponinfo[player.readyweapon.ordinal()].readystate; //System.out.println("Weapon raised, setting new state."); player.SetPsprite (ps_weapon, newstate); } } // // A_ReFire // The player can re-fire the weapon // without lowering it entirely. // class A_ReFire implements ActionType2 { public void invoke( player_t player, pspdef_t psp ) { // check for fire // (if a weaponchange is pending, let it go through instead) if ( eval(player.cmd.buttons & BT_ATTACK) && player.pendingweapon == weapontype_t.wp_nochange && eval(player.health[0])) { player.refire++; EN.FireWeapon (player); } else { player.refire = 0; player.CheckAmmo (); } } } // // A_GunFlash // class A_GunFlash implements ActionType2 { public void invoke( player_t player, pspdef_t psp ) { player.mo.SetMobjState (statenum_t.S_PLAY_ATK2); player.SetPsprite (ps_flash,weaponinfo[player.readyweapon.ordinal()].flashstate); } } // // A_Punch // class A_Punch implements ActionType2 { public void invoke( player_t player, pspdef_t psp ) { long angle; //angle_t int damage; int slope; damage = (RND.P_Random ()%10+1)<<1; if (eval(player.powers[pw_strength])) damage *= 10; angle = player.mo.angle; //angle = (angle+(RND.P_Random()-RND.P_Random())<<18)/*&BITS32*/; // _D_: for some reason, punch didnt work until I change this // I think it's because of "+" VS "<<" prioritys... angle += (RND.P_Random()-RND.P_Random())<<18; slope = A.AimLineAttack (player.mo, angle, MELEERANGE); A.LineAttack (player.mo, angle, MELEERANGE, slope, damage); // turn to face target if (eval(A.linetarget)) { S.StartSound(player.mo, sfxenum_t.sfx_punch); player.mo.angle = R.PointToAngle2 (player.mo.x, player.mo.y, A.linetarget.x, A.linetarget.y)&BITS32; } } } // // A_Saw // class A_Saw implements ActionType2 { public void invoke( player_t player, pspdef_t psp ) { long angle; // angle_t int damage; int slope; damage = 2*(RND.P_Random ()%10+1); angle = player.mo.angle; angle += (RND.P_Random()-RND.P_Random())<<18; angle&=BITS32; // use meleerange + 1 se the puff doesn't skip the flash slope = A.AimLineAttack (player.mo, angle, MELEERANGE+1); A.LineAttack (player.mo, angle, MELEERANGE+1, slope, damage); if (!eval(A.linetarget)) { S.StartSound(player.mo, sfxenum_t.sfx_sawful); return; } S.StartSound(player.mo, sfxenum_t.sfx_sawhit); // turn to face target angle = R.PointToAngle2 (player.mo.x, player.mo.y, A.linetarget.x, A.linetarget.y)&BITS32; /* FIXME: this comparison is going to fail.... or not? If e.g. angle = 359 degrees (which will be mapped to a small negative number), and player.mo.angle = 160 degrees (a large, positive value), the result will be a large negative value, which will still be "greater" than ANG180. It seems that *differences* between angles will always compare correctly, but not direct inequalities. */ // Yet another screwy place where unsigned BAM angles are used as SIGNED comparisons. long dangle= (angle - player.mo.angle); dangle&=BITS32; if (dangle > ANG180) { if ((int)dangle < -ANG90/20) player.mo.angle = angle + ANG90/21; else player.mo.angle -= ANG90/20; } else { if (dangle > ANG90/20) player.mo.angle = angle - ANG90/21; else player.mo.angle += ANG90/20; } player.mo.angle&=BITS32; player.mo.flags |= MF_JUSTATTACKED; } } // // A_FireMissile // class A_FireMissile implements ActionType2 { public void invoke( player_t player, pspdef_t psp ) { player.ammo[weaponinfo[player.readyweapon.ordinal()].ammo.ordinal()]--; A.SpawnPlayerMissile (player.mo, mobjtype_t.MT_ROCKET); } } // // A_FireBFG // class A_FireBFG implements ActionType2 { // plasma cells for a bfg attack // IDEA: make action functions partially parametrizable? private static final int BFGCELLS = 40; public void invoke( player_t player, pspdef_t psp ) { player.ammo[weaponinfo[player.readyweapon.ordinal()].ammo.ordinal()] -= BFGCELLS; A.SpawnPlayerMissile (player.mo, mobjtype_t.MT_BFG); } } // // A_FireCGun // class A_FireCGun implements ActionType2 { public void invoke( player_t player, pspdef_t psp ) { // For convenience. int readyweap=player.readyweapon.ordinal(); int flashstate=weaponinfo[readyweap].flashstate.ordinal(); int current_state=psp.state.id; S.StartSound (player.mo, sfxenum_t.sfx_pistol); if (!eval(player.ammo[weaponinfo[readyweap].ammo.ordinal()])) return; player.mo.SetMobjState (statenum_t.S_PLAY_ATK2); player.ammo[weaponinfo[readyweap].ammo.ordinal()]--; // MAES: Code to alternate between two different gun flashes // needed a clear rewrite, as it was way too messy. // We know that the flash states are a certain amount away from // the firing states. This amount is two frames. player.SetPsprite (ps_flash,statenum_t.values()[flashstate+current_state-statenum_t.S_CHAIN1.ordinal()] ); A.P_BulletSlope (player.mo); A.P_GunShot (player.mo, !eval(player.refire)); } } // // A_FirePlasma // class A_FirePlasma implements ActionType2 { public void invoke( player_t player, pspdef_t psp ) { player.ammo[weaponinfo[player.readyweapon.ordinal()].ammo.ordinal()]--; player.SetPsprite ( ps_flash, weaponinfo[player.readyweapon.ordinal()].flashstate ); A.SpawnPlayerMissile (player.mo, mobjtype_t.MT_PLASMA); } } class A_XScream implements ActionType1 { public void invoke(mobj_t actor){ S.StartSound (actor, sfxenum_t.sfx_slop); } } class A_Pain implements ActionType1 { public void invoke(mobj_t actor){ if (actor.info.painsound!=null) S.StartSound(actor, actor.info.painsound); } } class A_Fall implements ActionType1 { public void invoke(mobj_t actor){ // actor is on ground, it can be walked over actor.flags &= ~MF_SOLID; // So change this if corpse objects // are meant to be obstacles. } } // // A_Explode // class A_Explode implements ActionType1 { public void invoke(mobj_t thingy){ A.RadiusAttack ( thingy, thingy.target, 128 ); } } /** * A_BossDeath * Possibly trigger special effects * if on first boss level * * TODO: find out how Plutonia/TNT does cope with this. * Special clauses? * */ class A_BossDeath implements ActionType1 { public void invoke(mobj_t mo){ thinker_t th; mobj_t mo2; line_t junk = new line_t(); int i; if ( DS.isCommercial()) { if (DS.gamemap != 7) return; if ((mo.type != mobjtype_t.MT_FATSO) && (mo.type != mobjtype_t.MT_BABY)) return; } else { switch(DS.gameepisode) { case 1: if (DS.gamemap != 8) return; if (mo.type != mobjtype_t.MT_BRUISER) return; break; case 2: if (DS.gamemap != 8) return; if (mo.type != mobjtype_t.MT_CYBORG) return; break; case 3: if (DS.gamemap != 8) return; if (mo.type != mobjtype_t.MT_SPIDER) return; break; case 4: switch(DS.gamemap) { case 6: if (mo.type != mobjtype_t.MT_CYBORG) return; break; case 8: if (mo.type != mobjtype_t.MT_SPIDER) return; break; default: return; } break; default: if (DS.gamemap != 8) return; break; } } // make sure there is a player alive for victory for (i=0 ; i<MAXPLAYERS ; i++) if (DS.playeringame[i] && DS.players[i].health[0] > 0) break; if (i==MAXPLAYERS) return; // no one left alive, so do not end game // scan the remaining thinkers to see // if all bosses are dead for (th = A.thinkercap.next ; th != A.thinkercap ; th=th.next) { if (th.function != think_t.P_MobjThinker) continue; mo2 = (mobj_t)th; if (mo2 != mo && mo2.type == mo.type && mo2.health > 0) { // other boss not dead return; } } // victory! if ( DS.isCommercial()) { if (DS.gamemap == 7) { if (mo.type == mobjtype_t.MT_FATSO) { junk.tag = 666; A.DoFloor(junk,floor_e.lowerFloorToLowest); return; } if (mo.type == mobjtype_t.MT_BABY) { junk.tag = 667; A.DoFloor(junk,floor_e.raiseToTexture); return; } } } else { switch(DS.gameepisode) { case 1: junk.tag = 666; A.DoFloor (junk, floor_e.lowerFloorToLowest); return; case 4: switch(DS.gamemap) { case 6: junk.tag = 666; A.DoDoor (junk, vldoor_e.blazeOpen); return; case 8: junk.tag = 666; A.DoFloor (junk, floor_e.lowerFloorToLowest); return; } } } A.DM.ExitLevel (); } } class A_Hoof implements ActionType1 { public void invoke(mobj_t mo){ S.StartSound(mo, sfxenum_t.sfx_hoof); Chase.invoke(mo); } } class A_KeenDie implements ActionType1 { public void invoke(mobj_t mo){ thinker_t th; mobj_t mo2; line_t junk = new line_t(); // MAES: fixed null 21/5/2011 Fall.invoke(mo); // scan the remaining thinkers // to see if all Keens are dead for (th = A.thinkercap.next ; th != A.thinkercap ; th=th.next) { if (th.function != think_t.P_MobjThinker) continue; mo2 = (mobj_t)th; if (mo2 != mo && mo2.type == mo.type && mo2.health > 0) { // other Keen not dead return; } } junk.tag = 666; A.DoDoor(junk,vldoor_e.open); } } class A_Metal implements ActionType1 { public void invoke(mobj_t mo){ S.StartSound(mo, sfxenum_t.sfx_metal); Chase.invoke(mo); } } class A_BabyMetal implements ActionType1 { public void invoke(mobj_t mo){ S.StartSound(mo, sfxenum_t.sfx_bspwlk); Chase.invoke (mo); } } // // A_BFGsound // class A_BFGsound implements ActionType2{ public void invoke( player_t player, pspdef_t psp ) { S.StartSound(player.mo, sfxenum_t.sfx_bfg); } } // // A_BFGSpray // Spawn a BFG explosion on every monster in view // class A_BFGSpray implements ActionType1 { public void invoke(mobj_t mo){ int i; int j; int damage; long an; // angle_t // offset angles from its attack angle for (i=0 ; i<40 ; i++) { an = (mo.angle - ANG90/2 + ANG90/40*i)&BITS32; // mo.target is the originator (player) // of the missile A.AimLineAttack (mo.target, an, 16*64*FRACUNIT); if (!eval(A.linetarget)) continue; A.SpawnMobj (A.linetarget.x, A.linetarget.y, A.linetarget.z + (A.linetarget.height>>2), mobjtype_t.MT_EXTRABFG); damage = 0; for (j=0;j<15;j++) damage += (RND.P_Random()&7) + 1; A.DamageMobj (A.linetarget, mo.target,mo.target, damage); } } } class A_OpenShotgun2 implements ActionType2{ public void invoke (player_t player,pspdef_t psp){ S.StartSound(player.mo, sfxenum_t.sfx_dbopn); } } class A_LoadShotgun2 implements ActionType2{ public void invoke (player_t player,pspdef_t psp){ S.StartSound(player.mo, sfxenum_t.sfx_dbload); } } // // A_Look // Stay in state until a player is sighted. // class A_Look implements ActionType1 { public void invoke(mobj_t actor){ mobj_t targ; boolean seeyou=false; // to avoid the fugly goto actor.threshold = 0; // any shot will wake up targ = actor.subsector.sector.soundtarget; if (targ!=null && eval(targ.flags& MF_SHOOTABLE) ) { actor.target = targ; if ( eval(actor.flags&MF_AMBUSH )) { seeyou= (EN.CheckSight (actor, actor.target)); } else seeyou=true; } if (!seeyou){ if (!EN.LookForPlayers (actor, false) ) return; } // go into chase state seeyou: if (actor.info.seesound!=null && actor.info.seesound!=sfxenum_t.sfx_None) { int sound; switch (actor.info.seesound) { case sfx_posit1: case sfx_posit2: case sfx_posit3: sound = sfxenum_t.sfx_posit1.ordinal()+RND.P_Random()%3; break; case sfx_bgsit1: case sfx_bgsit2: sound = sfxenum_t.sfx_bgsit1.ordinal()+RND.P_Random()%2; break; default: sound = actor.info.seesound.ordinal(); break; } if (actor.type==mobjtype_t.MT_SPIDER || actor.type == mobjtype_t.MT_CYBORG) { // full volume S.StartSound(null, sound); } else S.StartSound(actor, sound); } actor.SetMobjState(actor.info.seestate); } } class A_CloseShotgun2 implements ActionType2{ public void invoke (player_t player,pspdef_t psp){ S.StartSound(player.mo, sfxenum_t.sfx_dbcls); ReFire.invoke(player,psp); } } // // ? // class A_Light0 implements ActionType2{ public void invoke (player_t player,pspdef_t psp){ player.extralight = 0; } } class A_Light1 implements ActionType2{ public void invoke (player_t player,pspdef_t psp){ player.extralight = 1; } } class A_Light2 implements ActionType2{ public void invoke (player_t player,pspdef_t psp){ player.extralight = 2; } } // // A_Lower // Lowers current weapon, // and changes weapon at bottom. // class A_Lower implements ActionType2{ public void invoke (player_t player,pspdef_t psp){ psp.sy += LOWERSPEED; // Is already down. if (psp.sy < WEAPONBOTTOM ) return; // Player is dead. if (player.playerstate == PST_DEAD) { psp.sy = WEAPONBOTTOM; // don't bring weapon back up return; } // The old weapon has been lowered off the screen, // so change the weapon and start raising it if (!eval(player.health[0])) { // Player is dead, so keep the weapon off screen. player.SetPsprite (ps_weapon, statenum_t.S_NULL); return; } player.readyweapon = player.pendingweapon; player.BringUpWeapon (); } } class A_CheckReload implements ActionType2{ public void invoke (player_t player,pspdef_t psp){ player.CheckAmmo (); /* if (player.ammo[am_shell]<2) P_SetPsprite (player, ps_weapon, S_DSNR1); */} } // Brain status mobj_t[] braintargets=new mobj_t[NUMBRAINTARGETS]; int numbraintargets; int braintargeton; class A_BrainAwake implements ActionType1{ public void invoke(mobj_t mo){ thinker_t thinker; mobj_t m; // find all the target spots numbraintargets = 0; braintargeton = 0; thinker = A.thinkercap.next; for (thinker = A.thinkercap.next ; thinker != A.thinkercap ; thinker = thinker.next) { if (thinker.function != think_t.P_MobjThinker) continue; // not a mobj m = (mobj_t)thinker; if (m.type == mobjtype_t.MT_BOSSTARGET ) { braintargets[numbraintargets] = m; numbraintargets++; } } S.StartSound(null,sfxenum_t.sfx_bossit); } } class A_BrainPain implements ActionType1 { public void invoke(mobj_t mo){ S.StartSound(null,sfxenum_t.sfx_bospn); } } class A_BrainScream implements ActionType1{ public void invoke(mobj_t mo){ int x; int y; int z; mobj_t th; for (x=mo.x - 196*FRACUNIT ; x< mo.x + 320*FRACUNIT ; x+= FRACUNIT*8) { y = mo.y - 320*FRACUNIT; z = 128 + RND.P_Random()*2*FRACUNIT; th = A.SpawnMobj (x,y,z, mobjtype_t.MT_ROCKET); th.momz = RND.P_Random()*512; th.SetMobjState (statenum_t.S_BRAINEXPLODE1); th.tics -= RND.P_Random()&7; if (th.tics < 1) th.tics = 1; } S.StartSound(null,sfxenum_t.sfx_bosdth); } } class A_BrainExplode implements ActionType1{ public void invoke(mobj_t mo){ int x; int y; int z; mobj_t th; x = mo.x + (RND.P_Random () - RND.P_Random ())*2048; y = mo.y; z = 128 + RND.P_Random()*2*FRACUNIT; th = A.SpawnMobj (x,y,z, mobjtype_t.MT_ROCKET); th.momz = RND.P_Random()*512; th.SetMobjState (statenum_t.S_BRAINEXPLODE1); th.tics -= RND.P_Random()&7; if (th.tics < 1) th.tics = 1; } } class A_BrainDie implements ActionType1{ public void invoke(mobj_t mo){ DG.ExitLevel (); } } private int easy = 0; class A_BrainSpit implements ActionType1{ public void invoke(mobj_t mo){ mobj_t targ; mobj_t newmobj; easy ^= 1; if (DS.gameskill.ordinal() <= skill_t.sk_easy.ordinal() && (easy==0)) return; // shoot a cube at current target targ = braintargets[braintargeton]; // Load-time fix: awake on zero numbrain targets, if A_BrainSpit is called. if (numbraintargets==0) {BrainAwake.invoke(mo); return; } braintargeton = (braintargeton+1)%numbraintargets; // spawn brain missile newmobj = A.SpawnMissile (mo, targ, mobjtype_t.MT_SPAWNSHOT); newmobj.target = targ; newmobj.reactiontime = (int) (((targ.y - mo.y)/newmobj.momy) / newmobj.state.tics); S.StartSound(null, sfxenum_t.sfx_bospit); } } // travelling cube sound class A_SpawnSound implements ActionType1{ public void invoke(mobj_t mo){ S.StartSound (mo,sfxenum_t.sfx_boscub); SpawnFly.invoke(mo); } } class A_SpawnFly implements ActionType1{ public void invoke(mobj_t mo){ mobj_t newmobj; mobj_t fog; mobj_t targ; int r; mobjtype_t type; if (--mo.reactiontime!=0) return; // still flying targ = mo.target; // First spawn teleport fog. fog = A.SpawnMobj (targ.x, targ.y, targ.z, mobjtype_t.MT_SPAWNFIRE); S.StartSound (fog, sfxenum_t.sfx_telept); // Randomly select monster to spawn. r = RND.P_Random (); // Probability distribution (kind of :), // decreasing likelihood. if ( r<50 ) type = mobjtype_t.MT_TROOP; else if (r<90) type = mobjtype_t.MT_SERGEANT; else if (r<120) type = mobjtype_t.MT_SHADOWS; else if (r<130) type = mobjtype_t.MT_PAIN; else if (r<160) type = mobjtype_t.MT_HEAD; else if (r<162) type = mobjtype_t.MT_VILE; else if (r<172) type = mobjtype_t.MT_UNDEAD; else if (r<192) type = mobjtype_t.MT_BABY; else if (r<222) type = mobjtype_t.MT_FATSO; else if (r<246) type = mobjtype_t.MT_KNIGHT; else type = mobjtype_t.MT_BRUISER; newmobj = A.SpawnMobj (targ.x, targ.y, targ.z, type); if (EN.LookForPlayers (newmobj, true) ) newmobj.SetMobjState (newmobj.info.seestate); // telefrag anything in this spot A.TeleportMove (newmobj, newmobj.x, newmobj.y); // remove self (i.e., cube). A.RemoveMobj (mo); } } // //P_MobjThinker // class A_PlayerScream implements ActionType1{ public void invoke(mobj_t actor){ // Default death sound. sfxenum_t sound = sfxenum_t.sfx_pldeth; if ( DS.isCommercial() && (actor.health < -50)) { // IF THE PLAYER DIES // LESS THAN -50% WITHOUT GIBBING sound = sfxenum_t.sfx_pdiehi; } S.StartSound(actor, sound); } } // // A_FaceTarget. This is called by many other functions anyway, // other than autonomously. // void A_FaceTarget (mobj_t actor) { if (actor.target==null) return; actor.flags &= ~MF_AMBUSH; actor.angle = R.PointToAngle2 (actor.x, actor.y, actor.target.x, actor.target.y)&BITS32; if (eval(actor.target.flags & MF_SHADOW)) actor.angle += (RND.P_Random()-RND.P_Random())<<21; actor.angle&=BITS32; } /** * A_Chase * Actor has a melee attack, * so it tries to close as fast as possible */ class A_Chase implements ActionType1{ public void invoke(mobj_t actor){ int delta; boolean nomissile=false; // for the fugly goto if (actor.reactiontime!=0) actor.reactiontime--; // modify target threshold if (actor.threshold!=0) { if (actor.target==null || actor.target.health <= 0) { actor.threshold = 0; } else actor.threshold--; } // turn towards movement direction if not there yet if (actor.movedir < 8) { actor.angle &= (7<<29); actor.angle&=BITS32; // Nice problem, here! delta = (int) (actor.angle - (actor.movedir << 29)); if (delta > 0) actor.angle -= ANG45; else if (delta < 0) actor.angle += ANG45; actor.angle&=BITS32; } if (actor.target==null || !eval(actor.target.flags&MF_SHOOTABLE)) { // look for a new target if (EN.LookForPlayers(actor,true)) return; // got a new target actor.SetMobjState (actor.info.spawnstate); return; } // do not attack twice in a row if (eval(actor.flags & MF_JUSTATTACKED)) { actor.flags &= ~MF_JUSTATTACKED; if (DS.gameskill != skill_t.sk_nightmare && !DS.fastparm) A.NewChaseDir (actor); return; } // check for melee attack if (actor.info.meleestate!=statenum_t.S_NULL /*null*/ && EN.CheckMeleeRange (actor)) { if (actor.info.attacksound!=null){ S.StartSound (actor, actor.info.attacksound); } actor.SetMobjState(actor.info.meleestate); return; } // check for missile attack if (actor.info.missilestate!=statenum_t.S_NULL /*!= null*/) //_D_: this caused a bug where Demon for example were disappearing { // Assume that a missile attack is possible if (DS.gameskill.ordinal() < skill_t.sk_nightmare.ordinal() && !DS.fastparm && actor.movecount!=0) { // Uhm....no. nomissile=true; } else if (!EN.CheckMissileRange (actor)) nomissile=true; // Out of range if (!nomissile){ // Perform the attack actor.SetMobjState ( actor.info.missilestate); actor.flags |= MF_JUSTATTACKED; return; } } // This should be executed always, if not averted by returns. // possibly choose another target if (DS.netgame && actor.threshold==0 && !EN.CheckSight (actor, actor.target) ) { if (EN.LookForPlayers(actor,true)) return; // got a new target } // chase towards player if (--actor.movecount<0 || !A.Move (actor)) { A.NewChaseDir (actor); } // make active sound if (actor.info.activesound!=null && RND.P_Random() < 3) { S.StartSound (actor, actor.info.activesound); } } } } package p; import java.io.DataInputStream; import java.io.IOException; import java.nio.ByteBuffer; import m.IRandom; import rr.SectorAction; import w.DoomIO; // // BROKEN LIGHT EFFECT // public class lightflash_t extends SectorAction{ private IRandom RND; public int count; public int maxlight; public int minlight; public int maxtime; public int mintime; public lightflash_t(){ } public lightflash_t(IRandom RND){ this.RND=RND; } /** * T_LightFlash * Do flashing lights. */ public void LightFlash() { if (--count != 0) return; if (sector.lightlevel == maxlight) { sector.lightlevel = (short)minlight; count = (RND.P_Random() & mintime) + 1; } else { sector.lightlevel = (short)maxlight; count = (RND.P_Random() & maxtime) + 1; } } @Override public void read(DataInputStream f) throws IOException{ super.read(f); // Call thinker reader first super.sectorid=DoomIO.readLEInt(f); // Sector index count=DoomIO.readLEInt(f); maxlight=DoomIO.readLEInt(f); minlight=DoomIO.readLEInt(f); maxtime=DoomIO.readLEInt(f); mintime=DoomIO.readLEInt(f); } @Override public void pack(ByteBuffer b) throws IOException{ super.pack(b); //12 b.putInt(super.sectorid); // 16 b.putInt(count); //20 b.putInt(maxlight);//24 b.putInt(minlight);//28 b.putInt(maxtime);//32 b.putInt(mintime);//36 } } package p; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.Arrays; import m.BBox; import m.fixed_t; import boom.DeepBSPNodesV4; import boom.mapglvertex_t; import boom.mapnode_v4_t; import boom.mapnode_znod_t; import boom.mapseg_v4_t; import boom.mapseg_znod_t; import boom.mapsubsector_v4_t; import boom.mapsubsector_znod_t; import data.Limits; import data.maplinedef_t; import data.mapnode_t; import data.mapsector_t; import data.mapseg_t; import data.mapsidedef_t; import data.mapsubsector_t; import data.mapthing_t; import data.mapvertex_t; import defines.skill_t; import defines.slopetype_t; import doom.DoomStatus; import rr.RendererState; import rr.line_t; import static rr.line_t.ML_TWOSIDED; import rr.node_t; import rr.sector_t; import rr.seg_t; import rr.side_t; import rr.subsector_t; import rr.vertex_t; import rr.z_vertex_t; import s.degenmobj_t; import utils.C2JUtils; import w.CacheableDoomObjectContainer; import w.DoomBuffer; import w.wadfile_info_t; import static m.fixed_t.FRACUNIT; import static m.fixed_t.FRACBITS; import static data.Defines.*; import static utils.C2JUtils.flags; import static utils.C2JUtils.unsigned; import static boom.E6Y.NO_INDEX; import static m.BBox.*; import static boom.Compatibility.*; /* * Emacs style mode select -*- C++ -*- * ----------------------------------------------------------------------------- * PrBoom: a Doom port merged with LxDoom and LSDLDoom based on BOOM, a modified * and improved DOOM engine Copyright (C) 1999 by id Software, Chi Hoang, Lee * Killough, Jim Flynn, Rand Phares, Ty Halderman Copyright (C) 1999-2000 by * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze Copyright 2005, * 2006 by Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko 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. You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, Inc., * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. DESCRIPTION: Do all * the WAD I/O, get map description, set up initial state and misc. LUTs. * * MAES 30/9/2011: This is a direct translation of prBoom+'s 2.5.0.8 p_setup.c * and p_setup.h. * * * * ----------------------------------------------------------------------------- */ public class BoomLevelLoader extends AbstractLevelLoader { public BoomLevelLoader(DoomStatus<?,?> DC) { super(DC); // TODO Auto-generated constructor stub } // OpenGL related. byte[] map_subsectors; // ////////////////////////////////////////////////////////////////////////////////////////// // figgi 08/21/00 -- finalants and globals for glBsp support public static final int gNd2 = 0x32644E67; // figgi -- suppport for new // GL_VERT format v2.0 public static final int gNd3 = 0x33644E67; public static final int gNd4 = 0x34644E67; public static final int gNd5 = 0x35644E67; public static final int ZNOD = 0x444F4E5A; public static final int ZGLN = 0x4E4C475A; public static final int GL_VERT_OFFSET = 4; int firstglvertex = 0; int nodesVersion = 0; boolean forceOldBsp = false; // figgi 08/21/00 -- glSegs class glseg_t { char v1; // start vertex (16 bit) char v2; // end vertex (16 bit) char linedef; // linedef, or -1 for minisegs short side; // side on linedef: 0 for right, 1 for left short partner; // corresponding partner seg, or -1 on one-sided walls } public static final int ML_GL_LABEL = 0; // A separator name, GL_ExMx or // GL_MAPxx public static final int ML_GL_VERTS = 1; // Extra Vertices public static final int ML_GL_SEGS = 2; // Segs, from linedefs & minisegs public static final int ML_GL_SSECT = 3; // SubSectors, list of segs public static final int ML_GL_NODES = 4; // GL BSP nodes // ////////////////////////////////////////////////////////////////////////////////////////// // // REJECT // For fast sight rejection. // Speeds up enemy AI by skipping detailed // LineOf Sight calculation. // Without the special effect, this could // be used as a PVS lookup as well. // private int rejectlump = -1;// cph - store reject lump num if cached private int current_episode = -1; private int current_map = -1; private int current_nodesVersion = -1; private boolean samelevel = false; /** * e6y: Smart malloc Used by P_SetupLevel() for smart data loading. Do * nothing if level is the same. Passing a null array forces allocation. * * @param p * generically typed array to consider * @param numstuff * elements to realloc */ private <T> T[] malloc_IfSameLevel(T[] p, int numstuff, Class<T> type) { if (!samelevel || (p == null)) { return (T[]) C2JUtils.createArrayOfObjects(type, numstuff); } return p; } // e6y: Smart calloc // Used by P_SetupLevel() for smart data loading // Clear the memory without allocation if level is the same private <T> T[] calloc_IfSameLevel(T[] p, int numstuff, Class<T> type) { if (!samelevel) { return (T[]) C2JUtils.createArrayOfObjects(type, numstuff); } else { // TODO: stuff should be resetted! C2JUtils.resetAll((Resettable[]) p); return p; } } // // P_CheckForZDoomNodes // private boolean P_CheckForZDoomNodes(int lumpnum, int gl_lumpnum) { byte[] data; int check; data = W.CacheLumpNumAsRawBytes(lumpnum + ML_NODES, 0); check=ByteBuffer.wrap(data).getInt(); if (check == ZNOD) I.Error("P_CheckForZDoomNodes: ZDoom nodes not supported yet"); data = W.CacheLumpNumAsRawBytes(lumpnum + ML_SSECTORS, 0); if (check == ZGLN) I.Error("P_CheckForZDoomNodes: ZDoom GL nodes not supported yet"); // Unlock them to force different buffering interpretation. W.UnlockLumpNum(lumpnum + ML_NODES); W.UnlockLumpNum(lumpnum + ML_SSECTORS); return false; } // // P_CheckForDeePBSPv4Nodes // http://www.sbsoftware.com/files/DeePBSPV4specs.txt // private boolean P_CheckForDeePBSPv4Nodes(int lumpnum, int gl_lumpnum) { byte[] data; boolean result = false; data = W.CacheLumpNumAsRawBytes(lumpnum + ML_NODES, 0); byte[] compare = Arrays.copyOfRange(data, 0, 7); if (Arrays.equals(compare, DeepBSPNodesV4.DeepBSPHeader)) { System.out .println("P_CheckForDeePBSPv4Nodes: DeePBSP v4 Extended nodes are detected\n"); result = true; } W.UnlockLumpNum(lumpnum + ML_NODES); return result; } // // P_CheckForZDoomUncompressedNodes // http://zdoom.org/wiki/ZDBSP#Compressed_Nodes // private static final int XNOD = 0x584e4f44; private boolean P_CheckForZDoomUncompressedNodes(int lumpnum, int gl_lumpnum) { byte[] data; int wrapper; boolean result = false; data = W.CacheLumpNumAsRawBytes(lumpnum + ML_NODES, 0); wrapper=ByteBuffer.wrap(data).getInt(); if (wrapper==XNOD) { System.out.println("P_CheckForZDoomUncompressedNodes: ZDoom uncompressed normal nodes are detected\n"); result = true; } W.UnlockLumpNum(lumpnum + ML_NODES); return result; } // // P_GetNodesVersion // public void P_GetNodesVersion(int lumpnum, int gl_lumpnum) { int ver = -1; nodesVersion = 0; if ((gl_lumpnum > lumpnum) && (forceOldBsp == false) &&(DoomStatus.compatibility_level>=prboom_2_compatibility) ) { byte[] data = W.CacheLumpNumAsRawBytes(gl_lumpnum + ML_GL_VERTS, 0); int wrapper = ByteBuffer.wrap(data).getInt(); if (wrapper == gNd2) { data = W.CacheLumpNumAsRawBytes(gl_lumpnum + ML_GL_SEGS, 0); wrapper = ByteBuffer.wrap(data).getInt(); if (wrapper == gNd3) { ver = 3; } else { nodesVersion = gNd2; System.out .println("P_GetNodesVersion: found version 2 nodes\n"); } } if (wrapper == gNd4) { ver = 4; } if (wrapper == gNd5) { ver = 5; } // e6y: unknown gl nodes will be ignored if (nodesVersion == 0 && ver != -1) { System.out.printf( "P_GetNodesVersion: found version %d nodes\n", ver); System.out.printf( "P_GetNodesVersion: version %d nodes not supported\n", ver); } } else { nodesVersion = 0; System.out.println("P_GetNodesVersion: using normal BSP nodes\n"); if (P_CheckForZDoomNodes(lumpnum, gl_lumpnum)) I.Error("P_GetNodesVersion: ZDoom nodes not supported yet"); } } // // P_LoadVertexes // // killough 5/3/98: reformatted, cleaned up // private void P_LoadVertexes(int lump) { final mapvertex_t[] data; // cph - final // Determine number of lumps: // total lump length / vertex record length. numvertexes = W.LumpLength(lump) / mapvertex_t.sizeOf(); // Allocate zone memory for buffer. vertexes = (vertex_t[]) calloc_IfSameLevel(vertexes, numvertexes, vertex_t.class); // Load data into cache. // cph 2006/07/29 - cast to mapvertex_t here, making the loop below much // neater data = W.CacheLumpNumIntoArray(lump, numvertexes, mapvertex_t.class); // Copy and convert vertex coordinates, // internal representation as fixed. for (int i = 0; i < numvertexes; i++) { vertexes[i].x = data[i].x << m.fixed_t.FRACBITS; vertexes[i].y = data[i].y << FRACBITS; } // Free buffer memory. W.UnlockLumpNum(lump); } /******************************************* * Name : P_LoadVertexes2 * modified : 09/18/00, adapted for PrBoom * author * : figgi * what : support for gl nodes * * @throws IOException * * *******************************************/ // figgi -- FIXME: Automap showes wrong zoom boundaries when starting game // when P_LoadVertexes2 is used with classic BSP nodes. private void P_LoadVertexes2(int lump, int gllump) throws IOException { final ByteBuffer gldata; int i; mapvertex_t[] ml; // GL vertexes come after regular ones. firstglvertex = W.LumpLength(lump) / mapvertex_t.sizeOf(); numvertexes = W.LumpLength(lump) / mapvertex_t.sizeOf(); if (gllump >= 0) // check for glVertices { // Read GL lump into buffer. This allows some flexibility gldata = W.CacheLumpNumAsDoomBuffer(gllump).getBuffer(); if (nodesVersion == gNd2) // 32 bit GL_VERT format (16.16 fixed) { // These vertexes are double in size than regular Doom vertexes. // Furthermore, we have to skip the first 4 bytes // (GL_VERT_OFFSET) // of the gl lump. numvertexes += (W.LumpLength(gllump) - GL_VERT_OFFSET) / mapglvertex_t.sizeOf(); // Vertexes size accomodates both normal and GL nodes. vertexes = malloc_IfSameLevel(vertexes, numvertexes, vertex_t.class); final mapglvertex_t[] mgl = C2JUtils.createArrayOfObjects(mapglvertex_t.class, numvertexes - firstglvertex); // Get lump and skip first 4 bytes gldata.rewind(); gldata.position(GL_VERT_OFFSET); CacheableDoomObjectContainer.unpack(gldata, mgl); int mgl_count = 0; for (i = firstglvertex; i < numvertexes; i++) { vertexes[i].x = mgl[mgl_count].x; vertexes[i].y = mgl[mgl_count].y; mgl_count++; } } else { // Vertexes size accomodates both normal and GL nodes. numvertexes += W.LumpLength(gllump) / mapvertex_t.sizeOf(); vertexes = malloc_IfSameLevel(vertexes, numvertexes, vertex_t.class); ml = C2JUtils.createArrayOfObjects(mapvertex_t.class, numvertexes - firstglvertex); // We can read this "directly" because no skipping is involved. gldata.rewind(); CacheableDoomObjectContainer.unpack(gldata, ml); // ml = W.CacheLumpNumIntoArray(gllump, // numvertexes-firstglvertex,mapvertex_t.class); int ml_count = 0; for (i = firstglvertex; i < numvertexes; i++) { vertexes[i].x = ml[ml_count].x; vertexes[i].y = ml[ml_count].y; ml_count++; } } W.UnlockLumpNum(gllump); } // Loading of regular lumps (sheesh!) ml = W.CacheLumpNumIntoArray(lump, firstglvertex, mapvertex_t.class); for (i = 0; i < firstglvertex; i++) { vertexes[i].x = ml[i].x; vertexes[i].y = ml[i].y; } W.UnlockLumpNum(lump); } /******************************************* * created : 08/13/00 * modified : 09/18/00, adapted for PrBoom * author : * figgi * what : basic functions needed for * computing gl nodes * *******************************************/ public int checkGLVertex(int num) { if ((num & 0x8000) != 0) num = (num & 0x7FFF) + firstglvertex; return num; } public static float GetDistance(int dx, int dy) { float fx = (float) (dx) / FRACUNIT, fy = (float) (dy) / FRACUNIT; return (float) Math.sqrt(fx * fx + fy * fy); } public static float GetTexelDistance(int dx, int dy) { // return (float)((int)(GetDistance(dx, dy) + 0.5f)); float fx = (float) (dx) / FRACUNIT, fy = (float) (dy) / FRACUNIT; return (float) ((int) (0.5f + (float) Math.sqrt(fx * fx + fy * fy))); } public static int GetOffset(vertex_t v1, vertex_t v2) { float a, b; int r; a = (float) (v1.x - v2.x) / (float) FRACUNIT; b = (float) (v1.y - v2.y) / (float) FRACUNIT; r = (int) (Math.sqrt(a * a + b * b) * (float) FRACUNIT); return r; } // // P_LoadSegs // // killough 5/3/98: reformatted, cleaned up private void P_LoadSegs(int lump) { int i; final mapseg_t[] data; // cph - final numsegs = W.LumpLength(lump) / mapseg_t.sizeOf(); segs = (seg_t[]) calloc_IfSameLevel(segs, numsegs, seg_t.class); data = W.CacheLumpNumIntoArray(lump, numsegs, mapseg_t.class); // cph - // wad // lump // handling // updated if ((data == null) || (numsegs == 0)) I.Error("P_LoadSegs: no segs in level"); for (i = 0; i < numsegs; i++) { seg_t li = segs[i]; final mapseg_t ml = data[i]; char v1, v2; int side, linedef; line_t ldef; li.iSegID = i; // proff 11/05/2000: needed for OpenGL v1 = (char) ml.v1; v2 = (char) ml.v2; // e6y // moved down for additional checks to avoid overflow // if wrong vertexe's indexes are in SEGS lump // see below for more detailed information // li.v1 = &vertexes[v1]; // li.v2 = &vertexes[v2]; li.miniseg = false; // figgi -- there are no minisegs in classic BSP // nodes // e6y: moved down, see below // li.length = GetDistance(li.v2.x - li.v1.x, li.v2.y - li.v1.y); li.angle = ml.angle << 16; li.offset = ml.offset << 16; linedef = (char) ml.linedef; // e6y: check for wrong indexes if (linedef >= numlines) { I.Error( "P_LoadSegs: seg %d references a non-existent linedef %d", i, linedef); } ldef = lines[linedef]; li.linedef = ldef; side = ml.side; // e6y: fix wrong side index if (side != 0 && side != 1) { System.err .printf( "P_LoadSegs: seg %d contains wrong side index %d. Replaced with 1.\n", i, side); side = 1; } // e6y: check for wrong indexes if ((char) ldef.sidenum[side] >= (char) numsides) { I.Error( "P_LoadSegs: linedef %d for seg %d references a non-existent sidedef %d", linedef, i, ldef.sidenum[side]); } li.sidedef = sides[ldef.sidenum[side]]; /* * cph 2006/09/30 - our frontsector can be the second side of the * linedef, so must check for NO_INDEX in case we are incorrectly * referencing the back of a 1S line */ if (ldef.sidenum[side] != NO_INDEX) li.frontsector = sides[ldef.sidenum[side]].sector; else { li.frontsector = null; System.err.printf( "P_LoadSegs: front of seg %i has no sidedef\n", i); } if (flags(ldef.flags, ML_TWOSIDED) && ldef.sidenum[side ^ 1] != NO_INDEX) li.backsector = sides[ldef.sidenum[side ^ 1]].sector; else li.backsector = null; // e6y // check and fix wrong references to non-existent vertexes // see e1m9 @ NIVELES.WAD // http://www.doomworld.com/idgames/index.php?id=12647 if (v1 >= numvertexes || v2 >= numvertexes) { String str = "P_LoadSegs: compatibility loss - seg %d references a non-existent vertex %d\n"; if (DM.demorecording) { I.Error( str + "Demo recording on levels with invalid nodes is not allowed", i, (v1 >= numvertexes ? v1 : v2)); } if (v1 >= numvertexes) System.err.printf(str, i, v1); if (v2 >= numvertexes) System.err.printf(str, i, v2); if (li.sidedef == sides[li.linedef.sidenum[0]]) { li.v1 = lines[ml.linedef].v1; li.v2 = lines[ml.linedef].v2; } else { li.v1 = lines[ml.linedef].v2; li.v2 = lines[ml.linedef].v1; } } else { li.v1 = vertexes[v1]; li.v2 = vertexes[v2]; } li.assignVertexValues(); // e6y: now we can calculate it li.length = GetDistance(li.v2x - li.v1x, li.v2y - li.v1y); // Recalculate seg offsets that are sometimes incorrect // with certain nodebuilders. Fixes among others, line 20365 // of DV.wad, map 5 li.offset = GetOffset(li.v1, (ml.side != 0 ? ldef.v2 : ldef.v1)); } W.UnlockLumpNum(lump); // cph - release the data } private void P_LoadSegs_V4(int lump) { int i; mapseg_v4_t[] data; numsegs = W.LumpLength(lump) / mapseg_v4_t.sizeOf(); segs = (seg_t[]) calloc_IfSameLevel(segs, numsegs, seg_t.class); data = W.CacheLumpNumIntoArray(lump, numsegs, mapseg_v4_t.class); if ((data == null) || (numsegs == 0)) I.Error("P_LoadSegs_V4: no segs in level"); for (i = 0; i < numsegs; i++) { seg_t li = segs[i]; final mapseg_v4_t ml = data[i]; int v1, v2; int side, linedef; line_t ldef; li.iSegID = i; // proff 11/05/2000: needed for OpenGL v1 = ml.v1; v2 = ml.v2; li.miniseg = false; // figgi -- there are no minisegs in classic BSP // nodes li.angle = ml.angle << 16; li.offset = ml.offset << 16; linedef = ml.linedef; // e6y: check for wrong indexes if (unsigned(linedef) >= unsigned(numlines)) { I.Error( "P_LoadSegs_V4: seg %d references a non-existent linedef %d", i, unsigned(linedef)); } ldef = lines[linedef]; li.linedef = ldef; side = ml.side; // e6y: fix wrong side index if (side != 0 && side != 1) { System.err .printf( "P_LoadSegs_V4: seg %d contains wrong side index %d. Replaced with 1.\n", i, side); side = 1; } // e6y: check for wrong indexes if (unsigned(ldef.sidenum[side]) >= unsigned(numsides)) { I.Error( "P_LoadSegs_V4: linedef %d for seg %d references a non-existent sidedef %d", linedef, i, unsigned(ldef.sidenum[side])); } li.sidedef = sides[ldef.sidenum[side]]; /* * cph 2006/09/30 - our frontsector can be the second side of the * linedef, so must check for NO_INDEX in case we are incorrectly * referencing the back of a 1S line */ if (ldef.sidenum[side] != NO_INDEX) { li.frontsector = sides[ldef.sidenum[side]].sector; } else { li.frontsector = null; System.err.printf( "P_LoadSegs_V4: front of seg %i has no sidedef\n", i); } if (flags(ldef.flags, ML_TWOSIDED) && ldef.sidenum[side ^ 1] != NO_INDEX) li.backsector = sides[ldef.sidenum[side ^ 1]].sector; else li.backsector = null; // e6y // check and fix wrong references to non-existent vertexes // see e1m9 @ NIVELES.WAD // http://www.doomworld.com/idgames/index.php?id=12647 if (v1 >= numvertexes || v2 >= numvertexes) { String str = "P_LoadSegs_V4: compatibility loss - seg %d references a non-existent vertex %d\n"; if (DM.demorecording) { I.Error( (str + "Demo recording on levels with invalid nodes is not allowed"), i, (v1 >= numvertexes ? v1 : v2)); } if (v1 >= numvertexes) System.err.printf(str, i, v1); if (v2 >= numvertexes) System.err.printf(str, i, v2); if (li.sidedef == sides[li.linedef.sidenum[0]]) { li.v1 = lines[ml.linedef].v1; li.v2 = lines[ml.linedef].v2; } else { li.v1 = lines[ml.linedef].v2; li.v2 = lines[ml.linedef].v1; } } else { li.v1 = vertexes[v1]; li.v2 = vertexes[v2]; } // e6y: now we can calculate it li.length = GetDistance(li.v2.x - li.v1.x, li.v2.y - li.v1.y); // Recalculate seg offsets that are sometimes incorrect // with certain nodebuilders. Fixes among others, line 20365 // of DV.wad, map 5 li.offset = GetOffset(li.v1, (ml.side != 0 ? ldef.v2 : ldef.v1)); } W.UnlockLumpNum(lump); // cph - release the data } /******************************************* * Name : P_LoadGLSegs * created : 08/13/00 * modified : 09/18/00, adapted * for PrBoom * author : figgi * what : support for gl nodes * *******************************************/ /* * private void P_LoadGLSegs(int lump) { int i; final glseg_t ml; line_t * ldef; numsegs = W.LumpLength(lump) / sizeof(glseg_t); segs = * malloc_IfSameLevel(segs, numsegs * sizeof(seg_t)); memset(segs, 0, * numsegs * sizeof(seg_t)); ml = (final glseg_t*)W.CacheLumpNum(lump); if * ((!ml) || (!numsegs)) I_Error("P_LoadGLSegs: no glsegs in level"); for(i * = 0; i < numsegs; i++) { // check for gl-vertices segs[i].v1 = * &vertexes[checkGLVertex(LittleShort(ml.v1))]; segs[i].v2 = * &vertexes[checkGLVertex(LittleShort(ml.v2))]; segs[i].iSegID = i; * if(ml.linedef != (unsigned short)-1) // skip minisegs { ldef = * &lines[ml.linedef]; segs[i].linedef = ldef; segs[i].miniseg = false; * segs[i].angle = * R_PointToAngle2(segs[i].v1.x,segs[i].v1.y,segs[i].v2.x,segs[i].v2.y); * segs[i].sidedef = &sides[ldef.sidenum[ml.side]]; segs[i].length = * GetDistance(segs[i].v2.x - segs[i].v1.x, segs[i].v2.y - segs[i].v1.y); * segs[i].frontsector = sides[ldef.sidenum[ml.side]].sector; if (ldef.flags * & ML_TWOSIDED) segs[i].backsector = * sides[ldef.sidenum[ml.side^1]].sector; else segs[i].backsector = 0; if * (ml.side) segs[i].offset = GetOffset(segs[i].v1, ldef.v2); else * segs[i].offset = GetOffset(segs[i].v1, ldef.v1); } else { segs[i].miniseg * = true; segs[i].angle = 0; segs[i].offset = 0; segs[i].length = 0; * segs[i].linedef = NULL; segs[i].sidedef = NULL; segs[i].frontsector = * NULL; segs[i].backsector = NULL; } ml++; } W.UnlockLumpNum(lump); } */ // // P_LoadSubsectors // // killough 5/3/98: reformatted, cleaned up private void P_LoadSubsectors(int lump) { /* * cph 2006/07/29 - make data a final mapsubsector_t *, so the loop * below is simpler & gives no finalness warnings */ final mapsubsector_t[] data; int i; numsubsectors = W.LumpLength(lump) / mapsubsector_t.sizeOf(); subsectors = (subsector_t[]) calloc_IfSameLevel(subsectors, numsubsectors, subsector_t.class); data = W.CacheLumpNumIntoArray(lump, numsubsectors, mapsubsector_t.class); if ((data == null) || (numsubsectors == 0)) I.Error("P_LoadSubsectors: no subsectors in level"); for (i = 0; i < numsubsectors; i++) { // e6y: support for extended nodes subsectors[i].numlines = (char) data[i].numsegs; subsectors[i].firstline = (char) data[i].firstseg; } W.UnlockLumpNum(lump); // cph - release the data } private void P_LoadSubsectors_V4(int lump) { /* * cph 2006/07/29 - make data a final mapsubsector_t *, so the loop * below is simpler & gives no finalness warnings */ final mapsubsector_v4_t[] data; int i; numsubsectors = W.LumpLength(lump) / mapsubsector_v4_t.sizeOf(); subsectors = calloc_IfSameLevel(subsectors, numsubsectors, subsector_t.class); data = W.CacheLumpNumIntoArray(lump, numsubsectors, mapsubsector_v4_t.class); if ((data == null) || (numsubsectors == 0)) I.Error("P_LoadSubsectors_V4: no subsectors in level"); for (i = 0; i < numsubsectors; i++) { subsectors[i].numlines = (int) data[i].numsegs; subsectors[i].firstline = (int) data[i].firstseg; } W.UnlockLumpNum(lump); // cph - release the data } // // P_LoadSectors // // killough 5/3/98: reformatted, cleaned up private void P_LoadSectors(int lump) { final mapsector_t[] data; // cph - final* int i; numsectors = W.LumpLength(lump) / mapsector_t.sizeOf(); sectors = calloc_IfSameLevel(sectors, numsectors, sector_t.class); data = W.CacheLumpNumIntoArray(lump, numsectors, mapsector_t.class); // cph // - // wad // lump // handling // updated for (i = 0; i < numsectors; i++) { sector_t ss = sectors[i]; final mapsector_t ms = data[i]; ss.id = i; // proff 04/05/2000: needed for OpenGL ss.floorheight = ms.floorheight << FRACBITS; ss.ceilingheight = ms.ceilingheight << FRACBITS; ss.floorpic = (short) TM.FlatNumForName(ms.floorpic); ss.ceilingpic = (short) TM.FlatNumForName(ms.ceilingpic); ss.lightlevel = ms.lightlevel; ss.special = ms.special; // ss.oldspecial = ms.special; huh? ss.tag = ms.tag; ss.thinglist = null; // MAES: link to thinker list and RNG ss.TL = this.P; ss.RND = this.DM.RND; // ss.touching_thinglist = null; // phares 3/14/98 // ss.nextsec = -1; //jff 2/26/98 add fields to support locking out // ss.prevsec = -1; // stair retriggering until build completes // killough 3/7/98: // ss.floor_xoffs = 0; // ss.floor_yoffs = 0; // floor and ceiling flats offsets // ss.ceiling_xoffs = 0; // ss.ceiling_yoffs = 0; // ss.heightsec = -1; // sector used to get floor and ceiling height // ss.floorlightsec = -1; // sector used to get floor lighting // killough 3/7/98: end changes // killough 4/11/98 sector used to get ceiling lighting: // ss.ceilinglightsec = -1; // killough 4/4/98: colormaps: // ss.bottommap = ss.midmap = ss.topmap = 0; // killough 10/98: sky textures coming from sidedefs: // ss.sky = 0; } W.UnlockLumpNum(lump); // cph - release the data } // // P_LoadNodes // // killough 5/3/98: reformatted, cleaned up private void P_LoadNodes(int lump) { final mapnode_t[] data; // cph - final* int i; numnodes = W.LumpLength(lump) / mapnode_t.sizeOf(); nodes = (node_t[]) malloc_IfSameLevel(nodes, numnodes, node_t.class); data = W.CacheLumpNumIntoArray(lump, numnodes, mapnode_t.class); // cph // - // wad // lump // handling // updated if ((data == null) || (numnodes == 0)) { // allow trivial maps if (numsubsectors == 1) System.out .print("P_LoadNodes: trivial map (no nodes, one subsector)\n"); else I.Error("P_LoadNodes: no nodes in level"); } for (i = 0; i < numnodes; i++) { node_t no = nodes[i]; final mapnode_t mn = data[i]; int j; no.x = mn.x << FRACBITS; no.y = mn.y << FRACBITS; no.dx = mn.dx << FRACBITS; no.dy = mn.dy << FRACBITS; for (j = 0; j < 2; j++) { int k; // e6y: support for extended nodes no.children[j] = (char) mn.children[j]; // e6y: support for extended nodes if (no.children[j] == 0xFFFF) { no.children[j] = 0xFFFFFFFF; } else if (flags(no.children[j], NF_SUBSECTOR_CLASSIC)) { // Convert to extended type no.children[j] &= ~NF_SUBSECTOR_CLASSIC; // haleyjd 11/06/10: check for invalid subsector reference if (no.children[j] >= numsubsectors) { System.err .printf( "P_LoadNodes: BSP tree references invalid subsector %d.\n", no.children[j]); no.children[j] = 0; } no.children[j] |= NF_SUBSECTOR; } for (k = 0; k < 4; k++) no.bbox[j].set(k, mn.bbox[j][k] << FRACBITS); } } W.UnlockLumpNum(lump); // cph - release the data } private void P_LoadNodes_V4(int lump) { final DeepBSPNodesV4 data; // cph - final* int i; numnodes = (W.LumpLength(lump) - 8) / mapnode_v4_t.sizeOf(); nodes = (node_t[]) malloc_IfSameLevel(nodes, numnodes, node_t.class); data = (DeepBSPNodesV4) W.CacheLumpNum(lump, 0, DeepBSPNodesV4.class); // cph // - // wad // lump // handling // updated if ((data == null) || (numnodes == 0)) { // allow trivial maps if (numsubsectors == 1) System.out .print("P_LoadNodes_V4: trivial map (no nodes, one subsector)\n"); else I.Error("P_LoadNodes_V4: no nodes in level"); } for (i = 0; i < numnodes; i++) { node_t no = nodes[i]; final mapnode_v4_t mn = data.getNodes()[i]; int j; no.x = mn.x << FRACBITS; no.y = mn.y << FRACBITS; no.dx = mn.dx << FRACBITS; no.dy = mn.dy << FRACBITS; for (j = 0; j < 2; j++) { int k; no.children[j] = mn.children[j]; for (k = 0; k < 4; k++) no.bbox[j].bbox[k] = mn.bbox[j][k] << FRACBITS; } } W.UnlockLumpNum(lump); // cph - release the data } private void P_LoadZSegs(ByteBuffer data) throws IOException { int i; final mapseg_znod_t nodes[] = C2JUtils.createArrayOfObjects(mapseg_znod_t.class,numsegs); CacheableDoomObjectContainer.unpack(data,nodes); for (i = 0; i < numsegs; i++) { line_t ldef; int v1, v2; int linedef; char side; seg_t li = segs[i]; final mapseg_znod_t ml = nodes[i]; v1 = ml.v1; v2 = ml.v2; li.iSegID = i; // proff 11/05/2000: needed for OpenGL li.miniseg = false; linedef = (char) ml.linedef; // e6y: check for wrong indexes if (unsigned(linedef) >= unsigned(numlines)) { I.Error( "P_LoadZSegs: seg %d references a non-existent linedef %d", i, unsigned(linedef)); } ldef = lines[linedef]; li.linedef = ldef; side = (char) ml.side; // e6y: fix wrong side index if (side != 0 && side != 1) { System.err .printf( "P_LoadZSegs: seg %d contains wrong side index %d. Replaced with 1.\n", i, side); side = 1; } // e6y: check for wrong indexes if (unsigned(ldef.sidenum[side]) >= unsigned(numsides)) { I.Error( "P_LoadZSegs: linedef %d for seg %d references a non-existent sidedef %d", linedef, i, unsigned(ldef.sidenum[side])); } li.sidedef = sides[ldef.sidenum[side]]; /* * cph 2006/09/30 - our frontsector can be the second side of the * linedef, so must check for NO_INDEX in case we are incorrectly * referencing the back of a 1S line */ if (ldef.sidenum[side] != NO_INDEX) { li.frontsector = sides[ldef.sidenum[side]].sector; } else { li.frontsector = null; System.err.printf( "P_LoadZSegs: front of seg %i has no sidedef\n", i); } if (flags(ldef.flags, ML_TWOSIDED) && (ldef.sidenum[side ^ 1] != NO_INDEX)) li.backsector = sides[ldef.sidenum[side ^ 1]].sector; else li.backsector = null; li.v1 = vertexes[v1]; li.v2 = vertexes[v2]; li.length = GetDistance(li.v2.x - li.v1.x, li.v2.y - li.v1.y); li.offset = GetOffset(li.v1, (side != 0 ? ldef.v2 : ldef.v1)); li.angle = RendererState.PointToAngle(segs[i].v1.x, segs[i].v1.y, segs[i].v2.x, segs[i].v2.y); // li.angle = (int)((float)atan2(li.v2.y - li.v1.y,li.v2.x - // li.v1.x) * (ANG180 / M_PI)); } } private int CheckZNodesOverflow(int size, int count) { size -= count; if (size < 0) { I.Error("P_LoadZNodes: incorrect nodes"); } return size; } private void P_LoadZNodes(int lump, int glnodes) throws IOException { ByteBuffer data; int i, len; int header; // for debugging int orgVerts, newVerts; int numSubs, currSeg; int numSegs; int numNodes; vertex_t[] newvertarray = null; data = W.CacheLumpNumAsDoomBuffer(lump).getBuffer(); data.order(ByteOrder.LITTLE_ENDIAN); len = W.LumpLength(lump); // skip header len=CheckZNodesOverflow(len, 4); header=data.getInt(); // Read extra vertices added during node building len=CheckZNodesOverflow(len, 4); orgVerts = data.getInt(); len=CheckZNodesOverflow(len, 4); newVerts = data.getInt(); if (!samelevel) { if (orgVerts + newVerts == numvertexes) { newvertarray = vertexes; } else { newvertarray = C2JUtils.createArrayOfObjects(vertex_t.class, orgVerts + newVerts); System.arraycopy(vertexes, 0, newvertarray, 0, orgVerts); } //(sizeof(newvertarray[0].x) + sizeof(newvertarray[0].y)) len=CheckZNodesOverflow(len, newVerts * vertex_t.sizeOf()); z_vertex_t tmp=new z_vertex_t(); for (i = 0; i < newVerts; i++) { tmp.unpack(data); newvertarray[i + orgVerts].x=tmp.x; newvertarray[i + orgVerts].y=tmp.y; } // Extra vertexes read in if (vertexes != newvertarray) { for (i = 0; i < numlines; i++) { //lines[i].v1 = lines[i].v1 - vertexes + newvertarray; //lines[i].v2 = lines[i].v2 - vertexes + newvertarray; // Find indexes of v1 & v2 inside old vertexes array // (.v1-vertexes) and use that index to re-point inside newvertarray lines[i].v1=newvertarray[C2JUtils.indexOf(vertexes,lines[i].v1)]; lines[i].v2=newvertarray[C2JUtils.indexOf(vertexes,lines[i].v2)]; } // free(vertexes); vertexes = newvertarray; numvertexes = orgVerts + newVerts; } } else { // Skip the reading of all these new vertices and the expensive indexOf searches. int size = newVerts * z_vertex_t.sizeOf(); len=CheckZNodesOverflow(len, size); data.position(data.position()+ size); } // Read the subsectors len=CheckZNodesOverflow(len, 4); numSubs=data.getInt(); numsubsectors = numSubs; if (numsubsectors <= 0) I.Error("P_LoadZNodes: no subsectors in level"); subsectors = calloc_IfSameLevel(subsectors, numsubsectors,subsector_t.class); len=CheckZNodesOverflow(len, numSubs * mapsubsector_znod_t.sizeOf()); final mapsubsector_znod_t mseg=new mapsubsector_znod_t(); for (i = currSeg = 0; i < numSubs; i++) { mseg.unpack(data); subsectors[i].firstline = currSeg; subsectors[i].numlines = (int) mseg.numsegs; currSeg += mseg.numsegs; } // Read the segs len=CheckZNodesOverflow(len, 4); numSegs = data.getInt(); // The number of segs stored should match the number of // segs used by subsectors. if (numSegs != currSeg) { I.Error("P_LoadZNodes: Incorrect number of segs in nodes."); } numsegs = numSegs; segs = calloc_IfSameLevel(segs, numsegs, seg_t.class); if (glnodes == 0) { len=CheckZNodesOverflow(len, numsegs *mapseg_znod_t.sizeOf()); P_LoadZSegs(data); } else { //P_LoadGLZSegs (data, glnodes); I.Error("P_LoadZNodes: GL segs are not supported."); } // Read nodes len=CheckZNodesOverflow(len, 4); numNodes = data.getInt(); numnodes = numNodes; nodes = calloc_IfSameLevel(nodes, numNodes, node_t.class); len=CheckZNodesOverflow(len, numNodes * mapnode_znod_t.sizeOf()); mapnode_znod_t[] znodes=C2JUtils.createArrayOfObjects(mapnode_znod_t.class, numNodes); CacheableDoomObjectContainer.unpack(data,znodes); for (i = 0; i < numNodes; i++) { int j, k; node_t no = nodes[i]; final mapnode_znod_t mn = znodes[i]; no.x = mn.x << FRACBITS; no.y = mn.y << FRACBITS; no.dx = mn.dx<< FRACBITS; no.dy = mn.dy<< FRACBITS; for (j = 0; j < 2; j++) { no.children[j] = mn.children[j]; for (k = 0; k < 4; k++) no.bbox[j].bbox[k] = mn.bbox[j][k] << FRACBITS; } } W.UnlockLumpNum(lump); // cph - release the data } private boolean no_overlapped_sprites; private final int GETXY(mobj_t mobj) { return (mobj.x + (mobj.y >> 16)); } private final int dicmp_sprite_by_pos(final Object a, final Object b) { mobj_t m1 = (mobj_t) a; mobj_t m2 = (mobj_t) b; int res = GETXY(m2) - GETXY(m1); no_overlapped_sprites = no_overlapped_sprites && (res != 0); return res; } /* * P_LoadThings killough 5/3/98: reformatted, cleaned up cph 2001/07/07 - * don't write into the lump cache, especially non-idepotent changes like * byte order reversals. Take a copy to edit. */ private void P_LoadThings(int lump) { int i, numthings = W.LumpLength(lump) / mapthing_t.sizeOf(); final mapthing_t[] data = W.CacheLumpNumIntoArray(lump, numthings, mapthing_t.class); mobj_t mobj; int mobjcount = 0; mobj_t[] mobjlist = C2JUtils.createArrayOfObjects(mobj_t.class, numthings); if ((data == null) || (numthings == 0)) I.Error("P_LoadThings: no things in level"); for (i = 0; i < numthings; i++) { mapthing_t mt = data[i]; /* * Not needed. Handled during unmarshaling. mt.x = * LittleShort(mt.x); mt.y = LittleShort(mt.y); mt.angle = * LittleShort(mt.angle); mt.type = LittleShort(mt.type); mt.options * = LittleShort(mt.options); */ if (!P_IsDoomnumAllowed(mt.type)) continue; // Do spawn all other stuff. mobj = P.SpawnMapThing(mt/* , i */); if (mobj != null && mobj.info.speed == 0) mobjlist[mobjcount++] = mobj; } W.UnlockLumpNum(lump); // cph - release the data /* * #ifdef GL_DOOM if (V_GetMode() == VID_MODEGL) { no_overlapped_sprites * = true; qsort(mobjlist, mobjcount, sizeof(mobjlist[0]), * dicmp_sprite_by_pos); if (!no_overlapped_sprites) { i = 1; while (i < * mobjcount) { mobj_t *m1 = mobjlist[i - 1]; mobj_t *m2 = mobjlist[i - * 0]; if (GETXY(m1) == GETXY(m2)) { mobj_t *mo = (m1.index < m2.index ? * m1 : m2); i++; while (i < mobjcount && GETXY(mobjlist[i]) == * GETXY(m1)) { if (mobjlist[i].index < mo.index) { mo = mobjlist[i]; } * i++; } // 'nearest' mo.flags |= MF_FOREGROUND; } i++; } } } #endif */ } /* * P_IsDoomnumAllowed() Based on code taken from P_LoadThings() in * src/p_setup.c Return TRUE if the thing in question is expected to be * available in the gamemode used. */ boolean P_IsDoomnumAllowed(int doomnum) { // Do not spawn cool, new monsters if !commercial if (!DM.isCommercial()) switch (doomnum) { case 64: // Archvile case 65: // Former Human Commando case 66: // Revenant case 67: // Mancubus case 68: // Arachnotron case 69: // Hell Knight case 71: // Pain Elemental case 84: // Wolf SS case 88: // Boss Brain case 89: // Boss Shooter return false; } return true; } // // P_LoadLineDefs // Also counts secret lines for intermissions. // ^^^ // ??? killough ??? // Does this mean secrets used to be linedef-based, rather than // sector-based? // // killough 4/4/98: split into two functions, to allow sidedef overloading // // killough 5/3/98: reformatted, cleaned up private void P_LoadLineDefs(int lump) { final maplinedef_t[] data; // cph - final* int i; numlines = W.LumpLength(lump) / maplinedef_t.sizeOf(); lines = calloc_IfSameLevel(lines, numlines, line_t.class); data = W.CacheLumpNumIntoArray(lump, numlines, maplinedef_t.class); // cph // - // wad // lump // handling // updated for (i = 0; i < numlines; i++) { final maplinedef_t mld = data[i]; line_t ld = lines[i]; ld.id=i; vertex_t v1, v2; ld.flags = mld.flags; ld.special = mld.special; ld.tag = mld.tag; v1 = ld.v1 = vertexes[(char) mld.v1]; v2 = ld.v2 = vertexes[(char) mld.v2]; ld.dx = v2.x - v1.x; ld.dy = v2.y - v1.y; // Maes: map value semantics. ld.assignVertexValues(); /* * #ifdef GL_DOOM // e6y // Rounding the wall length to the nearest * integer // when determining length instead of always rounding * down // There is no more glitches on seams between identical * textures. ld.texel_length = GetTexelDistance(ld.dx, ld.dy); * #endif */ ld.tranlump = -1; // killough 4/11/98: no translucency by default ld.slopetype = (ld.dx == 0) ? slopetype_t.ST_VERTICAL : (ld.dy == 0) ? slopetype_t.ST_HORIZONTAL : fixed_t.FixedDiv(ld.dy, ld.dx) > 0 ? slopetype_t.ST_POSITIVE : slopetype_t.ST_NEGATIVE; if (v1.x < v2.x) { ld.bbox[BBox.BOXLEFT] = v1.x; ld.bbox[BBox.BOXRIGHT] = v2.x; } else { ld.bbox[BBox.BOXLEFT] = v2.x; ld.bbox[BBox.BOXRIGHT] = v1.x; } if (v1.y < v2.y) { ld.bbox[BBox.BOXBOTTOM] = v1.y; ld.bbox[BBox.BOXTOP] = v2.y; } else { ld.bbox[BBox.BOXBOTTOM] = v2.y; ld.bbox[BBox.BOXTOP] = v1.y; } /* calculate sound origin of line to be its midpoint */ // e6y: fix sound origin for large levels // no need for comp_sound test, these are only used when comp_sound // = 0 ld.soundorg = new degenmobj_t(ld.bbox[BBox.BOXLEFT] / 2 + ld.bbox[BBox.BOXRIGHT] / 2, ld.bbox[BBox.BOXTOP] / 2 + ld.bbox[BBox.BOXBOTTOM] / 2, 0); // TODO // ld.iLineID=i; // proff 04/05/2000: needed for OpenGL ld.sidenum[0] = mld.sidenum[0]; ld.sidenum[1] = mld.sidenum[1]; { /* * cph 2006/09/30 - fix sidedef errors right away. cph * 2002/07/20 - these errors are fatal if not fixed, so apply * them in compatibility mode - a desync is better than a crash! */ int j; for (j = 0; j < 2; j++) { if (ld.sidenum[j] != NO_INDEX && ld.sidenum[j] >= numsides) { ld.sidenum[j] = NO_INDEX; System.err .printf( "P_LoadLineDefs: linedef %d has out-of-range sidedef number\n", numlines - i - 1); } } // killough 11/98: fix common wad errors (missing sidedefs): if (ld.sidenum[0] == NO_INDEX) { ld.sidenum[0] = 0; // Substitute dummy sidedef for missing // right side // cph - print a warning about the bug System.err.printf( "P_LoadLineDefs: linedef %d missing first sidedef\n", numlines - i - 1); } if ((ld.sidenum[1] == NO_INDEX) && flags(ld.flags, ML_TWOSIDED)) { // e6y // ML_TWOSIDED flag shouldn't be cleared for compatibility // purposes // see CLNJ-506.LMP at http://doomedsda.us/wad1005.html // TODO: we don't really care, but still... // if (!demo_compatibility || // !overflows[OVERFLOW.MISSEDBACKSIDE].emulate) // { ld.flags &= ~ML_TWOSIDED; // Clear 2s flag for missing left // side // } // Mark such lines and do not draw them only in // demo_compatibility, // because Boom's behaviour is different // See OTTAWAU.WAD E1M1, sectors 226 and 300 // http://www.doomworld.com/idgames/index.php?id=1651 // TODO ehhh? // ld.r_flags = RF_IGNORE_COMPAT; // cph - print a warning about the bug System.err .printf( "P_LoadLineDefs: linedef %d has two-sided flag set, but no second sidedef\n", numlines - i - 1); } } // killough 4/4/98: support special sidedef interpretation below // TODO: // if (ld.sidenum[0] != NO_INDEX && ld.special!=0) // sides[(ld.sidenum[0]<<16)& (0x0000FFFF&ld.sidenum[1])].special = // ld.special; } W.UnlockLumpNum(lump); // cph - release the lump } // killough 4/4/98: delay using sidedefs until they are loaded // killough 5/3/98: reformatted, cleaned up private void P_LoadLineDefs2(int lump) { line_t ld; for (int i = 0; i < numlines; i++) { ld = lines[i]; ld.frontsector = sides[ld.sidenum[0]].sector; // e6y: Can't be // NO_INDEX here ld.backsector = ld.sidenum[1] != NO_INDEX ? sides[ld.sidenum[1]].sector : null; switch (ld.special) { // killough 4/11/98: handle special types case 260: // killough 4/11/98: translucent 2s textures // TODO: transparentpresent = true;//e6y // int lmp = sides[ld.getSpecialSidenum()].special; // // translucency from sidedef // if (!ld.tag) // if tag==0, // ld.tranlump = lmp; // affect this linedef only // else // for (int j=0;j<numlines;j++) // if tag!=0, // if (lines[j].tag == ld.tag) // affect all matching linedefs // lines[j].tranlump = lump; // break; } } } // // P_LoadSideDefs // // killough 4/4/98: split into two functions private void P_LoadSideDefs(int lump) { numsides = W.LumpLength(lump) / mapsidedef_t.sizeOf(); sides = calloc_IfSameLevel(sides, numsides, side_t.class); } // killough 4/4/98: delay using texture names until // after linedefs are loaded, to allow overloading. // killough 5/3/98: reformatted, cleaned up private void P_LoadSideDefs2(int lump) { // cph - final*, wad lump handling updated final mapsidedef_t[] data = W.CacheLumpNumIntoArray(lump, numsides, mapsidedef_t.class); int i; for (i = 0; i < numsides; i++) { final mapsidedef_t msd = data[i]; side_t sd = sides[i]; sector_t sec; sd.textureoffset = msd.textureoffset << FRACBITS; sd.rowoffset = msd.rowoffset << FRACBITS; { /* * cph 2006/09/30 - catch out-of-range sector numbers; use sector * 0 instead */ char sector_num = (char) msd.sector; if (sector_num >= numsectors) { System.err .printf( "P_LoadSideDefs2: sidedef %i has out-of-range sector num %u\n", i, sector_num); sector_num = 0; } sd.sector = sec = sectors[sector_num]; } // killough 4/4/98: allow sidedef texture names to be overloaded // killough 4/11/98: refined to allow colormaps to work as wall // textures if invalid as colormaps but valid as textures. switch (sd.special) { case 242: // variable colormap via 242 linedef /* * sd.bottomtexture = (sec.bottommap = * R.ColormapNumForName(msd.bottomtexture)) < 0 ? sec.bottommap * = 0, R.TextureNumForName(msd.bottomtexture): 0 ; * sd.midtexture = (sec.midmap = * R.ColormapNumForName(msd.midtexture)) < 0 ? sec.midmap = 0, * R.TextureNumForName(msd.midtexture) : 0 ; sd.toptexture = * (sec.topmap = R.ColormapNumForName(msd.toptexture)) < 0 ? * sec.topmap = 0, R.TextureNumForName(msd.toptexture) : 0 ; */ break; case 260: // killough 4/11/98: apply translucency to 2s normal // texture if (msd.midtexture.compareToIgnoreCase("TRANMAP") == 0) { if ((sd.special = W.CheckNumForName(msd.midtexture)) < 0 || W.LumpLength(sd.special) != 65536) { sd.special = 0; sd.midtexture = (short) TM.TextureNumForName(msd.midtexture); } else { sd.special++; sd.midtexture = 0; } } else sd.midtexture = (short) (sd.special = 0); sd.toptexture = (short) TM.TextureNumForName(msd.toptexture); sd.bottomtexture = (short) TM.TextureNumForName(msd.bottomtexture); break; /* * #ifdef GL_DOOM case 271: case 272: if * (R_CheckTextureNumForName(msd.toptexture) == -1) { * sd.skybox_index = R_BoxSkyboxNumForName(msd.toptexture); } #endif */ default: // normal cases // TODO: Boom uses "SafeTextureNumForName" here. Find out what // it does. sd.midtexture = (short) TM.CheckTextureNumForName(msd.midtexture); sd.toptexture = (short) TM.CheckTextureNumForName(msd.toptexture); sd.bottomtexture = (short) TM.CheckTextureNumForName(msd.bottomtexture); break; } } W.UnlockLumpNum(lump); // cph - release the lump } // // P_LoadBlockMap // // killough 3/1/98: substantially modified to work // towards removing blockmap limit (a wad limitation) // // killough 3/30/98: Rewritten to remove blockmap limit, // though current algorithm is brute-force and unoptimal. // private void P_LoadBlockMap(int lump) throws IOException { int count = 0; if (DM.CM.CheckParmBool("-blockmap") || W.LumpLength(lump) < 8 || (count = W.LumpLength(lump) / 2) >= 0x10000) // e6y CreateBlockMap(); else { int i; // cph - final*, wad lump handling updated final char[] wadblockmaplump; DoomBuffer data = (DoomBuffer) W.CacheLumpNum(lump, PU_LEVEL, DoomBuffer.class); count = W.LumpLength(lump) / 2; wadblockmaplump = new char[count]; data.setOrder(ByteOrder.LITTLE_ENDIAN); data.rewind(); data.readCharArray(wadblockmaplump, count); if (!samelevel) // Reallocate if required. blockmaplump = new int[count]; // killough 3/1/98: Expand wad blockmap into larger internal one, // by treating all offsets except -1 as unsigned and zero-extending // them. This potentially doubles the size of blockmaps allowed, // because Doom originally considered the offsets as always signed. blockmaplump[0] = wadblockmaplump[0]; blockmaplump[1] = wadblockmaplump[1]; blockmaplump[2] = (int) (wadblockmaplump[2] & 0xffff); blockmaplump[3] = (int) (wadblockmaplump[3] & 0xffff); for (i = 4; i < count; i++) { short t = (short) wadblockmaplump[i]; // killough 3/1/98 blockmaplump[i] = (int) (t == -1 ? -1l : t & 0xffff); } W.UnlockLumpNum(lump); // cph - unlock the lump bmaporgx = blockmaplump[0] << FRACBITS; bmaporgy = blockmaplump[1] << FRACBITS; bmapwidth = blockmaplump[2]; bmapheight = blockmaplump[3]; // haleyjd 03/04/10: check for blockmap problems // http://www.doomworld.com/idgames/index.php?id=12935 if (!VerifyBlockMap(count)) { System.err .printf("P_LoadBlockMap: erroneous BLOCKMAP lump may cause crashes.\n"); System.err .printf("P_LoadBlockMap: use \"-blockmap\" command line switch for rebuilding\n"); } } // MAES: blockmap was generated, rather than loaded. if (count == 0) count = blockmaplump.length - 4; // clear out mobj chains - CPhipps - use calloc // blocklinks = calloc_IfSameLevel(blocklinks, bmapwidth * // bmapheight.mobj_t.); // clear out mobj chains // ATTENTION! BUG!!! // If blocklinks are "cleared" to void -but instantiated- objects, // very bad bugs happen, especially the second time a level is // re-instantiated. // Probably caused other bugs as well, as an extra object would appear // in iterators. if (blocklinks != null && samelevel) for (int i = 0; i < bmapwidth * bmapheight; i++) blocklinks[i] = null; else blocklinks = new mobj_t[bmapwidth * bmapheight]; // IMPORTANT MODIFICATION: no need to have both blockmaplump AND // blockmap. // If the offsets in the lump are OK, then we can modify them (remove 4) // and copy the rest of the data in one single data array. This avoids // reserving memory for two arrays (we can't simply alias one in Java) blockmap = new int[blockmaplump.length - 4]; count = bmapwidth * bmapheight; // Offsets are relative to START OF BLOCKMAP, and IN SHORTS, not bytes. for (int i = 0; i < blockmaplump.length - 4; i++) { // Modify indexes so that we don't need two different lumps. // Can probably be further optimized if we simply shift everything // backwards. // and reuse the same memory space. if (i < count) blockmaplump[i] = blockmaplump[i + 4] - 4; else blockmaplump[i] = blockmaplump[i + 4]; } // MAES: set blockmapxneg and blockmapyneg // E.g. for a full 512x512 map, they should be both // -1. For a 257*257, they should be both -255 etc. if (bmapwidth>255) blockmapxneg= bmapwidth-512; if (bmapheight>255) blockmapyneg= bmapheight-512; blockmap = blockmaplump; } // // P_LoadReject - load the reject table // private void P_LoadReject(int lumpnum, int totallines) { // dump any old cached reject lump, then cache the new one if (rejectlump != -1) W.UnlockLumpNum(rejectlump); rejectlump = lumpnum + ML_REJECT; rejectmatrix = W.CacheLumpNumAsRawBytes(rejectlump, 0); // e6y: check for overflow // TODO: g.Overflow.RejectOverrun(rejectlump, rejectmatrix, // totallines,numsectors); } // // P_GroupLines // Builds sector line lists and subsector sector numbers. // Finds block bounding boxes for sectors. // // killough 5/3/98: reformatted, cleaned up // cph 18/8/99: rewritten to avoid O(numlines * numsectors) section // It makes things more complicated, but saves seconds on big levels // figgi 09/18/00 -- adapted for gl-nodes // modified to return totallines (needed by P_LoadReject) private int P_GroupLines() { line_t li; sector_t sector; int i, j, total = numlines; // figgi for (i = 0; i < numsubsectors; i++) { int seg = subsectors[i].firstline; subsectors[i].sector = null; for (j = 0; j < subsectors[i].numlines; j++) { if (segs[seg].sidedef != null) { subsectors[i].sector = segs[seg].sidedef.sector; break; } seg++; } if (subsectors[i].sector == null) I.Error("P_GroupLines: Subsector a part of no sector!\n"); } // count number of lines in each sector for (i = 0; i < numlines; i++) { li = lines[i]; li.frontsector.linecount++; if (li.backsector != null && (li.backsector != li.frontsector)) { li.backsector.linecount++; total++; } } // allocate line tables for each sector // line_t[] linebuffer = // C2JUtils.createArrayOfObjects(line_t.class,total); // e6y: REJECT overrun emulation code // moved to P_LoadReject for (i = 0; i < numsectors; i++) { sector = sectors[i]; sector.lines = C2JUtils.createArrayOfObjects(line_t.class, sector.linecount); // linebuffer += sector.linecount; sector.linecount = 0; BBox.ClearBox(sector.blockbox); } // Enter those lines for (i = 0; i < numlines; i++) { li = lines[i]; AddLineToSector(li, li.frontsector); if (li.backsector != null && li.backsector != li.frontsector) AddLineToSector(li, li.backsector); } for (i = 0; i < numsectors; i++) { sector = sectors[i]; int[] bbox = sector.blockbox; // cph - For convenience, so // I can sue the old code unchanged int block; // set the degenmobj_t to the middle of the bounding box // TODO if (true/* comp[comp_sound] */) { sector.soundorg = new degenmobj_t((bbox[BOXRIGHT] + bbox[BOXLEFT]) / 2, (bbox[BOXTOP] + bbox[BOXBOTTOM]) / 2); } else { // e6y: fix sound origin for large levels sector.soundorg = new degenmobj_t((bbox[BOXRIGHT] / 2 + bbox[BOXLEFT] / 2), bbox[BOXTOP] / 2 + bbox[BOXBOTTOM] / 2); } // adjust bounding box to map blocks block =getSafeBlockY(bbox[BOXTOP] - bmaporgy + Limits.MAXRADIUS); block = block >= bmapheight ? bmapheight - 1 : block; sector.blockbox[BOXTOP] = block; block =getSafeBlockY(bbox[BOXBOTTOM] - bmaporgy - Limits.MAXRADIUS); block = block < 0 ? 0 : block; sector.blockbox[BOXBOTTOM] = block; block =getSafeBlockX(bbox[BOXRIGHT] - bmaporgx + Limits.MAXRADIUS); block = block >= bmapwidth ? bmapwidth - 1 : block; sector.blockbox[BOXRIGHT] = block; block =getSafeBlockX(bbox[BOXLEFT] - bmaporgx - Limits.MAXRADIUS); block = block < 0 ? 0 : block; sector.blockbox[BOXLEFT] = block; } return total; // this value is needed by the reject overrun emulation // code } // // killough 10/98 // // Remove slime trails. // // Slime trails are inherent to Doom's coordinate system -- i.e. there is // nothing that a node builder can do to prevent slime trails ALL of the // time, // because it's a product of the integer coodinate system, and just because // two lines pass through exact integer coordinates, doesn't necessarily // mean // that they will intersect at integer coordinates. Thus we must allow for // fractional coordinates if we are to be able to split segs with node // lines, // as a node builder must do when creating a BSP tree. // // A wad file does not allow fractional coordinates, so node builders are // out // of luck except that they can try to limit the number of splits (they // might // also be able to detect the degree of roundoff error and try to avoid // splits // with a high degree of roundoff error). But we can use fractional // coordinates // here, inside the engine. It's like the difference between square inches // and // square miles, in terms of granularity. // // For each vertex of every seg, check to see whether it's also a vertex of // the linedef associated with the seg (i.e, it's an endpoint). If it's not // an endpoint, and it wasn't already moved, move the vertex towards the // linedef by projecting it using the law of cosines. Formula: // // 2 2 2 2 // dx x0 + dy x1 + dx dy (y0 - y1) dy y0 + dx y1 + dx dy (x0 - x1) // {---------------------------------, ---------------------------------} // 2 2 2 2 // dx + dy dx + dy // // (x0,y0) is the vertex being moved, and (x1,y1)-(x1+dx,y1+dy) is the // reference linedef. // // Segs corresponding to orthogonal linedefs (exactly vertical or horizontal // linedefs), which comprise at least half of all linedefs in most wads, // don't // need to be considered, because they almost never contribute to slime // trails // (because then any roundoff error is parallel to the linedef, which // doesn't // cause slime). Skipping simple orthogonal lines lets the code finish // quicker. // // Please note: This section of code is not interchangable with TeamTNT's // code which attempts to fix the same problem. // // Firelines (TM) is a Rezistered Trademark of MBF Productions // private void P_RemoveSlimeTrails() // killough 10/98 { // Hitlist for vertices boolean[] hit = new boolean[numvertexes]; // Searchlist for int i; for (i = 0; i < numsegs; i++) // Go through each seg { final line_t l; if (segs[i].miniseg == true) // figgi -- skip minisegs return; l = segs[i].linedef; // The parent linedef if (l.dx != 0 && l.dy != 0) // We can ignore orthogonal lines { vertex_t v = segs[i].v1; do { int index = C2JUtils.indexOf(vertexes, v); if (!hit[index]) // If we haven't processed vertex { hit[index] = true; // Mark this vertex as processed if (v != l.v1 && v != l.v2) // Exclude endpoints of // linedefs { // Project the vertex back onto the parent linedef long dx2 = (l.dx >> FRACBITS) * (l.dx >> FRACBITS); long dy2 = (l.dy >> FRACBITS) * (l.dy >> FRACBITS); long dxy = (l.dx >> FRACBITS) * (l.dy >> FRACBITS); long s = dx2 + dy2; int x0 = v.x, y0 = v.y, x1 = l.v1.x, y1 = l.v1.y; v.x = (int) ((dx2 * x0 + dy2 * x1 + dxy * (y0 - y1)) / s); v.y = (int) ((dy2 * y0 + dx2 * y1 + dxy * (x0 - x1)) / s); } } // Obsfucated C contest entry: :) } while ((v != segs[i].v2) && ((v = segs[i].v2) != null)); } // Assign modified vertex values. l.assignVertexValues(); } } // // P_CheckLumpsForSameSource // // Are these lumps in the same wad file? // boolean P_CheckLumpsForSameSource(int lump1, int lump2) { int wad1_index, wad2_index; wadfile_info_t wad1, wad2; if ((unsigned(lump1) >= unsigned(W.NumLumps())) || (unsigned(lump2) >= unsigned(W.NumLumps()))) return false; wad1 = W.GetLumpInfo(lump1).wadfile; wad2 = W.GetLumpInfo(lump2).wadfile; if (wad1 == null || wad2 == null) return false; wad1_index = W.GetWadfileIndex(wad1); wad2_index = W.GetWadfileIndex(wad2); if (wad1_index != wad2_index) return false; if ((wad1_index < 0) || (wad1_index >= W.GetNumWadfiles())) return false; if ((wad2_index < 0) || (wad2_index >= W.GetNumWadfiles())) return false; return true; } private static final String[] ml_labels = { "ML_LABEL", // A separator, // name, ExMx or // MAPxx "ML_THINGS", // Monsters, items.. "ML_LINEDEFS", // LineDefs, from editing "ML_SIDEDEFS", // SideDefs, from editing "ML_VERTEXES", // Vertices, edited and BSP splits generated "ML_SEGS", // LineSegs, from LineDefs split by BSP "ML_SSECTORS", // SubSectors, list of LineSegs "ML_NODES", // BSP nodes "ML_SECTORS", // Sectors, from editing "ML_REJECT", // LUT, sector-sector visibility "ML_BLOCKMAP", // LUT, motion clipping, walls/grid element }; private static final boolean GL_DOOM = false; // // P_CheckLevelFormat // // Checking for presence of necessary lumps // void P_CheckLevelWadStructure(final String mapname) { int i, lumpnum; if (mapname == null) { I.Error("P_SetupLevel: Wrong map name"); } lumpnum = W.CheckNumForName(mapname.toUpperCase()); if (lumpnum < 0) { I.Error("P_SetupLevel: There is no %s map.", mapname); } for (i = ML_THINGS + 1; i <= ML_SECTORS; i++) { if (!P_CheckLumpsForSameSource(lumpnum, lumpnum + i)) { I.Error( "P_SetupLevel: Level wad structure is incomplete. There is no %s lump. (%s)", ml_labels[i], W.GetNameForLump(lumpnum)); } } // refuse to load Hexen-format maps, avoid segfaults i = lumpnum + ML_BLOCKMAP + 1; if (P_CheckLumpsForSameSource(lumpnum, i)) { if (W.GetLumpInfo(i).name.compareToIgnoreCase("BEHAVIOR") == 0) { I.Error("P_SetupLevel: %s: Hexen format not supported", mapname); } } } // // P_SetupLevel // // killough 5/3/98: reformatted, cleaned up public void SetupLevel(int episode, int map, int playermask, skill_t skill) throws IOException { int i; String lumpname; int lumpnum; String gl_lumpname; int gl_lumpnum; // e6y DM.totallive = 0; // TODO: transparentpresent = false; // R_StopAllInterpolations(); DM.totallive = DM.totalkills = DM.totalitems = DM.totalsecret = DM.wminfo.maxfrags = 0; DM.wminfo.partime = 180; for (i = 0; i < Limits.MAXPLAYERS; i++) { DM.players[i].killcount = DM.players[i].secretcount = DM.players[i].itemcount = 0; // TODO DM.players[i].resurectedkillcount = 0;//e6y } // Initial height of PointOfView will be set by player think. DM.players[DM.consoleplayer].viewz = 1; // Make sure all sounds are stopped before Z_FreeTags. S.Start(); // Z_FreeTags(PU_LEVEL, PU_PURGELEVEL-1); if (rejectlump != -1) { // cph - unlock the reject table W.UnlockLumpNum(rejectlump); rejectlump = -1; } P.InitThinkers(); // if working with a devlopment map, reload it // W.Reload (); killough 1/31/98: W.Reload obsolete // find map name if (DM.isCommercial()) { lumpname = String.format("map%02d", map); // killough 1/24/98: // simplify gl_lumpname = String.format("gl_map%02d", map); // figgi } else { lumpname = String.format("E%dM%d", episode, map); // killough // 1/24/98: // simplify gl_lumpname = String.format("GL_E%dM%d", episode, map); // figgi } lumpnum = W.GetNumForName(lumpname); gl_lumpnum = W.CheckNumForName(gl_lumpname); // figgi // e6y // Refuse to load a map with incomplete pwad structure. // Avoid segfaults on levels without nodes. P_CheckLevelWadStructure(lumpname); DM.leveltime = 0; DM.totallive = 0; // note: most of this ordering is important // killough 3/1/98: P_LoadBlockMap call moved down to below // killough 4/4/98: split load of sidedefs into two parts, // to allow texture names to be used in special linedefs // figgi 10/19/00 -- check for gl lumps and load them P_GetNodesVersion(lumpnum, gl_lumpnum); // e6y: speedup of level reloading // Most of level's structures now are allocated with PU_STATIC instead // of PU_LEVEL // It is important for OpenGL, because in case of the same data in // memory // we can skip recalculation of much stuff samelevel = (map == current_map) && (episode == current_episode) && (nodesVersion == current_nodesVersion); current_episode = episode; current_map = map; current_nodesVersion = nodesVersion; if (!samelevel) { /* * if (GL_DOOM){ // proff 11/99: clean the memory from textures etc. * gld_CleanMemory(); } */ // free(segs); // free(nodes); // free(subsectors); /* * #ifdef GL_DOOM free(map_subsectors); #endif */ // free(blocklinks); // free(blockmaplump); // free(lines); // free(sides); // free(sectors); // free(vertexes); } if (nodesVersion > 0) this.P_LoadVertexes2(lumpnum + ML_VERTEXES, gl_lumpnum + ML_GL_VERTS); else P_LoadVertexes(lumpnum + ML_VERTEXES); P_LoadSectors(lumpnum + ML_SECTORS); P_LoadSideDefs(lumpnum + ML_SIDEDEFS); P_LoadLineDefs(lumpnum + ML_LINEDEFS); P_LoadSideDefs2(lumpnum + ML_SIDEDEFS); P_LoadLineDefs2(lumpnum + ML_LINEDEFS); // e6y: speedup of level reloading // Do not reload BlockMap for same level, // because in case of big level P_CreateBlockMap eats much time if (!samelevel) { P_LoadBlockMap(lumpnum + ML_BLOCKMAP); } else { // clear out mobj chains if (blocklinks != null && blocklinks.length == bmapwidth * bmapheight) { for (i = 0; i < bmapwidth * bmapheight; i++) { blocklinks[i] = null; } } else blocklinks = C2JUtils.createArrayOfObjects(mobj_t.class, bmapwidth * bmapheight); } if (nodesVersion > 0) { P_LoadSubsectors(gl_lumpnum + ML_GL_SSECT); P_LoadNodes(gl_lumpnum + ML_GL_NODES); // TODO: P_LoadGLSegs(gl_lumpnum + ML_GL_SEGS); } else { if (P_CheckForZDoomUncompressedNodes(lumpnum, gl_lumpnum)) { P_LoadZNodes(lumpnum + ML_NODES, 0); } else if (P_CheckForDeePBSPv4Nodes(lumpnum, gl_lumpnum)) { P_LoadSubsectors_V4(lumpnum + ML_SSECTORS); P_LoadNodes_V4(lumpnum + ML_NODES); P_LoadSegs_V4(lumpnum + ML_SEGS); } else { P_LoadSubsectors(lumpnum + ML_SSECTORS); P_LoadNodes(lumpnum + ML_NODES); P_LoadSegs(lumpnum + ML_SEGS); } } /* * if (GL_DOOM){ map_subsectors = calloc_IfSameLevel(map_subsectors, * numsubsectors); } */ // reject loading and underflow padding separated out into new function // P_GroupLines modified to return a number the underflow padding needs // P_LoadReject(lumpnum, P_GroupLines()); P_GroupLines(); super.LoadReject(lumpnum+ML_REJECT); // e6y // Correction of desync on dv04-423.lmp/dv.wad // http://www.doomworld.com/vb/showthread.php?s=&postid=627257#post627257 // if (DoomStatus.compatibility_level>=lxdoom_1_compatibility || // Compatibility.prboom_comp[PC.PC_REMOVE_SLIME_TRAILS.ordinal()].state) P_RemoveSlimeTrails(); // killough 10/98: remove slime trails from wad // Note: you don't need to clear player queue slots -- // a much simpler fix is in g_game.c -- killough 10/98 DM.bodyqueslot = 0; /* cph - reset all multiplayer starts */ for (i = 0; i < playerstarts.length; i++) { DM.playerstarts[i] = null; } deathmatch_p = 0; for (i = 0; i < Limits.MAXPLAYERS; i++) DM.players[i].mo = null; // TODO: TracerClearStarts(); // Hmm? P_MapStart(); P_LoadThings(lumpnum + ML_THINGS); // if deathmatch, randomly spawn the active players if (DM.deathmatch) { for (i = 0; i < Limits.MAXPLAYERS; i++) if (DM.playeringame[i]) { DM.players[i].mo = null; // not needed? - done before // P_LoadThings DM.DG.DeathMatchSpawnPlayer(i); } } else // if !deathmatch, check all necessary player starts actually // exist { for (i = 0; i < Limits.MAXPLAYERS; i++) if (DM.playeringame[i] && !C2JUtils.eval(DM.players[i].mo)) I.Error("P_SetupLevel: missing player %d start\n", i + 1); } // killough 3/26/98: Spawn icon landings: // TODO: if (DM.isCommercial()) // P.SpawnBrainTargets(); if (!DM.isShareware()) { // TODO: S.ParseMusInfo(lumpname); } // clear special respawning que P.iquehead = P.iquetail = 0; // set up world state P.SpawnSpecials(); // TODO: P.MapEnd(); // preload graphics if (DM.precache) TM.PrecacheLevel(); // MAES: thinkers are separate than texture management. Maybe split // sprite management as well? R.PreCacheThinkers(); /* * if (GL_DOOM){ if (V_GetMode() == VID_MODEGL) { // e6y // Do not * preprocess GL data during skipping, // because it potentially will * not be used. // But preprocessing must be called immediately after * stop of skipping. if (!doSkip) { // proff 11/99: calculate all OpenGL * specific tables etc. gld_PreprocessLevel(); } } } */ // e6y // TODO P_SyncWalkcam(true, true); // TODO R_SmoothPlaying_Reset(NULL); } } package p; public enum sd_e { sd_opening, sd_waiting, sd_closing } package p; import java.io.DataInputStream; import java.io.IOException; import java.nio.ByteBuffer; import rr.SectorAction; import rr.sector_t; import w.DoomIO; import w.IReadableDoomObject; public class plat_t extends SectorAction implements IReadableDoomObject{ public sector_t sector; /** fixed_t */ public int speed,low,high; int wait; int count; public plat_e status; public plat_e oldstatus; boolean crush; public int tag; public plattype_e type; public plat_t (){ // These must never be null so they get the lowest ordinal value. // by default. this.status=plat_e.up; this.oldstatus=plat_e.up; } @Override public void read(DataInputStream f) throws IOException{ super.read(f); // Call thinker reader first super.sectorid=DoomIO.readLEInt(f); // Sector index speed=DoomIO.readLEInt(f); low=DoomIO.readLEInt(f); high=DoomIO.readLEInt(f); wait=DoomIO.readLEInt(f); count=DoomIO.readLEInt(f); status=plat_e.values()[DoomIO.readLEInt(f)]; oldstatus=plat_e.values()[DoomIO.readLEInt(f)]; System.out.println(status); System.out.println(oldstatus); crush=DoomIO.readIntBoolean(f); tag=DoomIO.readLEInt(f); type=plattype_e.values()[DoomIO.readLEInt(f)]; } @Override public void pack(ByteBuffer b) throws IOException{ super.pack(b); //12 b.putInt(super.sectorid); // 16 b.putInt(speed);//20 b.putInt(low); // 24 b.putInt(high); //28 b.putInt(wait); //32 b.putInt(count); //36 b.putInt(status.ordinal()); //40 b.putInt(oldstatus.ordinal()); //44 System.out.println(status); System.out.println(oldstatus); b.putInt(crush?1:0); // 48 b.putInt(tag); // 52 b.putInt(type.ordinal()); // 56 } public vldoor_t asVlDoor(sector_t[] sectors){ /* typedef struct { thinker_t thinker; vldoor_e type; sector_t* sector; fixed_t topheight; fixed_t speed; // 1 = up, 0 = waiting at top, -1 = down int direction; // tics to wait at the top int topwait; // (keep in case a door going down is reset) // when it reaches 0, start going down int topcountdown; } vldoor_t; */ vldoor_t tmp=new vldoor_t(); tmp.next=this.next; tmp.prev=this.prev; tmp.function=this.function; tmp.type=vldoor_e.values()[sector.id%vldoor_e.VALUES]; tmp.sector=sectors[this.speed%sectors.length]; tmp.topheight=this.low; tmp.speed=this.high; tmp.direction=this.wait; tmp.topwait=this.count; tmp.topcountdown=this.status.ordinal(); return tmp; } } package p; public class slidename_t { public slidename_t() { } public slidename_t(String frontFrame1, String frontFrame2, String frontFrame3, String frontFrame4, String backFrame1, String backFrame2, String backFrame3, String backFrame4) { this.frontFrame1 = frontFrame1; this.frontFrame2 = frontFrame2; this.frontFrame3 = frontFrame3; this.frontFrame4 = frontFrame4; this.backFrame1 = backFrame1; this.backFrame2 = backFrame2; this.backFrame3 = backFrame3; this.backFrame4 = backFrame4; } String frontFrame1; String frontFrame2; String frontFrame3; String frontFrame4; String backFrame1; String backFrame2; String backFrame3; String backFrame4; } package p; // // P_DOORS // public enum vldoor_e { normal, close30ThenOpen, close, open, raiseIn5Mins, blazeRaise, blazeOpen, blazeClose; public static final int VALUES=vldoor_e.values().length; } package p; import doom.thinker_t; /** Generic single-thinker argument action function. * Useful for handling stuff such as sector actions, doors, etc. * or special thinker objects that don't qualify as mobj_t's. * * @author velktron * * @param <T> */ public interface ActionTypeSS<T extends thinker_t>{ public void invoke (T a); } package p; import static rr.line_t.*; import static data.Defines.ITEMQUESIZE; import static data.Defines.MELEERANGE; import static data.Defines.NF_SUBSECTOR; import static data.Defines.NUMAMMO; import static data.Defines.RANGECHECK; import static data.Defines.pw_allmap; import static data.Defines.pw_infrared; import static data.Defines.pw_invisibility; import static data.Defines.pw_invulnerability; import static data.Defines.pw_ironfeet; import static data.Defines.pw_strength; import static data.Limits.BUTTONTIME; import static data.Limits.MAXANIMS; import static data.Limits.MAXBUTTONS; import static data.Limits.MAXINT; import static data.Limits.MAXINTERCEPTS; import static data.Limits.MAXSPECIALCROSS; import static data.Limits.MAXSWITCHES; import static data.Limits.PLATSPEED; import static data.Limits.PLATWAIT; import static data.Tables.ANG270; import static data.Tables.ANG90; import static data.Tables.BITS32; import static data.info.mobjinfo; import static data.info.states; import static doom.englsh.GOTARMBONUS; import static doom.englsh.GOTARMOR; import static doom.englsh.GOTBACKPACK; import static doom.englsh.GOTBERSERK; import static doom.englsh.GOTBFG9000; import static doom.englsh.GOTBLUECARD; import static doom.englsh.GOTBLUESKUL; import static doom.englsh.GOTCELL; import static doom.englsh.GOTCELLBOX; import static doom.englsh.GOTCHAINGUN; import static doom.englsh.GOTCHAINSAW; import static doom.englsh.GOTCLIP; import static doom.englsh.GOTCLIPBOX; import static doom.englsh.GOTHTHBONUS; import static doom.englsh.GOTINVIS; import static doom.englsh.GOTINVUL; import static doom.englsh.GOTLAUNCHER; import static doom.englsh.GOTMAP; import static doom.englsh.GOTMEDIKIT; import static doom.englsh.GOTMEDINEED; import static doom.englsh.GOTMEGA; import static doom.englsh.GOTMSPHERE; import static doom.englsh.GOTPLASMA; import static doom.englsh.GOTREDCARD; import static doom.englsh.GOTREDSKULL; import static doom.englsh.GOTROCKBOX; import static doom.englsh.GOTROCKET; import static doom.englsh.GOTSHELLBOX; import static doom.englsh.GOTSHELLS; import static doom.englsh.GOTSHOTGUN; import static doom.englsh.GOTSHOTGUN2; import static doom.englsh.GOTSTIM; import static doom.englsh.GOTSUIT; import static doom.englsh.GOTSUPER; import static doom.englsh.GOTVISOR; import static doom.englsh.GOTYELWCARD; import static doom.englsh.GOTYELWSKUL; import static doom.items.weaponinfo; import static m.fixed_t.FRACUNIT; import static m.fixed_t.MAPFRACUNIT; import static m.fixed_t.FixedDiv; import static p.DoorDefines.SLOWDARK; import static p.MapUtils.AproxDistance; import static p.MapUtils.InterceptVector; import static p.MobjFlags.*; import static utils.C2JUtils.eval; import static utils.C2JUtils.flags; import java.util.Arrays; import hu.HU; import i.DoomStatusAware; import i.IDoomSystem; import m.IRandom; import rr.ISpriteManager; import rr.Renderer; import rr.TextureManager; import rr.line_t; import rr.node_t; import rr.sector_t; import rr.side_t; import rr.subsector_t; import s.IDoomSound; import st.StatusBar; import utils.C2JUtils; import w.IWadLoader; import automap.IAutoMap; import data.Limits; import data.mapthing_t; import data.mobjtype_t; import data.state_t; import data.sounds.sfxenum_t; import defines.ammotype_t; import defines.card_t; import defines.statenum_t; import doom.DoomMain; import doom.DoomStatus; import doom.IDoomGame; import doom.player_t; import doom.th_class; import doom.think_t; import doom.thinker_t; import doom.weapontype_t; // // FROM SIGHT public abstract class UnifiedGameMap implements ThinkerList,DoomStatusAware{ public UnifiedGameMap(DoomStatus<?,?> DS){ this.SW=new Switches(); this.LEV=new Lights(); this.SPECS=new Specials(); this.PEV=new Plats(); this.See=new Sight(); // Didn't initialize that. this.EN=new Enemies(); this.thinkercap=new thinker_t(); for (int i=0; i<th_class.NUMTHCLASS; i++) // killough 8/29/98: initialize threaded lists thinkerclasscap[i]=new thinker_t(); this.RemoveThinkerDelayed=new P_RemoveThinkerDelayed(); intercepts = new intercept_t[MAXINTERCEPTS]; C2JUtils.initArrayOfObjects(intercepts,intercept_t.class); this.updateStatus(DS); // Normally unused. It clashes with line attribute 124, and looks like ass // anyway. However it's fully implemented. //this.SL=new SlideDoor(DS); //DS.SL=SL; this.FUNS=new ActionFunctions(DS,EN); // "Wire" all states to the proper functions. for (int i=0;i<states.length;i++){ FUNS.doWireState(states[i]); } } /////////////////// STATUS /////////////////// IWadLoader W; IAutoMap<?,?> AM; IRandom RND; Renderer<?,?> R; TextureManager<?> TM; AbstractLevelLoader LL; DoomMain<?,?> DM; IDoomGame DG; StatusBar ST; HU HU; IDoomSystem I; IDoomSound S; ISpriteManager SM; @Override public void updateStatus(DoomStatus<?,?> DC) { this.I=DC.I; this.DG=DC.DG; this.S=DC.S; this.LL=DC.LL; this.RND=DC.RND; this.DM=DC.DM; this.R=DC.R; this.W=DC.W; this.AM=DC.AM; this.ST= (StatusBar) DC.ST; this.AM=DC.AM; this.HU=DC.HU; this.TM=DC.TM; this.SM=DC.SM; if (FUNS!=null) FUNS.updateStatus(DC); } // //////////// Internal singletons ////////////// public Actions A; protected Specials SPECS; // DoorsFloors EV; protected Plats PEV; protected Lights LEV; protected Switches SW; protected Sight See; protected Enemies EN; protected ActionFunctions FUNS; protected SlideDoor SL; // //////////////////////////////////////////// public int topslope; public int bottomslope; // slopes to top and bottom of target int attackrange; // // UTILITIES // // // getSide() // Will return a side_t* // given the number of the current sector, // the line number, and the side (0/1) that you want. // side_t getSide(int currentSector, int line, int side) { return LL.sides[(LL.sectors[currentSector].lines[line]).sidenum[side]]; } /** * getSector() * Will return a sector_t * given the number of the current sector, * the line number and the side (0/1) that you want. */ sector_t getSector(int currentSector, int line, int side) { return LL.sides[(LL.sectors[currentSector].lines[line]).sidenum[side]].sector; } /** * twoSided() * Given the sector number and the line number, * it will tell you whether the line is two-sided or not. */ protected boolean twoSided(int sector, int line) { return eval((LL.sectors[sector].lines[line]).flags& ML_TWOSIDED); } /** * RETURN NEXT SECTOR # THAT LINE TAG REFERS TO */ protected int FindSectorFromLineTag(line_t line, int start) { int i; for (i = start + 1; i < LL.numsectors; i++) if (LL.sectors[i].tag == line.tag) return i; return -1; } // //////////////////// FROM p_maputl.c //////////////////// /** fixed_t */ protected int opentop, openbottom, openrange, lowfloor; /** * P_LineOpening Sets opentop and openbottom to the window through a two * sided line. OPTIMIZE: keep this precalculated */ public void LineOpening(line_t linedef) { sector_t front; sector_t back; if (linedef.sidenum[1] == line_t.NO_INDEX) { // single sided line openrange = 0; return; } front = linedef.frontsector; back = linedef.backsector; if (front.ceilingheight < back.ceilingheight) opentop = front.ceilingheight; else opentop = back.ceilingheight; if (front.floorheight > back.floorheight) { openbottom = front.floorheight; lowfloor = back.floorheight; } else { openbottom = back.floorheight; lowfloor = front.floorheight; } openrange = opentop - openbottom; } // // THING POSITION SETTING // /** * P_UnsetThingPosition Unlinks a thing from block map and sectors. On each * position change, BLOCKMAP and other lookups maintaining lists ot things * inside these structures need to be updated. */ public void UnsetThingPosition(mobj_t thing) { final int blockx; final int blocky; if (!eval(thing.flags& MF_NOSECTOR)) { // inert things don't need to be in blockmap? // unlink from subsector if (thing.snext != null) ((mobj_t) thing.snext).sprev = thing.sprev; if (thing.sprev != null) ((mobj_t) thing.sprev).snext = thing.snext; else thing.subsector.sector.thinglist = (mobj_t) thing.snext; } if (!eval(thing.flags& MF_NOBLOCKMAP)) { // inert things don't need to be in blockmap // unlink from block map if (thing.bnext != null) ((mobj_t) thing.bnext).bprev = thing.bprev; if (thing.bprev != null) ((mobj_t) thing.bprev).bnext = thing.bnext; else { blockx = LL.getSafeBlockX(thing.x - LL.bmaporgx); blocky = LL.getSafeBlockY(thing.y - LL.bmaporgy); if (blockx >= 0 && blockx < LL.bmapwidth && blocky >= 0 && blocky < LL.bmapheight) { LL.blocklinks[blocky * LL.bmapwidth + blockx] = (mobj_t) thing.bnext; } } } } // // BLOCK MAP ITERATORS // For each line/thing in the given mapblock, // call the passed PIT_* function. // If the function returns false, // exit with false without checking anything else. // // // INTERCEPT ROUTINES // protected intercept_t[] intercepts; int intercept_p; public divline_t trace=new divline_t(); boolean earlyout; int ptflags; // //P_TraverseIntercepts //Returns true if the traverser function returns true //for all lines. // boolean TraverseIntercepts ( PTR_InterceptFunc func, int maxfrac ) { int count; int dist; //fixed_t intercept_t in=null; // shut up compiler warning count = intercept_p; while (count-->0) { dist = MAXINT; for (int scan = 0 ; scan<intercept_p ; scan++) { if (intercepts[scan].frac < dist) { dist = intercepts[scan].frac; in = intercepts[scan]; } } if (dist > maxfrac) return true; // checked everything in range /* // UNUSED { // don't check these yet, there may be others inserted in = scan = intercepts; for ( scan = intercepts ; scan<intercept_p ; scan++) if (scan.frac > maxfrac) *in++ = *scan; intercept_p = in; return false; } */ if ( !func.invoke(in) ) return false; // don't bother going farther in.frac = MAXINT; } return true; // everything was traversed } protected void UpdateThinker(thinker_t thinker) { thinker_t th; // find the class the thinker belongs to th_class cls = thinker.function == think_t.NOP ? th_class.th_delete : thinker.function == think_t.P_MobjThinker && ((mobj_t) thinker).health > 0 && (eval((((mobj_t) thinker).flags) & MF_COUNTKILL) || ((mobj_t) thinker).type == mobjtype_t.MT_SKULL) ? eval((((mobj_t) thinker).flags) & MF_FRIEND) ? th_class.th_friends : th_class.th_enemies : th_class.th_misc; { /* Remove from current thread, if in one */ if ((th = thinker.cnext)!= null) (th.cprev = thinker.cprev).cnext = th; } // Add to appropriate thread th = thinkerclasscap[cls.ordinal()]; th.cprev.cnext = thinker; thinker.cnext = th; thinker.cprev = th.cprev; th.cprev = thinker; } protected final thinker_t[] thinkerclasscap=new thinker_t[th_class.NUMTHCLASS]; public boolean sight_debug; protected final void ResizeIntercepts() { intercepts=C2JUtils.resize(intercepts[0],intercepts,intercepts.length*2); } class Lights { // // Start strobing lights (usually from a trigger) // void StartLightStrobing(line_t line) { int secnum; sector_t sec; secnum = -1; while ((secnum = FindSectorFromLineTag(line, secnum)) >= 0) { sec = LL.sectors[secnum]; if (sec.specialdata != null) continue; sec.SpawnStrobeFlash(SLOWDARK, 0); } } // // TURN LINE'S TAG LIGHTS OFF // void TurnTagLightsOff(line_t line) { int i; int min; sector_t sector; sector_t tsec; line_t templine; for (int j = 0; j < LL.numsectors; j++) { sector = LL.sectors[j]; if (sector.tag == line.tag) { min = sector.lightlevel; for (i = 0; i < sector.linecount; i++) { templine = sector.lines[i]; tsec = templine.getNextSector(sector); if (tsec == null) continue; if (tsec.lightlevel < min) min = tsec.lightlevel; } sector.lightlevel = (short) min; } } } // // TURN LINE'S TAG LIGHTS ON // void LightTurnOn(line_t line, int bright) { sector_t sector; sector_t temp; line_t templine; for (int i = 0; i < LL.numsectors; i++) { sector = LL.sectors[i]; if (sector.tag == line.tag) { // bright = 0 means to search // for highest light level // surrounding sector if (bright == 0) { for (int j = 0; j < sector.linecount; j++) { templine = sector.lines[j]; temp = templine.getNextSector( sector); if (temp == null) continue; if (temp.lightlevel > bright) bright = temp.lightlevel; } } sector.lightlevel = (short) bright; } } } } class Enemies { // void A_Fall (mobj_t *actor); // // ENEMY THINKING // Enemies are allways spawned // with targetplayer = -1, threshold = 0 // Most monsters are spawned unaware of all players, // but some can be made preaware // /** * P_CheckMeleeRange */ boolean CheckMeleeRange(mobj_t actor) { mobj_t pl; int dist; // fixed_t if (actor.target == null) return false; pl = actor.target; dist = AproxDistance(pl.x - actor.x, pl.y - actor.y); if (dist >= MELEERANGE - 20 * FRACUNIT + pl.info.radius) return false; if (!EN.CheckSight(actor, actor.target)) return false; return true; } /** * P_CheckMissileRange */ boolean CheckMissileRange(mobj_t actor) { int dist; // fixed_t if (!CheckSight(actor, actor.target)) return false; if ((actor.flags & MF_JUSTHIT) != 0) { // the target just hit the enemy, // so fight back! actor.flags &= ~MF_JUSTHIT; return true; } if (actor.reactiontime != 0) return false; // do not attack yet // OPTIMIZE: get this from a global checksight dist = AproxDistance(actor.x - actor.target.x, actor.y - actor.target.y) - 64 * FRACUNIT; // [SYNC}: Major desync cause of desyncs. // DO NOT compare with null! if (actor.info.meleestate == statenum_t.S_NULL) dist -= 128 * FRACUNIT; // no melee attack, so fire more dist >>= 16; if (actor.type == mobjtype_t.MT_VILE) { if (dist > 14 * 64) return false; // too far away } if (actor.type == mobjtype_t.MT_UNDEAD) { if (dist < 196) return false; // close for fist attack dist >>= 1; } if (actor.type == mobjtype_t.MT_CYBORG || actor.type == mobjtype_t.MT_SPIDER || actor.type == mobjtype_t.MT_SKULL) { dist >>= 1; } if (dist > 200) dist = 200; if (actor.type == mobjtype_t.MT_CYBORG && dist > 160) dist = 160; if (RND.P_Random() < dist) return false; return true; } /** * P_CheckSight Returns true if a straight line between t1 and t2 is * unobstructed. Uses REJECT. */ boolean CheckSight(mobj_t t1, mobj_t t2) { int s1; int s2; int pnum; int bytenum; int bitnum; // First check for trivial rejection. // Determine subsector entries in REJECT table. s1 = t1.subsector.sector.id; // (t1.subsector.sector - sectors); s2 = t2.subsector.sector.id;// - sectors); pnum = s1 * LL.numsectors + s2; bytenum = pnum >> 3; bitnum = 1 << (pnum & 7); // Check in REJECT table. if (eval(LL.rejectmatrix[bytenum]& bitnum)) { See.sightcounts[0]++; // can't possibly be connected return false; } // An unobstructed LOS is possible. // Now look from eyes of t1 to any part of t2. See.sightcounts[1]++; R.increaseValidCount(1); See.sightzstart = t1.z + t1.height - (t1.height >> 2); topslope = (t2.z + t2.height) - See.sightzstart; bottomslope = (t2.z) - See.sightzstart; See.strace.x = t1.x; See.strace.y = t1.y; See.t2x = t2.x; See.t2y = t2.y; See.strace.dx = t2.x - t1.x; See.strace.dy = t2.y - t1.y; // the head node is the last node output return See.CrossBSPNode(LL.numnodes - 1); } // // Called by P_NoiseAlert. // Recursively traverse adjacent sectors, // sound blocking lines cut off traversal. // mobj_t soundtarget; private void RecursiveSound(sector_t sec, int soundblocks) { int i; line_t check; sector_t other; // wake up all monsters in this sector if (sec.validcount == R.getValidCount() && sec.soundtraversed <= soundblocks + 1) { return; // already flooded } sec.validcount = R.getValidCount(); sec.soundtraversed = soundblocks + 1; sec.soundtarget = soundtarget; // "peg" to the level loader for syntactic sugar side_t[] sides = LL.sides; for (i = 0; i < sec.linecount; i++) { check = sec.lines[i]; if ((check.flags & ML_TWOSIDED) == 0) continue; LineOpening(check); if (openrange <= 0) continue; // closed door if (sides[check.sidenum[0]].sector == sec) other = sides[check.sidenum[1]].sector; else other = sides[check.sidenum[0]].sector; if ((check.flags & ML_SOUNDBLOCK) != 0) { if (soundblocks == 0) RecursiveSound(other, 1); } else RecursiveSound(other, soundblocks); } } /** * P_NoiseAlert * If a monster yells at a player, * it will alert other monsters to the player. */ void NoiseAlert(mobj_t target, mobj_t emmiter) { soundtarget = target; R.increaseValidCount(1); RecursiveSound(emmiter.subsector.sector, 0); } /** * P_FireWeapon. Originally in pspr */ public void FireWeapon(player_t player) { statenum_t newstate; if (!player.CheckAmmo()) return; player.mo.SetMobjState(statenum_t.S_PLAY_ATK1); newstate = weaponinfo[player.readyweapon.ordinal()].atkstate; player.SetPsprite(player_t.ps_weapon, newstate); NoiseAlert(player.mo, player.mo); } // // P_Move // Move in the current direction, // returns false if the move is blocked. // // Peg to map movement line_t[] spechitp = new line_t[MAXSPECIALCROSS]; int numspechit; /** * P_LookForPlayers If allaround is false, only look 180 degrees in * front. Returns true if a player is targeted. */ boolean LookForPlayers(mobj_t actor, boolean allaround) { int c; int stop; player_t player; // sector_t sector; long an; // angle int dist; // fixed // sector = actor.subsector.sector; c = 0; stop = (actor.lastlook - 1) & 3; for (;; actor.lastlook = (actor.lastlook + 1) & 3) { if (!DM.playeringame[actor.lastlook]) continue; if (c++ == 2 || actor.lastlook == stop) { // done looking return false; } player = DM.players[actor.lastlook]; if (player.health[0] <= 0) continue; // dead if (!CheckSight(actor, player.mo)) continue; // out of sight if (!allaround) { an = (R.PointToAngle2(actor.x, actor.y, player.mo.x, player.mo.y) - actor.angle)&BITS32; if (an > ANG90 && an < ANG270) { dist = AproxDistance(player.mo.x - actor.x, player.mo.y - actor.y); // if real close, react anyway if (dist > MELEERANGE) continue; // behind back } } actor.target = player.mo; return true; } // The compiler complains that this is unreachable // return false; } } class Plats { public Plats() { initActivePlats(); } plat_t[] activeplats; // // Do Platforms // "amount" is only used for SOME platforms. // boolean DoPlat(line_t line, plattype_e type, int amount) { plat_t plat; int secnum = -1; boolean rtn = false; sector_t sec; // Activate all <type> plats that are in_stasis switch (type) { case perpetualRaise: ActivateInStasis(line.tag); break; default: break; } while ((secnum = FindSectorFromLineTag(line, secnum)) >= 0) { sec = LL.sectors[secnum]; if (sec.specialdata != null) continue; // Find lowest & highest floors around sector rtn = true; plat = new plat_t(); plat.type = type; plat.sector = sec; plat.sector.specialdata = plat; plat.function = think_t.T_PlatRaise; AddThinker(plat); plat.crush = false; plat.tag = line.tag; switch (type) { case raiseToNearestAndChange: plat.speed = PLATSPEED / 2; sec.floorpic = LL.sides[line.sidenum[0]].sector.floorpic; plat.high = sec.FindNextHighestFloor(sec.floorheight); plat.wait = 0; plat.status = plat_e.up; // NO MORE DAMAGE, IF APPLICABLE sec.special = 0; S.StartSound(sec.soundorg,sfxenum_t.sfx_stnmov); break; case raiseAndChange: plat.speed = PLATSPEED / 2; sec.floorpic = LL.sides[line.sidenum[0]].sector.floorpic; plat.high = sec.floorheight + amount * FRACUNIT; plat.wait = 0; plat.status = plat_e.up; S.StartSound(sec.soundorg,sfxenum_t.sfx_stnmov); break; case downWaitUpStay: plat.speed = PLATSPEED * 4; plat.low = sec.FindLowestFloorSurrounding(); if (plat.low > sec.floorheight) plat.low = sec.floorheight; plat.high = sec.floorheight; plat.wait = 35 * PLATWAIT; plat.status = plat_e.down; S.StartSound(sec.soundorg,sfxenum_t.sfx_pstart); break; case blazeDWUS: plat.speed = PLATSPEED * 8; plat.low = sec.FindLowestFloorSurrounding(); if (plat.low > sec.floorheight) plat.low = sec.floorheight; plat.high = sec.floorheight; plat.wait = 35 * PLATWAIT; plat.status = plat_e.down; S.StartSound(sec.soundorg,sfxenum_t.sfx_pstart); break; case perpetualRaise: plat.speed = PLATSPEED; plat.low = sec.FindLowestFloorSurrounding(); if (plat.low > sec.floorheight) plat.low = sec.floorheight; plat.high = sec.FindHighestFloorSurrounding(); if (plat.high < sec.floorheight) plat.high = sec.floorheight; plat.wait = 35 * PLATWAIT; // Guaranteed to be 0 or 1. plat.status = plat_e.values()[RND.P_Random() & 1]; S.StartSound(sec.soundorg,sfxenum_t.sfx_pstart);; break; } AddActivePlat(plat); } return rtn; } void ActivateInStasis(int tag) { int i; for (i = 0; i < activeplats.length; i++) if ((activeplats[i] != null) && (activeplats[i].tag == tag) && (activeplats[i].status == plat_e.in_stasis)) { (activeplats[i]).status = (activeplats[i]).oldstatus; (activeplats[i]).function = think_t.T_PlatRaise; FUNS.doWireThinker(activeplats[i]); } } void StopPlat(line_t line) { int j; for (j = 0; j < activeplats.length; j++) if ((activeplats[j] != null) && (activeplats[j].status != plat_e.in_stasis) && (activeplats[j].tag == line.tag)) { (activeplats[j]).oldstatus = (activeplats[j]).status; (activeplats[j]).status = plat_e.in_stasis; (activeplats[j]).function = null; FUNS.doWireThinker(activeplats[j]); } } /* void AddActivePlat(plat_t plat) { int i; for (i = 0; i < MAXPLATS; i++) if (activeplats[i] == null) { activeplats[i] = plat; return; } I.Error("P_AddActivePlat: no more plats!"); } */ void RemoveActivePlat(plat_t plat) { int i; for (i = 0; i < activeplats.length; i++) if (plat == activeplats[i]) { (activeplats[i]).sector.specialdata = null; RemoveThinker(activeplats[i]); activeplats[i] = null; return; } I.Error("P_RemoveActivePlat: can't find plat!"); } public void initActivePlats() { // activeplats is just a placeholder. Plat objects aren't // actually reused, so we don't need an initialized array. // Same rule when resizing. activeplats=new plat_t[data.Limits.MAXPLATS]; } } class Sight { public Sight(){ strace=new divline_t(); sightcounts= new int[2]; } int sightzstart; // eye z of looker divline_t strace; // from t1 to t2 int t2x; int t2y; int[] sightcounts ; /** * P_CrossSubsector Returns true if strace crosses the given subsector * successfully. */ boolean CrossSubsector(int num) { int seg; // pointer inside segs line_t line; int s1; int s2; int count; subsector_t sub; sector_t front; sector_t back; int opentop; // fixed_t int openbottom; divline_t divl = new divline_t(); //vertex_t v1; //vertex_t v2; int frac; // fixed_t int slope; if (RANGECHECK) { if (num >= LL.numsubsectors) I.Error("P_CrossSubsector: ss %d with numss = %d", num, LL.numsubsectors); } sub = LL.subsectors[num]; // check lines count = sub.numlines; seg = sub.firstline;// LL.segs[sub.firstline]; for (; count > 0; seg++, count--) { line = LL.segs[seg].linedef; // allready checked other side? if (line.validcount == R.getValidCount()) continue; line.validcount = R.getValidCount(); //v1 = line.v1; //v2 = line.v2; s1 = strace.DivlineSide(line.v1x, line.v1y); s2 = strace.DivlineSide(line.v2x, line.v2y); // line isn't crossed? if (s1 == s2) continue; divl.x = line.v1x; divl.y = line.v1y; divl.dx = line.v2x - line.v1x; divl.dy = line.v2y - line.v1y; s1 = divl.DivlineSide(strace.x, strace.y); s2 = divl.DivlineSide(t2x, t2y); // line isn't crossed? if (s1 == s2) continue; // stop because it is not two sided anyway // might do this after updating validcount? if (!flags(line.flags,ML_TWOSIDED)) return false; // crosses a two sided line front = LL.segs[seg].frontsector; back = LL.segs[seg].backsector; // no wall to block sight with? if (front.floorheight == back.floorheight && front.ceilingheight == back.ceilingheight) continue; // possible occluder // because of ceiling height differences if (front.ceilingheight < back.ceilingheight) opentop = front.ceilingheight; else opentop = back.ceilingheight; // because of ceiling height differences if (front.floorheight > back.floorheight) openbottom = front.floorheight; else openbottom = back.floorheight; // quick test for totally closed doors if (openbottom >= opentop) return false; // stop frac = MapUtils.P_InterceptVector(strace, divl); if (front.floorheight != back.floorheight) { slope = FixedDiv(openbottom - sightzstart, frac); if (slope > bottomslope) bottomslope = slope; } if (front.ceilingheight != back.ceilingheight) { slope = FixedDiv(opentop - sightzstart, frac); if (slope < topslope) topslope = slope; } if (topslope <= bottomslope) return false; // stop } // passed the subsector ok return true; } /** * P_CrossBSPNode Returns true if strace crosses the given node * successfully. */ boolean CrossBSPNode(int bspnum) { node_t bsp; int side; if (eval(bspnum& NF_SUBSECTOR)) { if (bspnum == -1) return CrossSubsector(0); else return CrossSubsector(bspnum & (~NF_SUBSECTOR)); } bsp = LL.nodes[bspnum]; // decide which side the start point is on side = bsp.DivlineSide(strace.x, strace.y); if (side == 2) side = 0; // an "on" should cross both sides // cross the starting side if (!CrossBSPNode(bsp.children[side])) return false; // the partition plane is crossed here if (side == bsp.DivlineSide(t2x, t2y)) { // the line doesn't touch the other side return true; } // cross the ending side return CrossBSPNode(bsp.children[side ^ 1]); } } // // P_InitPicAnims // /** * Floor/ceiling animation sequences, defined by first and last frame, i.e. * the flat (64x64 tile) name to be used. The full animation sequence is * given using all the flats between the start and end entry, in the order * found in the WAD file. */ private final animdef_t[] animdefs = { new animdef_t(false, "NUKAGE3", "NUKAGE1", 8), new animdef_t(false, "FWATER4", "FWATER1", 8), new animdef_t(false, "SWATER4", "SWATER1", 8), new animdef_t(false, "LAVA4", "LAVA1", 8), new animdef_t(false, "BLOOD3", "BLOOD1", 8), // DOOM II flat animations. new animdef_t(false, "RROCK08", "RROCK05", 8), new animdef_t(false, "SLIME04", "SLIME01", 8), new animdef_t(false, "SLIME08", "SLIME05", 8), new animdef_t(false, "SLIME12", "SLIME09", 8), new animdef_t(true, "BLODGR4", "BLODGR1", 8), new animdef_t(true, "SLADRIP3", "SLADRIP1", 8), new animdef_t(true, "BLODRIP4", "BLODRIP1", 8), new animdef_t(true, "FIREWALL", "FIREWALA", 8), new animdef_t(true, "GSTFONT3", "GSTFONT1", 8), new animdef_t(true, "FIRELAVA", "FIRELAV3", 8), new animdef_t(true, "FIREMAG3", "FIREMAG1", 8), new animdef_t(true, "FIREBLU2", "FIREBLU1", 8), new animdef_t(true, "ROCKRED3", "ROCKRED1", 8), new animdef_t(true, "BFALL4", "BFALL1", 8), new animdef_t(true, "SFALL4", "SFALL1", 8), new animdef_t(true, "WFALL4", "WFALL1", 8), new animdef_t(true, "DBRAIN4", "DBRAIN1", 8) }; // MAES: this was a cheap trick to mark the end of the sequence // with a value of "-1". // It won't work in Java, so just use animdefs.length-1 // new animdef_t(false, "", "", 0) }; // // SPECIAL SPAWNING // class Specials { public static final int ok = 0, crushed = 1, pastdest = 2; protected line_t[] linespeciallist = new line_t[Limits.MAXLINEANIMS]; public short numlinespecials; /** * These are NOT the same anims found in defines. Dunno why they fucked up * this one so badly. Even the type has the same name, but is entirely * different. No way they could be overlapped/unionized either. So WTF. * Really. WTF. */ public anim_t[] anims = new anim_t[MAXANIMS]; // MAES: was a pointer public int lastanim; // // P_UpdateSpecials // Animate planes, scroll walls, etc. // boolean levelTimer; int levelTimeCount; public void UpdateSpecials() { int pic; line_t line; anim_t anim; // LEVEL TIMER if (levelTimer == true) { levelTimeCount--; if (levelTimeCount == 0) DG.ExitLevel(); } // ANIMATE FLATS AND TEXTURES GLOBALLY for (int j = 0; j < lastanim; j++) { anim = anims[j]; for (int i = anim.basepic; i < anim.basepic + anim.numpics; i++) { pic = anim.basepic + ((DM.leveltime / anim.speed + i) % anim.numpics); if (anim.istexture) TM.setTextureTranslation(i,pic); else TM.setFlatTranslation(i,pic); } } // ANIMATE LINE SPECIALS for (int i = 0; i < numlinespecials; i++) { line = linespeciallist[i]; switch (line.special) { case 48: // EFFECT FIRSTCOL SCROLL + LL.sides[line.sidenum[0]].textureoffset += MAPFRACUNIT; break; } } // DO BUTTONS SW.doButtons(); } public void InitPicAnims() { C2JUtils.initArrayOfObjects(anims); anim_t lstanim; // Init animation. MAES: sneaky base pointer conversion ;-) this.lastanim = 0; // MAES: for (i=0 ; animdefs[i].istexture != -1 ; i++) for (int i = 0; i<animdefs.length-1; i++) { lstanim= anims[this.lastanim]; if (animdefs[i].istexture) { // different episode ? if (TM.CheckTextureNumForName(animdefs[i].startname) == -1) continue; // So, if it IS a valid texture, it goes straight into anims. lstanim.picnum = TM.TextureNumForName(animdefs[i].endname); lstanim.basepic = TM.TextureNumForName(animdefs[i].startname); } else { // If not a texture, it's a flat. if (W.CheckNumForName(animdefs[i].startname) == -1) continue; System.out.println(animdefs[i]); // Otherwise, lstanim seems to go nowhere :-/ lstanim.picnum = TM.FlatNumForName(animdefs[i].endname); lstanim.basepic = TM.FlatNumForName(animdefs[i].startname); } lstanim.istexture = animdefs[i].istexture; lstanim.numpics = lstanim.picnum - lstanim.basepic + 1; if (lstanim.numpics < 2) I.Error("P_InitPicAnims: bad cycle from %s to %s", animdefs[i].startname, animdefs[i].endname); lstanim.speed = animdefs[i].speed; this.lastanim++; } } protected final void resizeLinesSpecialList() { linespeciallist=C2JUtils.resize(linespeciallist[0],linespeciallist,linespeciallist.length*2); } } class Switches { public Switches(){ switchlist= new int[MAXSWITCHES]; initButtonList(); } public void doButtons() { for (int i = 0; i < buttonlist.length; i++) if (eval(buttonlist[i].btimer)) { buttonlist[i].btimer--; if (!eval(buttonlist[i].btimer)) { switch (buttonlist[i].where) { case top: LL.sides[buttonlist[i].line.sidenum[0]].toptexture = (short) buttonlist[i].btexture; break; case middle: LL.sides[buttonlist[i].line.sidenum[0]].midtexture = (short) buttonlist[i].btexture; break; case bottom: LL.sides[buttonlist[i].line.sidenum[0]].bottomtexture = (short) buttonlist[i].btexture; break; } S.StartSound(buttonlist[i].soundorg,sfxenum_t.sfx_swtchn); buttonlist[i].reset(); } } } // // CHANGE THE TEXTURE OF A WALL SWITCH TO ITS OPPOSITE // switchlist_t[] alphSwitchList = { // Doom shareware episode 1 switches new switchlist_t("SW1BRCOM", "SW2BRCOM", 1), new switchlist_t("SW1BRN1", "SW2BRN1", 1), new switchlist_t("SW1BRN2", "SW2BRN2", 1), new switchlist_t("SW1BRNGN", "SW2BRNGN", 1), new switchlist_t("SW1BROWN", "SW2BROWN", 1), new switchlist_t("SW1COMM", "SW2COMM", 1), new switchlist_t("SW1COMP", "SW2COMP", 1), new switchlist_t("SW1DIRT", "SW2DIRT", 1), new switchlist_t("SW1EXIT", "SW2EXIT", 1), new switchlist_t("SW1GRAY", "SW2GRAY", 1), new switchlist_t("SW1GRAY1", "SW2GRAY1", 1), new switchlist_t("SW1METAL", "SW2METAL", 1), new switchlist_t("SW1PIPE", "SW2PIPE", 1), new switchlist_t("SW1SLAD", "SW2SLAD", 1), new switchlist_t("SW1STARG", "SW2STARG", 1), new switchlist_t("SW1STON1", "SW2STON1", 1), new switchlist_t("SW1STON2", "SW2STON2", 1), new switchlist_t("SW1STONE", "SW2STONE", 1), new switchlist_t("SW1STRTN", "SW2STRTN", 1), // Doom registered episodes 2&3 switches new switchlist_t("SW1BLUE", "SW2BLUE", 2), new switchlist_t("SW1CMT", "SW2CMT", 2), new switchlist_t("SW1GARG", "SW2GARG", 2), new switchlist_t("SW1GSTON", "SW2GSTON", 2), new switchlist_t("SW1HOT", "SW2HOT", 2), new switchlist_t("SW1LION", "SW2LION", 2), new switchlist_t("SW1SATYR", "SW2SATYR", 2), new switchlist_t("SW1SKIN", "SW2SKIN", 2), new switchlist_t("SW1VINE", "SW2VINE", 2), new switchlist_t("SW1WOOD", "SW2WOOD", 2), // Doom II switches new switchlist_t("SW1PANEL", "SW2PANEL", 3), new switchlist_t("SW1ROCK", "SW2ROCK", 3), new switchlist_t("SW1MET2", "SW2MET2", 3), new switchlist_t("SW1WDMET", "SW2WDMET", 3), new switchlist_t("SW1BRIK", "SW2BRIK", 3), new switchlist_t("SW1MOD1", "SW2MOD1", 3), new switchlist_t("SW1ZIM", "SW2ZIM", 3), new switchlist_t("SW1STON6", "SW2STON6", 3), new switchlist_t("SW1TEK", "SW2TEK", 3), new switchlist_t("SW1MARB", "SW2MARB", 3), new switchlist_t("SW1SKULL", "SW2SKULL", 3), new switchlist_t("\0", "\0", 0) }; /** A (runtime generated) list of the KNOWN button types */ int[] switchlist; int numswitches; button_t[] buttonlist; // // P_InitSwitchList // Only called at game initialization. // public void InitSwitchList() { int i; int index; int episode; episode = 1; // MAES: if this isn't changed Ultimate Doom's switches // won't work visually. if (DM.isRegistered()) episode = 2; else if (DM.isCommercial()) episode = 3; for (index = 0, i = 0; i < MAXSWITCHES; i++) { if (index>=switchlist.length) { // Remove limit switchlist=Arrays.copyOf(switchlist,switchlist.length>0?switchlist.length*2:8); } // Trickery. Looks for "end of list" marker // Since the list has pairs of switches, the // actual number of distinct switches is index/2 if (alphSwitchList[i].episode == 0) { numswitches = index / 2; switchlist[index] = -1; break; } if (alphSwitchList[i].episode <= episode) { /* * // UNUSED - debug? int value; if * (R_CheckTextureNumForName(alphSwitchList[i].name1) < 0) { * system.Error("Can't find switch texture '%s'!", * alphSwitchList[i].name1); continue; } value = * R_TextureNumForName(alphSwitchList[i].name1); */ switchlist[index++] = TM.TextureNumForName(alphSwitchList[i].name1); switchlist[index++] = TM.TextureNumForName(alphSwitchList[i].name2); } } } // // Start a button counting down till it turns off. // void StartButton(line_t line, bwhere_e w, int texture, int time) { int i; // See if button is already pressed for (i = 0; i < buttonlist.length; i++) { if (buttonlist[i].btimer != 0 && buttonlist[i].line == line) { return; } } // At this point, it may mean that THE button of that particular // line was not active, or simply that there were not enough // buttons in buttonlist to support an additional entry. // Search for a free button slot. for (i = 0; i < buttonlist.length; i++) { if (buttonlist[i].btimer == 0) { buttonlist[i].line = line; buttonlist[i].where = w; buttonlist[i].btexture = texture; buttonlist[i].btimer = time; buttonlist[i].soundorg = line.soundorg; return; } } // Extremely rare event, We must be able to push more than MAXBUTTONS buttons // in one tic, which can't normally happen except in really pathological maps. // In any case, resizing should solve this problem. buttonlist=C2JUtils.resize(buttonlist[0], buttonlist, buttonlist.length*2); // Try again StartButton(line,w,texture,time); // I.Error("P_StartButton: no button slots left!"); } // // Function that changes wall texture. // Tell it if switch is ok to use again (true=yes, it's a button). // void ChangeSwitchTexture(line_t line, boolean useAgain) { int texTop; int texMid; int texBot; int i; int sound; if (!useAgain) line.special = 0; texTop = LL.sides[line.sidenum[0]].toptexture; texMid = LL.sides[line.sidenum[0]].midtexture; texBot = LL.sides[line.sidenum[0]].bottomtexture; sound = sfxenum_t.sfx_swtchn.ordinal(); // EXIT SWITCH? if (line.special == 11) sound = sfxenum_t.sfx_swtchx.ordinal(); for (i = 0; i < numswitches * 2; i++) { if (switchlist[i] == texTop) { S.StartSound(buttonlist[0].soundorg,sound); LL.sides[line.sidenum[0]].toptexture = (short) switchlist[i ^ 1]; if (useAgain) StartButton(line, bwhere_e.top, switchlist[i], BUTTONTIME); return; } else { if (switchlist[i] == texMid) { S.StartSound(buttonlist[0].soundorg,sound); LL.sides[line.sidenum[0]].midtexture = (short) switchlist[i ^ 1]; if (useAgain) StartButton(line, bwhere_e.middle, switchlist[i], BUTTONTIME); return; } else { if (switchlist[i] == texBot) { S.StartSound(buttonlist[0].soundorg,sound); LL.sides[line.sidenum[0]].bottomtexture = (short) switchlist[i ^ 1]; if (useAgain) StartButton(line, bwhere_e.bottom, switchlist[i], BUTTONTIME); return; } } } } } public void initButtonList() { // Unlike plats, buttonlist needs statically allocated and reusable // objects. The MAXBUTTONS limit actually applied to buttons PRESSED // or ACTIVE at once, not how many there can actually be in a map. buttonlist = C2JUtils.createArrayOfObjects(button_t.class,MAXBUTTONS); } } // // MOVEMENT ITERATOR FUNCTIONS // interface PIT_LineFunction { public boolean invoke(line_t ld); } interface PIT_MobjFunction { public boolean invoke(mobj_t thing); } interface PTR_InterceptFunc { public boolean invoke(intercept_t in); } /* enum PTR { SlideTraverse, AimTraverse, ShootTraverse, UseTraverse } */ //////////////// PIT FUNCTION OBJECTS /////////////////// // // PIT_AddLineIntercepts. // Looks for lines in the given block // that intercept the given trace // to add to the intercepts list. // // A line is crossed if its endpoints // are on opposite sides of the trace. // Returns true if earlyout and a solid line hit. // protected class PIT_AddLineIntercepts implements PIT_LineFunction{ divline_t dl = new divline_t(); public boolean invoke(line_t ld) { boolean s1; boolean s2; int frac; // avoid precision problems with two routines if (trace.dx > FRACUNIT * 16 || trace.dy > FRACUNIT * 16 || trace.dx < -FRACUNIT * 16 || trace.dy < -FRACUNIT * 16) { s1 = trace.PointOnDivlineSide(ld.v1x, ld.v1y); s2 = trace.PointOnDivlineSide(ld.v2x, ld.v2y); //s1 = trace.DivlineSide(ld.v1x, ld.v1.y); //s2 = trace.DivlineSide(ld.v2x, ld.v2y); } else { s1 = ld.PointOnLineSide(trace.x, trace.y); s2 = ld.PointOnLineSide(trace.x + trace.dx, trace.y + trace.dy); //s1 = new divline_t(ld).DivlineSide(trace.x, trace.y); //s2 = new divline_t(ld).DivlineSide(trace.x + trace.dx, trace.y + trace.dy); } if (s1 == s2) return true; // line isn't crossed // hit the line dl.MakeDivline(ld); frac = InterceptVector(trace, dl); if (frac < 0) return true; // behind source // try to early out the check if (earlyout && frac < FRACUNIT && ld.backsector == null) { return false; // stop checking } // "create" a new intercept in the static intercept pool. if (intercept_p>=intercepts.length){ ResizeIntercepts(); } intercepts[intercept_p].frac = frac; intercepts[intercept_p].isaline = true; intercepts[intercept_p].line = ld; intercept_p++; return true; // continue } } // // PIT_AddThingIntercepts // protected class PIT_AddThingIntercepts implements PIT_MobjFunction{ // maybe make this a shared instance variable? private divline_t dl = new divline_t(); public boolean invoke(mobj_t thing) { // FIXME: is false ever returned? int x1, y1, x2, y2; // fixed_t boolean s1, s2; boolean tracepositive; int frac; // fixed_t tracepositive = (trace.dx ^ trace.dy) > 0; // check a corner to corner crossection for hit if (tracepositive) { x1 = thing.x - thing.radius; y1 = thing.y + thing.radius; x2 = thing.x + thing.radius; y2 = thing.y - thing.radius; } else { x1 = thing.x - thing.radius; y1 = thing.y - thing.radius; x2 = thing.x + thing.radius; y2 = thing.y + thing.radius; } s1 = trace.PointOnDivlineSide(x1, y1); s2 = trace.PointOnDivlineSide(x2, y2); if (s1 == s2) return true; // line isn't crossed dl.x = x1; dl.y = y1; dl.dx = x2 - x1; dl.dy = y2 - y1; frac = InterceptVector(trace, dl); if (frac < 0) return true; // behind source // "create" a new intercept in the static intercept pool. // FIXME: this is certainly NOT vanilla compatible behavior. // TODO: add overflow emulation for this? if (intercept_p>=intercepts.length){ ResizeIntercepts(); } intercepts[intercept_p].frac = frac; intercepts[intercept_p].isaline = false; intercepts[intercept_p].thing = thing; intercept_p++; return true; // keep going } } /////////// BEGIN MAP OBJECT CODE, USE AS BASIC /** * P_ExplodeMissile */ protected void ExplodeMissile(mobj_t mo) { mo.momx = mo.momy = mo.momz = 0; // MAES 9/5/2011: using mobj code for that. mo.SetMobjState(mobjinfo[mo.type.ordinal()].deathstate); mo.tics -= RND.P_Random() & 3; if (mo.tics < 1) mo.tics = 1; mo.flags &= ~MF_MISSILE; if (mo.info.deathsound != null) S.StartSound(mo, mo.info.deathsound); } // // P_RemoveMobj // mapthing_t[] itemrespawnque = new mapthing_t[ITEMQUESIZE]; int[] itemrespawntime = new int[ITEMQUESIZE]; int iquehead; int iquetail; public void RemoveMobj(mobj_t mobj) { if (eval(mobj.flags& MF_SPECIAL) && !eval(mobj.flags& MF_DROPPED) && (mobj.type != mobjtype_t.MT_INV) && (mobj.type != mobjtype_t.MT_INS)) { itemrespawnque[iquehead] = mobj.spawnpoint; itemrespawntime[iquehead] = DM.leveltime; iquehead = (iquehead + 1) & (ITEMQUESIZE - 1); // lose one off the end? if (iquehead == iquetail) iquetail = (iquetail + 1) & (ITEMQUESIZE - 1); } // unlink from sector and block lists UnsetThingPosition(mobj); // stop any playing sound S.StopSound (mobj); // free block RemoveThinker((thinker_t) mobj); } // //////////////////////////////// THINKER CODE, GLOBALLY VISIBLE // ///////////////// // // THINKERS // All thinkers should be allocated by Z_Malloc // so they can be operated on uniformly. // The actual structures will vary in size, // but the first element must be thinker_t. // /** Both the head and the tail of the thinkers list */ public thinker_t thinkercap; /* An approximation of the active number of thinkers */ private int numthinkers; // // P_InitThinkers // @Override public void InitThinkers() { // mobjpool.drain(); for (int i=0; i<th_class.NUMTHCLASS; i++) // killough 8/29/98: initialize threaded lists thinkerclasscap[i].cprev = thinkerclasscap[i].cnext = thinkerclasscap[i]; thinker_t next=thinkercap.next; thinker_t prev=thinkercap.prev; // Unlink the "dangling" thinkers that may still be attached // to the thinkercap. When loading a new level, they DO NOT get unloaded, // wtf... if (next!=null && next!=thinkercap) { //System.err.println("Next link to thinkercap nulled"); next.prev=null; } if (prev!=null && prev!=thinkercap) { //System.err.println("Prev link to thinkercap nulled"); prev.next=null; } thinkercap.next = thinkercap; thinkercap.prev = thinkercap; } /** cph 2002/01/13 - iterator for thinker list * WARNING: Do not modify thinkers between calls to this functin */ thinker_t NextThinker(thinker_t th, th_class cl) { thinker_t top = thinkerclasscap[cl.ordinal()]; if (th==null) th = top; th = cl == th_class.th_all ? th.next : th.cnext; return th == top ? null : th; } /** * P_AddThinker Adds a new thinker at the end of the list. */ public void AddThinker(thinker_t thinker) { // If something was too weird to be wired before, it will // be wired here for sure, so don't worry about searching // all of the code. if (thinker.function!=null && (thinker.acp1==null && thinker.acp2==null)) FUNS.doWireThinker(thinker); thinkercap.prev.next = thinker; thinker.next = thinkercap; thinker.prev = thinkercap.prev; thinkercap.prev = thinker; // killough 8/29/98: set sentinel pointers, and then add to appropriate list thinker.cnext = thinker.cprev = null; UpdateThinker(thinker); // [Maes] seems only used for interpolations //newthinkerpresent = true; numthinkers++; } public void ClearPlatsBeforeLoading(){ for (int i = 0; i < PEV.activeplats.length; i++) { PEV.activeplats[i] = null; } } public void AddActivePlat(plat_t plat) { int i; for (i = 0; i < PEV.activeplats.length; i++) if (PEV.activeplats[i] == null) { PEV.activeplats[i] = plat; return; } // Uhh... lemme guess. Needs to resize? // Resize but leave extra items empty. PEV.activeplats=C2JUtils.resizeNoAutoInit(PEV.activeplats,2*PEV.activeplats.length); AddActivePlat(plat); //I.Error("P_AddActivePlat: no more plats!"); } // MAES: works, but not worth it. // MobjPool mobjpool; // // P_RemoveThinker // Deallocation is lazy -- it will not actually be freed // until its thinking turn comes up. // // // killough 4/25/98: // // Instead of marking the function with -1 value cast to a function pointer, // set the function to P_RemoveThinkerDelayed(), so that later, it will be // removed automatically as part of the thinker process. // public void RemoveThinker(thinker_t thinker) { //thinker.function = think_t.NOP; // Wire to this special function. thinker.function=think_t.NOP; thinker.acpss = this.RemoveThinkerDelayed; // Remove any type 1 or 2 special functions. thinker.acp1 = null; thinker.acp2 = null; numthinkers--; } // // P_AllocateThinker // Allocates memory and adds a new thinker at the end of the list. // public void AllocateThinker(thinker_t thinker) { // UNUSED } public thinker_t getRandomThinker() { int pick=(int) (Math.random()*numthinkers); thinker_t th=this.getThinkerCap(); for (int i=0;i<pick;i++){ th=th.next; } return th; } public int getNumThinkers(){ return numthinkers; } // // P_Init // public void Init() { SW.InitSwitchList(); SPECS.InitPicAnims(); SM.InitSprites(ISpriteManager.doomsprnames); } /** * P_TouchSpecialThing LIKE ROMERO's ASS!!! */ public void TouchSpecialThing(mobj_t special, mobj_t toucher) { player_t player; int i; int delta;// fixed_t sfxenum_t sound; delta = special.z - toucher.z; if (delta > toucher.height || delta < -8 * FRACUNIT) { // out of reach return; } sound = sfxenum_t.sfx_itemup; player = toucher.player; // Dead thing touching. // Can happen with a sliding player corpse. if (toucher.health <= 0) return; // Identify by sprite. switch (special.sprite) { // armor case SPR_ARM1: if (!player.GiveArmor(1)) return; player.message = GOTARMOR; break; case SPR_ARM2: if (!player.GiveArmor(2)) return; player.message = GOTMEGA; break; // bonus items case SPR_BON1: player.health[0]++; // can go over 100% if (player.health[0] > 200) player.health[0] = 200; player.mo.health = player.health[0]; player.message = GOTHTHBONUS; break; case SPR_BON2: player.armorpoints[0]++; // can go over 100% if (player.armorpoints[0] > 200) player.armorpoints[0] = 200; if (player.armortype == 0) player.armortype = 1; player.message = GOTARMBONUS; break; case SPR_SOUL: player.health[0] += 100; if (player.health[0] > 200) player.health[0] = 200; player.mo.health = player.health[0]; player.message = GOTSUPER; sound = sfxenum_t.sfx_getpow; break; case SPR_MEGA: if (!DM.isCommercial()) return; player.health[0] = 200; player.mo.health = player.health[0]; player.GiveArmor(2); player.message = GOTMSPHERE; sound = sfxenum_t.sfx_getpow; break; // cards // leave cards for everyone case SPR_BKEY: if (!player.cards[card_t.it_bluecard.ordinal()]) player.message = GOTBLUECARD; player.GiveCard(card_t.it_bluecard); if (!DM.netgame) break; return; case SPR_YKEY: if (!player.cards[card_t.it_yellowcard.ordinal()]) player.message = GOTYELWCARD; player.GiveCard(card_t.it_yellowcard); if (!DM.netgame) break; return; case SPR_RKEY: if (!player.cards[card_t.it_redcard.ordinal()]) player.message = GOTREDCARD; player.GiveCard(card_t.it_redcard); if (!DM.netgame) break; return; case SPR_BSKU: if (!player.cards[card_t.it_blueskull.ordinal()]) player.message = GOTBLUESKUL; player.GiveCard(card_t.it_blueskull); if (!DM.netgame) break; return; case SPR_YSKU: if (!player.cards[card_t.it_yellowskull.ordinal()]) player.message = GOTYELWSKUL; player.GiveCard(card_t.it_yellowskull); if (!DM.netgame) break; return; case SPR_RSKU: if (!player.cards[card_t.it_redskull.ordinal()]) player.message = GOTREDSKULL; player.GiveCard(card_t.it_redskull); if (!DM.netgame) break; return; // medikits, heals case SPR_STIM: if (!player.GiveBody(10)) return; player.message = GOTSTIM; break; case SPR_MEDI: if (!player.GiveBody(25)) return; if (player.health[0] < 25) player.message = GOTMEDINEED; else player.message = GOTMEDIKIT; break; // power ups case SPR_PINV: if (!player.GivePower(pw_invulnerability)) return; player.message = GOTINVUL; sound = sfxenum_t.sfx_getpow; break; case SPR_PSTR: if (!player.GivePower(pw_strength)) return; player.message = GOTBERSERK; if (player.readyweapon != weapontype_t.wp_fist) player.pendingweapon = weapontype_t.wp_fist; sound = sfxenum_t.sfx_getpow; break; case SPR_PINS: if (!player.GivePower(pw_invisibility)) return; player.message = GOTINVIS; sound = sfxenum_t.sfx_getpow; break; case SPR_SUIT: if (!player.GivePower(pw_ironfeet)) return; player.message = GOTSUIT; sound = sfxenum_t.sfx_getpow; break; case SPR_PMAP: if (!player.GivePower(pw_allmap)) return; player.message = GOTMAP; sound = sfxenum_t.sfx_getpow; break; case SPR_PVIS: if (!player.GivePower(pw_infrared)) return; player.message = GOTVISOR; sound = sfxenum_t.sfx_getpow; break; // ammo case SPR_CLIP: if ((special.flags & MF_DROPPED) != 0) { if (!player.GiveAmmo(ammotype_t.am_clip, 0)) return; } else { if (!player.GiveAmmo(ammotype_t.am_clip, 1)) return; } player.message = GOTCLIP; break; case SPR_AMMO: if (!player.GiveAmmo(ammotype_t.am_clip, 5)) return; player.message = GOTCLIPBOX; break; case SPR_ROCK: if (!player.GiveAmmo(ammotype_t.am_misl, 1)) return; player.message = GOTROCKET; break; case SPR_BROK: if (!player.GiveAmmo(ammotype_t.am_misl, 5)) return; player.message = GOTROCKBOX; break; case SPR_CELL: if (!player.GiveAmmo(ammotype_t.am_cell, 1)) return; player.message = GOTCELL; break; case SPR_CELP: if (!player.GiveAmmo(ammotype_t.am_cell, 5)) return; player.message = GOTCELLBOX; break; case SPR_SHEL: if (!player.GiveAmmo(ammotype_t.am_shell, 1)) return; player.message = GOTSHELLS; break; case SPR_SBOX: if (!player.GiveAmmo(ammotype_t.am_shell, 5)) return; player.message = GOTSHELLBOX; break; case SPR_BPAK: if (!player.backpack) { for (i = 0; i < NUMAMMO; i++) player.maxammo[i] *= 2; player.backpack = true; } for (i = 0; i < NUMAMMO; i++) player.GiveAmmo(ammotype_t.values()[i], 1); player.message = GOTBACKPACK; break; // weapons case SPR_BFUG: if (!player.GiveWeapon(weapontype_t.wp_bfg, false)) return; player.message = GOTBFG9000; sound = sfxenum_t.sfx_wpnup; break; case SPR_MGUN: if (!player.GiveWeapon(weapontype_t.wp_chaingun, (special.flags & MF_DROPPED) != 0)) return; player.message = GOTCHAINGUN; sound = sfxenum_t.sfx_wpnup; break; case SPR_CSAW: if (!player.GiveWeapon(weapontype_t.wp_chainsaw, false)) return; player.message = GOTCHAINSAW; sound = sfxenum_t.sfx_wpnup; break; case SPR_LAUN: if (!player.GiveWeapon(weapontype_t.wp_missile, false)) return; player.message = GOTLAUNCHER; sound = sfxenum_t.sfx_wpnup; break; case SPR_PLAS: if (!player.GiveWeapon(weapontype_t.wp_plasma, false)) return; player.message = GOTPLASMA; sound = sfxenum_t.sfx_wpnup; break; case SPR_SHOT: if (!player.GiveWeapon(weapontype_t.wp_shotgun, (special.flags & MF_DROPPED) != 0)) return; player.message = GOTSHOTGUN; sound = sfxenum_t.sfx_wpnup; break; case SPR_SGN2: if (!player.GiveWeapon(weapontype_t.wp_supershotgun, (special.flags & MF_DROPPED) != 0)) return; player.message = GOTSHOTGUN2; sound = sfxenum_t.sfx_wpnup; break; default: I.Error("P_SpecialThing: Unknown gettable thing"); } if ((special.flags & MF_COUNTITEM) != 0) player.itemcount++; RemoveMobj(special); player.bonuscount += player_t.BONUSADD; if (player == DM.players[DM.consoleplayer]) S.StartSound (null, sound); } @Override public thinker_t getThinkerCap() { return thinkercap; } /** * killough 11/98: * * Make currentthinker external, so that P_RemoveThinkerDelayed * can adjust currentthinker when thinkers self-remove. */ protected thinker_t currentthinker; protected final P_RemoveThinkerDelayed RemoveThinkerDelayed; public class P_RemoveThinkerDelayed implements ActionTypeSS <thinker_t>{ @Override public void invoke(thinker_t thinker) { /* try { System.err.printf("Delete: %s %d<= %s %d => %s %d\n", ((mobj_t)thinker.prev).type,((mobj_t)thinker.prev).thingnum, ((mobj_t)thinker).type,((mobj_t)thinker).thingnum, ((mobj_t)thinker.next).type,((mobj_t)thinker.next).thingnum); } catch (ClassCastException e){ } */ // Unlike Boom, if we reach here it gets zapped anyway //if (!thinker->references) //{ { /* Remove from main thinker list */ thinker_t next = thinker.next; /* Note that currentthinker is guaranteed to point to us, * and since we're freeing our memory, we had better change that. So * point it to thinker->prev, so the iterator will correctly move on to * thinker->prev->next = thinker->next */ (next.prev = currentthinker = thinker.prev).next = next; //thinker.next=thinker.prev=null; try { // System.err.printf("Delete: %s %d <==> %s %d\n", // ((mobj_t)currentthinker.prev).type,((mobj_t)currentthinker.prev).thingnum, // ((mobj_t)currentthinker.next).type,((mobj_t)currentthinker.next).thingnum); } catch (ClassCastException e){ } } { /* Remove from current thinker class list */ thinker_t th = thinker.cnext; (th.cprev = thinker.cprev).cnext = th; //thinker.cnext=thinker.cprev=null; } } } } // End unified map package p; import static m.fixed_t.*; import rr.line_t; import static utils.C2JUtils.eval; // // P_MAPUTL // public class divline_t { /** fixed_t */ public int x, y, dx, dy; /** *P_PointOnDivlineSide *Returns 0 or 1. (false or true) *@param x fixed *@param y fixed *@param divline_t */ public boolean PointOnDivlineSide ( int x, int y ) { // Using Killough's version. return (dx==0) ? x <= this.x ? dy > 0 : dy < 0 : (dy==0) ? y <= this.y ? dx < 0 : dx > 0 : (dy^dx^(x -= this.x)^(y -= this.y)) < 0 ? (dy^x) < 0 : FixedMul(y>>8, this.dx>>8) >= FixedMul(this.dy>>8, x>>8); /* int PUREFUNC P_PointOnDivlineSide(fixed_t x, fixed_t y, const divline_t *line) { return !line->dx ? x <= line->x ? line->dy > 0 : line->dy < 0 : !line->dy ? y <= line->y ? line->dx < 0 : line->dx > 0 : (line->dy^line->dx^(x -= line->x)^(y -= line->y)) < 0 ? (line->dy^x) < 0 : FixedMul(y>>8, line->dx>>8) >= FixedMul(line->dy>>8, x>>8); }*/ /* int dx; int dy; int left; int right; if (this.dx==0) { if (x <= this.x) return this.dy > 0; return this.dy < 0; } if (this.dy==0) { if (y <= this.y) return this.dx < 0; return this.dx > 0; } dx = (x - this.x); dy = (y - this.y); // try to quickly decide by looking at sign bits if ( ((this.dy ^ this.dx ^ dx ^ dy)&0x80000000) !=0) { if (((this.dy ^ dx) & 0x80000000) !=0) return true; // (left is negative) return false; } left = FixedMul ( this.dy>>8, dx>>8 ); right = FixedMul ( dy>>8 , this.dx>>8 ); if (right < left) return false; // front side return true; // back side */ } // //P_MakeDivline // public void MakeDivline ( line_t li) { this.x = li.v1x; this.y = li.v1y; this.dx = li.dx; this.dy = li.dy; } public divline_t(line_t li) { this.x = li.v1x; this.y = li.v1y; this.dx = li.dx; this.dy = li.dy; } public divline_t() { // TODO Auto-generated constructor stub } /** * P_DivlineSide * Returns side 0 (front), 1 (back), or 2 (on). */ public int DivlineSide ( int x, int y) { int left,right; // Boom-style code. Da fack. // [Maes:] it is MUCH more corrent than the linuxdoom one, for whatever reason. return (this.dx==0) ? x == this.x ? 2 : x <= this.x ? eval(this.dy > 0) : eval(this.dy < 0) : (this.dy==0) ? (olddemo ? x : y) == this.y ? 2 : y <= this.y ? eval(this.dx < 0) : eval(this.dx > 0) : (this.dy==0) ? y == this.y ? 2 : y <= this.y ? eval(this.dx < 0) : eval(this.dx > 0) : (right = ((y - this.y) >> FRACBITS) * (this.dx >> FRACBITS)) < (left = ((x - this.x) >> FRACBITS) * (this.dy >> FRACBITS)) ? 0 : right == left ? 2 : 1; /* int left,right,dx,dy; if (this.dx==0) { if (x==this.x) return 2; if (x <= this.x) return eval(this.dy > 0); return eval(this.y < 0); } if (this.dy==0) { if (x==this.y) return 2; if (y <= this.y) return eval(this.dx < 0); return eval(this.dx > 0); } dx = (x - this.x); dy = (y - this.y); left = (this.dy>>FRACBITS) * (dx>>FRACBITS); right = (dy>>FRACBITS) * (this.dx>>FRACBITS); if (right < left) return 0; // front side if (left == right) return 2; return 1; // back side */ } private static final boolean olddemo = true; } package p; import java.io.DataInputStream; import java.io.IOException; import java.nio.ByteBuffer; import rr.SectorAction; import w.DoomIO; import w.IReadableDoomObject; public class floormove_t extends SectorAction implements IReadableDoomObject{ public floormove_t(){ // MAES HACK: floors are implied to be at least of "lowerFloor" type // unless set otherwise, due to implicit zero-enum value. this.type=floor_e.lowerFloor; } public floor_e type; public boolean crush; public int direction; public int newspecial; public short texture; /** fixed_t */ public int floordestheight; /** fixed_t */ public int speed; @Override public void read(DataInputStream f) throws IOException{ super.read(f); // Call thinker reader first type=floor_e.values()[DoomIO.readLEInt(f)]; crush=DoomIO.readIntBoolean(f); super.sectorid=DoomIO.readLEInt(f); // Sector index (or pointer?) direction=DoomIO.readLEInt(f); newspecial=DoomIO.readLEInt(f); texture=DoomIO.readLEShort(f); floordestheight=DoomIO.readLEInt(f); speed=DoomIO.readLEInt(f); } @Override public void pack(ByteBuffer b) throws IOException{ super.pack(b); //12 b.putInt(type.ordinal()); // 16 b.putInt(crush?1:0); //20 b.putInt(super.sectorid); // 24 b.putInt(direction); // 28 b.putInt(newspecial); // 32 b.putShort(texture); // 34 b.putInt(floordestheight); // 38 b.putInt(speed); // 42 } } package p; import static m.fixed_t.FRACUNIT; import static m.fixed_t.MAPFRACUNIT; public final class DoorDefines { // Doors public static final int VDOORSPEED = MAPFRACUNIT * 2; public static final int VDOORWAIT = 150; // Lights public static final int GLOWSPEED = 5; public static final int STROBEBRIGHT = 5; public static final int FASTDARK = 15; public static final int SLOWDARK = 35; } package p; public enum plattype_e { perpetualRaise, downWaitUpStay, raiseAndChange, raiseToNearestAndChange, blazeDWUS } package p; public enum stair_e { build8, // slowly build by 8 turbo16 // quickly build by 16 } package p; import i.DoomStatusAware; import i.IDoomSystem; import doom.DoomStatus; import doom.think_t; import rr.TextureManager; import rr.line_t; import rr.sector_t; import utils.C2JUtils; public class SlideDoor implements DoomStatusAware { protected TextureManager<?> TM; protected AbstractLevelLoader LL; protected DoomStatus<?,?> DM; protected Actions P; protected IDoomSystem I; // UNUSED // Separate into p_slidoor.c? // ABANDONED TO THE MISTS OF TIME!!! // // EV_SlidingDoor : slide a door horizontally // (animate midtexture, then set noblocking line) // public static final int MAXSLIDEDOORS =5; // how many frames of animation public static final int SNUMFRAMES = 4; public static final int SDOORWAIT =35*3; public static final int SWAITTICS = 4; final slidename_t[] slideFrameNames= { new slidename_t("GDOORF1","GDOORF2","GDOORF3","GDOORF4", // front "GDOORB1","GDOORB2","GDOORB3","GDOORB4"), // back new slidename_t(),new slidename_t(),new slidename_t(),new slidename_t() }; final slideframe_t[] slideFrames; public SlideDoor(){ slideFrames=new slideframe_t[MAXSLIDEDOORS]; C2JUtils.initArrayOfObjects(slideFrames); } public SlideDoor(DoomStatus<?,?> DS) { this(); this.updateStatus(DS); } void P_InitSlidingDoorFrames() { int i; int f1; int f2; int f3; int f4; // DOOM II ONLY... if (!DM.isCommercial()) return; for (i = 0;i < MAXSLIDEDOORS; i++) { if (slideFrameNames[i].frontFrame1==null) break; f1 = TM.TextureNumForName(slideFrameNames[i].frontFrame1); f2 = TM.TextureNumForName(slideFrameNames[i].frontFrame2); f3 = TM.TextureNumForName(slideFrameNames[i].frontFrame3); f4 = TM.TextureNumForName(slideFrameNames[i].frontFrame4); slideFrames[i].frontFrames[0] = f1; slideFrames[i].frontFrames[1] = f2; slideFrames[i].frontFrames[2] = f3; slideFrames[i].frontFrames[3] = f4; f1 = TM.TextureNumForName(slideFrameNames[i].backFrame1); f2 = TM.TextureNumForName(slideFrameNames[i].backFrame2); f3 = TM.TextureNumForName(slideFrameNames[i].backFrame3); f4 = TM.TextureNumForName(slideFrameNames[i].backFrame4); slideFrames[i].backFrames[0] = f1; slideFrames[i].backFrames[1] = f2; slideFrames[i].backFrames[2] = f3; slideFrames[i].backFrames[3] = f4; } } // // Return index into "slideFrames" array // for which door type to use // int P_FindSlidingDoorType(line_t line) { int i; int val; for (i = 0;i < MAXSLIDEDOORS;i++) { val = LL.sides[line.sidenum[0]].midtexture; if (val == slideFrames[i].frontFrames[0]) return i; } return -1; } public void EV_SlidingDoor ( line_t line, mobj_t thing ) { sector_t sec; slidedoor_t door; // DOOM II ONLY... if (!DM.isCommercial()) return; System.err.println("EV_SlidingDoor"); // Make sure door isn't already being animated sec = line.frontsector; door = null; if (sec.specialdata!=null) { if (thing.player==null) return; door = (slidedoor_t) sec.specialdata; if (door.type == sdt_e.sdt_openAndClose) { if (door.status == sd_e.sd_waiting) door.status = sd_e.sd_closing; } else return; } // Init sliding door vars if (door==null) { door = new slidedoor_t(); P.AddThinker (door); sec.specialdata = door; door.type = sdt_e.sdt_openAndClose; door.status = sd_e.sd_opening; door.whichDoorIndex = P_FindSlidingDoorType(line); if (door.whichDoorIndex < 0) I.Error("EV_SlidingDoor: Can't use texture for sliding door!"); door.frontsector = sec; door.backsector = line.backsector; door.function = think_t.T_SlidingDoor; door.timer = SWAITTICS; door.frame = 0; door.line = line; } } @Override public void updateStatus(DoomStatus<?,?> DS) { this.DM=DS; this.I=DM.I; this.LL=DM.LL; this.P=DM.P; this.TM=DM.TM; } } package p; /** For objects that needed to be memset to 0 in C, * rather than being reallocated. */ public interface Resettable { public void reset(); } package p; public interface ActionType1{ public void invoke (mobj_t actor); } package p; import java.io.DataInputStream; import java.io.IOException; import java.nio.ByteBuffer; import rr.SectorAction; import w.DoomIO; import w.IReadableDoomObject; public class vldoor_t extends SectorAction implements IReadableDoomObject{ public vldoor_e type; /** fixed_t */ public int topheight, speed; /** 1 = up, 0 = waiting at top, -1 = down */ public int direction; /** tics to wait at the top */ public int topwait; /**(keep in case a door going down is reset) when it reaches 0, start going down */ public int topcountdown; @Override public void read(DataInputStream f) throws IOException{ super.read(f); // Call thinker reader first type=vldoor_e.values()[DoomIO.readLEInt(f)]; super.sectorid=DoomIO.readLEInt(f); // Sector index (or pointer?) topheight=DoomIO.readLEInt(f); speed=DoomIO.readLEInt(f); direction=DoomIO.readLEInt(f); topwait=DoomIO.readLEInt(f); topcountdown=DoomIO.readLEInt(f); } @Override public void pack(ByteBuffer b) throws IOException{ super.pack(b); //12 b.putInt(type.ordinal()); // 16 b.putInt(super.sectorid); // 20 b.putInt(topheight); // 24 b.putInt(speed); //28 b.putInt(direction); // 32 b.putInt(topwait); //36 b.putInt(topcountdown); //40 } } package p; import static data.Defines.FLOATSPEED; import static data.Defines.GRAVITY; import static data.Defines.VIEWHEIGHT; import static data.info.states; import static p.MapUtils.AproxDistance; import static utils.C2JUtils.pointer; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import rr.subsector_t; import s.ISoundOrigin; import w.IPackableDoomObject; import w.IReadableDoomObject; import w.IWritableDoomObject; import data.Tables; import data.mapthing_t; import data.mobjinfo_t; import data.mobjtype_t; import data.sounds.sfxenum_t; import data.spritenum_t; import data.state_t; import defines.*; import doom.player_t; import doom.thinker_t; /** * * NOTES: mobj_t * * mobj_ts are used to tell the refresh where to draw an image, tell the world * simulation when objects are contacted, and tell the sound driver how to * position a sound. * * The refresh uses the next and prev links to follow lists of things in sectors * as they are being drawn. The sprite, frame, and angle elements determine * which patch_t is used to draw the sprite if it is visible. The sprite and * frame values are allmost allways set from state_t structures. The * statescr.exe utility generates the states.h and states.c files that contain * the sprite/frame numbers from the statescr.txt source file. The xyz origin * point represents a point at the bottom middle of the sprite (between the feet * of a biped). This is the default origin position for patch_ts grabbed with * lumpy.exe. A walking creature will have its z equal to the floor it is * standing on. * * The sound code uses the x,y, and subsector fields to do stereo positioning of * any sound effited by the mobj_t. * * The play simulation uses the blocklinks, x,y,z, radius, height to determine * when mobj_ts are touching each other, touching lines in the map, or hit by * trace lines (gunshots, lines of sight, etc). The mobj_t->flags element has * various bit flags used by the simulation. * * Every mobj_t is linked into a single sector based on its origin coordinates. * The subsector_t is found with R_PointInSubsector(x,y), and the sector_t can * be found with subsector->sector. The sector links are only used by the * rendering code, the play simulation does not care about them at all. * * Any mobj_t that needs to be acted upon by something else in the play world * (block movement, be shot, etc) will also need to be linked into the blockmap. * If the thing has the MF_NOBLOCK flag set, it will not use the block links. It * can still interact with other things, but only as the instigator (missiles * will run into other things, but nothing can run into a missile). Each block * in the grid is 128*128 units, and knows about every line_t that it contains a * piece of, and every interactable mobj_t that has its origin contained. * * A valid mobj_t is a mobj_t that has the proper subsector_t filled in for its * xy coordinates and is linked into the sector from which the subsector was * made, or has the MF_NOSECTOR flag set (the subsector_t needs to be valid even * if MF_NOSECTOR is set), and is linked into a blockmap block or has the * MF_NOBLOCKMAP flag set. Links should only be modified by the * P_[Un]SetThingPosition() functions. Do not change the MF_NO? flags while a * thing is valid. * * Any questions? * * @author admin * */ public class mobj_t extends thinker_t implements ISoundOrigin, Interceptable, IWritableDoomObject, IPackableDoomObject, IReadableDoomObject { Actions A; public mobj_t() { this.spawnpoint = new mapthing_t(); } public mobj_t(Actions A) { this(); this.A = A; // A mobj_t is ALSO a thinker, as it always contains the struct. // Don't fall for C's trickery ;-) // this.thinker=new thinker_t(); } /* List: thinker links. */ // public thinker_t thinker; /** (fixed_t) Info for drawing: position. */ public int x, y, z; /** More list: links in sector (if needed) */ public thinker_t snext, sprev; // More drawing info: to determine current sprite. /** * orientation. This needs to be long or else certain checks will fail...but * I need to see it working in order to confirm */ public long angle; /** used to find patch_t and flip value */ public spritenum_t sprite; /** might be ORed with FF_FULLBRIGHT */ public int frame; /** Interaction info, by BLOCKMAP. Links in blocks (if needed). */ public thinker_t bnext, bprev; /** MAES: was actually a pointer to a struct subsector_s */ public subsector_t subsector; /** (fixed_t) The closest interval over all contacted Sectors. */ public int floorz, ceilingz; /** (fixed_t) For movement checking. */ public int radius, height; /** (fixed_t) Momentums, used to update position. */ public int momx, momy, momz; /** If == validcount, already checked. */ public int validcount; public mobjtype_t type; // MAES: was a pointer public mobjinfo_t info; // &mobjinfo[mobj.type] public long tics; // state tic counter // MAES: was a pointer public state_t state; public long flags; public int health; /** Movement direction, movement generation (zig-zagging). */ public int movedir; // 0-7 public int movecount; // when 0, select a new dir /** * Thing being chased/attacked (or NULL), also the originator for missiles. * MAES: was a pointer */ public mobj_t target; public int p_target; // for savegames /** * Reaction time: if non 0, don't attack yet. Used by player to freeze a bit * after teleporting. */ public int reactiontime; /** * If >0, the target will be chased no matter what (even if shot) */ public int threshold; /** * Additional info record for player avatars only. Only valid if type == * MT_PLAYER struct player_s* player; */ public player_t player; /** Player number last looked for. */ public int lastlook; /** For nightmare respawn. */ public mapthing_t spawnpoint; // struct /** Thing being chased/attacked for tracers. */ public mobj_t tracer; // MAES: was a pointer // // MF_ flags for mobjs. // Call P_SpecialThing when touched. public static final int MF_SPECIAL = 1; // Blocks. public static final int MF_SOLID = 2; // Can be hit. public static final int MF_SHOOTABLE = 4; // Don't use the sector links (invisible but touchable). public static final int MF_NOSECTOR = 8; // Don't use the blocklinks (inert but displayable) public static final int MF_NOBLOCKMAP = 16; // Not to be activated by sound, deaf monster. public static final int MF_AMBUSH = 32; // Will try to attack right back. public static final int MF_JUSTHIT = 64; // Will take at least one step before attacking. public static final int MF_JUSTATTACKED = 128; // On level spawning (initial position), // hang from ceiling instead of stand on floor. public static final int MF_SPAWNCEILING = 256; // Don't apply gravity (every tic), // that is, object will float, keeping current height // or changing it actively. public static final int MF_NOGRAVITY = 512; // Movement flags. // This allows jumps from high places. public static final int MF_DROPOFF = 0x400; // For players, will pick up items. public static final int MF_PICKUP = 0x800; // Player cheat. ??? public static final int MF_NOCLIP = 0x1000; // Player: keep info about sliding along walls. public static final int MF_SLIDE = 0x2000; // Allow moves to any height, no gravity. // For active floaters, e.g. cacodemons, pain elementals. public static final int MF_FLOAT = 0x4000; // Don't cross lines // ??? or look at heights on teleport. public static final int MF_TELEPORT = 0x8000; // Don't hit same species, explode on block. // Player missiles as well as fireballs of various kinds. public static final int MF_MISSILE = 0x10000; // Dropped by a demon, not level spawned. // E.g. ammo clips dropped by dying former humans. public static final int MF_DROPPED = 0x20000; // Use fuzzy draw (shadow demons or spectres), // temporary player invisibility powerup. public static final int MF_SHADOW = 0x40000; // Flag: don't bleed when shot (use puff), // barrels and shootable furniture shall not bleed. public static final int MF_NOBLOOD = 0x80000; // Don't stop moving halfway off a step, // that is, have dead bodies slide down all the way. public static final int MF_CORPSE = 0x100000; // Floating to a height for a move, ??? // don't auto float to target's height. public static final int MF_INFLOAT = 0x200000; // On kill, count this enemy object // towards intermission kill total. // Happy gathering. public static final int MF_COUNTKILL = 0x400000; // On picking up, count this item object // towards intermission item total. public static final int MF_COUNTITEM = 0x800000; // Special handling: skull in flight. // Neither a cacodemon nor a missile. public static final int MF_SKULLFLY = 0x1000000; // Don't spawn this object // in death match mode (e.g. key cards). public static final int MF_NOTDMATCH = 0x2000000; // Player sprites in multiplayer modes are modified // using an internal color lookup table for re-indexing. // If 0x4 0x8 or 0xc, // use a translation table for player colormaps public static final int MF_TRANSLATION = 0xc000000; // Hmm ???. public static final int MF_TRANSSHIFT = 26; /* * The following methods were for the most part "contextless" and * instance-specific, so they were implemented here rather that being * scattered all over the package. */ /** * P_SetMobjState Returns true if the mobj is still present. */ public boolean SetMobjState(statenum_t state) { state_t st; do { if (state == statenum_t.S_NULL) { state = null; // MAES/_D_: uncommented this as it should work by now (?). A.RemoveMobj(this); return false; } st = states[state.ordinal()]; this.state = st; tics = st.tics; sprite = st.sprite; frame = (int) st.frame; // Modified handling. // Call action functions when the state is set if (st.acp1 != null) { st.acp1.invoke(this); } state = st.nextstate; } while (tics == 0); return true; } /** * P_ZMovement */ public void ZMovement() { // fixed_t int dist; int delta; // check for smooth step up if ((player != null) && z < floorz) { player.viewheight -= floorz - z; player.deltaviewheight = (VIEWHEIGHT - player.viewheight) >> 3; } // adjust height z += momz; if (((flags & MF_FLOAT) != 0) && target != null) { // float down towards target if too close if ((flags & MF_SKULLFLY) == 0 && (flags & MF_INFLOAT) == 0) { dist = AproxDistance(x - target.x, y - target.y); delta = (target.z + (height >> 1)) - z; if (delta < 0 && dist < -(delta * 3)) z -= FLOATSPEED; else if (delta > 0 && dist < (delta * 3)) z += FLOATSPEED; } } // clip movement if (z <= floorz) { // hit the floor // Note (id): // somebody left this after the setting momz to 0, // kinda useless there. if ((flags & MF_SKULLFLY) != 0) { // the skull slammed into something momz = -momz; } if (momz < 0) { if (player != null && (momz < -GRAVITY * 8)) { // Squat down. // Decrease viewheight for a moment // after hitting the ground (hard), // and utter appropriate sound. player.deltaviewheight = momz >> 3; A.S.StartSound(this, sfxenum_t.sfx_oof); } momz = 0; } z = floorz; if ((flags & MF_MISSILE) != 0 && (flags & MF_NOCLIP) == 0) { A.ExplodeMissile(this); return; } } else if ((flags & MF_NOGRAVITY) == 0) { if (momz == 0) momz = -GRAVITY * 2; else momz -= GRAVITY; } if (z + height > ceilingz) { // hit the ceiling if (momz > 0) momz = 0; { z = ceilingz - height; } if ((flags & MF_SKULLFLY) != 0) { // the skull slammed into // something momz = -momz; } if ((flags & MF_MISSILE) != 0 && (flags & MF_NOCLIP) == 0) { A.ExplodeMissile(this); return; } } } public int eflags; // DOOM LEGACY // Fields used only during DSG unmarshalling public int stateid; public int playerid; public int p_tracer; /** Unique thing id, used during sync debugging */ public int thingnum; public void clear() { fastclear.rewind(); try { this.unpack(mobj_t.fastclear); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } // _D_: to permit this object to save/load public void read(DataInputStream f) throws IOException { // More efficient, avoids duplicating code and // handles little endian better. buffer.position(0); buffer.order(ByteOrder.LITTLE_ENDIAN); f.read(buffer.array()); this.unpack(buffer); } @Override public void write(DataOutputStream f) throws IOException { // More efficient, avoids duplicating code and // handles little endian better. buffer.position(0); buffer.order(ByteOrder.LITTLE_ENDIAN); this.pack(buffer); f.write(buffer.array()); } public void pack(ByteBuffer b) throws IOException { b.order(ByteOrder.LITTLE_ENDIAN); super.pack(b); // Pack the head thinker. b.putInt(x); b.putInt(y); b.putInt(z); b.putInt(pointer(snext)); b.putInt(pointer(sprev)); b.putInt((int) (this.angle & Tables.BITS32)); b.putInt(this.sprite.ordinal()); b.putInt(this.frame); b.putInt(pointer(bnext)); b.putInt(pointer(bprev)); b.putInt(pointer(subsector)); b.putInt(floorz); b.putInt(ceilingz); b.putInt(radius); b.putInt(height); b.putInt(momx); b.putInt(momy); b.putInt(momz); b.putInt(validcount); b.putInt(type.ordinal()); b.putInt(pointer(info)); // TODO: mobjinfo b.putInt((int) (this.tics & Tables.BITS32)); b.putInt(this.state.id); // TODO: state OK? b.putInt((int) this.flags); // truncate b.putInt(this.health); b.putInt(this.movedir); b.putInt(this.movecount); b.putInt(pointer(target)); // TODO: p_target? b.putInt(this.reactiontime); b.putInt(this.threshold); // Check for player. if (this.player != null) { b.putInt(1 + this.player.identify()); // System.out.printf("Mobj with hashcode %d is player %d",pointer(this),1+this.player.identify()); } else b.putInt(0); b.putInt(lastlook); spawnpoint.pack(b); b.putInt(pointer(tracer)); // tracer pointer stored. } public void unpack(ByteBuffer b) throws IOException { b.order(ByteOrder.LITTLE_ENDIAN); super.unpack(b); // 12 Read the head thinker. this.x = b.getInt(); // 16 this.y = b.getInt(); // 20 this.z = b.getInt(); // 24 b.getLong(); // TODO: snext, sprev. When are those set? 32 this.angle = Tables.BITS32 & b.getInt(); // 36 this.sprite = spritenum_t.values()[b.getInt()]; // 40 this.frame = b.getInt(); // 44 b.getLong(); // TODO: bnext, bprev. When are those set? 52 b.getInt(); // TODO: subsector 56 this.floorz = b.getInt(); // 60 this.ceilingz = b.getInt(); // 64 this.radius = b.getInt(); // 68 this.height = b.getInt(); // 72 this.momx = b.getInt(); // 76 this.momy = b.getInt(); // 80 this.momz = b.getInt(); // 84 this.validcount = b.getInt(); // 88 this.type = mobjtype_t.values()[b.getInt()]; // 92 b.getInt(); // TODO: mobjinfo (deduced from type) //96 this.tics = Tables.BITS32 & b.getInt(); // 100 // System.out.println("State"+f.readLEInt()); this.stateid = b.getInt(); // TODO: state OK? this.flags = b.getInt()&Tables.BITS32; // Only 32-bit flags can be restored this.health = b.getInt(); this.movedir = b.getInt(); this.movecount = b.getInt(); this.p_target = b.getInt(); this.reactiontime = b.getInt(); this.threshold = b.getInt(); this.playerid = b.getInt(); // TODO: player. Non null should mean that // it IS a player. this.lastlook = b.getInt(); spawnpoint.unpack(b); this.p_tracer = b.getInt(); // TODO: tracer } private static ByteBuffer buffer = ByteBuffer.allocate(154); private static ByteBuffer fastclear = ByteBuffer.allocate(154); /* * @Override protected void finalize(){ count++; if (count%100==0) * System.err * .printf("Total %d Mobj %s@%d finalized free memory: %d\n",count, * this.type.name(),this.hashCode(),Runtime.getRuntime().freeMemory()); } */ protected static int count = 0; // TODO: a linked list of sectors where this object appears // public msecnode_t touching_sectorlist; // Sound origin stuff @Override public final int getX() { return x; } @Override public final int getY() { return y; } @Override public final int getZ() { return z; } public String toString(){ return String.format("%s %d",this.type,this.thingnum); } } package p; import doom.thinker_t; public interface ThinkerList { void AddThinker(thinker_t thinker); void RemoveThinker(thinker_t thinker); void InitThinkers(); int getNumThinkers(); thinker_t getRandomThinker(); thinker_t getThinkerCap(); } package p; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import w.CacheableDoomObject; import w.DoomBuffer; // // P_SWITCH // public class switchlist_t implements CacheableDoomObject { public switchlist_t(){ } // Were char[9] public String name1; public String name2; public short episode; public switchlist_t(String name1, String name2, int episode) { super(); this.name1 = name1; this.name2 = name2; this.episode = (short) episode; } @Override public void unpack(ByteBuffer buf) throws IOException { // Like most Doom structs... buf.order(ByteOrder.LITTLE_ENDIAN); this.name1 = DoomBuffer.getNullTerminatedString(buf, 9); this.name2 = DoomBuffer.getNullTerminatedString(buf, 9); this.episode = buf.getShort(); } public final static int size() { return 20; } public String toString() { return String.format("%s %s %d", name1, name2, episode); } } package p; import rr.line_t; /** An object that carries...interception information, I guess...with either a line * or an object? * * @author Velktron * */ public class intercept_t { /** most intercepts will belong to a static pool */ public intercept_t() { } public intercept_t(int frac, mobj_t thing){ this.frac=frac; this.thing=thing; this.isaline=false; } public intercept_t(int frac, line_t line){ this.frac=frac; this.line=line; this.isaline=true; } /** fixed_t, along trace line */ public int frac; public boolean isaline; // MAES: this was an union of a mobj_t and a line_t, // returned as "d". mobj_t thing; line_t line; public Interceptable d(){ return (isaline)? line: thing; } } package p; import m.fixed_t; public interface DoomPlayer { public fixed_t AimLineAttack ( mobj_t t1, int angle, fixed_t distance ); public void LineAttack ( mobj_t t1, int angle, fixed_t distance, fixed_t slope, int damage ); void RadiusAttack ( mobj_t spot, mobj_t source, int damage ); void TouchSpecialThing ( mobj_t special, mobj_t toucher ); void DamageMobj ( mobj_t target, mobj_t inflictor, mobj_t source, int damage ); } package p; /** Animating textures and planes * There is another anim_t used in wi_stuff, unrelated. * * @author admin * */ public class anim_t { public anim_t(){ } public anim_t(boolean istexture, int picnum, int basepic, int numpics, int speed) { super(); this.istexture = istexture; this.picnum = picnum; this.basepic = basepic; this.numpics = numpics; this.speed = speed; } public boolean istexture; public int picnum; public int basepic; public int numpics; public int speed; } package p; import java.io.DataInputStream; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import rr.SectorAction; import w.CacheableDoomObject; import w.IPackableDoomObject; import w.IReadableDoomObject; public class ceiling_t extends SectorAction implements CacheableDoomObject,IReadableDoomObject,IPackableDoomObject{ public ceiling_e type; /** fixed_t */ public int bottomheight; /** fixed_t */ public int topheight; /** fixed_t */ public int speed; boolean crush; // 1 = up, 0 = waiting, -1 = down public int direction; // ID public int tag; public int olddirection; public ceiling_t(){ // Set to the smallest ordinal type. this.type=ceiling_e.lowerToFloor; } // HACK for speed. public static final ceiling_e[] values=ceiling_e.values(); @Override public void read(DataInputStream f) throws IOException{ // Read 48 bytes. readbuffer.position(0); readbuffer.order(ByteOrder.LITTLE_ENDIAN); f.read(readbuffer.array(), 0,48); unpack(readbuffer); } @Override public void pack(ByteBuffer b) throws IOException{ b.order(ByteOrder.LITTLE_ENDIAN); super.pack(b); //12 b.putInt(type.ordinal()); // 16 b.putInt(super.sectorid); // 20 b.putInt(bottomheight); b.putInt(topheight); // 28 b.putInt(speed); b.putInt(crush?1:0); b.putInt(direction); // 40 b.putInt(tag); b.putInt(olddirection); //48 } @Override public void unpack(ByteBuffer b) throws IOException { b.order(ByteOrder.LITTLE_ENDIAN); super.unpack(b); // Call thinker reader first type=values[b.getInt()]; super.sectorid=b.getInt(); // sector pointer. bottomheight=b.getInt(); topheight=b.getInt(); speed=b.getInt(); crush=(b.getInt()!=0); direction=b.getInt(); tag=b.getInt(); olddirection=b.getInt(); } private static ByteBuffer readbuffer=ByteBuffer.allocate(48); } package p; public enum sdt_e { sdt_openOnly, sdt_closeOnly, sdt_openAndClose } package p; public enum bwhere_e { top, middle, bottom } package p; import java.io.DataInputStream; import java.io.IOException; import java.nio.ByteBuffer; import rr.SectorAction; import w.DoomIO; public class strobe_t extends SectorAction{ public int count; public int minlight; public int maxlight; public int darktime; public int brighttime; // // T_StrobeFlash // public void StrobeFlash() { if (--count != 0) return; if (sector.lightlevel == minlight) { sector.lightlevel = (short) maxlight; count = brighttime; } else { sector.lightlevel = (short) minlight; count = darktime; } } @Override public void read(DataInputStream f) throws IOException{ super.read(f); // Call thinker reader first super.sectorid=DoomIO.readLEInt(f); // Sector index count=DoomIO.readLEInt(f); maxlight=DoomIO.readLEInt(f); minlight=DoomIO.readLEInt(f); darktime=DoomIO.readLEInt(f); brighttime=DoomIO.readLEInt(f); } @Override public void pack(ByteBuffer b) throws IOException{ super.pack(b); //12 b.putInt(super.sectorid); // 16 b.putInt(count); //20 b.putInt(maxlight);//24 b.putInt(minlight);//28 b.putInt(darktime);//32 b.putInt(brighttime);//36 } };
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: | 73ca03f98e9862a606ae662c56a197f1 |
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: | 11839 / 21636 |
Referenced in: | [show references] |