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 void initArrayOfObjects(T[] os, Class 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 void initArrayOfObjects(T[] os) { Class c = (Class) 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 * @param c * @param num * @return * @return */ public static final T[] createArrayOfObjects(Class 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 * @param instance An instance of a particular class. * @param num * @return * @return */ @SuppressWarnings("unchecked") public static final T[] createArrayOfObjects(T instance, int num) { T[] os = null; Class c=(Class) 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 void initArrayOfObjects(T[] os, int startpos, int endpos) { @SuppressWarnings("unchecked") Class c = (Class) 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[] 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 * @param instance * @param oldarray * @param newsize * @return */ public static final 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 * @param oldarray * @param newsize * @return */ public static final 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[] getNewArray(T instance,int size){ T[] os=null; Class c=(Class) 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[] getNewArray(int size,T instance){ @SuppressWarnings("unchecked") Class c=(Class) instance.getClass(); return getNewArray(c,size); } @SuppressWarnings("unchecked") public final static T[] getNewArray(Class 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{ public DoomMain DM; public IDoomGame DG; public IWadLoader W; public IRandom RND; public IDoomSystem I; public IDoomSound S; public ISoundDriver ISND; public IMusic IMUS; public DoomVideoInterface VI; public AbstractStatusBar ST; public DoomVideoRenderer V; public DoomSystemNetworking DNI; public IDoomGameNetworking DGN; public AbstractLevelLoader LL; public IDoomMenu M; public Actions P; public Renderer R; public HU HU; public IAutoMap AM; public Finale F; public EndLevel WI; public Wiper WIPE; public TextureManager 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 VersionChecker=new Hashtable(); 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 extends DoomContext 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 selectFinale(); protected abstract DoomVideoRenderer selectVideoRenderer(); protected abstract Renderer selectRenderer(); protected abstract Wiper selectWiper(); protected abstract IAutoMap 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 extends DoomStatus 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 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 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 videoScaleChildren; public void initializeVideoScaleStuff() { videoScaleChildren=new ArrayList(); // 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 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< 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>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= 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= pars.length) wminfo.partime = 0; else wminfo.partime = 35*pars[gameepisode][gamemap]; wminfo.pnum = consoleplayer; for (i=0 ; i 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>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 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 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 ; iretransmitfrom)[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 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= 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 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 ; i0) && HGetPacket(); --i) { if((netbuffer.player&0x7f) < MAXNETNODES) gotinfo[netbuffer.player&0x7f] = true; } /* while (HGetPacket ()) { gotinfo[netbuffer.player&0x7f] = true; } */ for (i=1 ; i 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= 20) { M.Ticker (); return; } } // run the count * ticdup dics while (counts-->0) { for (i=0 ; i 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(); // 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{ public HiColor(){ super(); } protected IAutoMap selectAutoMap() { return new Map.HiColor(this); } protected final Finale selectFinale(){ return new Finale(this); } protected final DoomVideoRenderer selectVideoRenderer(){ return new BufferedRenderer16(SCREENWIDTH,SCREENHEIGHT); } protected final DoomVideoInterface selectVideoInterface(){ return new AWTDoom.HiColor(this,V); } protected final Wiper selectWiper(){ return new Wiper.HiColor(this); } protected final Renderer 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) new ParallelRenderer(this,walls,floors,masked); // else // return (Renderer) 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{ public Indexed(){ super(); } protected IAutoMap selectAutoMap() { return new Map.Indexed(this); } protected final Finale selectFinale(){ return new Finale(this); } protected final DoomVideoRenderer selectVideoRenderer(){ return new BufferedRenderer(SCREENWIDTH,SCREENHEIGHT); } protected final DoomVideoInterface selectVideoInterface(){ return new AWTDoom.Indexed(this,V); } protected final Wiper selectWiper(){ return new Wiper.Indexed(this); } protected final Renderer 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{ public TrueColor(){ super(); } protected IAutoMap selectAutoMap() { return new Map.TrueColor(this); } protected final Finale selectFinale(){ return new Finale(this); } protected final DoomVideoRenderer selectVideoRenderer(){ return new BufferedRenderer32(SCREENWIDTH,SCREENHEIGHT); } protected final DoomVideoInterface selectVideoInterface(){ return new AWTDoom.TrueColor(this,V); } protected final Wiper selectWiper(){ return new Wiper.TrueColor(this); } protected final Renderer 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) new ParallelRenderer(this,walls,floors,masked); // else // return (Renderer) 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=0) return id; int i; // Let's assume that we know jack. for (i=0;i= 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>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 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 ; iretransmitfrom)[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 ; idatalength ; 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 ; idatalength ; 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= 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 ; inumnodes ; 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 ; inumnodes ; 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 ; inumnodes ; 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 ; iid != 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 ; inumplayers ; i++) playeringame[i] = true; for (i=0 ; inumnodes ; 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 ; jnumnodes ; 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 ; inumnodes ; 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 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 ; inumnodes ; 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 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 ; jchatchar = 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(); } /* (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 total 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 @ * 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 parsedargs=new ArrayList(); ArrayList moreargs=new ArrayList(); 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 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 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=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 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 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 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 ... 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 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 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 " Finished!" */ protected void drawLF() { int y = WI_TITLEY; // draw 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 " */ 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 2) return; int aaptr=wbs.epsd; for (i=0;i= 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= 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 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 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= (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= (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= (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= (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= (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 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 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 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 crap = new ArrayList(); 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) 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 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) DS.V; this.VI=(DoomVideoInterface) DS.VI; } IRandom RND; DoomVideoRenderer V; DoomVideoInterface 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 extends AbstractWiper { static final String rcsid = "$Id: Wiper.java,v 1.18 2012/09/24 17:16:23 velktron Exp $"; protected wipefun[] wipes; public Wiper(DoomMain DC){ this.updateStatus(DC); } public static final class HiColor extends Wiper{ public HiColor(DoomMain 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 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= 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{ public Indexed(DoomMain 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 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= 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{ public TrueColor(DoomMain 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 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= 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 0) y[i] = 0; else if (y[i] == -16) y[i] = -15; for (int j=1;j 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 //#include // // 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 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< 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=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 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 #include #include #include #include #include #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= ("
"+ "===========================================================================
"+ "ATTENTION: This version of DOOM has been modified. If you would like to
"+ "get a copy of the original game, call 1-800-IDGAMES or see the readme file.
"+ " You will not receive technical support for modified games.
"+ " press OK to continue
"+ "===========================================================================
"+ "
"); public static final String LEVEL_FAILURE_TITLE="Level loading failure"; public static final String LEVEL_FAILURE_CAUSE= ("
"+ "Level loading failed!
"+ "Press OK to end this game without exiting, or cancel to quit Doom."+ "
"); } 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 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 { 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 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 eventQueue = new LinkedList(); 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 extends IGetColumn, IGetCachedColumn,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 implements Renderer, ILimitResettable { protected static final boolean DEBUG = false; protected static final boolean DEBUG2 = false; // ///////////////////// STATUS //////////////////////// protected DoomMain DM; protected IDoomGameNetworking DGN; protected AbstractLevelLoader LL; protected ISegDrawer MySegs; protected IDoomMenu Menu; protected BSP MyBSP; protected PlaneDrawer MyPlanes; protected IMaskedDrawer MyThings; protected DoomVideoRenderer V; protected UnifiedGameMap P; public IWadLoader W; public ISpriteManager SM; public IVisSpriteManagement VIS; public IDoomSystem I; protected TextureManager TexMan; public ViewVars view; public LightsAndColors colormaps; public SegVars seg_vars; public Visplanes vp_vars; // Rendering subsystems that are detailshift-aware protected List 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 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(); this.dsvars=new SpanVars(); this.maskedcvars=new ColVars(); this.skydcvars=new ColVars(); this.colfunclow=new ColFuncs(); this.colfunchi=new ColFuncs(); this.detailaware = new ArrayList(); this.colormaps=new LightsAndColors(); // It's better to construct this here this.TexMan = (TextureManager) 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(this); this.MyThings = new SimpleThings(this); } @SuppressWarnings("unchecked") @Override public void updateStatus(DoomStatus DC) { this.DM = (DoomMain) 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) 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)< 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 { public Planes(RendererState 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 dcvars; /** Used for spans */ protected SpanVars dsvars; // Used for sky drawer, to avoid clashing with shared dcvars protected ColVars 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 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 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 colfunc; // Keep two sets of functions. protected ColFuncs colfunchi; protected ColFuncs 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 getColFuncsHi(){ return this.colfunchi; } public ColFuncs getColFuncsLow(){ return this.colfunclow; } public ColVars 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 DrawTranslatedColumn; protected DoomColumnFunction DrawTranslatedColumnLow; protected DoomColumnFunction DrawColumnPlayer; protected DoomColumnFunction DrawColumnSkies; protected DoomColumnFunction DrawColumnSkiesLow; protected DoomColumnFunction DrawFuzzColumn; protected DoomColumnFunction DrawFuzzColumnLow; protected DoomColumnFunction DrawColumn; protected DoomColumnFunction DrawColumnLow; protected DoomColumnFunction DrawColumnMasked; protected DoomColumnFunction DrawColumnMaskedLow; protected DoomColumnFunction DrawTLColumn; /** to be set in UnifiedRenderer */ protected DoomSpanFunction 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<= 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 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 getTextureManager() { return TexMan; } public PlaneDrawer getPlaneDrawer() { return this.MyPlanes; } public ViewVars getView() { return this.view; } public SpanVars getDSVars() { return this.dsvars; } public LightsAndColors 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 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()< ranges=new ArrayList(); // Scan column for continuous pixel ranges for (int i=0;i 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 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 */ public final class VisSprites implements IVisSpriteManagement { private final static boolean DEBUG = false; private final static boolean RANGECHECK = false; protected IDoomSystem I; protected ISpriteManager SM; protected ViewVars view; protected LightsAndColors colormaps; protected RendererState R; public VisSprites(RendererState R) { updateStatus(R); vissprite_t tmp = new vissprite_t(); vissprites = C2JUtils.createArrayOfObjects(tmp, MAXVISSPRITES); } public void updateStatus(RendererState R) { this.R = R; this.view = R.view; this.I = R.I; this.SM = R.SM; this.colormaps = R.colormaps; } protected vissprite_t[] 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 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 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[] getVisSprites() { return vissprites; } public void resetLimits() { vissprite_t[] 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 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 dsvars; /** The visplane data. Set separately. For threads, use the same for * everyone. */ protected Visplanes vpvars; protected final LightsAndColors colormap; protected final TextureManager TexMan; protected final IDoomSystem I; protected PlaneDrawer(Renderer 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 { 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(); } /** Hash table used for matching flat lump to flat num */ Hashtable FlatCache; Hashtable 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 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(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 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.width) x2 = texture.width; for ( ; 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.width) x2 = texture.width; for ( ; 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(); else FlatCache.clear(); Hashtable FlatNames=new Hashtable (); // 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 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 roguePatches= new HashMap (); class TextureDirectoryEntry implements Comparable{ /** 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 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 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> 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>(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()); } // 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 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 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 getTextureManager(); public PlaneDrawer getPlaneDrawer(); public ViewVars getView(); public SpanVars getDSVars(); public LightsAndColors 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 getVisSpriteManager(); public ColFuncs getColFuncsHi(); public ColFuncs getColFuncsLow(); public ColVars 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 */ public interface IVisSpriteManagement extends ILimitResettable { void AddSprites(sector_t sec); /** Cache the sprite manager, if possible */ void cacheSpriteManager(ISpriteManager SM); void SortVisSprites(); int getNumVisSprites(); vissprite_t[] getVisSprites(); void ClearSprites(); void updateStatus(RendererState 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 extends RendererState { public UnifiedRenderer(DoomStatus 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 { public HiColor(DoomStatus 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 { public Indexed(DoomStatus 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 { public TrueColor(DoomStatus 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 badColumns=new Hashtable(); 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 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 { public DoomColumnFunction main; public DoomColumnFunction base; public DoomColumnFunction masked; public DoomColumnFunction fuzz; public DoomColumnFunction trans; public DoomColumnFunction glass; public DoomColumnFunction player; public DoomColumnFunction sky; } package rr.drawfuns; import static m.fixed_t.FRACBITS; import i.IDoomSystem; public final class R_DrawTLColumn extends DoomColumnFunction { public R_DrawTLColumn(int SCREENWIDTH, int SCREENHEIGHT, int[] ylookup, int[] columnofs, ColVars 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 extends DoomColumnFunction { public R_DrawFuzzColumn(int SCREENWIDTH, int SCREENHEIGHT, int[] ylookup, int[] columnofs, ColVars 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 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{ public HiColor(int SCREENWIDTH, int SCREENHEIGHT, int[] ylookup, int[] columnofs, ColVars 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{ public Indexed(int SCREENWIDTH, int SCREENHEIGHT, int[] ylookup, int[] columnofs, ColVars 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{ public TrueColor(int SCREENWIDTH, int SCREENHEIGHT, int[] ylookup, int[] columnofs, ColVars 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 */ public abstract class DoomColumnFunction implements ColumnFunction{ protected final boolean RANGECHECK=false; protected final int SCREENWIDTH; protected final int SCREENHEIGHT; protected ColVars 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 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 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 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 { /* * 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 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 { public R_DrawSpanUnrolled2(int sCREENWIDTH, int sCREENHEIGHT, int[] ylookup, int[] columnofs, SpanVars 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 extends DoomColumnFunction { public R_DrawColumnBoomLow(int SCREENWIDTH, int SCREENHEIGHT, int[] ylookup, int[] columnofs, ColVars 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 { public HiColor(int SCREENWIDTH, int SCREENHEIGHT, int[] ylookup, int[] columnofs, ColVars 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 { public Indexed(int SCREENWIDTH, int SCREENHEIGHT, int[] ylookup, int[] columnofs, ColVars 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 { public TrueColor(int SCREENWIDTH, int SCREENHEIGHT, int[] ylookup, int[] columnofs, ColVars 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 { public R_DrawSpan(int sCREENWIDTH, int sCREENHEIGHT, int[] ylookup, int[] columnofs, SpanVars 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 { public R_DrawColumn(int SCREENWIDTH, int SCREENHEIGHT, int[] ylookup, int[] columnofs, ColVars 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 { public R_DrawColumnBoomSuperOpt(int SCREENWIDTH, int SCREENHEIGHT, int[] ylookup, int[] columnofs, ColVars 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 extends DoomSpanFunction { public R_DrawSpanUnrolled(int sCREENWIDTH, int sCREENHEIGHT, int[] ylookup, int[] columnofs, SpanVars 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{ public HiColor(int sCREENWIDTH, int sCREENHEIGHT, int[] ylookup, int[] columnofs, SpanVars 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{ public Indexed(int sCREENWIDTH, int sCREENHEIGHT, int[] ylookup, int[] columnofs, SpanVars 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{ public TrueColor(int sCREENWIDTH, int sCREENHEIGHT, int[] ylookup, int[] columnofs, SpanVars 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 extends DoomColumnFunction { public R_DrawFuzzColumnLow(int SCREENWIDTH, int SCREENHEIGHT, int[] ylookup, int[] columnofs, ColVars 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 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 { public HiColor(int SCREENWIDTH, int SCREENHEIGHT, int[] ylookup, int[] columnofs, ColVars 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 { public Indexed(int SCREENWIDTH, int SCREENHEIGHT, int[] ylookup, int[] columnofs, ColVars 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 { public TrueColor(int SCREENWIDTH, int SCREENHEIGHT, int[] ylookup, int[] columnofs, ColVars 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 { public void invoke(); public void invoke(ColVars 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 extends DoomColumnFunction { public R_DrawColumnBoomOpt(int sCREENWIDTH, int sCREENHEIGHT, int[] ylookup, int[] columnofs, ColVars dcvars, V screen, IDoomSystem I) { super(sCREENWIDTH, sCREENHEIGHT, ylookup, columnofs, dcvars, screen, I); } public static final class HiColor extends R_DrawColumnBoomOpt { public HiColor(int sCREENWIDTH, int sCREENHEIGHT, int[] ylookup, int[] columnofs, ColVars 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 { public Indexed(int sCREENWIDTH, int sCREENHEIGHT, int[] ylookup, int[] columnofs, ColVars 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 { public TrueColor(int sCREENWIDTH, int sCREENHEIGHT, int[] ylookup, int[] columnofs, ColVars 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 extends DoomColumnFunction { public R_DrawColumnBoomOptLow(int SCREENWIDTH, int SCREENHEIGHT, int[] ylookup, int[] columnofs, ColVars 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{ public HiColor(int SCREENWIDTH, int SCREENHEIGHT, int[] ylookup, int[] columnofs, ColVars 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{ public Indexed(int SCREENWIDTH, int SCREENHEIGHT, int[] ylookup, int[] columnofs, ColVars 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{ public TrueColor(int SCREENWIDTH, int SCREENHEIGHT, int[] ylookup, int[] columnofs, ColVars 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 extends DoomColumnFunction { public R_DrawTranslatedColumn(int SCREENWIDTH, int SCREENHEIGHT, int[] ylookup, int[] columnofs, ColVars 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 { public HiColor(int SCREENWIDTH, int SCREENHEIGHT, int[] ylookup, int[] columnofs, ColVars 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 { public Indexed(int SCREENWIDTH, int SCREENHEIGHT, int[] ylookup, int[] columnofs, ColVars 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 { public TrueColor(int SCREENWIDTH, int SCREENHEIGHT, int[] ylookup, int[] columnofs, ColVars 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 extends DoomColumnFunction { public R_DrawTranslatedColumnLow(int SCREENWIDTH, int SCREENHEIGHT, int[] ylookup, int[] columnofs, ColVars 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 { public HiColor(int SCREENWIDTH, int SCREENHEIGHT, int[] ylookup, int[] columnofs, ColVars 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 { public Indexed(int SCREENWIDTH, int SCREENHEIGHT, int[] ylookup, int[] columnofs, ColVars 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 { public TrueColor(int SCREENWIDTH, int SCREENHEIGHT, int[] ylookup, int[] columnofs, ColVars 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 implements SpanFunction { protected final boolean RANGECHECK=false; protected final int SCREENWIDTH; protected final int SCREENHEIGHT; protected SpanVars 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 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 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 extends DoomColumnFunction { public R_DrawColumnBoom(int SCREENWIDTH, int SCREENHEIGHT, int[] ylookup, int[] columnofs, ColVars dcvars, V screen, IDoomSystem I) { super(SCREENWIDTH, SCREENHEIGHT, ylookup, columnofs, dcvars, screen, I); } public static final class HiColor extends R_DrawColumnBoom { public HiColor(int SCREENWIDTH, int SCREENHEIGHT, int[] ylookup, int[] columnofs, ColVars 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 { public Indexed(int SCREENWIDTH, int SCREENHEIGHT, int[] ylookup, int[] columnofs, ColVars 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 { public TrueColor(int SCREENWIDTH, int SCREENHEIGHT, int[] ylookup, int[] columnofs, ColVars 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 extends DoomSpanFunction { public R_DrawSpanLow(int SCREENWIDTH, int SCREENHEIGHT, int[] ylookup, int[] columnofs, SpanVars dsvars, V screen, IDoomSystem I) { super(SCREENWIDTH, SCREENHEIGHT, ylookup, columnofs, dsvars, screen, I); } public static final class HiColor extends R_DrawSpanLow { public HiColor(int SCREENWIDTH, int SCREENHEIGHT, int[] ylookup, int[] columnofs, SpanVars 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 { public Indexed(int SCREENWIDTH, int SCREENHEIGHT, int[] ylookup, int[] columnofs, SpanVars 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 { public TrueColor(int SCREENWIDTH, int SCREENHEIGHT, int[] ylookup, int[] columnofs, SpanVars 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 { public R_DrawColumnLow(int SCREENWIDTH, int SCREENHEIGHT, int[] ylookup, int[] columnofs, ColVars 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 { 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 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 { /** 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 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 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 { public void invoke(); public void invoke(SpanVars 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 { 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 implements IMaskedDrawer { 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 maskedcvars; protected ColumnFunction colfunc; protected ColFuncs colfuncs; protected ColFuncs colfuncshi; protected ColFuncs colfuncslow; protected final LightsAndColors colormaps; protected final ViewVars view; protected final SegVars seg_vars; protected final TextureManager TexMan; protected final IDoomSystem I; protected final ISpriteManager SM; protected final BSPVars MyBSP; protected final IVisSpriteManagement VIS; protected final IWadLoader W; protected final vissprite_t avis; public AbstractThings(Renderer 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(); 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 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 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 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[] 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< 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].scaletemp.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 The data type of the SCREEN */ public class LightsAndColors { /** 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<>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 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 * @param */ public final class SimpleThings extends AbstractThings { public SimpleThings(Renderer 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 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 implements IMaskedDrawer { MaskedWorker[] maskedworkers; CyclicBarrier maskedbarrier; Executor tp; protected final IVisSpriteManagement VIS; public ParallelThings2(Renderer 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 { 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 implements Runnable,IDetailAware { protected CyclicBarrier barrier; protected ColVars[] RMI; protected int rmiend; protected boolean lowdetail=false; protected int start, end; protected DoomColumnFunction colfunchi, colfunclow; protected DoomColumnFunction fuzzfunchi, fuzzfunclow; protected DoomColumnFunction transfunchi, transfunclow; protected DoomColumnFunction colfunc; public RenderMaskedExecutor(int SCREENWIDTH, int SCREENHEIGHT, ColVars[] 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[] 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{ public HiColor(int SCREENWIDTH, int SCREENHEIGHT, int[] columnofs, int[] ylookup, short[] screen, ColVars[] 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{ public Indexed(int SCREENWIDTH, int SCREENHEIGHT, int[] columnofs, int[] ylookup, byte[] screen, ColVars[] 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{ public TrueColor(int SCREENWIDTH, int SCREENHEIGHT, int[] columnofs, int[] ylookup, int[] screen, ColVars[] 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 * @param */ public abstract class MaskedWorker extends AbstractThings 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 maskedcvars; public MaskedWorker(Renderer 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(); this.colfuncslow=new ColFuncs(); this.maskedcvars=new ColVars(); 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{ public HiColor(Renderer 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{ public Indexed(Renderer 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{ public TrueColor(Renderer 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 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> 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 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[] 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 extends AbstractParallelRenderer { public ParallelRenderer(DoomMain 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(this); //this.MyPlanes = new Planes(this);// new ParallelPlanes(DM.R); } /** * Default constructor, 1 seg, 1 span and two masked threads. * * @param DM */ public ParallelRenderer(DoomMain 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 { public Indexed(DoomMain 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[] InitRWIExecutors( int num,ColVars[] RWI) { RenderWallExecutor[] tmp= new RenderWallExecutor.Indexed[num]; for (int i=0;i[] RWI=RWIs.getRWI(); RenderWallExecutor[] 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 fake = new ColVars(); 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 { public HiColor(DoomMain 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[] InitRWIExecutors( int num,ColVars[] RWI) { RenderWallExecutor[] tmp= new RenderWallExecutor.HiColor[num]; for (int i=0;i { public TrueColor(DoomMain 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[] InitRWIExecutors( int num,ColVars[] RWI) { RenderWallExecutor[] tmp= new RenderWallExecutor.TrueColor[num]; for (int i=0;i0.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 extends RendererState implements RWI.Init{ public AbstractParallelRenderer(DoomStatus 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 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[] 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{ 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[] RWIExec; /** Array of "wall" (actually, column) instructions */ protected ColVars[] 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 fake = new ColVars(); // 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 fake=new ColVars(); RWI=C2JUtils.createArrayOfObjects(fake, 3*SCREENWIDTH); } @Override public ColVars[] getRWI() { return RWI; } @Override public void setExecutors(RenderWallExecutor[] 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 extends PlaneDrawer { protected ParallelPlanes(Renderer 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[] 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[] 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 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 fake = new RenderSegInstruction(); // 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 extends PlaneDrawer { protected ParallelPlanes2(Renderer 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[] InitRWIExecutors(int num,ColVars[] RWI){ return null; } RWI.Get 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 implements Runnable,IDetailAware { protected CyclicBarrier barrier; protected ColVars[] RWI; protected int start, end; protected DoomColumnFunction colfunchi, colfunclow; protected DoomColumnFunction colfunc; public RenderWallExecutor(int SCREENWIDTH, int SCREENHEIGHT, int[] columnofs, int[] ylookup, V screen, ColVars[] 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[] 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 { public HiColor(int SCREENWIDTH, int SCREENHEIGHT, int[] columnofs, int[] ylookup, short[] screen, ColVars[] 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 { public Indexed(int SCREENWIDTH, int SCREENHEIGHT, int[] columnofs, int[] ylookup, byte[] screen, ColVars[] 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 { public TrueColor(int SCREENWIDTH, int SCREENHEIGHT, int[] columnofs, int[] ylookup, int[] screen, ColVars[] 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 extends AbstractThings { // stuff to get from container /** Render Masked Instuction subsystem. Essentially, a way to split sprite work * between threads on a column-basis. */ protected ColVars[] 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[] RMIExec; protected final int NUMMASKEDTHREADS; protected final CyclicBarrier maskedbarrier; protected final Executor tp; public ParallelThings(Renderer 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 fake = new ColVars(); ColVars[] 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 detailaware); public static class Indexed extends ParallelThings{ public Indexed(Renderer R, Executor tp, int numthreads) { super(R, tp, numthreads); } protected void InitRMISubsystem(int[] columnofs, int[] ylookup,byte[] screen, CyclicBarrier maskedbarrier,byte[] BLURRY_MAP, List 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{ public HiColor(Renderer R, Executor tp, int numthreads) { super(R, tp, numthreads); } protected void InitRMISubsystem(int[] columnofs, int[] ylookup,short[] screen, CyclicBarrier maskedbarrier,short[] BLURRY_MAP, List 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{ public TrueColor(Renderer R, Executor tp, int numthreads) { super(R, tp, numthreads); } protected void InitRMISubsystem(int[] columnofs, int[] ylookup,int[] screen, CyclicBarrier maskedbarrier,int[] BLURRY_MAP, List 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 { public interface Init{ RenderWallExecutor[] InitRWIExecutors(int num,ColVars[] RWI); } public interface Get{ ColVars[] getRWI(); void setExecutors(RenderWallExecutor[] 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 extends PlaneDrawer 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 vpw_dsvars; protected ColVars vpw_dcvars; // OBVIOUSLY each thread must have its own span functions. protected DoomSpanFunction vpw_spanfunc; protected DoomColumnFunction vpw_skyfunc; protected DoomSpanFunction vpw_spanfunchi; protected DoomSpanFunction vpw_spanfunclow; protected DoomColumnFunction vpw_skyfunchi; protected DoomColumnFunction vpw_skyfunclow; public VisplaneWorker(int id,int SCREENWIDTH, int SCREENHEIGHT, Renderer R,CyclicBarrier visplanebarrier,int NUMFLOORTHREADS) { super(R); this.barrier=visplanebarrier; this.id=id; this.NUMFLOORTHREADS=NUMFLOORTHREADS; } public static final class HiColor extends VisplaneWorker{ public HiColor(int id, int SCREENWIDTH, int SCREENHEIGHT,Renderer 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(); vpw_dcvars=new ColVars(); 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 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 extends PlaneDrawer 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 vpw_dsvars; protected final ColVars vpw_dcvars; protected DoomSpanFunction vpw_spanfunc; protected DoomColumnFunction vpw_skyfunc; protected DoomSpanFunction vpw_spanfunchi; protected DoomSpanFunction vpw_spanfunclow; protected DoomColumnFunction vpw_skyfunchi; protected DoomColumnFunction vpw_skyfunclow; protected visplane_t pln; public VisplaneWorker2(Renderer 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(); vpw_dcvars=new ColVars(); this.NUMFLOORTHREADS=NUMFLOORTHREADS; } public static class HiColor extends VisplaneWorker2{ public HiColor(Renderer 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{ public Indexed(Renderer 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{ public TrueColor(Renderer 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 endvp) || (pln.maxx 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; } 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 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 TexMan; protected final CyclicBarrier barrier; protected RenderSegInstruction[] 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< colfunchi,colfunclow; protected DoomColumnFunction colfunc; protected ColVars dcvars; public RenderSegExecutor(int SCREENWIDTH, int SCREENHEIGHT,int id,V screen, TextureManager texman, RenderSegInstruction[] 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 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 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=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 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[] rsi) { this.RSI=rsi; } public static final class HiColor extends RenderSegExecutor{ public HiColor(int SCREENWIDTH, int SCREENHEIGHT, int id, short[] screen, TextureManager texman, RenderSegInstruction[] 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(); 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{ public Indexed(int SCREENWIDTH, int SCREENHEIGHT, int id, byte[] screen, IGetColumn gc, TextureManager texman, RenderSegInstruction[] 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(); 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 implements Comparable>{ // Doubly linked list. public vissprite_t prev; public vissprite_t 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 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 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 planehash = new Hashtable( 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 eventQueue = new LinkedList(); 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 cancelMoves = new ArrayList(); 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 extends DoomFrame { /** * */ 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 DM, DoomVideoRenderer 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{ /** * */ private static final long serialVersionUID = 1L; public HiColor(DoomMain DM, DoomVideoRenderer 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{ /** * */ private static final long serialVersionUID = 1L; public Indexed(DoomMain DM, DoomVideoRenderer 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{ /** * */ private static final long serialVersionUID = 1L; public TrueColor(DoomMain DM, DoomVideoRenderer 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 eventQueue = new LinkedList(); //// 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 picks=new ArrayList(); 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{ @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{ @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 extends DoomFrame { 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 DM, DoomVideoRenderer 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{ /** * */ private static final long serialVersionUID = 1L; public HiColor(DoomMain DM, DoomVideoRenderer 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{ /** * */ private static final long serialVersionUID = 1L; public Indexed(DoomMain DM, DoomVideoRenderer 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{ /** * */ private static final long serialVersionUID = 1L; public TrueColor(DoomMain DM, DoomVideoRenderer 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 eventQueue = new LinkedList(); private HashMap lockingKeyStates = new HashMap(); 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 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> it = lockingKeyStates.entrySet().iterator(); it.hasNext(); ) { Map.Entry 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 extends JFrame implements DoomVideoInterface { 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 DM,DoomVideoRenderer 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 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; // 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 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;i32) && (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= '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 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= 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 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 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 getAllEntries(ZipInputStream zis) throws IOException { ArrayList zes = new ArrayList(); 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 */ public abstract T CacheLumpNum(int lump, int tag, Class 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 CacheLumpName(String name, int tag, Class 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[] CacheLumpNumIntoArray(int lump, int num, Class 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 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(); 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 c=s.getClass().getComponentType(); for (int i=0;i c) throws Exception { if ((s==null)||(len==0)) return; for (int i=0;i s,int len) throws IOException { if ((s==null)||(len<=0)) return; for (int i=0;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 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(); wadfiles=new ArrayList(); 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 (readmaxsize){ 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 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= 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 T CacheLumpNum(int lump, int tag, Class 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[] CacheLumpNumIntoArray(int lump, int num, Class 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(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)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 CacheLumpName(String name, int tag, Class 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 doomhash; protected void InitLumpHash() { doomhash = new Hashtable(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 list=new ArrayList(); @Override public lumpinfo_t GetLumpInfo(int i) { return this.lumpinfo[i]; } @Override public void CloseAllHandles(){ ArrayList d=new ArrayList(); for (int i=0;i 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 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. 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 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>>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 { E e; E next = null; public Elem(E e) { this.e = e; } } Elem head = new Elem(null);*/ //ArrayList al = new ArrayList(); LinkedList al = new LinkedList(); 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= 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< { E e; E next = null; public Elem(E e) { this.e = e; } } Elem head = new Elem(null);*/ //ArrayList al = new ArrayList(); LinkedList al = new LinkedList(); 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(), 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(), contained); ArrayList 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 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 path = new LinkedList_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 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(); // 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 q = D4.bgame.QueuePathTraverse(p1, p2); assrt(q.size()==1 && q.get(0).line == lineLava); LinkedList q2 = D4.bgame.CorrectedQueuePathTraverse(p1, p2); assrt(q2.size()==1 && q2.get(0).line == lineLava); LinkedList q3 = D4.bgame.QueuePathTraverse(p1, p3); assrt(q3.size()==1 && q3.get(0).line == lineLava); // on one side we will see the line LinkedList q4 = D4.bgame.CorrectedQueuePathTraverse(p1, p3); assrt(q4.size()==1 && q4.get(0).line == lineLava); LinkedList q5 = D4.bgame.QueuePathTraverse(p3, p1); assrt(q5.size()==1 && q5.get(0).line == lineLava); LinkedList q6 = D4.bgame.CorrectedQueuePathTraverse(p3, p1); assrt(q6.size()==0); // we want to ignore a line that srcPoint is on LinkedList q7 = D4.bgame.QueuePathTraverse(p2, p1); assrt(q7.size()==1 && q7.get(0).line == lineLava); LinkedList q8 = D4.bgame.CorrectedQueuePathTraverse(p2, p1); assrt(q8.size()==1 && q8.get(0).line == lineLava); LinkedList 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 q10 = D4.bgame.CorrectedQueuePathTraverse(p3, p2); assrt(q10.size()==0); LinkedList 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 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 bs1 = D4.bgame.TraversedSecLines(p1, p2); assrt(bs1.size() == 1); LinkedList 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 bs3 = D4.bgame.TraversedSecLines(p1, p3); assrt(bs3.size() == 1); LinkedList 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 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>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< 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 eventQueue = new LinkedList(); } 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) 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=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;ibam1 * */ 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>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>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)< 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= 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 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) 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 map1= new HashMap (); // And a NEW and ENHANCED implementation. static RoguePatchMap map2=new RoguePatchMap(); public static void main(String[] argv){ for (int i=0;i= 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 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;i0)?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>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< 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 implements IAutoMap { // ///////////////// Status objects /////////////////// IDoomStatusBar ST; IWadLoader W; DoomMain DM; DoomVideoRenderer 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 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) DC.V; this.W = DC.W; this.LL = DC.LL; this.DM = (DoomMain) 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 { public HiColor(DoomStatus 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 { public Indexed(DoomStatus 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 { public TrueColor(DoomStatus 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 { 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 { 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 { public RoguePatchMap(){ super(); patches = new byte[DEFAULT_CAPACITY][][]; } } package pooling; import java.util.Arrays; public abstract class GenericIntMap { 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= 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 { private static final boolean D=false; public ObjectQueuePool(long expirationTime) { locked = new Stack(); } 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 locked; // private Hashtable 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 { private static final boolean D=false; public ObjectPool(long expirationTime) { this.expirationTime = expirationTime; locked = new Hashtable(); unlocked = new Hashtable(); } 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 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 locked; private Hashtable 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 demorecorder; public VanillaDoomDemo(){ this.demorecorder=new ArrayList (); } 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 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{ public static final int FRACBITS = 16; public static final int FRACUNIT = (1<>> 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;ipalette,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 settings; private final ICommandLineManager CLM; public VarsManager(ICommandLineManager CLM){ this.settings=new HashMap(); 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 ; i2){ // 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> stuff=settings.entrySet(); List save=new ArrayList(); for (Entry 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 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 { 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= 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= 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 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 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 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>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;i1) 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(); FlatHash=new HashMap(); int k=0; for (int lump=suck_markers[0];lump",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;y0 && 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 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 implements DoomVideoRenderer, 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;i0 && 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 { // 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;xx0 && 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 colcache=new HashMap(); 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 cmapcache=new HashMap(); /** 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 { // 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;xx0 && 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 colcache=new HashMap(); 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 cmapcache=new HashMap(); /** 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 { 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 ; colcolumnofs[col])); column=patch.columns[col]; // For each post... for (int i=0;icolumnofs[col])); column=patch.columns[col]; // For each post... for (int i=0;icolumnofs[col])); column=patch.columns[col]; // For each post... for (int i=0;icolumnofs[col])); column=patch.columns[col]; // For each post... for (int i=0;icolumnofs[col])); column=patch.columns[col]; // For each post... for (int i=0;icolumnofs[w-1-col])); column=patch.columns[w-1-col]; // For each post... //System.out.println("Column"+(w-1-col)); for (int i=0;i= 0 && col> FRACBITS])); column=patch.columns[col/colfrac]; int destPos; int ptr = 0; int ptrOfs; //while (column.topdelta != 0xff) for (int i=0;i 0) for (int j=0;j0) { // 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;i0 && 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 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 { 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;icolumnofs[col])); column=patch.columns[col]; // For each post... for (int i=0;i 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 sectors=new ArrayList(); // do sectors for (i=0; i TL=new ArrayList(); // //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 pointindex=new Hashtable (); /** 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);i0;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 (i0){ 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 A=new ArrayList(); 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 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>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 cachedSounds = new HashMap(); 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 audiochunks = new ArrayBlockingQueue(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 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(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= 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 using from sounds.h */ public void StartSound(ISoundOrigin origin, int sound_id); /** * Start sound for thing at using 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 */ public void StopSound(ISoundOrigin origin); /** * Start music using 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 from sounds.h */ public void StartMusic(int music_id); /** Start music using 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: *
  • Supports MUS lumps only (no MID, OGG etc.)
  • *
  • Creates its own thread
  • *
  • Pausing is not implemented yet
* * @author finnw * */ public class FinnwMusicModule implements IMusic { public FinnwMusicModule() { this.lock = new ReentrantLock(); this.channels = new ArrayList(15); this.songs = new ArrayList(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(); 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 messages; private final float volScale; } /** A collection of kludges to pick a MIDI output device until cvars are implemented */ static class MidiDeviceComparator implements Comparator { @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 channels; ScheduledExecutorService exec; float volume; private static Receiver getReceiver() throws MidiUnavailableException { List dInfos = new ArrayList(Arrays.asList(MidiSystem.getMidiDeviceInfo())); for (Iterator 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 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 VolumeScalingReceiver connected to a semi- * intelligently-chosen synthesizer. * */ public static VolumeScalingReceiver getInstance() { try { List dInfos = new ArrayList(Arrays.asList(MidiSystem.getMidiDeviceInfo())); for (Iterator 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 { @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 message 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(); } 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 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 phonemes=new Hashtable(); 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 cachedSounds=new ArrayList(); 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> 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;i0) 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 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;iMEMORYCACHE)? 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 cachedSounds = new HashMap(); // 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>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 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=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 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 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; i1) { for (i=0; i -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 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>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= 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 { 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 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 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 ; iMID 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 cachedSounds = new HashMap(); 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 audiochunks = new ArrayBlockingQueue(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 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; // 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; // 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<= 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)< 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)<>= 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)<=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= 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 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= 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]< 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= 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]<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>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 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{ @Override public void invoke(fireflicker_t a) { a.FireFlicker(); } } class T_LightFlash implements ActionTypeSS{ @Override public void invoke(lightflash_t a) { a.LightFlash(); } } class T_StrobeFlash implements ActionTypeSS{ @Override public void invoke(strobe_t a) { a.StrobeFlash(); } } class T_Glow implements ActionTypeSS{ @Override public void invoke(glow_t a) { a.Glow(); } } class T_MoveCeiling implements ActionTypeSS{ @Override public void invoke(ceiling_t a) { A.MoveCeiling(a); } } class T_MoveFloor implements ActionTypeSS{ @Override public void invoke(floormove_t a) { A.MoveFloor(a); } } class T_VerticalDoor implements ActionTypeSS{ @Override public void invoke(vldoor_t a) { A.VerticalDoor(a); } } class T_SlidingDoor implements ActionTypeSS { @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{ @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 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[] malloc_IfSameLevel(T[] p, int numstuff, Class 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[] calloc_IfSameLevel(T[] p, int numstuff, Class 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= 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 */ public interface ActionTypeSS{ 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 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 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 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 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=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 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 { @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 } };